diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000000..9eb0caba874d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,99 @@ +name: Bug Report +description: Something is not working as expected +labels: ["type=defect"] +body: + - type: markdown + attributes: + value: > + Thank you for filing a bug report. Please help us identify and resolve the bug by filling + out the following fields. Before we begin, please make sure that the bug is still present in + the [latest](https://github.com/google/guava/releases/latest) version of Guava available. + If it's already fixed in the latest version of Guava, then please just update your Guava + version instead. + + - type: input + attributes: + label: Guava Version + description: Which version of Guava are you using? + placeholder: e.g., 33.2.1-jre + validations: + required: true + + - type: textarea + attributes: + label: Description + description: Please describe the issue you encountered. + validations: + required: true + + - type: textarea + attributes: + label: Example + description: > + Please provide a [Short, Self Contained, Correct (Compilable), Example](http://sscce.org/) + demonstrating the bug. + render: java + validations: + required: true + + - type: textarea + attributes: + label: Expected Behavior + description: What did you expect to happen? + validations: + required: true + + - type: textarea + attributes: + label: Actual Behavior + description: What actually happened? + validations: + required: true + + - type: dropdown + attributes: + label: Packages + description: If this issue is package-specific, then please select the relevant packages. + multiple: true + options: + - com.google.common.annotations + - com.google.common.base + - com.google.common.cache + - com.google.common.collect + - com.google.common.escape + - com.google.common.eventbus + - com.google.common.graph + - com.google.common.hash + - com.google.common.io + - com.google.common.math + - com.google.common.net + - com.google.common.primitives + - com.google.common.reflect + - com.google.common.testing + - com.google.common.util.concurrent + + - type: dropdown + attributes: + label: Platforms + description: If this issue is platform-specific, then please select the relevant platforms. + multiple: true + options: + - Android + - GWT + - Java 8 + - Java 11 + - Java 17 + - Java 21 + + - type: checkboxes + attributes: + label: Checklist + options: + - label: > + I agree to follow the + [code of conduct](https://github.com/google/.github/blob/master/CODE_OF_CONDUCT.md). + required: true + - label: > + I can reproduce the bug with the + [latest](https://github.com/google/guava/releases/latest) version of Guava available. + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_addition_request.yaml b/.github/ISSUE_TEMPLATE/feature_addition_request.yaml new file mode 100644 index 000000000000..d86c4ca535dc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_addition_request.yaml @@ -0,0 +1,156 @@ +name: Feature Addition Request +description: I want to add a new feature +labels: ["type=addition"] +body: + - type: markdown + attributes: + value: > + Filing feature requests is one of the most popular ways to contribute to Guava. + + + Be aware, though: most feature requests are not accepted, even if they're suggested by + a full-time Guava team member. [Feedback](https://stackoverflow.com/a/4543114) from our + users indicates that they really appreciate Guava's high power-to-weight ratio. It's + important to us to keep Guava as easy to use and understand as we can. That means boiling + features down to compact but powerful abstractions, and controlling feature bloat carefully. + + + Guava's main yardstick for evaluating proposed features can be summed up as [utility times + ubiquity](https://github.com/google/guava/wiki/PhilosophyExplained#utility-times-ubiquity). + + + #### Utility: compare with alternatives + + + There is always *some* alternative to adding a new feature to Guava, even if it's just + forking Guava yourself. + + + We want to see that new features have some significant advantage over the alternatives. + These advantages can take + [many forms](https://github.com/google/guava/wiki/PhilosophyExplained#utility), but taking + the time to discuss them in detail will make it much clearer why this feature should be + added to Guava. + + + Please fill out the following fields to give us a better understanding of your proposed + feature and its potential value for other Guava users. + + - type: textarea + attributes: + label: 1. What are you trying to do? + validations: + required: true + + - type: textarea + attributes: + label: 2. What's the best code you can write to accomplish that without the new feature? + validations: + required: true + + - type: textarea + attributes: + label: 3. What would that same code look like if we added your feature? + validations: + required: true + + - type: markdown + attributes: + value: > + Comparing two approaches to a use case side by side can make it easier to examine the + differences between them. + + + Additionally, it's very useful to us if you can provide a "straw API" — what the + method signatures would look like, for example, even if the method and class names are still + in flux. This can make the feature you're suggesting much clearer to us. + + - type: textarea + attributes: + label: (Optional) What would the method signatures for your feature look like? + placeholder: | + e.g., + public static ImmutableList of(); + public static ImmutableList of(E element); + public static ImmutableList of(E e1, E e2); + ... + render: java + validations: + required: false + + - type: markdown + attributes: + value: > + #### Ubiquity: provide concrete use cases + + + Did you *actually* encounter the need for this feature in a real-world scenario, or is it + just a feature that seems like a sensible addition to Guava? + + + Before new features get added to Guava, we really want to be sure that it's for a use case + that actually comes up in the real world. We want to hear the real-world use case so the + community can discuss and debate whether this feature is actually the *best* way to address + the real use case, or whether or not a different abstraction might be more appropriate. + + + It's okay if you can't provide complete context on a use case. We understand if you are not + able to discuss the full details of what you're working on. + + + But Guava aims to provide features that are useful across boundaries of projects, companies, + or even industries — utilities useful for a sizable proportion of all Java programmers + everywhere. If you can give enough detail such that any of us can imagine coming across + a similar need in our own work, that's extremely helpful in studying how broadly useful the + feature will be. + + - type: textarea + attributes: + label: Concrete Use Cases + description: Please provide use cases that actually came up in the real world. + validations: + required: true + + - type: dropdown + attributes: + label: Packages + description: Please select all of the packages that are relevant to this feature request. + multiple: true + options: + - com.google.common.annotations + - com.google.common.base + - com.google.common.cache + - com.google.common.collect + - com.google.common.escape + - com.google.common.eventbus + - com.google.common.graph + - com.google.common.hash + - com.google.common.io + - com.google.common.math + - com.google.common.net + - com.google.common.primitives + - com.google.common.reflect + - com.google.common.testing + - com.google.common.util.concurrent + + - type: checkboxes + attributes: + label: Checklist + options: + - label: > + I agree to follow the + [code of conduct](https://github.com/google/.github/blob/master/CODE_OF_CONDUCT.md). + required: true + - label: > + I have read and understood the [contribution + guidelines](https://github.com/google/guava/wiki/HowToContribute#feature-requests). + required: true + - label: > + I have read and understood + [Guava's philosophy](https://github.com/google/guava/wiki/PhilosophyExplained), and + I strongly believe that this proposal aligns with it. + required: true + - label: > + I have visited the [idea graveyard](https://github.com/google/guava/wiki/IdeaGraveyard), + and did not see anything similar to this idea. + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_enhancement_request.yaml b/.github/ISSUE_TEMPLATE/feature_enhancement_request.yaml new file mode 100644 index 000000000000..93be4411227a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_enhancement_request.yaml @@ -0,0 +1,108 @@ +name: Feature Enhancement Request +description: I want to make an existing feature better +labels: ["type=enhancement"] +body: + - type: markdown + attributes: + value: > + Filing feature requests is one of the most popular ways to contribute to Guava. + + + Be aware, though: most feature requests are not accepted, even if they're suggested by + a full-time Guava team member. [Feedback](https://stackoverflow.com/a/4543114) from our + users indicates that they really appreciate Guava's high power-to-weight ratio. It's + important to us to keep Guava as easy to use and understand as we can. That means boiling + features down to compact but powerful abstractions, and controlling feature bloat carefully. + + - type: textarea + attributes: + label: API(s) + description: Which existing classes or methods do you want to improve? + placeholder: e.g., `com.google.common.collect.ImmutableList::of` + render: java + validations: + required: true + + - type: textarea + attributes: + label: How do you want it to be improved? + validations: + required: true + + - type: textarea + attributes: + label: Why do we need it to be improved? + validations: + required: true + + - type: textarea + attributes: + label: Example + description: > + Please provide an example usage of the feature that would be different with the improvement. + render: java + validations: + required: true + + - type: textarea + attributes: + label: Current Behavior + description: What does the feature currently do? + validations: + required: true + + - type: textarea + attributes: + label: Desired Behavior + description: What do you want it to do instead? + validations: + required: true + + - type: markdown + attributes: + value: > + Did you *actually* encounter the need for this enhancement in a real-world scenario, or does + it just seem like a sensible behavior for the feature to have? + + + Before we make significant changes to existing features in Guava, we really want to be sure + that it's for a use case that actually comes up in the real world. We want to hear the + real-world use case so the community can discuss and debate whether this feature is actually + the *best* way to address the real use case, or whether or not a different approach might be + more appropriate. + + + It's okay if you can't provide complete context on a use case. We understand if you are not + able to discuss the full details of what you're working on. + + + But Guava aims to provide functionality that is useful across boundaries of projects, + companies, or even industries — utilities useful for a sizable proportion of all Java + programmers everywhere. If you can give enough detail such that any of us can imagine coming + across a similar need in our own work, that's extremely helpful in studying how broadly + useful the proposed change will be. + + - type: textarea + attributes: + label: Concrete Use Cases + description: Please provide use cases that actually came up in the real world. + validations: + required: true + + - type: checkboxes + attributes: + label: Checklist + options: + - label: > + I agree to follow the + [code of conduct](https://github.com/google/.github/blob/master/CODE_OF_CONDUCT.md). + required: true + - label: > + I have read and understood the [contribution + guidelines](https://github.com/google/guava/wiki/HowToContribute#feature-requests). + required: true + - label: > + I have read and understood + [Guava's philosophy](https://github.com/google/guava/wiki/PhilosophyExplained), and + I strongly believe that this proposal aligns with it. + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1e956d2faa00..ec26a1ac1152 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,12 +5,27 @@ updates: # - package-ecosystem: "maven" # directory: "/" # schedule: -# interval: "daily" +# interval: "weekly" +# groups: +# dependencies: +# applies-to: version-updates +# patterns: +# - "*" # - package-ecosystem: "maven" # directory: "/android" # schedule: -# interval: "daily" +# interval: "weekly" +# groups: +# dependencies: +# applies-to: version-updates +# patterns: +# - "*" - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" + groups: + github-actions: + applies-to: version-updates + patterns: + - "*" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..a41327e07ba3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ce82664fa61..26b64c473409 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,47 +8,62 @@ on: branches: - master +permissions: + contents: read + jobs: test: - name: "${{ matrix.root-pom }} on JDK ${{ matrix.java }}" + permissions: + actions: write # for styfle/cancel-workflow-action to cancel/stop running workflows + contents: read # for actions/checkout to fetch code + name: "${{ matrix.root-pom }} on JDK ${{ matrix.java }} on ${{ matrix.os }}" strategy: matrix: - java: [ 8, 11 ] + os: [ ubuntu-latest ] + java: [ 8, 11, 17, 21 ] root-pom: [ 'pom.xml', 'android/pom.xml' ] - runs-on: ubuntu-latest + include: + - os: windows-latest + java: 21 + root-pom: pom.xml + runs-on: ${{ matrix.os }} env: ROOT_POM: ${{ matrix.root-pom }} steps: # Cancel any previous runs for the same branch that are still running. - name: 'Cancel previous runs' - uses: styfle/cancel-workflow-action@0.9.1 + uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # 0.12.1 with: access_token: ${{ github.token }} - name: 'Check out repository' - uses: actions/checkout@v2.4.0 - - name: 'Cache local Maven repository' - uses: actions/cache@v2.1.6 - with: - path: ~/.m2/repository - key: maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - maven- - - name: 'Set up JDK ${{ matrix.java }}' - uses: actions/setup-java@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + # When we specify multiple JDKs, the final one becomes the default, which is used to execute Maven itself. + # Our Maven configuration then specifies different JDKs to use for some of the steps: + # - 11 (sometimes) to *download* to support anyone who runs JDiff or our Gradle integration tests (including our doc snapshots and our Java 11 CI test run) but not to use directly + # - 23 for running Javadoc and javac (to help people who build Guava locally and might not use a recent JDK to run Maven) + - name: 'Set up JDKs' + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: - java-version: ${{ matrix.java }} - distribution: 'zulu' + java-version: | + ${{ matrix.java }} + 23 + distribution: 'temurin' + cache: 'maven' - name: 'Install' shell: bash - run: mvn -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn install -U -DskipTests=true -f $ROOT_POM + run: ./mvnw -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dtoolchain.skip install -U -DskipTests=true -f $ROOT_POM - name: 'Test' shell: bash - run: mvn -B -P!standard-with-extra-repos verify -U -Dmaven.javadoc.skip=true -f $ROOT_POM + run: ./mvnw -B -P!standard-with-extra-repos -Dtoolchain.skip verify -U -Dmaven.javadoc.skip=true -Dsurefire.toolchain.version=${{ matrix.java }} -f $ROOT_POM - name: 'Print Surefire reports' # Note: Normally a step won't run if the job has failed, but this causes it to if: ${{ failure() }} shell: bash run: ./util/print_surefire_reports.sh + - name: 'Integration Test' + if: matrix.java == 11 + shell: bash + run: util/gradle_integration_tests.sh publish_snapshot: name: 'Publish snapshot' @@ -57,22 +72,18 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Check out repository' - uses: actions/checkout@v2.4.0 - - name: 'Cache local Maven repository' - uses: actions/cache@v2.1.6 - with: - path: ~/.m2/repository - key: maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - maven- - - name: 'Set up JDK 11' - uses: actions/setup-java@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Set up JDKs' + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: - java-version: 11 - distribution: 'zulu' + # For discussion, see the first setup-java block. + # The publish-snapshot workflow doesn't run tests, so we don't have to care which version Maven would select for that step. + java-version: 23 + distribution: 'temurin' server-id: sonatype-nexus-snapshots server-username: CI_DEPLOY_USERNAME server-password: CI_DEPLOY_PASSWORD + cache: 'maven' - name: 'Publish' env: CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} @@ -80,25 +91,26 @@ jobs: run: ./util/deploy_snapshot.sh generate_docs: + permissions: + contents: write name: 'Generate latest docs' needs: test if: github.event_name == 'push' && github.repository == 'google/guava' runs-on: ubuntu-latest steps: - name: 'Check out repository' - uses: actions/checkout@v2.4.0 - - name: 'Cache local Maven repository' - uses: actions/cache@v2.1.6 - with: - path: ~/.m2/repository - key: maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - maven- - - name: 'Set up JDK 11' - uses: actions/setup-java@v2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Set up JDKs' + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0 with: - java-version: 11 - distribution: 'zulu' + # For discussion, see the first setup-java block. + # The generate-docs workflow doesn't run tests, so we don't have to care which version Maven would select for that step. + # But we need Java 11 for JDiff. + java-version: | + 11 + 23 + distribution: 'temurin' + cache: 'maven' - name: 'Generate latest docs' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000000..44e5c1a69295 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,13 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +permissions: + contents: read + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: gradle/wrapper-validation-action@f9c9c575b8b21b6485636a91ffecd10e558c62f6 # v3.5.0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000000..9dbd6ef2cf9f --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '45 9 * * 0' + push: + branches: [ "master" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 942c3986a9b7..c6c875cd186a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ target/ *.ser *.ec +.mvn/wrapper/maven-wrapper.jar # IntelliJ Idea .idea/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000000..2c1966721354 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar +distributionSha256Sum=4ec3f26fb1a692473aea0235c300bd20f0f9fe741947c82c1234cefd76ac3a3c diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e54c6f9aada..1c1bd8fba349 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,10 @@ How to contribute Thank you so much for wanting to contribute to Guava! Here are a few important things you should know about contributing: - 1. API changes require discussion, use cases, etc. Code comes later. - 2. Pull requests are great for small fixes for bugs, documentation, etc. - 3. Pull requests are not merged directly into the master branch. - 3. Code contributions require signing a Google CLA. +1. API changes require discussion, use cases, etc. Code comes later. +2. Pull requests are great for small fixes for bugs, documentation, etc. +3. Pull requests are not merged directly into the master branch. +4. Code contributions require signing a Google CLA. API changes ----------- @@ -21,7 +21,7 @@ for it. If the feature has merit, it will go through a thorough process of API design and review. Any code should come after this. -[APIs]: http://en.wikipedia.org/wiki/Application_programming_interface +[APIs]: https://en.wikipedia.org/wiki/Application_programming_interface [issue]: https://github.com/google/guava/issues Pull requests @@ -53,7 +53,7 @@ Guidelines for any code contributions: [well-formed commit message][] for the change. [Java style guide]: https://google.github.io/styleguide/javaguide.html -[well-formed commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[well-formed commit message]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html #### Merging pull requests #### diff --git a/COPYING b/LICENSE similarity index 100% rename from COPYING rename to LICENSE diff --git a/README.md b/README.md index 5a7f1d8e89bb..aa69d5b724db 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,25 @@ # Guava: Google Core Libraries for Java -[![Latest release](https://img.shields.io/github/release/google/guava.svg)](https://github.com/google/guava/releases/latest) -[![Build Status](https://github.com/google/guava/workflows/CI/badge.svg?branch=master)](https://github.com/google/guava/actions) +[![GitHub Release](https://img.shields.io/github/v/release/google/guava)](https://github.com/google/guava/releases/latest) +[![CI](https://github.com/google/guava/actions/workflows/ci.yml/badge.svg)](https://github.com/google/guava/actions/workflows/ci.yml) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7197/badge)](https://www.bestpractices.dev/projects/7197) -Guava is a set of core Java libraries from Google that includes new collection types -(such as multimap and multiset), immutable collections, a graph library, and -utilities for concurrency, I/O, hashing, caching, primitives, strings, and more! It + + +Guava is a set of core Java libraries from Google that includes new collection +types (such as multimap and multiset), immutable collections, a graph library, +and utilities for concurrency, I/O, hashing, primitives, strings, and more! It is widely used on most Java projects within Google, and widely used by many other companies as well. + + Guava comes in two flavors: * The JRE flavor requires JDK 1.8 or higher. -* If you need support for Android, use the Android flavor. You can find the - Android Guava source in the [`android` directory]. +* If you need support for Android, use + [the Android flavor](https://github.com/google/guava/wiki/Android). You can + find the Android Guava source in the [`android` directory]. [`android` directory]: https://github.com/google/guava/tree/master/android @@ -22,8 +28,8 @@ Guava comes in two flavors: Guava's Maven group ID is `com.google.guava`, and its artifact ID is `guava`. Guava provides two different "flavors": one for use on a (Java 8+) JRE and one for use on Android or by any library that wants to be compatible with Android. -These flavors are specified in the Maven version field as either `31.0.1-jre` or -`31.0.1-android`. For more about depending on Guava, see +These flavors are specified in the Maven version field as either `33.4.0-jre` or +`33.4.0-android`. For more about depending on Guava, see [using Guava in your build]. To add a dependency on Guava using Maven, use the following: @@ -32,9 +38,9 @@ To add a dependency on Guava using Maven, use the following: com.google.guava guava - 31.0.1-jre + 33.4.0-jre - 31.0.1-android + 33.4.0-android ``` @@ -45,16 +51,16 @@ dependencies { // Pick one: // 1. Use Guava in your implementation only: - implementation("com.google.guava:guava:31.0.1-jre") + implementation("com.google.guava:guava:33.4.0-jre") // 2. Use Guava types in your public API: - api("com.google.guava:guava:31.0.1-jre") + api("com.google.guava:guava:33.4.0-jre") // 3. Android - Use Guava in your implementation only: - implementation("com.google.guava:guava:31.0.1-android") + implementation("com.google.guava:guava:33.4.0-android") // 4. Android - Use Guava types in your public API: - api("com.google.guava:guava:31.0.1-android") + api("com.google.guava:guava:33.4.0-android") } ``` @@ -65,16 +71,16 @@ consult the ## Snapshots and Documentation Snapshots of Guava built from the `master` branch are available through Maven -using version `HEAD-jre-SNAPSHOT`, or `HEAD-android-SNAPSHOT` for the Android -flavor. +using version `999.0.0-HEAD-jre-SNAPSHOT`, or `999.0.0-HEAD-android-SNAPSHOT` +for the Android flavor. -- Snapshot API Docs: [guava][guava-snapshot-api-docs] -- Snapshot API Diffs: [guava][guava-snapshot-api-diffs] +[Snapshot API Javadoc][guava-snapshot-api-docs] as well as +[Snapshot API Diffs][guava-snapshot-api-diffs] are also available. ## Learn about Guava - Our users' guide, [Guava Explained] -- [A nice collection](http://www.tfnico.com/presentations/google-guava) of +- [A nice collection](https://www.tfnico.com/presentations/google-guava) of other helpful links ## Links @@ -82,8 +88,8 @@ flavor. - [GitHub project](https://github.com/google/guava) - [Issue tracker: Report a defect or feature request](https://github.com/google/guava/issues/new) - [StackOverflow: Ask "how-to" and "why-didn't-it-work" questions](https://stackoverflow.com/questions/ask?tags=guava+java) -- [guava-announce: Announcements of releases and upcoming significant changes](http://groups.google.com/group/guava-announce) -- [guava-discuss: For open-ended questions and discussion](http://groups.google.com/group/guava-discuss) +- [guava-announce: Announcements of releases and upcoming significant changes](https://groups.google.com/group/guava-announce) +- [guava-discuss: For open-ended questions and discussion](https://groups.google.com/group/guava-discuss) ## IMPORTANT WARNINGS @@ -102,7 +108,7 @@ flavor. options open in case of surprises (like, say, a serious security problem). 3. Guava has one dependency that is needed for linkage at runtime: - `com.google.guava:failureaccess:1.0.1`. It also has + `com.google.guava:failureaccess:1.0.2`. It also has [some annotation-only dependencies][guava-deps], which we discuss in more detail at that link. @@ -113,10 +119,11 @@ flavor. 5. Our classes are not designed to protect against a malicious caller. You should not use them for communication between trusted and untrusted code. -6. For the mainline flavor, we test the libraries using only OpenJDK 8 and - OpenJDK 11 on Linux. Some features, especially in `com.google.common.io`, - may not work correctly in other environments. For the Android flavor, our - unit tests also run on API level 15 (Ice Cream Sandwich). +6. For the mainline flavor, we test the libraries using OpenJDK 8, 11, and 17 + on Linux, with some additional testing on newer JDKs and on Windows. Some + features, especially in `com.google.common.io`, may not work correctly in + non-Linux environments. For the Android flavor, our unit tests also run on + API level 21 (Lollipop). [guava-snapshot-api-docs]: https://guava.dev/releases/snapshot-jre/api/docs/ [guava-snapshot-api-diffs]: https://guava.dev/releases/snapshot-jre/api/diffs/ diff --git a/android/guava-bom/pom.xml b/android/guava-bom/pom.xml index 8fde93d1a75a..9f5c78b7709b 100644 --- a/android/guava-bom/pom.xml +++ b/android/guava-bom/pom.xml @@ -8,7 +8,7 @@ com.google.guava guava-bom - HEAD-android-SNAPSHOT + 999.0.0-HEAD-android-SNAPSHOT pom diff --git a/android/guava-testlib/pom.xml b/android/guava-testlib/pom.xml index b3211af9c71a..9ebeb7da6090 100644 --- a/android/guava-testlib/pom.xml +++ b/android/guava-testlib/pom.xml @@ -5,7 +5,7 @@ com.google.guava guava-parent - HEAD-android-SNAPSHOT + 999.0.0-HEAD-android-SNAPSHOT guava-testlib Guava Testing Library @@ -14,9 +14,15 @@ unit testing - particularly to assist the tests for Guava itself. + + org.jspecify + jspecify + com.google.code.findbugs jsr305 + 3.0.2 + test com.google.errorprone @@ -34,7 +40,8 @@ junit junit - compile + + 4.13.2 com.google.truth truth + ${truth.version} test + + + + com.google.guava + guava + + + + org.mvnsearch + toolchains-maven-plugin + + + maven-toolchains-plugin + maven-compiler-plugin diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTestSuiteBuilder.java index ef7917b93122..453ef743d267 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTestSuiteBuilder.java @@ -46,8 +46,7 @@ public abstract class AbstractCollectionTestSuiteBuilder< B extends AbstractCollectionTestSuiteBuilder, E> extends PerCollectionSizeTestSuiteBuilder, Collection, E> { - // Class parameters must be raw. - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { return Arrays.>asList( diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTester.java index c5191be960d4..7ebb133e3cb4 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractCollectionTester.java @@ -17,7 +17,10 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -27,8 +30,11 @@ * @author Kevin Bourrillion */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public abstract class AbstractCollectionTester +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public abstract class AbstractCollectionTester extends AbstractContainerTester, E> { // TODO: replace this with an accessor. @@ -41,17 +47,22 @@ protected Collection actualContents() { // TODO: dispose of this once collection is encapsulated. @Override + @CanIgnoreReturnValue protected Collection resetContainer(Collection newContents) { collection = super.resetContainer(newContents); return collection; } - /** @see AbstractContainerTester#resetContainer() */ + /** + * @see AbstractContainerTester#resetContainer() + */ protected void resetCollection() { resetContainer(); } - /** @return an array of the proper size with {@code null} inserted into the middle element. */ + /** + * @return an array of the proper size with {@code null} inserted into the middle element. + */ protected E[] createArrayWithNullElement() { E[] array = createSamplesArray(); array[getNullLocation()] = null; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractContainerTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractContainerTester.java index b86ef1750dee..baa8371cf10e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractContainerTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractContainerTester.java @@ -16,13 +16,19 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.copyToList; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -34,8 +40,11 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public abstract class AbstractContainerTester +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public abstract class AbstractContainerTester extends AbstractTester> { protected SampleElements samples; protected C container; @@ -61,6 +70,7 @@ public void setUp() throws Exception { * @see #resetContainer(Object) resetContainer(C) * @return the new container instance. */ + @CanIgnoreReturnValue protected C resetContainer() { return resetContainer(getSubjectGenerator().createTestSubject()); } @@ -75,6 +85,7 @@ protected C resetContainer() { * @return the new container instance * @param newValue the new container instance */ + @CanIgnoreReturnValue protected C resetContainer(C newValue) { container = newValue; return container; @@ -85,7 +96,7 @@ protected C resetContainer(C newValue) { * @param elements expected contents of {@link #container} */ protected final void expectContents(E... elements) { - expectContents(Arrays.asList(elements)); + expectContents(asList(elements)); } /** @@ -105,7 +116,7 @@ protected final void expectContents(E... elements) { * examining whether the features include KNOWN_ORDER? */ protected void expectContents(Collection expected) { - Helpers.assertEqualIgnoringOrder(expected, actualContents()); + assertEqualIgnoringOrder(expected, actualContents()); } protected void expectUnchanged() { @@ -132,17 +143,17 @@ protected void expectUnchanged() { * @param elements expected additional contents of {@link #container} */ protected final void expectAdded(E... elements) { - List expected = Helpers.copyToList(getSampleElements()); - expected.addAll(Arrays.asList(elements)); + List expected = copyToList(getSampleElements()); + expected.addAll(asList(elements)); expectContents(expected); } protected final void expectAdded(int index, E... elements) { - expectAdded(index, Arrays.asList(elements)); + expectAdded(index, asList(elements)); } protected final void expectAdded(int index, Collection elements) { - List expected = Helpers.copyToList(getSampleElements()); + List expected = copyToList(getSampleElements()); expected.addAll(index, elements); expectContents(expected); } @@ -171,7 +182,7 @@ protected E[] createOrderedArray() { return array; } - public static class ArrayWithDuplicate { + public static class ArrayWithDuplicate { public final E[] elements; public final E duplicate; @@ -207,7 +218,7 @@ protected Collection getSampleElements() { /** * Returns the {@linkplain #getSampleElements() sample elements} as ordered by {@link - * TestContainerGenerator#order(List)}. Tests should used this method only if they declare + * TestContainerGenerator#order(List)}. Tests should use this method only if they declare * requirement {@link com.google.common.collect.testing.features.CollectionFeature#KNOWN_ORDER}. */ protected List getOrderedElements() { @@ -215,7 +226,7 @@ protected List getOrderedElements() { for (E e : getSubjectGenerator().order(new ArrayList(getSampleElements()))) { list.add(e); } - return Collections.unmodifiableList(list); + return unmodifiableList(list); } /** @@ -226,12 +237,10 @@ protected int getNullLocation() { return getNumElements() / 2; } - @SuppressWarnings("unchecked") protected MinimalCollection createDisjointCollection() { return MinimalCollection.of(e3(), e4()); } - @SuppressWarnings("unchecked") protected MinimalCollection emptyCollection() { return MinimalCollection.of(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java index 5e6a583c43de..2b77adbe29c6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractIteratorTester.java @@ -16,21 +16,28 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static java.lang.System.arraycopy; +import static java.util.Arrays.asList; +import static java.util.Collections.frequency; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Set; import java.util.Stack; -import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Most of the logic for {@link IteratorTester} and {@link ListIteratorTester}. @@ -41,7 +48,8 @@ * @author Chris Povirk */ @GwtCompatible -abstract class AbstractIteratorTester> { +@NullMarked +abstract class AbstractIteratorTester> { private Stimulus[] stimuli; private final Iterator elementsToInsert; private final Set features; @@ -57,7 +65,7 @@ private abstract static class PermittedMetaException extends RuntimeException { static final PermittedMetaException UOE_OR_ISE = new PermittedMetaException("UnsupportedOperationException or IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException || exception instanceof IllegalStateException; } @@ -65,21 +73,21 @@ boolean isPermitted(RuntimeException exception) { static final PermittedMetaException UOE = new PermittedMetaException("UnsupportedOperationException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof UnsupportedOperationException; } }; static final PermittedMetaException ISE = new PermittedMetaException("IllegalStateException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof IllegalStateException; } }; static final PermittedMetaException NSEE = new PermittedMetaException("NoSuchElementException") { @Override - boolean isPermitted(RuntimeException exception) { + boolean isPermitted(Exception exception) { return exception instanceof NoSuchElementException; } }; @@ -88,20 +96,20 @@ private PermittedMetaException(String message) { super(message); } - abstract boolean isPermitted(RuntimeException exception); + abstract boolean isPermitted(Exception exception); - void assertPermitted(RuntimeException exception) { + void assertPermitted(Exception exception) { if (!isPermitted(exception)) { String message = "Exception " + exception.getClass().getSimpleName() + " was thrown; expected " + getMessage(); - Helpers.fail(exception, message); + throw new AssertionError(message, exception); } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static final class UnknownElementException extends RuntimeException { @@ -109,7 +117,7 @@ private UnknownElementException(Collection expected, Object actual) { super("Returned value '" + actual + "' not found. Remaining elements: " + expected); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -135,20 +143,22 @@ protected final class MultiExceptionListIterator implements ListIterator { * The elements to be returned by future calls to {@code next()}, with the first at the top of * the stack. */ - final Stack nextElements = new Stack(); + final Stack nextElements = new Stack<>(); + /** * The elements to be returned by future calls to {@code previous()}, with the first at the top * of the stack. */ - final Stack previousElements = new Stack(); + final Stack previousElements = new Stack<>(); + /** * {@link #nextElements} if {@code next()} was called more recently then {@code previous}, * {@link #previousElements} if the reverse is true, or -- overriding both of these -- {@code * null} if {@code remove()} or {@code add()} has been called more recently than either. We use * this to determine which stack to pop from on a call to {@code remove()} (or to pop from and - * push to on a call to {@code set()}. + * push to on a call to {@code set()}). */ - Stack stackWithLastReturnedElementAtTop = null; + @Nullable Stack stackWithLastReturnedElementAtTop = null; MultiExceptionListIterator(List expectedElements) { Helpers.addAll(nextElements, Helpers.reverse(expectedElements)); @@ -254,7 +264,7 @@ private void throwIfInvalid(IteratorFeature methodFeature) { } private List getElements() { - List elements = new ArrayList(); + List elements = new ArrayList<>(); Helpers.addAll(elements, previousElements); Helpers.addAll(elements, Helpers.reverse(nextElements)); return elements; @@ -266,7 +276,7 @@ public enum KnownOrder { UNKNOWN_ORDER } - @SuppressWarnings("unchecked") // creating array of generic class Stimulus + @SuppressWarnings("unchecked") // TODO(cpovirk): Stop using arrays. AbstractIteratorTester( int steps, Iterable elementsToInsertIterable, @@ -276,13 +286,13 @@ public enum KnownOrder { int startIndex) { // periodically we should manually try (steps * 3 / 2) here; all tests but // one should still pass (testVerifyGetsCalled()). - stimuli = new Stimulus[steps]; + stimuli = (Stimulus[]) new Stimulus[steps]; if (!elementsToInsertIterable.iterator().hasNext()) { throw new IllegalArgumentException(); } elementsToInsert = Helpers.cycle(elementsToInsertIterable); - this.features = Helpers.copyToSet(features); - this.expectedElements = Helpers.copyToList(expectedElements); + this.features = copyToSet(features); + this.expectedElements = copyToList(expectedElements); this.knownOrder = knownOrder; this.startIndex = startIndex; } @@ -312,10 +322,11 @@ public enum KnownOrder { protected void verify(List elements) {} /** Executes the test. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final void test() { try { recurse(0); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new RuntimeException(Arrays.toString(stimuli), e); } } @@ -336,7 +347,7 @@ private void recurse(int level) { } private void compareResultsForThisListOfStimuli() { - int removes = Collections.frequency(Arrays.asList(stimuli), remove); + int removes = frequency(asList(stimuli), remove); if ((!features.contains(IteratorFeature.SUPPORTS_REMOVE) && removes > 1) || (stimuli.length >= 5 && removes > 2)) { // removes are the most expensive thing to test, since they often throw exceptions with stack @@ -350,20 +361,20 @@ private void compareResultsForThisListOfStimuli() { try { stimuli[i].executeAndCompare(reference, target); verify(reference.getElements()); - } catch (AssertionFailedError cause) { - Helpers.fail(cause, "failed with stimuli " + subListCopy(stimuli, i + 1)); + } catch (AssertionError cause) { + throw new AssertionError("failed with stimuli " + subListCopy(stimuli, i + 1), cause); } } } private static List subListCopy(Object[] source, int size) { - final Object[] copy = new Object[size]; - System.arraycopy(source, 0, copy, 0, size); - return Arrays.asList(copy); + Object[] copy = new Object[size]; + arraycopy(source, 0, copy, 0, size); + return asList(copy); } private interface IteratorOperation { - Object execute(Iterator iterator); + @Nullable Object execute(Iterator iterator); } /** @@ -371,16 +382,17 @@ private interface IteratorOperation { * * @see Stimulus#executeAndCompare(ListIterator, Iterator) */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private > void internalExecuteAndCompare( T reference, T target, IteratorOperation method) { Object referenceReturnValue = null; PermittedMetaException referenceException = null; Object targetReturnValue = null; - RuntimeException targetException = null; + Exception targetException = null; try { targetReturnValue = method.execute(target); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception targetException = e; } @@ -395,20 +407,15 @@ private > void internalExecuteAndCompare( @SuppressWarnings("unchecked") E targetReturnValueFromNext = (E) targetReturnValue; /* - * We have an Iterator and want to cast it to - * MultiExceptionListIterator. Because we're inside an - * AbstractIteratorTester, that's implicitly a cast to - * AbstractIteratorTester.MultiExceptionListIterator. The runtime - * won't be able to verify the AbstractIteratorTester part, so it's - * an unchecked cast. We know, however, that the only possible value for - * the type parameter is , since otherwise the - * MultiExceptionListIterator wouldn't be an Iterator. The cast is - * safe, even though javac can't tell. - * - * Sun bug 6665356 is an additional complication. Until OpenJDK 7, javac - * doesn't recognize this kind of cast as unchecked cast. Neither does - * Eclipse 3.4. Right now, this suppression is mostly unnecessary. + * We have an Iterator and want to cast it to MultiExceptionListIterator. Because we're + * inside an AbstractIteratorTester, that's implicitly a cast to + * AbstractIteratorTester.MultiExceptionListIterator. The runtime won't be able to verify + * the AbstractIteratorTester part, so it's an unchecked cast. We know, however, that the + * only possible value for the type parameter is , since otherwise the + * MultiExceptionListIterator wouldn't be an Iterator. The cast is safe, even though + * javac can't tell. */ + @SuppressWarnings("unchecked") MultiExceptionListIterator multiExceptionListIterator = (MultiExceptionListIterator) reference; multiExceptionListIterator.promoteToNext(targetReturnValueFromNext); @@ -418,12 +425,12 @@ private > void internalExecuteAndCompare( } catch (PermittedMetaException e) { referenceException = e; } catch (UnknownElementException e) { - Helpers.fail(e, e.getMessage()); + throw new AssertionError(e); } if (referenceException == null) { if (targetException != null) { - Helpers.fail(targetException, "Target threw exception when reference did not"); + throw new AssertionError("Target threw exception when reference did not", targetException); } /* @@ -449,7 +456,7 @@ private > void internalExecuteAndCompare( private static final IteratorOperation REMOVE_METHOD = new IteratorOperation() { @Override - public Object execute(Iterator iterator) { + public @Nullable Object execute(Iterator iterator) { iterator.remove(); return null; } @@ -458,7 +465,7 @@ public Object execute(Iterator iterator) { private static final IteratorOperation NEXT_METHOD = new IteratorOperation() { @Override - public Object execute(Iterator iterator) { + public @Nullable Object execute(Iterator iterator) { return iterator.next(); } }; @@ -466,16 +473,16 @@ public Object execute(Iterator iterator) { private static final IteratorOperation PREVIOUS_METHOD = new IteratorOperation() { @Override - public Object execute(Iterator iterator) { + public @Nullable Object execute(Iterator iterator) { return ((ListIterator) iterator).previous(); } }; private final IteratorOperation newAddMethod() { - final Object toInsert = elementsToInsert.next(); + Object toInsert = elementsToInsert.next(); return new IteratorOperation() { @Override - public Object execute(Iterator iterator) { + public @Nullable Object execute(Iterator iterator) { @SuppressWarnings("unchecked") ListIterator rawIterator = (ListIterator) iterator; rawIterator.add(toInsert); @@ -485,10 +492,10 @@ public Object execute(Iterator iterator) { } private final IteratorOperation newSetMethod() { - final E toInsert = elementsToInsert.next(); + E toInsert = elementsToInsert.next(); return new IteratorOperation() { @Override - public Object execute(Iterator iterator) { + public @Nullable Object execute(Iterator iterator) { @SuppressWarnings("unchecked") ListIterator li = (ListIterator) iterator; li.set(toInsert); @@ -497,7 +504,7 @@ public Object execute(Iterator iterator) { }; } - abstract static class Stimulus> { + abstract static class Stimulus> { private final String toString; protected Stimulus(String toString) { @@ -538,9 +545,8 @@ void executeAndCompare(ListIterator reference, Iterator target) { } }; - @SuppressWarnings("unchecked") List>> iteratorStimuli() { - return Arrays.asList(hasNext, next, remove); + return asList(hasNext, next, remove); } Stimulus> hasPrevious = @@ -586,8 +592,7 @@ void executeAndCompare(ListIterator reference, ListIterator target) { } }; - @SuppressWarnings("unchecked") List>> listIteratorStimuli() { - return Arrays.asList(hasPrevious, nextIndex, previousIndex, previous, add, set); + return asList(hasPrevious, nextIndex, previousIndex, previous, add, set); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractMapTester.java index 5e0de3ccc443..23e5584ba72b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractMapTester.java @@ -16,6 +16,9 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Iterator; @@ -23,6 +26,8 @@ import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -36,8 +41,11 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public abstract class AbstractMapTester +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public abstract class AbstractMapTester extends AbstractContainerTester, Entry> { protected Map getMap() { return container; @@ -48,7 +56,9 @@ protected Collection> actualContents() { return getMap().entrySet(); } - /** @see AbstractContainerTester#resetContainer() */ + /** + * @see AbstractContainerTester#resetContainer() + */ protected final void resetMap() { resetContainer(); } @@ -69,7 +79,9 @@ protected void expectMissingValues(V... elements) { } } - /** @return an array of the proper size with {@code null} as the key of the middle element. */ + /** + * @return an array of the proper size with {@code null} as the key of the middle element. + */ protected Entry[] createArrayWithNullKey() { Entry[] array = createSamplesArray(); int nullKeyLocation = getNullLocation(); @@ -94,7 +106,9 @@ private Entry getEntryNullReplaces() { return entries.next(); } - /** @return an array of the proper size with {@code null} as the value of the middle element. */ + /** + * @return an array of the proper size with {@code null} as the value of the middle element. + */ protected Entry[] createArrayWithNullValue() { Entry[] array = createSamplesArray(); int nullValueLocation = getNullLocation(); @@ -139,7 +153,6 @@ protected void expectNullValueMissingWhenNullValuesUnsupported(String message) { } } - @SuppressWarnings("unchecked") @Override protected MinimalCollection> createDisjointCollection() { return MinimalCollection.of(e3(), e4()); @@ -167,13 +180,13 @@ protected void expectMissing(Entry... entries) { } } - private static boolean equal(Object a, Object b) { + private static boolean equal(@Nullable Object a, @Nullable Object b) { return a == b || (a != null && a.equals(b)); } // This one-liner saves us from some ugly casts protected Entry entry(K key, V value) { - return Helpers.mapEntry(key, value); + return mapEntry(key, value); } @Override @@ -187,7 +200,7 @@ protected void expectContents(Collection> expected) { } protected final void expectReplacement(Entry newEntry) { - List> expected = Helpers.copyToList(getSampleElements()); + List> expected = copyToList(getSampleElements()); replaceValue(expected, newEntry); expectContents(expected); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/AbstractTester.java b/android/guava-testlib/src/com/google/common/collect/testing/AbstractTester.java index dc1e1a0674fc..13e86cb6d9af 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/AbstractTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/AbstractTester.java @@ -17,7 +17,11 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * This abstract base class for testers allows the framework to inject needed information after @@ -30,12 +34,13 @@ * parameterize the test. * @author George van den Driessche */ -@GwtCompatible +@GwtCompatible(emulated = true) +@NullMarked public class AbstractTester extends TestCase { private G subjectGenerator; private String suiteName; - private Runnable setUp; - private Runnable tearDown; + private @Nullable Runnable setUp; + private @Nullable Runnable tearDown; // public so that it can be referenced in generated GWT tests. @Override @@ -54,7 +59,8 @@ public void tearDown() throws Exception { } // public so that it can be referenced in generated GWT tests. - public final void init(G subjectGenerator, String suiteName, Runnable setUp, Runnable tearDown) { + public final void init( + G subjectGenerator, String suiteName, @Nullable Runnable setUp, @Nullable Runnable tearDown) { this.subjectGenerator = subjectGenerator; this.suiteName = suiteName; this.setUp = setUp; @@ -71,12 +77,29 @@ public G getSubjectGenerator() { } /** Returns the name of the test method invoked by this test instance. */ + @J2ktIncompatible + @GwtIncompatible // not used under GWT, and super.getName() is not available under J2CL public final String getTestMethodName() { return super.getName(); } + @J2ktIncompatible + @GwtIncompatible // not used under GWT, and super.getName() is not available under J2CL @Override public String getName() { - return Platform.format("%s[%s]", super.getName(), suiteName); + return super.getName() + '[' + suiteName + ']'; + } + + /** + * Asserts that the given object is non-null, with a better failure message than {@link + * TestCase#assertNull(String, Object)}. + * + *

The {@link TestCase} version (which is from JUnit 3) produces a failure message that does + * not include the value of the object. + * + * @since 33.4.0 + */ + public static void assertNull(String message, Object object) { + assertEquals(message, null, object); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/BaseComparable.java b/android/guava-testlib/src/com/google/common/collect/testing/BaseComparable.java index 3847709894cd..13e1b9e032cf 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/BaseComparable.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/BaseComparable.java @@ -17,7 +17,10 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; +import org.jspecify.annotations.Nullable; /** * Simple base class to verify that we handle generics correctly. @@ -38,7 +41,7 @@ public int hashCode() { // delegate to 's' } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == null) { return false; } else if (other instanceof BaseComparable) { @@ -53,5 +56,5 @@ public int compareTo(BaseComparable o) { return s.compareTo(o.s); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentMapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentMapTestSuiteBuilder.java index 47220b0f1f8d..048723e40385 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentMapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentMapTestSuiteBuilder.java @@ -14,12 +14,14 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.testers.ConcurrentMapPutIfAbsentTester; import com.google.common.collect.testing.testers.ConcurrentMapRemoveTester; import com.google.common.collect.testing.testers.ConcurrentMapReplaceEntryTester; import com.google.common.collect.testing.testers.ConcurrentMapReplaceTester; -import java.util.Arrays; import java.util.List; /** @@ -36,16 +38,18 @@ public static ConcurrentMapTestSuiteBuilder using(TestMapGenerator< return result; } + @SuppressWarnings("rawtypes") // class literals static final List> TESTERS = - Arrays.asList( + asList( ConcurrentMapPutIfAbsentTester.class, ConcurrentMapRemoveTester.class, ConcurrentMapReplaceTester.class, ConcurrentMapReplaceEntryTester.class); + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.addAll(TESTERS); return testers; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentNavigableMapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentNavigableMapTestSuiteBuilder.java index cccd06ef6bf2..d0f71b61db40 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentNavigableMapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/ConcurrentNavigableMapTestSuiteBuilder.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtIncompatible; import java.util.List; @@ -37,9 +39,10 @@ public static ConcurrentNavigableMapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.addAll(ConcurrentMapTestSuiteBuilder.TESTERS); return testers; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java index a04d3ffe300a..85a6e8a7913e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/DerivedCollectionGenerators.java @@ -17,15 +17,15 @@ package com.google.common.collect.testing; import static com.google.common.collect.testing.Helpers.castOrCopyToList; +import static com.google.common.collect.testing.Helpers.entryComparator; import static com.google.common.collect.testing.Helpers.equal; import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.util.Arrays.asList; import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -33,6 +33,8 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Derived suite generators, split out of the suite builders so that they are available to GWT. @@ -40,8 +42,9 @@ * @author George van den Driessche */ @GwtCompatible +@NullMarked public final class DerivedCollectionGenerators { - public static class MapEntrySetGenerator + public static class MapEntrySetGenerator implements TestSetGenerator>, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> mapGenerator; @@ -79,8 +82,9 @@ public OneSizeTestContainerGenerator, Entry> getInnerGenerator() // TODO: investigate some API changes to SampleElements that would tidy up // parts of the following classes. - static TestSetGenerator keySetGenerator( - OneSizeTestContainerGenerator, Entry> mapGenerator) { + static + TestSetGenerator keySetGenerator( + OneSizeTestContainerGenerator, Entry> mapGenerator) { TestContainerGenerator, Entry> generator = mapGenerator.getInnerGenerator(); if (generator instanceof TestSortedMapGenerator && ((TestSortedMapGenerator) generator).create().keySet() instanceof SortedSet) { @@ -90,7 +94,8 @@ static TestSetGenerator keySetGenerator( } } - public static class MapKeySetGenerator implements TestSetGenerator, DerivedGenerator { + public static class MapKeySetGenerator + implements TestSetGenerator, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> mapGenerator; private final SampleElements samples; @@ -123,7 +128,7 @@ public Set create(Object... elements) { Collection> entries = new ArrayList<>(elements.length); int i = 0; for (Entry entry : originalEntries) { - entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue())); + entries.add(mapEntry(keysArray[i++], entry.getValue())); } return mapGenerator.create(entries.toArray()).keySet(); @@ -159,8 +164,9 @@ public OneSizeTestContainerGenerator, Entry> getInnerGenerator() } } - public static class MapSortedKeySetGenerator extends MapKeySetGenerator - implements TestSortedSetGenerator, DerivedGenerator { + public static class MapSortedKeySetGenerator< + K extends @Nullable Object, V extends @Nullable Object> + extends MapKeySetGenerator implements TestSortedSetGenerator { private final TestSortedMapGenerator delegate; public MapSortedKeySetGenerator( @@ -195,7 +201,8 @@ public K aboveSamplesGreater() { } } - public static class MapValueCollectionGenerator + public static class MapValueCollectionGenerator< + K extends @Nullable Object, V extends @Nullable Object> implements TestCollectionGenerator, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> mapGenerator; private final SampleElements samples; @@ -230,7 +237,7 @@ public Collection create(Object... elements) { Collection> entries = new ArrayList<>(elements.length); int i = 0; for (Entry entry : originalEntries) { - entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); + entries.add(mapEntry(entry.getKey(), valuesArray[i++])); } return mapGenerator.create(entries.toArray()).values(); @@ -276,7 +283,8 @@ public OneSizeTestContainerGenerator, Entry> getInnerGenerator() } // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator? - static class ForwardingTestMapGenerator implements TestMapGenerator { + static class ForwardingTestMapGenerator + implements TestMapGenerator { TestMapGenerator delegate; ForwardingTestMapGenerator(TestMapGenerator delegate) { @@ -321,7 +329,8 @@ public enum Bound { NO_BOUND; } - public static class SortedSetSubsetTestSetGenerator implements TestSortedSetGenerator { + public static class SortedSetSubsetTestSetGenerator + implements TestSortedSetGenerator { final Bound to; final Bound from; final E firstInclusive; @@ -340,7 +349,7 @@ public SortedSetSubsetTestSetGenerator( SampleElements samples = delegate.samples(); List samplesList = new ArrayList<>(samples.asList()); - Collections.sort(samplesList, comparator); + sort(samplesList, comparator); this.firstInclusive = samplesList.get(0); this.lastInclusive = samplesList.get(samplesList.size() - 1); } @@ -374,7 +383,7 @@ public Iterable order(List insertionOrder) { @Override public SortedSet create(Object... elements) { - List normalValues = (List) Arrays.asList(elements); + List normalValues = (List) asList(elements); List extremeValues = new ArrayList<>(); // nulls are usually out of bounds for a subset, so ban them altogether @@ -397,7 +406,7 @@ public SortedSet create(Object... elements) { } // the regular values should be visible after filtering - List allEntries = new ArrayList<>(); + List<@Nullable Object> allEntries = new ArrayList<>(); allEntries.addAll(extremeValues); allEntries.addAll(normalValues); SortedSet set = delegate.create(allEntries.toArray()); @@ -443,8 +452,9 @@ public E aboveSamplesGreater() { * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters, * exposes as many getters, does work in the constructor, and has both a superclass and a subclass */ - public static class SortedMapSubmapTestMapGenerator extends ForwardingTestMapGenerator - implements TestSortedMapGenerator { + public static class SortedMapSubmapTestMapGenerator< + K extends @Nullable Object, V extends @Nullable Object> + extends ForwardingTestMapGenerator implements TestSortedMapGenerator { final Bound to; final Bound from; final K firstInclusive; @@ -458,14 +468,13 @@ public SortedMapSubmapTestMapGenerator( this.from = from; SortedMap emptyMap = delegate.create(); - this.entryComparator = Helpers.entryComparator(emptyMap.comparator()); + this.entryComparator = entryComparator(emptyMap.comparator()); // derive values for inclusive filtering from the input samples SampleElements> samples = delegate.samples(); - @SuppressWarnings("unchecked") // no elements are inserted into the array List> samplesList = - Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); - Collections.sort(samplesList, entryComparator); + asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); + sort(samplesList, entryComparator); this.firstInclusive = samplesList.get(0).getKey(); this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/DerivedComparable.java b/android/guava-testlib/src/com/google/common/collect/testing/DerivedComparable.java index 5b0d16f32657..fd9ba97da9b9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/DerivedComparable.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/DerivedComparable.java @@ -17,6 +17,8 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; /** * Simple derived class to verify that we handle generics correctly. @@ -29,5 +31,5 @@ public DerivedComparable(String s) { super(s); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/DerivedGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/DerivedGenerator.java index d80f5a93c917..b96e1d07b63f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/DerivedGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/DerivedGenerator.java @@ -24,7 +24,7 @@ * collection generator. * *

{@code GwtTestSuiteGenerator} expects every {@code DerivedIterator} implementation to provide - * a one-arg constructor accepting its inner generator as an argument). This requirement enables it + * a one-arg constructor accepting its inner generator as an argument. This requirement enables it * to generate source code (since GWT cannot use reflection to generate the suites). * * @author Chris Povirk diff --git a/android/guava-testlib/src/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilder.java index db086aa3075e..4481d7619587 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilder.java @@ -16,7 +16,12 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static com.google.common.collect.testing.Helpers.getMethod; +import static com.google.common.collect.testing.features.FeatureUtil.addImpliedFeatures; +import static java.util.Arrays.asList; import static java.util.Collections.disjoint; +import static java.util.Collections.unmodifiableSet; import static java.util.logging.Level.FINER; import com.google.common.annotations.GwtIncompatible; @@ -24,11 +29,10 @@ import com.google.common.collect.testing.features.Feature; import com.google.common.collect.testing.features.FeatureUtil; import com.google.common.collect.testing.features.TesterRequirements; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedHashSet; @@ -38,6 +42,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.Nullable; /** * Creates, based on your criteria, a JUnit test suite that exhaustively tests the object generated @@ -61,12 +66,13 @@ protected B self() { // Test Data - private G subjectGenerator; + private @Nullable G subjectGenerator; // Gets run before every test. private Runnable setUp; // Gets run at the conclusion of every test. private Runnable tearDown; + @CanIgnoreReturnValue protected B usingGenerator(G subjectGenerator) { this.subjectGenerator = subjectGenerator; return self(); @@ -76,6 +82,7 @@ public G getSubjectGenerator() { return subjectGenerator; } + @CanIgnoreReturnValue public B withSetUp(Runnable setUp) { this.setUp = setUp; return self(); @@ -85,6 +92,7 @@ public Runnable getSetUp() { return setUp; } + @CanIgnoreReturnValue public B withTearDown(Runnable tearDown) { this.tearDown = tearDown; return self(); @@ -102,10 +110,12 @@ public Runnable getTearDown() { * Configures this builder to produce tests appropriate for the given features. This method may be * called more than once to add features in multiple groups. */ + @CanIgnoreReturnValue public B withFeatures(Feature... features) { - return withFeatures(Arrays.asList(features)); + return withFeatures(asList(features)); } + @CanIgnoreReturnValue public B withFeatures(Iterable> features) { for (Feature feature : features) { this.features.add(feature); @@ -114,14 +124,15 @@ public B withFeatures(Iterable> features) { } public Set> getFeatures() { - return Collections.unmodifiableSet(features); + return unmodifiableSet(features); } // Name - private String name; + private @Nullable String name; /** Configures this builder produce a TestSuite with the given name. */ + @CanIgnoreReturnValue public B named(String name) { if (name.contains("(")) { throw new IllegalArgumentException( @@ -147,10 +158,12 @@ public String getName() { * semantics of an implementation disagree in unforeseen ways with the semantics expected by a * test, or to keep dependent builds clean in spite of an erroneous test. */ + @CanIgnoreReturnValue public B suppressing(Method... methods) { - return suppressing(Arrays.asList(methods)); + return suppressing(asList(methods)); } + @CanIgnoreReturnValue public B suppressing(Collection methods) { suppressedTests.addAll(methods); return self(); @@ -164,27 +177,23 @@ public Set getSuppressedTests() { Logger.getLogger(FeatureSpecificTestSuiteBuilder.class.getName()); /** Creates a runnable JUnit test suite based on the criteria already given. */ - /* - * Class parameters must be raw. This annotation should go on testerClass in - * the for loop, but the 1.5 javac crashes on annotations in for loops: - * - */ - @SuppressWarnings("unchecked") public TestSuite createTestSuite() { checkCanCreate(); logger.fine(" Testing: " + name); logger.fine("Features: " + formatFeatureSet(features)); - FeatureUtil.addImpliedFeatures(features); + addImpliedFeatures(features); logger.fine("Expanded: " + formatFeatureSet(features)); - // Class parameters must be raw. + @SuppressWarnings("rawtypes") // class literals List> testers = getTesters(); TestSuite suite = new TestSuite(name); - for (Class testerClass : testers) { + for (@SuppressWarnings("rawtypes") // class literals + Class testerClass : testers) { + @SuppressWarnings("unchecked") // getting rid of the raw type, for better or for worse TestSuite testerSuite = makeSuiteForTesterClass((Class>) testerClass); if (testerSuite.countTestCases() > 0) { @@ -207,7 +216,7 @@ protected void checkCanCreate() { } } - // Class parameters must be raw. + @SuppressWarnings("rawtypes") // class literals protected abstract List> getTesters(); private boolean matches(Test test) { @@ -230,7 +239,7 @@ private boolean matches(Test test) { } if (!features.containsAll(requirements.getPresentFeatures())) { if (logger.isLoggable(FINER)) { - Set> missingFeatures = Helpers.copyToSet(requirements.getPresentFeatures()); + Set> missingFeatures = copyToSet(requirements.getPresentFeatures()); missingFeatures.removeAll(features); logger.finer( Platform.format( @@ -240,7 +249,7 @@ private boolean matches(Test test) { } if (intersect(features, requirements.getAbsentFeatures())) { if (logger.isLoggable(FINER)) { - Set> unwantedFeatures = Helpers.copyToSet(requirements.getAbsentFeatures()); + Set> unwantedFeatures = copyToSet(requirements.getAbsentFeatures()); unwantedFeatures.retainAll(features); logger.finer( Platform.format( @@ -258,10 +267,10 @@ private static boolean intersect(Set a, Set b) { private static Method extractMethod(Test test) { if (test instanceof AbstractTester) { AbstractTester tester = (AbstractTester) test; - return Helpers.getMethod(tester.getClass(), tester.getTestMethodName()); + return getMethod(tester.getClass(), tester.getTestMethodName()); } else if (test instanceof TestCase) { TestCase testCase = (TestCase) test; - return Helpers.getMethod(testCase.getClass(), testCase.getName()); + return getMethod(testCase.getClass(), testCase.getName()); } else { throw new IllegalArgumentException("unable to extract method from test: not a TestCase."); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java b/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java index aac71cc4efe3..971d4513833e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/Helpers.java @@ -16,17 +16,24 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.entryComparator; +import static java.lang.Math.max; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonMap; import static java.util.Collections.sort; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.lang.reflect.Method; +import java.util.AbstractList; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -37,41 +44,43 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import junit.framework.Assert; -import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @GwtCompatible(emulated = true) +@NullMarked public class Helpers { // Clone of Objects.equal - static boolean equal(Object a, Object b) { + static boolean equal(@Nullable Object a, @Nullable Object b) { return a == b || (a != null && a.equals(b)); } // Clone of Lists.newArrayList - public static List copyToList(Iterable elements) { + public static List copyToList(Iterable elements) { List list = new ArrayList<>(); addAll(list, elements); return list; } - public static List copyToList(E[] elements) { - return copyToList(Arrays.asList(elements)); + public static List copyToList(E[] elements) { + return copyToList(asList(elements)); } // Clone of Sets.newLinkedHashSet - public static Set copyToSet(Iterable elements) { + public static Set copyToSet(Iterable elements) { Set set = new LinkedHashSet<>(); addAll(set, elements); return set; } - public static Set copyToSet(E[] elements) { - return copyToSet(Arrays.asList(elements)); + public static Set copyToSet(E[] elements) { + return copyToSet(asList(elements)); } // Would use Maps.immutableEntry - public static Entry mapEntry(K key, V value) { - return Collections.singletonMap(key, value).entrySet().iterator().next(); + public static Entry mapEntry( + K key, V value) { + return singletonMap(key, value).entrySet().iterator().next(); } private static boolean isEmpty(Iterable iterable) { @@ -82,13 +91,13 @@ private static boolean isEmpty(Iterable iterable) { public static void assertEmpty(Iterable iterable) { if (!isEmpty(iterable)) { - Assert.fail("Not true that " + iterable + " is empty"); + fail("Not true that " + iterable + " is empty"); } } public static void assertEmpty(Map map) { if (!map.isEmpty()) { - Assert.fail("Not true that " + map + " is empty"); + fail("Not true that " + map + " is empty"); } } @@ -98,7 +107,7 @@ public static void assertEqualInOrder(Iterable expected, Iterable actual) while (expectedIter.hasNext() && actualIter.hasNext()) { if (!equal(expectedIter.next(), actualIter.next())) { - Assert.fail( + fail( "contents were not equal and in the same order: " + "expected = " + expected @@ -109,7 +118,7 @@ public static void assertEqualInOrder(Iterable expected, Iterable actual) if (expectedIter.hasNext() || actualIter.hasNext()) { // actual either had too few or too many elements - Assert.fail( + fail( "contents were not equal and in the same order: " + "expected = " + expected @@ -119,7 +128,7 @@ public static void assertEqualInOrder(Iterable expected, Iterable actual) } public static void assertContentsInOrder(Iterable actual, Object... expected) { - assertEqualInOrder(Arrays.asList(expected), actual); + assertEqualInOrder(asList(expected), actual); } public static void assertEqualIgnoringOrder(Iterable expected, Iterable actual) { @@ -133,7 +142,7 @@ public static void assertEqualIgnoringOrder(Iterable expected, Iterable ac // Yeah it's n^2. for (Object object : exp) { if (!act.remove(object)) { - Assert.fail( + fail( "did not contain expected element " + object + ", " @@ -147,7 +156,7 @@ public static void assertEqualIgnoringOrder(Iterable expected, Iterable ac } public static void assertContentsAnyOrder(Iterable actual, Object... expected) { - assertEqualIgnoringOrder(Arrays.asList(expected), actual); + assertEqualIgnoringOrder(asList(expected), actual); } public static void assertContains(Iterable actual, Object expected) { @@ -164,23 +173,25 @@ public static void assertContains(Iterable actual, Object expected) { } if (!contained) { - Assert.fail("Not true that " + actual + " contains " + expected); + fail("Not true that " + actual + " contains " + expected); } } public static void assertContainsAllOf(Iterable actual, Object... expected) { - List expectedList = new ArrayList<>(Arrays.asList(expected)); + List expectedList = new ArrayList<>(asList(expected)); for (Object o : actual) { expectedList.remove(o); } if (!expectedList.isEmpty()) { - Assert.fail("Not true that " + actual + " contains all of " + Arrays.asList(expected)); + fail("Not true that " + actual + " contains all of " + asList(expected)); } } - public static boolean addAll(Collection addTo, Iterable elementsToAdd) { + @CanIgnoreReturnValue + public static boolean addAll( + Collection addTo, Iterable elementsToAdd) { boolean modified = false; for (E e : elementsToAdd) { modified |= addTo.add(e); @@ -188,12 +199,11 @@ public static boolean addAll(Collection addTo, Iterable elem return modified; } - static Iterable reverse(List list) { - return new Iterable() { - @Override - public Iterator iterator() { - ListIterator listIter = list.listIterator(list.size()); - return new Iterator() { + static Iterable reverse(List list) { + return () -> + new Iterator() { + private final ListIterator listIter = list.listIterator(list.size()); + @Override public boolean hasNext() { return listIter.hasPrevious(); @@ -209,11 +219,9 @@ public void remove() { listIter.remove(); } }; - } - }; } - static Iterator cycle(Iterable iterable) { + static Iterator cycle(Iterable iterable) { return new Iterator() { Iterator iterator = Collections.emptySet().iterator(); @@ -237,30 +245,33 @@ public void remove() { }; } - static T get(Iterator iterator, int position) { + static T get(Iterator iterator, int position) { for (int i = 0; i < position; i++) { iterator.next(); } return iterator.next(); } - static void fail(Throwable cause, Object message) { - AssertionFailedError assertionFailedError = new AssertionFailedError(String.valueOf(message)); - assertionFailedError.initCause(cause); - throw assertionFailedError; + private static class EntryComparator + implements Comparator> { + final @Nullable Comparator keyComparator; + + public EntryComparator(@Nullable Comparator keyComparator) { + this.keyComparator = keyComparator; + } + + @Override + @SuppressWarnings("unchecked") // no less safe than putting it in the map! + public int compare(Entry a, Entry b) { + return (keyComparator == null) + ? ((Comparable) a.getKey()).compareTo(b.getKey()) + : keyComparator.compare(a.getKey(), b.getKey()); + } } - public static Comparator> entryComparator( - Comparator keyComparator) { - return new Comparator>() { - @Override - @SuppressWarnings("unchecked") // no less safe than putting it in the map! - public int compare(Entry a, Entry b) { - return (keyComparator == null) - ? ((Comparable) a.getKey()).compareTo(b.getKey()) - : keyComparator.compare(a.getKey(), b.getKey()); - } - }; + public static + Comparator> entryComparator(@Nullable Comparator keyComparator) { + return new EntryComparator(keyComparator); } /** @@ -270,9 +281,9 @@ public int compare(Entry a, Entry b) { * * @see #testComparator(Comparator, List) */ - public static void testComparator( + public static void testComparator( Comparator comparator, T... valuesInExpectedOrder) { - testComparator(comparator, Arrays.asList(valuesInExpectedOrder)); + testComparator(comparator, asList(valuesInExpectedOrder)); } /** @@ -290,7 +301,7 @@ public static void testComparator( * valuesInExpectedOrder.get(i)} and {@code tj = valuesInExpectedOrder.get(j)}. * */ - public static void testComparator( + public static void testComparator( Comparator comparator, List valuesInExpectedOrder) { // This does an O(n^2) test of all pairs of values in both orders for (int i = 0; i < valuesInExpectedOrder.size(); i++) { @@ -345,14 +356,47 @@ public static > void testCompareToAndEquals( * @param delta the difference between the true size of the collection and the values returned by * the size method */ - public static Collection misleadingSizeCollection(int delta) { + public static Collection misleadingSizeCollection(int delta) { // It would be nice to be able to return a real concurrent // collection like ConcurrentLinkedQueue, so that e.g. concurrent // iteration would work, but that would not be GWT-compatible. - return new ArrayList() { + // We are not "just" inheriting from ArrayList here as this doesn't work for J2kt. + return new AbstractList() { + ArrayList data = new ArrayList<>(); + @Override public int size() { - return Math.max(0, super.size() + delta); + return max(0, data.size() + delta); + } + + @Override + public T get(int index) { + return data.get(index); + } + + @Override + public T set(int index, T element) { + return data.set(index, element); + } + + @Override + public boolean add(T element) { + return data.add(element); + } + + @Override + public void add(int index, T element) { + data.add(index, element); + } + + @Override + public T remove(int index) { + return data.remove(index); + } + + @Override + public @Nullable Object[] toArray() { + return data.toArray(); } }; } @@ -363,7 +407,8 @@ public int size() { * equals. This is used for testing unmodifiable collections of map entries; for example, it * should not be possible to access the raw (modifiable) map entry via a nefarious equals method. */ - public static Entry nefariousMapEntry(K key, V value) { + public static + Entry nefariousMapEntry(K key, V value) { return new Entry() { @Override public K getKey() { @@ -382,7 +427,7 @@ public V setValue(V value) { @SuppressWarnings("unchecked") @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof Entry) { Entry e = (Entry) o; e.setValue(value); // muhahaha! @@ -406,7 +451,7 @@ public String toString() { }; } - static List castOrCopyToList(Iterable iterable) { + static List castOrCopyToList(Iterable iterable) { if (iterable instanceof List) { return (List) iterable; } @@ -417,18 +462,12 @@ static List castOrCopyToList(Iterable iterable) { return list; } - private static final Comparator NATURAL_ORDER = - new Comparator() { - @SuppressWarnings("unchecked") // assume any Comparable is Comparable - @Override - public int compare(Comparable left, Comparable right) { - return left.compareTo(right); - } - }; - - public static Iterable> orderEntriesByKey( - List> insertionOrder) { - sort(insertionOrder, Helpers.entryComparator(NATURAL_ORDER)); + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 + public static + Iterable> orderEntriesByKey(List> insertionOrder) { + @SuppressWarnings("unchecked") // assume any Comparable is Comparable + Comparator keyComparator = (Comparator) Comparable::compareTo; + sort(insertionOrder, entryComparator(keyComparator)); return insertionOrder; } @@ -444,7 +483,7 @@ public static Iterable> orderEntriesByKey( * values, it lies outside the submap/submultiset ranges we test, and the variety of tests that * exercise null handling fail on those subcollections. */ - public abstract static class NullsBefore implements Comparator, Serializable { + public abstract static class NullsBefore implements Comparator<@Nullable String>, Serializable { /* * We don't serialize this class in GWT, so we don't care about whether GWT will serialize this * field. @@ -460,7 +499,7 @@ protected NullsBefore(String justAfterNull) { } @Override - public int compare(String lhs, String rhs) { + public int compare(@Nullable String lhs, @Nullable String rhs) { if (lhs == rhs) { return 0; } @@ -484,7 +523,7 @@ public int compare(String lhs, String rhs) { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NullsBefore) { NullsBefore other = (NullsBefore) obj; return justAfterNull.equals(other.justAfterNull); @@ -514,6 +553,7 @@ private NullsBeforeTwo() { } } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getMethod(Class clazz, String name) { try { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/IgnoreJRERequirement.java b/android/guava-testlib/src/com/google/common/collect/testing/IgnoreJRERequirement.java new file mode 100644 index 000000000000..ac9e70cac1dc --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/IgnoreJRERequirement.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect.testing; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; +import org.jspecify.annotations.NullMarked; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@NullMarked +@interface IgnoreJRERequirement {} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/IteratorFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/IteratorFeature.java index c447e2922e31..c1e502272dea 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/IteratorFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/IteratorFeature.java @@ -16,10 +16,13 @@ package com.google.common.collect.testing; +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Collections.unmodifiableSet; + import com.google.common.annotations.GwtCompatible; -import java.util.Collections; -import java.util.EnumSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.ListIterator; import java.util.Set; @@ -49,12 +52,12 @@ public enum IteratorFeature { * A set containing none of the optional features of the {@link Iterator} or {@link ListIterator} * interfaces. */ - public static final Set UNMODIFIABLE = Collections.emptySet(); + public static final Set UNMODIFIABLE = emptySet(); /** * A set containing all of the optional features of the {@link Iterator} and {@link ListIterator} * interfaces. */ public static final Set MODIFIABLE = - Collections.unmodifiableSet(EnumSet.allOf(IteratorFeature.class)); + unmodifiableSet(new LinkedHashSet<>(asList(values()))); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java index fdc418a73745..ecb81dacf5e6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/IteratorTester.java @@ -19,6 +19,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collections; import java.util.Iterator; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A utility for testing an Iterator implementation by comparing its behavior to that of a "known @@ -84,7 +86,9 @@ * @author Chris Povirk */ @GwtCompatible -public abstract class IteratorTester extends AbstractIteratorTester> { +@NullMarked +public abstract class IteratorTester + extends AbstractIteratorTester> { /** * Creates an IteratorTester. * diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ListIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/ListIteratorTester.java index 126eb860780d..96c920f4a2ec 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/ListIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/ListIteratorTester.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; import java.util.ListIterator; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A utility similar to {@link IteratorTester} for testing a {@link ListIterator} against a known @@ -35,7 +37,9 @@ * @author Chris Povirk */ @GwtCompatible -public abstract class ListIteratorTester extends AbstractIteratorTester> { +@NullMarked +public abstract class ListIteratorTester + extends AbstractIteratorTester> { protected ListIteratorTester( int steps, Iterable elementsToInsert, diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java index 061e31038bda..40de8ed16e8a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/ListTestSuiteBuilder.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS; @@ -63,9 +64,10 @@ public static ListTestSuiteBuilder using(TestListGenerator generator) return new ListTestSuiteBuilder().usingGenerator(generator); } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(CollectionSerializationEqualTester.class); testers.add(ListAddAllAtIndexTester.class); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java b/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java index fb09b6e63741..4ba68a54c340 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/MapInterfaceTest.java @@ -16,18 +16,25 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.mapEntry; +import static com.google.common.collect.testing.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; -import java.util.Arrays; +import com.google.common.annotations.J2ktIncompatible; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests representing the contract of {@link Map}. Concrete subclasses of this base class test @@ -42,7 +49,9 @@ // check the order if so. // TODO: Refactor to share code with SetTestBuilder etc. @GwtCompatible -public abstract class MapInterfaceTest extends TestCase { +@NullMarked +public abstract class MapInterfaceTest + extends TestCase { /** A key type that is not assignable to any classes but Object. */ private static final class IncompatibleKeyType { @@ -143,14 +152,15 @@ protected Map makeEitherMap() { } } + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception protected final boolean supportsValuesHashCode(Map map) { // get the first non-null value Collection values = map.values(); for (V value : values) { if (value != null) { try { - value.hashCode(); - } catch (Exception e) { + int unused = value.hashCode(); + } catch (Exception e) { // sneaky checked exception return false; } return true; @@ -183,7 +193,7 @@ protected final void assertInvariants(Map map) { assertTrue(map.containsKey(key)); assertTrue(map.containsValue(value)); assertTrue(valueCollection.contains(value)); - assertTrue(valueCollection.containsAll(Collections.singleton(value))); + assertTrue(valueCollection.containsAll(singleton(value))); assertTrue(entrySet.contains(mapEntry(key, value))); assertTrue(allowsNullKeys || (key != null)); } @@ -221,23 +231,23 @@ protected final void assertInvariants(Map map) { Object[] entrySetToArray1 = entrySet.toArray(); assertEquals(map.size(), entrySetToArray1.length); - assertTrue(Arrays.asList(entrySetToArray1).containsAll(entrySet)); + assertTrue(asList(entrySetToArray1).containsAll(entrySet)); Entry[] entrySetToArray2 = new Entry[map.size() + 2]; entrySetToArray2[map.size()] = mapEntry("foo", 1); assertSame(entrySetToArray2, entrySet.toArray(entrySetToArray2)); assertNull(entrySetToArray2[map.size()]); - assertTrue(Arrays.asList(entrySetToArray2).containsAll(entrySet)); + assertTrue(asList(entrySetToArray2).containsAll(entrySet)); Object[] valuesToArray1 = valueCollection.toArray(); assertEquals(map.size(), valuesToArray1.length); - assertTrue(Arrays.asList(valuesToArray1).containsAll(valueCollection)); + assertTrue(asList(valuesToArray1).containsAll(valueCollection)); Object[] valuesToArray2 = new Object[map.size() + 2]; valuesToArray2[map.size()] = "foo"; assertSame(valuesToArray2, valueCollection.toArray(valuesToArray2)); assertNull(valuesToArray2[map.size()]); - assertTrue(Arrays.asList(valuesToArray2).containsAll(valueCollection)); + assertTrue(asList(valuesToArray2).containsAll(valueCollection)); if (supportsValuesHashCode) { int expectedHash = 0; @@ -276,15 +286,12 @@ public void testClear() { map.clear(); assertTrue(map.isEmpty()); } else { - try { - map.clear(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, map::clear); } assertInvariants(map); } + @J2ktIncompatible // https://youtrack.jetbrains.com/issue/KT-58242/ undefined behavior (crash) public void testContainsKey() { Map map; K unmappedKey; @@ -335,7 +342,6 @@ public void testContainsValue() { public void testEntrySet() { Map map; - Set> entrySet; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { @@ -343,7 +349,7 @@ public void testEntrySet() { } assertInvariants(map); - entrySet = map.entrySet(); + Set> entrySet = map.entrySet(); K unmappedKey; V unmappedValue; try { @@ -368,9 +374,9 @@ public void testEntrySetForEmptyMap() { assertInvariants(map); } + @J2ktIncompatible // https://youtrack.jetbrains.com/issue/KT-58242/ undefined behavior (crash) public void testEntrySetContainsEntryIncompatibleKey() { Map map; - Set> entrySet; try { map = makeEitherMap(); } catch (UnsupportedOperationException e) { @@ -378,7 +384,7 @@ public void testEntrySetContainsEntryIncompatibleKey() { } assertInvariants(map); - entrySet = map.entrySet(); + Set> entrySet = map.entrySet(); V unmappedValue; try { unmappedValue = getValueNotInPopulatedMap(); @@ -397,7 +403,6 @@ public void testEntrySetContainsEntryNullKeyPresent() { return; } Map map; - Set> entrySet; try { map = makeEitherMap(); } catch (UnsupportedOperationException e) { @@ -405,7 +410,7 @@ public void testEntrySetContainsEntryNullKeyPresent() { } assertInvariants(map); - entrySet = map.entrySet(); + Set> entrySet = map.entrySet(); V unmappedValue; try { unmappedValue = getValueNotInPopulatedMap(); @@ -414,14 +419,14 @@ public void testEntrySetContainsEntryNullKeyPresent() { } map.put(null, unmappedValue); - Entry entry = mapEntry(null, unmappedValue); + Entry<@Nullable K, V> entry = mapEntry(null, unmappedValue); assertTrue(entrySet.contains(entry)); - assertFalse(entrySet.contains(mapEntry(null, null))); + Entry<@Nullable K, @Nullable V> nonEntry = mapEntry(null, null); + assertFalse(entrySet.contains(nonEntry)); } public void testEntrySetContainsEntryNullKeyMissing() { Map map; - Set> entrySet; try { map = makeEitherMap(); } catch (UnsupportedOperationException e) { @@ -429,21 +434,22 @@ public void testEntrySetContainsEntryNullKeyMissing() { } assertInvariants(map); - entrySet = map.entrySet(); + Set> entrySet = map.entrySet(); V unmappedValue; try { unmappedValue = getValueNotInPopulatedMap(); } catch (UnsupportedOperationException e) { return; } - Entry entry = mapEntry(null, unmappedValue); + Entry<@Nullable K, V> nullKeyEntry = mapEntry(null, unmappedValue); try { - assertFalse(entrySet.contains(entry)); + assertFalse(entrySet.contains(nullKeyEntry)); } catch (NullPointerException e) { assertFalse(allowsNullKeys); } + Entry<@Nullable K, @Nullable V> nullKeyValueEntry = mapEntry(null, null); try { - assertFalse(entrySet.contains(mapEntry(null, null))); + assertFalse(entrySet.contains(nullKeyValueEntry)); } catch (NullPointerException e) { assertFalse(allowsNullKeys && allowsNullValues); } @@ -462,7 +468,7 @@ public void testEntrySetIteratorRemove() { if (supportsIteratorRemove) { int initialSize = map.size(); Entry entry = iterator.next(); - Entry entryCopy = Helpers.mapEntry(entry.getKey(), entry.getValue()); + Entry entryCopy = mapEntry(entry.getKey(), entry.getValue()); iterator.remove(); assertEquals(initialSize - 1, map.size()); @@ -471,18 +477,10 @@ public void testEntrySetIteratorRemove() { // iterator.remove(). assertFalse(entrySet.contains(entryCopy)); assertInvariants(map); - try { - iterator.remove(); - fail("Expected IllegalStateException."); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iterator::remove); } else { - try { - iterator.next(); - iterator.remove(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + iterator.next(); + assertThrows(UnsupportedOperationException.class, iterator::remove); } assertInvariants(map); } @@ -502,11 +500,8 @@ public void testEntrySetRemove() { assertTrue(didRemove); assertEquals(initialSize - 1, map.size()); } else { - try { - entrySet.remove(entrySet.iterator().next()); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> entrySet.remove(entrySet.iterator().next())); } assertInvariants(map); } @@ -571,7 +566,6 @@ public void testEntrySetRemoveNullKeyPresent() { return; } Map map; - Set> entrySet; try { map = makeEitherMap(); } catch (UnsupportedOperationException e) { @@ -579,7 +573,7 @@ public void testEntrySetRemoveNullKeyPresent() { } assertInvariants(map); - entrySet = map.entrySet(); + Set> entrySet = map.entrySet(); V unmappedValue; try { unmappedValue = getValueNotInPopulatedMap(); @@ -590,7 +584,7 @@ public void testEntrySetRemoveNullKeyPresent() { map.put(null, unmappedValue); assertEquals(unmappedValue, map.get(null)); assertTrue(map.containsKey(null)); - Entry entry = mapEntry(null, unmappedValue); + Entry<@Nullable K, V> entry = mapEntry(null, unmappedValue); assertTrue(entrySet.remove(entry)); assertNull(map.get(null)); assertFalse(map.containsKey(null)); @@ -605,7 +599,7 @@ public void testEntrySetRemoveNullKeyMissing() { } Set> entrySet = map.entrySet(); - Entry entry = mapEntry(null, getValueNotInPopulatedMap()); + Entry<@Nullable K, V> entry = mapEntry(null, getValueNotInPopulatedMap()); int initialSize = map.size(); if (supportsRemove) { try { @@ -641,8 +635,7 @@ public void testEntrySetRemoveAll() { // We use a copy of "entryToRemove" in the assertion because "entryToRemove" might be // invalidated and have undefined behavior after entrySet.removeAll(entriesToRemove), // for example entryToRemove.getValue() might be null. - Entry entryToRemoveCopy = - Helpers.mapEntry(entryToRemove.getKey(), entryToRemove.getValue()); + Entry entryToRemoveCopy = mapEntry(entryToRemove.getKey(), entryToRemove.getValue()); int initialSize = map.size(); boolean didRemove = entrySet.removeAll(entriesToRemove); @@ -653,11 +646,7 @@ public void testEntrySetRemoveAll() { // have undefined behavior after entrySet.removeAll(entriesToRemove), assertFalse(entrySet.contains(entryToRemoveCopy)); } else { - try { - entrySet.removeAll(entriesToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entrySet.removeAll(entriesToRemove)); } assertInvariants(map); } @@ -672,11 +661,7 @@ public void testEntrySetRemoveAllNullFromEmpty() { Set> entrySet = map.entrySet(); if (supportsRemove) { - try { - entrySet.removeAll(null); - fail("Expected NullPointerException."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> entrySet.removeAll(null)); } else { try { entrySet.removeAll(null); @@ -697,7 +682,10 @@ public void testEntrySetRetainAll() { } Set> entrySet = map.entrySet(); - Set> entriesToRetain = singleton(entrySet.iterator().next()); + Entry originalEntry = entrySet.iterator().next(); + // Copy the Entry, as discussed in testEntrySetRemoveAll. + Set> entriesToRetain = + singleton(mapEntry(originalEntry.getKey(), originalEntry.getValue())); if (supportsRemove) { boolean shouldRemove = (entrySet.size() > entriesToRetain.size()); boolean didRemove = entrySet.retainAll(entriesToRetain); @@ -707,11 +695,7 @@ public void testEntrySetRetainAll() { assertTrue(entrySet.contains(entry)); } } else { - try { - entrySet.retainAll(entriesToRetain); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entrySet.retainAll(entriesToRetain)); } assertInvariants(map); } @@ -729,7 +713,7 @@ public void testEntrySetRetainAllNullFromEmpty() { try { entrySet.retainAll(null); // Returning successfully is not ideal, but tolerated. - } catch (NullPointerException expected) { + } catch (NullPointerException tolerated) { } } else { try { @@ -755,11 +739,7 @@ public void testEntrySetClear() { entrySet.clear(); assertTrue(entrySet.isEmpty()); } else { - try { - entrySet.clear(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, entrySet::clear); } assertInvariants(map); } @@ -768,9 +748,9 @@ public void testEntrySetAddAndAddAll() { Map map = makeEitherMap(); Set> entrySet = map.entrySet(); - Entry entryToAdd = mapEntry(null, null); + Entry<@Nullable K, @Nullable V> entryToAdd = mapEntry(null, null); try { - entrySet.add(entryToAdd); + entrySet.add((Entry) entryToAdd); fail("Expected UnsupportedOperationException or NullPointerException."); } catch (UnsupportedOperationException | NullPointerException e) { // Expected. @@ -778,7 +758,7 @@ public void testEntrySetAddAndAddAll() { assertInvariants(map); try { - entrySet.addAll(singleton(entryToAdd)); + entrySet.addAll(singleton((Entry) entryToAdd)); fail("Expected UnsupportedOperationException or NullPointerException."); } catch (UnsupportedOperationException | NullPointerException e) { // Expected. @@ -847,7 +827,7 @@ public void testEqualsForEqualMap() { // Explicitly call `equals`; `assertEquals` might return fast assertTrue(map.equals(map)); assertTrue(makePopulatedMap().equals(map)); - assertFalse(map.equals(Collections.emptyMap())); + assertFalse(map.equals(emptyMap())); // no-inspection ObjectEqualsNull assertFalse(map.equals(null)); } @@ -899,8 +879,8 @@ public void testEqualsForEmptyMap() { // Explicitly call `equals`; `assertEquals` might return fast assertTrue(map.equals(map)); assertTrue(makeEmptyMap().equals(map)); - assertEquals(Collections.emptyMap(), map); - assertFalse(map.equals(Collections.emptySet())); + assertEquals(emptyMap(), map); + assertFalse(map.equals(emptySet())); // noinspection ObjectEqualsNull assertFalse(map.equals(null)); } @@ -994,18 +974,13 @@ public void testPutNewKey() { assertEquals(initialSize + 1, map.size()); assertNull(oldValue); } else { - try { - map.put(keyToPut, valueToPut); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.put(keyToPut, valueToPut)); } assertInvariants(map); } public void testPutExistingKey() { Map map; - K keyToPut; V valueToPut; try { map = makePopulatedMap(); @@ -1013,7 +988,7 @@ public void testPutExistingKey() { } catch (UnsupportedOperationException e) { return; } - keyToPut = map.keySet().iterator().next(); + K keyToPut = map.keySet().iterator().next(); if (supportsPut) { int initialSize = map.size(); map.put(keyToPut, valueToPut); @@ -1022,11 +997,7 @@ public void testPutExistingKey() { assertTrue(map.containsValue(valueToPut)); assertEquals(initialSize, map.size()); } else { - try { - map.put(keyToPut, valueToPut); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.put(keyToPut, valueToPut)); } assertInvariants(map); } @@ -1050,11 +1021,7 @@ public void testPutNullKey() { assertTrue(map.containsKey(null)); assertTrue(map.containsValue(valueToPut)); } else { - try { - map.put(null, valueToPut); - fail("Expected RuntimeException"); - } catch (RuntimeException expected) { - } + assertThrows(RuntimeException.class, () -> map.put(null, valueToPut)); } assertInvariants(map); } @@ -1080,11 +1047,7 @@ public void testPutNullValue() { assertTrue(map.containsValue(null)); assertEquals(initialSize + 1, map.size()); } else { - try { - map.put(keyToPut, null); - fail("Expected RuntimeException"); - } catch (RuntimeException expected) { - } + assertThrows(RuntimeException.class, () -> map.put(keyToPut, null)); } assertInvariants(map); } @@ -1111,11 +1074,7 @@ public void testPutNullValueForExistingKey() { assertTrue(map.containsValue(null)); assertEquals(initialSize, map.size()); } else { - try { - map.put(keyToPut, null); - fail("Expected RuntimeException"); - } catch (RuntimeException expected) { - } + assertThrows(RuntimeException.class, () -> map.put(keyToPut, null)); } assertInvariants(map); } @@ -1130,7 +1089,7 @@ public void testPutAllNewKey() { } catch (UnsupportedOperationException e) { return; } - Map mapToPut = Collections.singletonMap(keyToPut, valueToPut); + Map mapToPut = singletonMap(keyToPut, valueToPut); if (supportsPut) { int initialSize = map.size(); map.putAll(mapToPut); @@ -1139,18 +1098,13 @@ public void testPutAllNewKey() { assertTrue(map.containsValue(valueToPut)); assertEquals(initialSize + 1, map.size()); } else { - try { - map.putAll(mapToPut); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.putAll(mapToPut)); } assertInvariants(map); } public void testPutAllExistingKey() { Map map; - K keyToPut; V valueToPut; try { map = makePopulatedMap(); @@ -1158,8 +1112,8 @@ public void testPutAllExistingKey() { } catch (UnsupportedOperationException e) { return; } - keyToPut = map.keySet().iterator().next(); - Map mapToPut = Collections.singletonMap(keyToPut, valueToPut); + K keyToPut = map.keySet().iterator().next(); + Map mapToPut = singletonMap(keyToPut, valueToPut); int initialSize = map.size(); if (supportsPut) { map.putAll(mapToPut); @@ -1167,11 +1121,7 @@ public void testPutAllExistingKey() { assertTrue(map.containsKey(keyToPut)); assertTrue(map.containsValue(valueToPut)); } else { - try { - map.putAll(mapToPut); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.putAll(mapToPut)); } assertEquals(initialSize, map.size()); assertInvariants(map); @@ -1179,13 +1129,12 @@ public void testPutAllExistingKey() { public void testRemove() { Map map; - K keyToRemove; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { return; } - keyToRemove = map.keySet().iterator().next(); + K keyToRemove = map.keySet().iterator().next(); if (supportsRemove) { int initialSize = map.size(); V expectedValue = map.get(keyToRemove); @@ -1194,11 +1143,7 @@ public void testRemove() { assertFalse(map.containsKey(keyToRemove)); assertEquals(initialSize - 1, map.size()); } else { - try { - map.remove(keyToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.remove(keyToRemove)); } assertInvariants(map); } @@ -1217,11 +1162,7 @@ public void testRemoveMissingKey() { assertNull(map.remove(keyToRemove)); assertEquals(initialSize, map.size()); } else { - try { - map.remove(keyToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.remove(keyToRemove)); } assertInvariants(map); } @@ -1246,11 +1187,7 @@ public void testKeySetRemove() { assertEquals(initialSize - 1, map.size()); assertFalse(map.containsKey(key)); } else { - try { - keys.remove(key); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> keys.remove(key)); } assertInvariants(map); } @@ -1267,15 +1204,11 @@ public void testKeySetRemoveAll() { K key = keys.iterator().next(); if (supportsRemove) { int initialSize = map.size(); - assertTrue(keys.removeAll(Collections.singleton(key))); + assertTrue(keys.removeAll(singleton(key))); assertEquals(initialSize - 1, map.size()); assertFalse(map.containsKey(key)); } else { - try { - keys.removeAll(Collections.singleton(key)); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> keys.removeAll(singleton(key))); } assertInvariants(map); } @@ -1291,15 +1224,11 @@ public void testKeySetRetainAll() { Set keys = map.keySet(); K key = keys.iterator().next(); if (supportsRemove) { - keys.retainAll(Collections.singleton(key)); + keys.retainAll(singleton(key)); assertEquals(1, map.size()); assertTrue(map.containsKey(key)); } else { - try { - keys.retainAll(Collections.singleton(key)); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> keys.retainAll(singleton(key))); } assertInvariants(map); } @@ -1317,11 +1246,7 @@ public void testKeySetClear() { keySet.clear(); assertTrue(keySet.isEmpty()); } else { - try { - keySet.clear(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, keySet::clear); } assertInvariants(map); } @@ -1336,11 +1261,7 @@ public void testKeySetRemoveAllNullFromEmpty() { Set keySet = map.keySet(); if (supportsRemove) { - try { - keySet.removeAll(null); - fail("Expected NullPointerException."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> keySet.removeAll(null)); } else { try { keySet.removeAll(null); @@ -1365,7 +1286,7 @@ public void testKeySetRetainAllNullFromEmpty() { try { keySet.retainAll(null); // Returning successfully is not ideal, but tolerated. - } catch (NullPointerException expected) { + } catch (NullPointerException tolerated) { } } else { try { @@ -1380,7 +1301,6 @@ public void testKeySetRetainAllNullFromEmpty() { public void testValues() { Map map; - Collection valueCollection; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { @@ -1388,7 +1308,7 @@ public void testValues() { } assertInvariants(map); - valueCollection = map.values(); + Collection valueCollection = map.values(); V unmappedValue; try { unmappedValue = getValueNotInPopulatedMap(); @@ -1419,18 +1339,10 @@ public void testValuesIteratorRemove() { // removed value, because the underlying map can have multiple mappings // to the same value.) assertInvariants(map); - try { - iterator.remove(); - fail("Expected IllegalStateException."); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iterator::remove); } else { - try { - iterator.next(); - iterator.remove(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + iterator.next(); + assertThrows(UnsupportedOperationException.class, iterator::remove); } assertInvariants(map); } @@ -1452,11 +1364,9 @@ public void testValuesRemove() { // removed value, because the underlying map can have multiple mappings // to the same value.) } else { - try { - valueCollection.remove(valueCollection.iterator().next()); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> valueCollection.remove(valueCollection.iterator().next())); } assertInvariants(map); } @@ -1505,11 +1415,8 @@ public void testValuesRemoveAll() { assertFalse(valuesToRemove.contains(value)); } } else { - try { - valueCollection.removeAll(valuesToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> valueCollection.removeAll(valuesToRemove)); } assertInvariants(map); } @@ -1527,7 +1434,7 @@ public void testValuesRemoveAllNullFromEmpty() { try { values.removeAll(null); // Returning successfully is not ideal, but tolerated. - } catch (NullPointerException expected) { + } catch (NullPointerException tolerated) { } } else { try { @@ -1559,11 +1466,8 @@ public void testValuesRetainAll() { assertTrue(valuesToRetain.contains(value)); } } else { - try { - valueCollection.retainAll(valuesToRetain); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> valueCollection.retainAll(valuesToRetain)); } assertInvariants(map); } @@ -1581,7 +1485,7 @@ public void testValuesRetainAllNullFromEmpty() { try { values.retainAll(null); // Returning successfully is not ideal, but tolerated. - } catch (NullPointerException expected) { + } catch (NullPointerException tolerated) { } } else { try { @@ -1607,16 +1511,8 @@ public void testValuesClear() { valueCollection.clear(); assertTrue(valueCollection.isEmpty()); } else { - try { - valueCollection.clear(); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, valueCollection::clear); } assertInvariants(map); } - - static Entry mapEntry(K key, V value) { - return Collections.singletonMap(key, value).entrySet().iterator().next(); - } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/MapTestSuiteBuilder.java index 7b8e40a2b1aa..f874387da2ae 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/MapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/MapTestSuiteBuilder.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing; import static com.google.common.collect.testing.DerivedCollectionGenerators.keySetGenerator; +import static com.google.common.collect.testing.Helpers.copyToSet; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.DerivedCollectionGenerators.MapEntrySetGenerator; @@ -62,7 +63,7 @@ public static MapTestSuiteBuilder using(TestMapGenerator gene return new MapTestSuiteBuilder().usingGenerator(generator); } - @SuppressWarnings("unchecked") // Class parameters must be raw. + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { return Arrays.>asList( @@ -153,7 +154,7 @@ protected CollectionTestSuiteBuilder createDerivedValueCollectionSuite( } private static Set> computeReserializedMapFeatures(Set> mapFeatures) { - Set> derivedFeatures = Helpers.copyToSet(mapFeatures); + Set> derivedFeatures = copyToSet(mapFeatures); derivedFeatures.remove(CollectionFeature.SERIALIZABLE); derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); return derivedFeatures; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MinimalCollection.java b/android/guava-testlib/src/com/google/common/collect/testing/MinimalCollection.java index 2adf0725147d..93645d9ea7d2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/MinimalCollection.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/MinimalCollection.java @@ -16,11 +16,16 @@ package com.google.common.collect.testing; +import static java.lang.System.arraycopy; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; import java.util.AbstractCollection; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A simplistic collection which implements only the bare minimum allowed by the spec, and throws @@ -29,24 +34,26 @@ * @author Kevin Bourrillion */ @GwtCompatible -public class MinimalCollection extends AbstractCollection { +@NullMarked +public class MinimalCollection extends AbstractCollection { // TODO: expose allow nulls parameter? - public static MinimalCollection of(E... contents) { + public static MinimalCollection of(E... contents) { return new MinimalCollection<>(Object.class, true, contents); } // TODO: use this - public static MinimalCollection ofClassAndContents(Class type, E... contents) { + public static MinimalCollection ofClassAndContents( + Class type, E... contents) { return new MinimalCollection<>(type, true, contents); } private final E[] contents; - private final Class type; + private final Class type; private final boolean allowNulls; // Package-private so that it can be extended. - MinimalCollection(Class type, boolean allowNulls, E... contents) { + MinimalCollection(Class type, boolean allowNulls, E... contents) { // TODO: consider making it shuffle the contents to test iteration order. this.contents = Platform.clone(contents); this.type = type; @@ -67,7 +74,7 @@ public int size() { } @Override - public boolean contains(Object object) { + public boolean contains(@Nullable Object object) { if (!allowNulls) { // behave badly if (object == null) { @@ -75,7 +82,7 @@ public boolean contains(Object object) { } } Platform.checkCast(type, object); // behave badly - return Arrays.asList(contents).contains(object); + return asList(contents).contains(object); } @Override @@ -93,13 +100,13 @@ public boolean containsAll(Collection collection) { @Override public Iterator iterator() { - return Arrays.asList(contents).iterator(); + return asList(contents).iterator(); } @Override - public Object[] toArray() { - Object[] result = new Object[contents.length]; - System.arraycopy(contents, 0, result, 0, contents.length); + public @Nullable Object[] toArray() { + @Nullable Object[] result = new @Nullable Object[contents.length]; + arraycopy(contents, 0, result, 0, contents.length); return result; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MinimalIterable.java b/android/guava-testlib/src/com/google/common/collect/testing/MinimalIterable.java index 15d48dbbd75b..1cc49efa3dcc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/MinimalIterable.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/MinimalIterable.java @@ -16,10 +16,12 @@ package com.google.common.collect.testing; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import org.jspecify.annotations.Nullable; /** * An implementation of {@code Iterable} which throws an exception on all invocations of the {@link @@ -47,11 +49,11 @@ * @author Kevin Bourrillion */ @GwtCompatible -public final class MinimalIterable implements Iterable { +public final class MinimalIterable implements Iterable { /** Returns an iterable whose iterator returns the given elements in order. */ - public static MinimalIterable of(E... elements) { + public static MinimalIterable of(E... elements) { // Make sure to get an unmodifiable iterator - return new MinimalIterable<>(Arrays.asList(elements).iterator()); + return new MinimalIterable<>(asList(elements).iterator()); } /** @@ -59,11 +61,11 @@ public static MinimalIterable of(E... elements) { * out of the source collection at the time this method is called. */ @SuppressWarnings("unchecked") // Es come in, Es go out - public static MinimalIterable from(Collection elements) { + public static MinimalIterable from(Collection elements) { return (MinimalIterable) of(elements.toArray()); } - private Iterator iterator; + private @Nullable Iterator iterator; private MinimalIterable(Iterator iterator) { this.iterator = iterator; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/MinimalSet.java b/android/guava-testlib/src/com/google/common/collect/testing/MinimalSet.java index 9a87d244d01f..db09a4edd544 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/MinimalSet.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/MinimalSet.java @@ -16,12 +16,16 @@ package com.google.common.collect.testing; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A simplistic set which implements the bare minimum so that it can be used in tests without @@ -31,20 +35,21 @@ * @author Regina O'Dell */ @GwtCompatible -public class MinimalSet extends MinimalCollection implements Set { +@NullMarked +public class MinimalSet extends MinimalCollection implements Set { @SuppressWarnings("unchecked") // empty Object[] as E[] - public static MinimalSet of(E... contents) { - return ofClassAndContents(Object.class, (E[]) new Object[0], Arrays.asList(contents)); + public static MinimalSet of(E... contents) { + return ofClassAndContents(Object.class, (E[]) new Object[0], asList(contents)); } @SuppressWarnings("unchecked") // empty Object[] as E[] - public static MinimalSet from(Collection contents) { + public static MinimalSet from(Collection contents) { return ofClassAndContents(Object.class, (E[]) new Object[0], contents); } - public static MinimalSet ofClassAndContents( - Class type, E[] emptyArrayForContents, Iterable contents) { + public static MinimalSet ofClassAndContents( + Class type, E[] emptyArrayForContents, Iterable contents) { List setContents = new ArrayList<>(); for (E e : contents) { if (!setContents.contains(e)) { @@ -54,7 +59,7 @@ public static MinimalSet ofClassAndContents( return new MinimalSet<>(type, setContents.toArray(emptyArrayForContents)); } - private MinimalSet(Class type, E... contents) { + private MinimalSet(Class type, E... contents) { super(type, true, contents); } @@ -63,7 +68,7 @@ private MinimalSet(Class type, E... contents) { */ @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Set) { Set that = (Set) object; return (this.size() == that.size()) && this.containsAll(that); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/NavigableMapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/NavigableMapTestSuiteBuilder.java index 0e1a229208e8..4e55d7abde7c 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/NavigableMapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/NavigableMapTestSuiteBuilder.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing; import static com.google.common.collect.testing.Helpers.castOrCopyToList; +import static com.google.common.collect.testing.Helpers.copyToList; import static java.util.Collections.reverse; import com.google.common.annotations.GwtIncompatible; @@ -46,9 +47,10 @@ public static NavigableMapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(NavigableMapNavigationTester.class); return testers; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/NavigableSetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/NavigableSetTestSuiteBuilder.java index 4c86ab46206c..31a1f1858f15 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/NavigableSetTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/NavigableSetTestSuiteBuilder.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.DESCENDING_VIEW; import static com.google.common.collect.testing.features.CollectionFeature.SUBSET_VIEW; @@ -143,9 +144,10 @@ public Set create(Object... elements) { .createTestSuite(); } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(NavigableSetNavigationTester.class); return testers; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/OneSizeGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/OneSizeGenerator.java index 1ff3facc4fd4..4baec8023be7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/OneSizeGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/OneSizeGenerator.java @@ -16,12 +16,15 @@ package com.google.common.collect.testing; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionSize; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Generator for collection of a particular size. @@ -29,7 +32,9 @@ * @author George van den Driessche */ @GwtCompatible -public final class OneSizeGenerator implements OneSizeTestContainerGenerator { +@NullMarked +public final class OneSizeGenerator + implements OneSizeTestContainerGenerator { private final TestContainerGenerator generator; private final CollectionSize collectionSize; @@ -67,9 +72,8 @@ public T createTestSubject() { @Override public Collection getSampleElements(int howMany) { SampleElements samples = samples(); - @SuppressWarnings("unchecked") List allSampleElements = - Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); + asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); return new ArrayList<>(allSampleElements.subList(0, howMany)); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/OneSizeTestContainerGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/OneSizeTestContainerGenerator.java index 7e727cd8242b..85feb9d3edfc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/OneSizeTestContainerGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/OneSizeTestContainerGenerator.java @@ -19,6 +19,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionSize; import java.util.Collection; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * The subject-generator interface accepted by Collection testers, for testing a Collection at one @@ -31,7 +33,8 @@ * @author George van den Driessche */ @GwtCompatible -public interface OneSizeTestContainerGenerator +@NullMarked +public interface OneSizeTestContainerGenerator extends TestSubjectGenerator, TestContainerGenerator { TestContainerGenerator getInnerGenerator(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/PerCollectionSizeTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/PerCollectionSizeTestSuiteBuilder.java index c0068b4f9274..f9a68a7d0693 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/PerCollectionSizeTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/PerCollectionSizeTestSuiteBuilder.java @@ -16,13 +16,15 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static com.google.common.collect.testing.features.FeatureUtil.addImpliedFeatures; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.Feature; -import com.google.common.collect.testing.features.FeatureUtil; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.logging.Logger; @@ -58,7 +60,8 @@ public TestSuite createTestSuite() { String name = getName(); // Copy this set, so we can modify it. - Set> features = Helpers.copyToSet(getFeatures()); + Set> features = copyToSet(getFeatures()); + @SuppressWarnings("rawtypes") // class literals List> testers = getTesters(); logger.fine(" Testing: " + name); @@ -68,9 +71,8 @@ public TestSuite createTestSuite() { sizesToTest.retainAll(features); features.removeAll(sizesToTest); - FeatureUtil.addImpliedFeatures(sizesToTest); - sizesToTest.retainAll( - Arrays.asList(CollectionSize.ZERO, CollectionSize.ONE, CollectionSize.SEVERAL)); + addImpliedFeatures(sizesToTest); + sizesToTest.retainAll(asList(CollectionSize.ZERO, CollectionSize.ONE, CollectionSize.SEVERAL)); logger.fine(" Sizes: " + formatFeatureSet(sizesToTest)); @@ -88,7 +90,7 @@ public TestSuite createTestSuite() { "%s [collection size: %s]", name, collectionSize.toString().toLowerCase()); OneSizeGenerator oneSizeGenerator = new OneSizeGenerator<>(getSubjectGenerator(), (CollectionSize) collectionSize); - Set> oneSizeFeatures = Helpers.copyToSet(features); + Set> oneSizeFeatures = copyToSet(features); oneSizeFeatures.add(collectionSize); Set oneSizeSuppressedTests = getSuppressedTests(); @@ -120,12 +122,15 @@ protected List createDerivedSuites( private static final class OneSizeTestSuiteBuilder extends FeatureSpecificTestSuiteBuilder< OneSizeTestSuiteBuilder, OneSizeGenerator> { + @SuppressWarnings("rawtypes") // class literals private final List> testers; + @SuppressWarnings("rawtypes") // class literals public OneSizeTestSuiteBuilder(List> testers) { this.testers = testers; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { return testers; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/Platform.java b/android/guava-testlib/src/com/google/common/collect/testing/Platform.java index a2c020c915d4..cfc7ac3b284e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/Platform.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/Platform.java @@ -34,7 +34,7 @@ static T[] clone(T[] array) { // Class.cast is not supported in GWT. This method is a no-op in GWT. static void checkCast(Class clazz, Object obj) { - clazz.cast(obj); + Object unused = clazz.cast(obj); } static String format(String template, Object... args) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/QueueTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/QueueTestSuiteBuilder.java index ac62d44b6999..304cc2caf41a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/QueueTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/QueueTestSuiteBuilder.java @@ -22,6 +22,7 @@ import com.google.common.collect.testing.testers.QueuePeekTester; import com.google.common.collect.testing.testers.QueuePollTester; import com.google.common.collect.testing.testers.QueueRemoveTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ArrayList; import java.util.List; @@ -45,11 +46,13 @@ public static QueueTestSuiteBuilder using(TestQueueGenerator generator * collection that's both a queue and a list, to avoid running the common collection tests twice. * By default, collection tests do run. */ + @CanIgnoreReturnValue public QueueTestSuiteBuilder skipCollectionTests() { runCollectionTests = false; return this; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { List> testers = new ArrayList<>(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ReflectionFreeAssertThrows.java b/android/guava-testlib/src/com/google/common/collect/testing/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..f38500e53388 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect.testing; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/ReserializingTestCollectionGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/ReserializingTestCollectionGenerator.java index 923f56fd45c0..b1e58aaac677 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/ReserializingTestCollectionGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/ReserializingTestCollectionGenerator.java @@ -59,9 +59,8 @@ static T reserialize(T object) { ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())); return (T) in.readObject(); } catch (IOException | ClassNotFoundException e) { - Helpers.fail(e, e.getMessage()); + throw new AssertionError(e); } - throw new AssertionError("not reachable"); } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeMap.java b/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeMap.java index 5856e3b543f9..b9fc32b941d9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeMap.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeMap.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtIncompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.util.AbstractSet; import java.util.Collection; @@ -28,6 +29,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import org.jspecify.annotations.Nullable; /** * A wrapper around {@code TreeMap} that aggressively checks to see if keys are mutually comparable. @@ -75,12 +77,12 @@ private SafeTreeMap(NavigableMap delegate) { } @Override - public Entry ceilingEntry(K key) { + public @Nullable Entry ceilingEntry(K key) { return delegate.ceilingEntry(checkValid(key)); } @Override - public K ceilingKey(K key) { + public @Nullable K ceilingKey(K key) { return delegate.ceilingKey(checkValid(key)); } @@ -89,7 +91,6 @@ public void clear() { delegate.clear(); } - @SuppressWarnings("unchecked") @Override public Comparator comparator() { Comparator comparator = delegate.comparator(); @@ -162,7 +163,7 @@ public void clear() { } @Override - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return delegate.firstEntry(); } @@ -172,17 +173,17 @@ public K firstKey() { } @Override - public Entry floorEntry(K key) { + public @Nullable Entry floorEntry(K key) { return delegate.floorEntry(checkValid(key)); } @Override - public K floorKey(K key) { + public @Nullable K floorKey(K key) { return delegate.floorKey(checkValid(key)); } @Override - public V get(Object key) { + public @Nullable V get(Object key) { return delegate.get(checkValid(key)); } @@ -197,12 +198,12 @@ public NavigableMap headMap(K toKey, boolean inclusive) { } @Override - public Entry higherEntry(K key) { + public @Nullable Entry higherEntry(K key) { return delegate.higherEntry(checkValid(key)); } @Override - public K higherKey(K key) { + public @Nullable K higherKey(K key) { return delegate.higherKey(checkValid(key)); } @@ -217,7 +218,7 @@ public NavigableSet keySet() { } @Override - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return delegate.lastEntry(); } @@ -227,12 +228,12 @@ public K lastKey() { } @Override - public Entry lowerEntry(K key) { + public @Nullable Entry lowerEntry(K key) { return delegate.lowerEntry(checkValid(key)); } @Override - public K lowerKey(K key) { + public @Nullable K lowerKey(K key) { return delegate.lowerKey(checkValid(key)); } @@ -242,17 +243,17 @@ public NavigableSet navigableKeySet() { } @Override - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return delegate.pollFirstEntry(); } @Override - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return delegate.pollLastEntry(); } @Override - public V put(K key, V value) { + public @Nullable V put(K key, V value) { return delegate.put(checkValid(key), value); } @@ -265,7 +266,7 @@ public void putAll(Map map) { } @Override - public V remove(Object key) { + public @Nullable V remove(Object key) { return delegate.remove(checkValid(key)); } @@ -300,16 +301,17 @@ public Collection values() { return delegate.values(); } + @CanIgnoreReturnValue private T checkValid(T t) { // a ClassCastException is what's supposed to happen! @SuppressWarnings("unchecked") K k = (K) t; - comparator().compare(k, k); + int unused = comparator().compare(k, k); return t; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return delegate.equals(obj); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeSet.java b/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeSet.java index 91d9fd9f750d..cbb8b1421aab 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeSet.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SafeTreeSet.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtIncompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.util.Collection; import java.util.Comparator; @@ -24,6 +25,7 @@ import java.util.NavigableSet; import java.util.SortedSet; import java.util.TreeSet; +import org.jspecify.annotations.Nullable; /** * A wrapper around {@code TreeSet} that aggressively checks to see if elements are mutually @@ -81,7 +83,7 @@ public boolean addAll(Collection collection) { } @Override - public E ceiling(E e) { + public @Nullable E ceiling(E e) { return delegate.ceiling(checkValid(e)); } @@ -90,7 +92,6 @@ public void clear() { delegate.clear(); } - @SuppressWarnings("unchecked") @Override public Comparator comparator() { Comparator comparator = delegate.comparator(); @@ -126,7 +127,7 @@ public E first() { } @Override - public E floor(E e) { + public @Nullable E floor(E e) { return delegate.floor(checkValid(e)); } @@ -141,7 +142,7 @@ public NavigableSet headSet(E toElement, boolean inclusive) { } @Override - public E higher(E e) { + public @Nullable E higher(E e) { return delegate.higher(checkValid(e)); } @@ -161,17 +162,17 @@ public E last() { } @Override - public E lower(E e) { + public @Nullable E lower(E e) { return delegate.lower(checkValid(e)); } @Override - public E pollFirst() { + public @Nullable E pollFirst() { return delegate.pollFirst(); } @Override - public E pollLast() { + public @Nullable E pollLast() { return delegate.pollLast(); } @@ -228,16 +229,17 @@ public T[] toArray(T[] a) { return delegate.toArray(a); } + @CanIgnoreReturnValue private T checkValid(T t) { // a ClassCastException is what's supposed to happen! @SuppressWarnings("unchecked") E e = (E) t; - comparator().compare(e, e); + int unused = comparator().compare(e, e); return t; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return delegate.equals(obj); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SampleElements.java b/android/guava-testlib/src/com/google/common/collect/testing/SampleElements.java index 400107dc58fc..1abda002ef39 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SampleElements.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SampleElements.java @@ -16,11 +16,15 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A container class for the five sample elements we need for testing. @@ -28,7 +32,8 @@ * @author Kevin Bourrillion */ @GwtCompatible -public class SampleElements implements Iterable { +@NullMarked +public class SampleElements implements Iterable { // TODO: rename e3, e4 => missing1, missing2 private final E e0; private final E e1; @@ -88,14 +93,14 @@ public Ints() { } } - public static SampleElements> mapEntries( - SampleElements keys, SampleElements values) { + public static + SampleElements> mapEntries(SampleElements keys, SampleElements values) { return new SampleElements<>( - Helpers.mapEntry(keys.e0(), values.e0()), - Helpers.mapEntry(keys.e1(), values.e1()), - Helpers.mapEntry(keys.e2(), values.e2()), - Helpers.mapEntry(keys.e3(), values.e3()), - Helpers.mapEntry(keys.e4(), values.e4())); + mapEntry(keys.e0(), values.e0()), + mapEntry(keys.e1(), values.e1()), + mapEntry(keys.e2(), values.e2()), + mapEntry(keys.e3(), values.e3()), + mapEntry(keys.e4(), values.e4())); } public E e0() { @@ -143,7 +148,7 @@ private static class Collider { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof Collider && ((Collider) obj).value == value; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java index 4ac51a4612f1..ed927bbcb442 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SetTestSuiteBuilder.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS; @@ -48,9 +49,10 @@ public static SetTestSuiteBuilder using(TestSetGenerator generator) { return new SetTestSuiteBuilder().usingGenerator(generator); } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(CollectionSerializationEqualTester.class); testers.add(SetAddAllTester.class); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SortedMapInterfaceTest.java b/android/guava-testlib/src/com/google/common/collect/testing/SortedMapInterfaceTest.java index 03266ab4776d..38f06daab4a7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SortedMapInterfaceTest.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SortedMapInterfaceTest.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import java.util.Iterator; import java.util.Map.Entry; @@ -74,11 +76,7 @@ public void testTailMapWriteThrough() { subMap.put(key, value); assertEquals(secondEntry.getValue(), value); assertEquals(map.get(key), value); - try { - subMap.put(firstEntry.getKey(), value); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> subMap.put(firstEntry.getKey(), value)); } public void testTailMapRemoveThrough() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SortedMapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/SortedMapTestSuiteBuilder.java index 7944aaa6a0b8..e8c46d51d222 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SortedMapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SortedMapTestSuiteBuilder.java @@ -16,7 +16,9 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static java.util.Collections.emptySet; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.DerivedCollectionGenerators.Bound; @@ -24,12 +26,12 @@ import com.google.common.collect.testing.features.Feature; import com.google.common.collect.testing.testers.SortedMapNavigationTester; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import junit.framework.TestSuite; +import org.jspecify.annotations.Nullable; /** * Creates, based on your criteria, a JUnit test suite that exhaustively tests a SortedMap @@ -44,9 +46,10 @@ public static SortedMapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(SortedMapNavigationTester.class); return testers; } @@ -54,7 +57,7 @@ protected List> getTesters() { @Override public TestSuite createTestSuite() { if (!getFeatures().contains(KNOWN_ORDER)) { - List> features = Helpers.copyToList(getFeatures()); + List> features = copyToList(getFeatures()); features.add(KNOWN_ORDER); withFeatures(features); } @@ -88,13 +91,13 @@ protected SetTestSuiteBuilder createDerivedKeySetSuite(TestSetGenerator ke * To avoid infinite recursion, test suites with these marker features won't have derived suites * created for them. */ - enum NoRecurse implements Feature { + enum NoRecurse implements Feature<@Nullable Void> { SUBMAP, DESCENDING; @Override - public Set> getImpliedFeatures() { - return Collections.emptySet(); + public Set> getImpliedFeatures() { + return emptySet(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java index 2835769e33f7..7505392fdf7e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/SortedSetTestSuiteBuilder.java @@ -16,15 +16,22 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.DerivedCollectionGenerators.Bound; import com.google.common.collect.testing.DerivedCollectionGenerators.SortedSetSubsetTestSetGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.Feature; +import com.google.common.collect.testing.testers.CollectionAddAllTester; +import com.google.common.collect.testing.testers.CollectionAddTester; import com.google.common.collect.testing.testers.SortedSetNavigationTester; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import junit.framework.TestSuite; /** @@ -39,9 +46,10 @@ public static SortedSetTestSuiteBuilder using(TestSortedSetGenerator g return builder; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(SortedSetNavigationTester.class); return testers; } @@ -49,7 +57,7 @@ protected List> getTesters() { @Override public TestSuite createTestSuite() { if (!getFeatures().contains(CollectionFeature.KNOWN_ORDER)) { - List> features = Helpers.copyToList(getFeatures()); + List> features = copyToList(getFeatures()); features.add(CollectionFeature.KNOWN_ORDER); withFeatures(features); } @@ -86,13 +94,20 @@ final TestSuite createSubsetSuite( (TestSortedSetGenerator) parentBuilder.getSubjectGenerator().getInnerGenerator(); List> features = new ArrayList<>(parentBuilder.getFeatures()); - features.remove(CollectionFeature.ALLOWS_NULL_VALUES); + Set suppressing = new HashSet<>(parentBuilder.getSuppressedTests()); features.add(CollectionFeature.SUBSET_VIEW); + if (features.remove(CollectionFeature.ALLOWS_NULL_VALUES)) { + // the null value might be out of bounds, so we can't always construct a subset with nulls + features.add(CollectionFeature.ALLOWS_NULL_QUERIES); + // but add null might still be supported if it happens to be within range of the subset + suppressing.add(CollectionAddTester.getAddNullUnsupportedMethod()); + suppressing.add(CollectionAddAllTester.getAddAllNullUnsupportedMethod()); + } return newBuilderUsing(delegate, to, from) .named(parentBuilder.getName() + " subSet " + from + "-" + to) .withFeatures(features) - .suppressing(parentBuilder.getSuppressedTests()) + .suppressing(suppressing) .withSetUp(parentBuilder.getSetUp()) .withTearDown(parentBuilder.getTearDown()) .createTestSuite(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/SpliteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/SpliteratorTester.java new file mode 100644 index 000000000000..a77cc1fde216 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/SpliteratorTester.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.assertEqualInOrder; +import static com.google.common.collect.testing.Platform.format; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableSet; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Ordering; +import com.google.common.primitives.Ints; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterator.OfPrimitive; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Tester for {@code Spliterator} implementations. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ +@GwtCompatible +@NullMarked +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // Users will use this only if they're already using Spliterator. +public final class SpliteratorTester { + /** Return type from "contains the following elements" assertions. */ + public interface Ordered { + /** + * Attests that the expected values must not just be present but must be present in the order + * they were given. + */ + void inOrder(); + } + + @IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester + private abstract static class GeneralSpliterator { + final Spliterator spliterator; + + GeneralSpliterator(Spliterator spliterator) { + this.spliterator = checkNotNull(spliterator); + } + + abstract void forEachRemaining(Consumer action); + + abstract boolean tryAdvance(Consumer action); + + abstract @Nullable GeneralSpliterator trySplit(); + + final int characteristics() { + return spliterator.characteristics(); + } + + final long estimateSize() { + return spliterator.estimateSize(); + } + + final Comparator getComparator() { + return spliterator.getComparator(); + } + + final long getExactSizeIfKnown() { + return spliterator.getExactSizeIfKnown(); + } + + final boolean hasCharacteristics(int characteristics) { + return spliterator.hasCharacteristics(characteristics); + } + } + + @IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester + private static final class GeneralSpliteratorOfObject + extends GeneralSpliterator { + GeneralSpliteratorOfObject(Spliterator spliterator) { + super(spliterator); + } + + @Override + void forEachRemaining(Consumer action) { + spliterator.forEachRemaining(action); + } + + @Override + boolean tryAdvance(Consumer action) { + return spliterator.tryAdvance(action); + } + + @Override + @Nullable GeneralSpliterator trySplit() { + Spliterator split = spliterator.trySplit(); + return split == null ? null : new GeneralSpliteratorOfObject<>(split); + } + } + + @IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester + private static final class GeneralSpliteratorOfPrimitive< + E extends @Nullable Object, C, S extends Spliterator.OfPrimitive> + extends GeneralSpliterator { + final OfPrimitive spliteratorOfPrimitive; + final Function, C> consumerizer; + + GeneralSpliteratorOfPrimitive( + Spliterator.OfPrimitive spliterator, + Function, C> consumerizer) { + super(spliterator); + this.spliteratorOfPrimitive = spliterator; + this.consumerizer = consumerizer; + } + + @Override + void forEachRemaining(Consumer action) { + spliteratorOfPrimitive.forEachRemaining(consumerizer.apply(action)); + } + + @Override + boolean tryAdvance(Consumer action) { + return spliteratorOfPrimitive.tryAdvance(consumerizer.apply(action)); + } + + @Override + @Nullable GeneralSpliterator trySplit() { + Spliterator.OfPrimitive split = spliteratorOfPrimitive.trySplit(); + return split == null ? null : new GeneralSpliteratorOfPrimitive<>(split, consumerizer); + } + } + + /** + * Different ways of decomposing a Spliterator, all of which must produce the same elements (up to + * ordering, if Spliterator.ORDERED is not present). + */ + @IgnoreJRERequirement // *should* be redundant with the annotation on SpliteratorTester + enum SpliteratorDecompositionStrategy { + NO_SPLIT_FOR_EACH_REMAINING { + @Override + void forEach( + GeneralSpliterator spliterator, Consumer consumer) { + spliterator.forEachRemaining(consumer); + } + }, + NO_SPLIT_TRY_ADVANCE { + @Override + void forEach( + GeneralSpliterator spliterator, Consumer consumer) { + while (spliterator.tryAdvance(consumer)) { + // do nothing + } + } + }, + MAXIMUM_SPLIT { + @Override + void forEach( + GeneralSpliterator spliterator, Consumer consumer) { + for (GeneralSpliterator prefix = trySplitTestingSize(spliterator); + prefix != null; + prefix = trySplitTestingSize(spliterator)) { + forEach(prefix, consumer); + } + long size = spliterator.getExactSizeIfKnown(); + long[] counter = {0}; + spliterator.forEachRemaining( + e -> { + consumer.accept(e); + counter[0]++; + }); + if (size >= 0) { + assertEquals(size, counter[0]); + } + } + }, + ALTERNATE_ADVANCE_AND_SPLIT { + @Override + void forEach( + GeneralSpliterator spliterator, Consumer consumer) { + while (spliterator.tryAdvance(consumer)) { + GeneralSpliterator prefix = trySplitTestingSize(spliterator); + if (prefix != null) { + forEach(prefix, consumer); + } + } + } + }; + + abstract void forEach( + GeneralSpliterator spliterator, Consumer consumer); + + static final Set ALL_STRATEGIES = + unmodifiableSet(new LinkedHashSet<>(asList(values()))); + } + + private static @Nullable GeneralSpliterator trySplitTestingSize( + GeneralSpliterator spliterator) { + boolean subsized = spliterator.hasCharacteristics(Spliterator.SUBSIZED); + long originalSize = spliterator.estimateSize(); + GeneralSpliterator trySplit = spliterator.trySplit(); + if (spliterator.estimateSize() > originalSize) { + fail( + format( + "estimated size of spliterator after trySplit (%s) is larger than original size (%s)", + spliterator.estimateSize(), originalSize)); + } + if (trySplit != null) { + if (trySplit.estimateSize() > originalSize) { + fail( + format( + "estimated size of trySplit result (%s) is larger than original size (%s)", + trySplit.estimateSize(), originalSize)); + } + } + if (subsized) { + if (trySplit != null) { + assertEquals( + "sum of estimated sizes of trySplit and original spliterator after trySplit", + originalSize, + trySplit.estimateSize() + spliterator.estimateSize()); + } else { + assertEquals( + "estimated size of spliterator after failed trySplit", + originalSize, + spliterator.estimateSize()); + } + } + return trySplit; + } + + public static SpliteratorTester of( + Supplier> spliteratorSupplier) { + return new SpliteratorTester<>( + ImmutableSet.of(() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()))); + } + + /** + * @since 33.4.0 (but since 28.1 in the JRE flavor) + */ + public static SpliteratorTester ofInt(Supplier spliteratorSupplier) { + return new SpliteratorTester<>( + ImmutableSet.of( + () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()), + () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept))); + } + + /** + * @since 33.4.0 (but since 28.1 in the JRE flavor) + */ + public static SpliteratorTester ofLong(Supplier spliteratorSupplier) { + return new SpliteratorTester<>( + ImmutableSet.of( + () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()), + () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept))); + } + + /** + * @since 33.4.0 (but since 28.1 in the JRE flavor) + */ + public static SpliteratorTester ofDouble( + Supplier spliteratorSupplier) { + return new SpliteratorTester<>( + ImmutableSet.of( + () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()), + () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept))); + } + + private final ImmutableSet>> spliteratorSuppliers; + + private SpliteratorTester(ImmutableSet>> spliteratorSuppliers) { + this.spliteratorSuppliers = checkNotNull(spliteratorSuppliers); + } + + @SafeVarargs + @CanIgnoreReturnValue + public final Ordered expect(Object... elements) { + return expect(asList(elements)); + } + + @CanIgnoreReturnValue + public final Ordered expect(Iterable elements) { + List> resultsForAllStrategies = new ArrayList<>(); + for (Supplier> spliteratorSupplier : spliteratorSuppliers) { + GeneralSpliterator spliterator = spliteratorSupplier.get(); + int characteristics = spliterator.characteristics(); + long estimatedSize = spliterator.estimateSize(); + for (SpliteratorDecompositionStrategy strategy : + SpliteratorDecompositionStrategy.ALL_STRATEGIES) { + List resultsForStrategy = new ArrayList<>(); + strategy.forEach(spliteratorSupplier.get(), resultsForStrategy::add); + + // TODO(cpovirk): better failure messages + if ((characteristics & Spliterator.NONNULL) != 0) { + assertFalse(resultsForStrategy.contains(null)); + } + if ((characteristics & Spliterator.SORTED) != 0) { + Comparator comparator = spliterator.getComparator(); + if (comparator == null) { + // A sorted spliterator with no comparator is already using natural order. + // (We could probably find a way to avoid rawtypes here if we wanted.) + @SuppressWarnings({"unchecked", "rawtypes"}) + Comparator naturalOrder = + (Comparator) Comparator.naturalOrder(); + comparator = naturalOrder; + } + assertTrue(Ordering.from(comparator).isOrdered(resultsForStrategy)); + } + if ((characteristics & Spliterator.SIZED) != 0) { + assertEquals(Ints.checkedCast(estimatedSize), resultsForStrategy.size()); + } + + assertEqualIgnoringOrder(elements, resultsForStrategy); + resultsForAllStrategies.add(resultsForStrategy); + } + } + return new Ordered() { + @Override + public void inOrder() { + for (List resultsForStrategy : resultsForAllStrategies) { + assertEqualInOrder(elements, resultsForStrategy); + } + } + }; + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestCharacterListGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestCharacterListGenerator.java index b7542b55b654..acf6ec52e5ab 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestCharacterListGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestCharacterListGenerator.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.SampleElements.Chars; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * Generates {@code List} instances for test suites. @@ -27,6 +28,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public abstract class TestCharacterListGenerator implements TestListGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestCollectionGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestCollectionGenerator.java index f1df8c9da0d8..704ec159172a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestCollectionGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestCollectionGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates collections, containing sample elements, to be tested. @@ -25,4 +27,6 @@ * @author Kevin Bourrillion */ @GwtCompatible -public interface TestCollectionGenerator extends TestContainerGenerator, E> {} +@NullMarked +public interface TestCollectionGenerator + extends TestContainerGenerator, E> {} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestCollidingSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestCollidingSetGenerator.java index 6aa0c33d3852..836f4bf971e7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestCollidingSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestCollidingSetGenerator.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.SampleElements.Colliders; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * A generator using sample elements whose hash codes all collide badly. @@ -26,6 +27,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public abstract class TestCollidingSetGenerator implements TestSetGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestContainerGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestContainerGenerator.java index d08cf90c27d2..23d702b266f4 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestContainerGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestContainerGenerator.java @@ -20,6 +20,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * To be implemented by test generators of things that can contain elements. Such things include @@ -29,7 +31,8 @@ * @author George van den Driessche */ @GwtCompatible -public interface TestContainerGenerator { +@NullMarked +public interface TestContainerGenerator { /** Returns the sample elements that this generate populates its container with. */ SampleElements samples(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestEnumMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestEnumMapGenerator.java index c117fcda749a..a6f1c610faf1 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestEnumMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestEnumMapGenerator.java @@ -16,12 +16,14 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.Helpers.orderEntriesByKey; import com.google.common.annotations.GwtCompatible; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * Implementation helper for {@link TestMapGenerator} for use with enum maps. @@ -29,22 +31,23 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public abstract class TestEnumMapGenerator implements TestMapGenerator { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry(AnEnum.A, "January"), - Helpers.mapEntry(AnEnum.B, "February"), - Helpers.mapEntry(AnEnum.C, "March"), - Helpers.mapEntry(AnEnum.D, "April"), - Helpers.mapEntry(AnEnum.E, "May")); + mapEntry(AnEnum.A, "January"), + mapEntry(AnEnum.B, "February"), + mapEntry(AnEnum.C, "March"), + mapEntry(AnEnum.D, "April"), + mapEntry(AnEnum.E, "May")); } @Override public final Map create(Object... entries) { @SuppressWarnings("unchecked") - Entry[] array = new Entry[entries.length]; + Entry[] array = (Entry[]) new Entry[entries.length]; int i = 0; for (Object o : entries) { @SuppressWarnings("unchecked") @@ -59,7 +62,7 @@ public final Map create(Object... entries) { @Override @SuppressWarnings("unchecked") public final Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestEnumSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestEnumSetGenerator.java index 88391ef03be7..7629836a5e6c 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestEnumSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestEnumSetGenerator.java @@ -16,11 +16,13 @@ package com.google.common.collect.testing; +import static java.util.Collections.sort; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.SampleElements.Enums; -import java.util.Collections; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NullMarked; /** * An abstract TestSetGenerator for generating sets containing enum values. @@ -28,6 +30,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public abstract class TestEnumSetGenerator implements TestSetGenerator { @Override public SampleElements samples() { @@ -52,9 +55,15 @@ public AnEnum[] createArray(int length) { } /** Sorts the enums according to their natural ordering. */ + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Callers of TestContainerGenerator.order need to be prepared for implementations to return a new + * collection. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSetGenerator.java index 9cd9ecb43d6b..b8e8b595d846 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSetGenerator.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.SampleElements.Ints; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NullMarked; /** * Create integer sets for collection tests. @@ -27,6 +28,7 @@ * @author Gregory Kick */ @GwtCompatible +@NullMarked public abstract class TestIntegerSetGenerator implements TestSetGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSortedSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSortedSetGenerator.java index babac37bcd79..c02008d59a9b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSortedSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestIntegerSortedSetGenerator.java @@ -16,10 +16,12 @@ package com.google.common.collect.testing; +import static java.util.Collections.sort; + import com.google.common.annotations.GwtCompatible; -import java.util.Collections; import java.util.List; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; /** * Create integer sets for testing collections that are sorted by natural ordering. @@ -28,14 +30,21 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestIntegerSortedSetGenerator extends TestIntegerSetGenerator { @Override protected abstract SortedSet create(Integer[] elements); /** Sorts the elements by their natural ordering. */ + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Callers of TestContainerGenerator.order need to be prepared for implementations to return a new + * collection. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestListGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestListGenerator.java index 99f95d0af0be..6dcffa779f4d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestListGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestListGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates sets, containing sample elements, to be tested. @@ -25,7 +27,8 @@ * @author Kevin Bourrillion */ @GwtCompatible -public interface TestListGenerator extends TestCollectionGenerator { +@NullMarked +public interface TestListGenerator extends TestCollectionGenerator { @Override List create(Object... elements); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestMapEntrySetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestMapEntrySetGenerator.java index 0c298cc1226b..95319d10e056 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestMapEntrySetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestMapEntrySetGenerator.java @@ -16,11 +16,15 @@ package com.google.common.collect.testing; +import static java.lang.System.arraycopy; + import com.google.common.annotations.GwtCompatible; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates map entries using sample keys and sample values. @@ -28,7 +32,10 @@ * @author Jesse Wilson */ @GwtCompatible -public abstract class TestMapEntrySetGenerator implements TestSetGenerator> { +@NullMarked +public abstract class TestMapEntrySetGenerator< + K extends @Nullable Object, V extends @Nullable Object> + implements TestSetGenerator> { private final SampleElements keys; private final SampleElements values; @@ -45,7 +52,7 @@ public SampleElements> samples() { @Override public Set> create(Object... elements) { Entry[] entries = createArray(elements.length); - System.arraycopy(elements, 0, entries, 0, elements.length); + arraycopy(elements, 0, entries, 0, elements.length); return createFromEntries(entries); } @@ -54,7 +61,7 @@ public Set> create(Object... elements) { @Override @SuppressWarnings("unchecked") // generic arrays make typesafety sad public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } /** Returns the original element list, unchanged. */ diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestMapGenerator.java index 2267a04f5250..50530ba16449 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestMapGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Map; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates maps, containing sample elements, to be tested. @@ -25,7 +27,9 @@ * @author George van den Driessche */ @GwtCompatible -public interface TestMapGenerator extends TestContainerGenerator, Map.Entry> { +@NullMarked +public interface TestMapGenerator + extends TestContainerGenerator, Map.Entry> { K[] createKeyArray(int length); V[] createValueArray(int length); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestQueueGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestQueueGenerator.java index 939b48583d4b..25863e224538 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestQueueGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestQueueGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Queue; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates queues, containing sample elements, to be tested. @@ -25,7 +27,8 @@ * @author Jared Levy */ @GwtCompatible -public interface TestQueueGenerator extends TestCollectionGenerator { +@NullMarked +public interface TestQueueGenerator extends TestCollectionGenerator { @Override Queue create(Object... elements); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestSetGenerator.java index 680ff44eee16..319feb449a2d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestSetGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates sets, containing sample elements, to be tested. @@ -25,7 +27,8 @@ * @author Kevin Bourrillion */ @GwtCompatible -public interface TestSetGenerator extends TestCollectionGenerator { +@NullMarked +public interface TestSetGenerator extends TestCollectionGenerator { @Override Set create(Object... elements); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestSortedMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestSortedMapGenerator.java index 5d902189cd76..3fc2e5b8ceca 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestSortedMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestSortedMapGenerator.java @@ -19,6 +19,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.Map.Entry; import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates sorted maps, containing sample elements, to be tested. @@ -26,7 +28,9 @@ * @author Louis Wasserman */ @GwtCompatible -public interface TestSortedMapGenerator extends TestMapGenerator { +@NullMarked +public interface TestSortedMapGenerator + extends TestMapGenerator { @Override SortedMap create(Object... elements); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestSortedSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestSortedSetGenerator.java index 8f7ee5c76f7a..6218fe5aee93 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestSortedSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestSortedSetGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates sorted sets, containing sample elements, to be tested. @@ -25,7 +27,8 @@ * @author Louis Wasserman */ @GwtCompatible -public interface TestSortedSetGenerator extends TestSetGenerator { +@NullMarked +public interface TestSortedSetGenerator extends TestSetGenerator { @Override SortedSet create(Object... elements); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringCollectionGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringCollectionGenerator.java index f9c58cd7f333..c637c223dafc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringCollectionGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringCollectionGenerator.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.SampleElements.Strings; import java.util.Collection; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * String creation for testing arbitrary collections. @@ -27,6 +28,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestStringCollectionGenerator implements TestCollectionGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringListGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringListGenerator.java index 7e22ab143059..9b2bddec9305 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringListGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringListGenerator.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.SampleElements.Strings; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * TODO: javadoc. @@ -26,6 +27,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public abstract class TestStringListGenerator implements TestListGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringMapGenerator.java index 3449d3a9c3f4..2edf70fff26d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringMapGenerator.java @@ -16,10 +16,13 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * Implementation helper for {@link TestMapGenerator} for use with maps of strings. @@ -29,22 +32,23 @@ * @author George van den Driessche */ @GwtCompatible +@NullMarked public abstract class TestStringMapGenerator implements TestMapGenerator { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry("one", "January"), - Helpers.mapEntry("two", "February"), - Helpers.mapEntry("three", "March"), - Helpers.mapEntry("four", "April"), - Helpers.mapEntry("five", "May")); + mapEntry("one", "January"), + mapEntry("two", "February"), + mapEntry("three", "March"), + mapEntry("four", "April"), + mapEntry("five", "May")); } @Override public Map create(Object... entries) { @SuppressWarnings("unchecked") - Entry[] array = new Entry[entries.length]; + Entry[] array = (Entry[]) new Entry[entries.length]; int i = 0; for (Object o : entries) { @SuppressWarnings("unchecked") @@ -59,7 +63,7 @@ public Map create(Object... entries) { @Override @SuppressWarnings("unchecked") public final Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringQueueGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringQueueGenerator.java index 16cb2539d1f3..1d2d3f4af55a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringQueueGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringQueueGenerator.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.SampleElements.Strings; import java.util.List; import java.util.Queue; +import org.jspecify.annotations.NullMarked; /** * Create queue of strings for tests. @@ -27,6 +28,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestStringQueueGenerator implements TestQueueGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSetGenerator.java index 1724bb2c8dc6..d8a517f6c991 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSetGenerator.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.SampleElements.Strings; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NullMarked; /** * Create string sets for collection tests. @@ -27,6 +28,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public abstract class TestStringSetGenerator implements TestSetGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedMapGenerator.java index c1878bea2214..7f2738c2236f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedMapGenerator.java @@ -16,12 +16,14 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.Helpers.orderEntriesByKey; import com.google.common.annotations.GwtCompatible; import java.util.List; import java.util.Map.Entry; import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; /** * Implementation helper for {@link TestMapGenerator} for use with sorted maps of strings. @@ -29,26 +31,27 @@ * @author Chris Povirk */ @GwtCompatible +@NullMarked public abstract class TestStringSortedMapGenerator extends TestStringMapGenerator implements TestSortedMapGenerator { @Override public Entry belowSamplesLesser() { - return Helpers.mapEntry("!! a", "below view"); + return mapEntry("!! a", "below view"); } @Override public Entry belowSamplesGreater() { - return Helpers.mapEntry("!! b", "below view"); + return mapEntry("!! b", "below view"); } @Override public Entry aboveSamplesLesser() { - return Helpers.mapEntry("~~ a", "above view"); + return mapEntry("~~ a", "above view"); } @Override public Entry aboveSamplesGreater() { - return Helpers.mapEntry("~~ b", "above view"); + return mapEntry("~~ b", "above view"); } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedSetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedSetGenerator.java index 7f5453d63cd9..a1548ed837f7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedSetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestStringSortedSetGenerator.java @@ -16,10 +16,12 @@ package com.google.common.collect.testing; +import static java.util.Collections.sort; + import com.google.common.annotations.GwtCompatible; -import java.util.Collections; import java.util.List; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; /** * Create string sets for testing collections that are sorted by natural ordering. @@ -27,6 +29,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestStringSortedSetGenerator extends TestStringSetGenerator implements TestSortedSetGenerator { @@ -39,9 +42,15 @@ public SortedSet create(Object... elements) { protected abstract SortedSet create(String[] elements); /** Sorts the elements by their natural ordering. */ + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Callers of TestContainerGenerator.order need to be prepared for implementations to return a new + * collection. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestSubjectGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestSubjectGenerator.java index 4a8ae76a8f40..2cf33c668909 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestSubjectGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestSubjectGenerator.java @@ -17,6 +17,8 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * To be implemented by test generators that can produce test subjects without requiring any @@ -26,6 +28,7 @@ * @author George van den Driessche */ @GwtCompatible -public interface TestSubjectGenerator { +@NullMarked +public interface TestSubjectGenerator { T createTestSubject(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestUnhashableCollectionGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/TestUnhashableCollectionGenerator.java index 9b4602d045d8..94719ef52a0a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestUnhashableCollectionGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestUnhashableCollectionGenerator.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.SampleElements.Unhashables; import java.util.Collection; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * Creates collections containing unhashable sample elements, to be tested. @@ -27,6 +28,7 @@ * @author Regina O'Dell */ @GwtCompatible +@NullMarked public abstract class TestUnhashableCollectionGenerator> implements TestCollectionGenerator { @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestsForListsInJavaUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/TestsForListsInJavaUtil.java index ac0d15fa9b47..04e3579a3c2b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestsForListsInJavaUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestsForListsInJavaUtil.java @@ -20,6 +20,11 @@ import static com.google.common.collect.testing.testers.ListSubListTester.getSubListOriginalListSetAffectsSubListLargeListMethod; import static com.google.common.collect.testing.testers.ListSubListTester.getSubListOriginalListSetAffectsSubListMethod; import static com.google.common.collect.testing.testers.ListSubListTester.getSubListSubListRemoveAffectsOriginalLargeListMethod; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonList; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.features.CollectionFeature; @@ -29,7 +34,6 @@ import java.util.AbstractList; import java.util.AbstractSequentialList; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; @@ -69,27 +73,27 @@ public Test allTests() { } protected Collection suppressForEmptyList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForSingletonList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForArraysAsList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForArrayList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCopyOnWriteArrayList() { - return Arrays.asList( + return asList( getSubListOriginalListSetAffectsSubListMethod(), getSubListOriginalListSetAffectsSubListLargeListMethod(), getSubListSubListRemoveAffectsOriginalLargeListMethod(), @@ -97,23 +101,23 @@ protected Collection suppressForCopyOnWriteArrayList() { } protected Collection suppressForUnmodifiableList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCheckedList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForAbstractList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForAbstractSequentialList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForVector() { - return Collections.emptySet(); + return emptySet(); } public Test testsForEmptyList() { @@ -121,7 +125,7 @@ public Test testsForEmptyList() { new TestStringListGenerator() { @Override public List create(String[] elements) { - return Collections.emptyList(); + return emptyList(); } }) .named("emptyList") @@ -135,7 +139,7 @@ public Test testsForSingletonList() { new TestStringListGenerator() { @Override public List create(String[] elements) { - return Collections.singletonList(elements[0]); + return singletonList(elements[0]); } }) .named("singletonList") @@ -152,7 +156,7 @@ public Test testsForArraysAsList() { new TestStringListGenerator() { @Override public List create(String[] elements) { - return Arrays.asList(elements.clone()); + return asList(elements.clone()); } }) .named("Arrays.asList") @@ -184,6 +188,8 @@ public List create(String[] elements) { .createTestSuite(); } + // We are testing LinkedList / testing our tests on LinkedList. + @SuppressWarnings("JdkObsolete") public Test testsForLinkedList() { return ListTestSuiteBuilder.using( new TestStringListGenerator() { @@ -232,7 +238,7 @@ public Test testsForUnmodifiableList() { public List create(String[] elements) { List innerList = new ArrayList<>(); Collections.addAll(innerList, elements); - return Collections.unmodifiableList(innerList); + return unmodifiableList(innerList); } }) .named("unmodifiableList/ArrayList") @@ -269,7 +275,7 @@ public Test testsForAbstractList() { return ListTestSuiteBuilder.using( new TestStringListGenerator() { @Override - protected List create(final String[] elements) { + protected List create(String[] elements) { return new AbstractList() { @Override public int size() { @@ -294,9 +300,9 @@ public Test testsForAbstractSequentialList() { return ListTestSuiteBuilder.using( new TestStringListGenerator() { @Override - protected List create(final String[] elements) { + protected List create(String[] elements) { // For this test we trust ArrayList works - final List list = new ArrayList<>(); + List list = new ArrayList<>(); Collections.addAll(list, elements); return new AbstractSequentialList() { @Override @@ -318,6 +324,8 @@ public ListIterator listIterator(int index) { .createTestSuite(); } + // We are testing Vector / testing our tests on Vector. + @SuppressWarnings("JdkObsolete") private Test testsForVector() { return ListTestSuiteBuilder.using( new TestStringListGenerator() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java index e9014fbfa1bf..593878d75ce7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestsForMapsInJavaUtil.java @@ -17,12 +17,17 @@ package com.google.common.collect.testing; import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonMap; +import static java.util.Collections.unmodifiableMap; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.collect.testing.testers.MapEntrySetTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Collection; @@ -75,55 +80,55 @@ public Test allTests() { } protected Collection suppressForCheckedMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCheckedSortedMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForEmptyMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForSingletonMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForHashMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForHashtable() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedHashMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForTreeMapNatural() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForTreeMapWithComparator() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForUnmodifiableMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForUnmodifiableSortedMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForEnumMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForConcurrentHashMap() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForConcurrentSkipListMap() { @@ -187,7 +192,7 @@ public Test testsForEmptyMap() { new TestStringMapGenerator() { @Override protected Map create(Entry[] entries) { - return Collections.emptyMap(); + return emptyMap(); } }) .named("emptyMap") @@ -201,7 +206,7 @@ public Test testsForSingletonMap() { new TestStringMapGenerator() { @Override protected Map create(Entry[] entries) { - return Collections.singletonMap(entries[0].getKey(), entries[0].getValue()); + return singletonMap(entries[0].getKey(), entries[0].getValue()); } }) .named("singletonMap") @@ -241,6 +246,8 @@ public Test testsForHashtable() { return MapTestSuiteBuilder.using( new TestStringMapGenerator() { @Override + // We are testing Hashtable / testing our tests on Hashtable. + @SuppressWarnings("JdkObsolete") protected Map create(Entry[] entries) { return populate(new Hashtable(), entries); } @@ -337,7 +344,7 @@ public Test testsForUnmodifiableMap() { new TestStringMapGenerator() { @Override protected Map create(Entry[] entries) { - return Collections.unmodifiableMap(toHashMap(entries)); + return unmodifiableMap(toHashMap(entries)); } }) .named("unmodifiableMap/HashMap") @@ -457,6 +464,7 @@ private static Map toHashMap(Entry[] entries) { // TODO: call conversion constructors or factory methods instead of using // populate() on an empty map + @CanIgnoreReturnValue private static > M populate(M map, Entry[] entries) { for (Entry entry : entries) { map.put(entry.getKey(), entry.getValue()); @@ -465,7 +473,7 @@ private static > M populate(M map, Entry[ } static Comparator arbitraryNullFriendlyComparator() { - return new NullFriendlyComparator(); + return new NullFriendlyComparator<>(); } private static final class NullFriendlyComparator implements Comparator, Serializable { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestsForQueuesInJavaUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/TestsForQueuesInJavaUtil.java index b10a5d9022e2..0f26097ed2fd 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestsForQueuesInJavaUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestsForQueuesInJavaUtil.java @@ -16,13 +16,14 @@ package com.google.common.collect.testing; +import static java.util.Collections.emptySet; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.Collection; -import java.util.Collections; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Queue; @@ -60,35 +61,35 @@ public Test allTests() { } protected Collection suppressForArrayDeque() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedList() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForArrayBlockingQueue() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForConcurrentLinkedQueue() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedBlockingDeque() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedBlockingQueue() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForPriorityBlockingQueue() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForPriorityQueue() { - return Collections.emptySet(); + return emptySet(); } public Test testsForArrayDeque() { @@ -110,6 +111,8 @@ public Test testsForLinkedList() { return QueueTestSuiteBuilder.using( new TestStringQueueGenerator() { @Override + // We are testing LinkedList / testing our tests on LinkedList. + @SuppressWarnings("JdkObsolete") public Queue create(String[] elements) { return new LinkedList<>(MinimalCollection.of(elements)); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/TestsForSetsInJavaUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/TestsForSetsInJavaUtil.java index 3324ace0fc3e..9179df9cbc9b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/TestsForSetsInJavaUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/TestsForSetsInJavaUtil.java @@ -16,6 +16,10 @@ package com.google.common.collect.testing; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static java.util.Collections.unmodifiableSet; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -72,31 +76,31 @@ public Test allTests() { } protected Collection suppressForEmptySet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForSingletonSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForHashSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForLinkedHashSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForEnumSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForTreeSetNatural() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForTreeSetWithComparator() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCopyOnWriteArraySet() { @@ -104,27 +108,27 @@ protected Collection suppressForCopyOnWriteArraySet() { } protected Collection suppressForUnmodifiableSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCheckedSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForCheckedSortedSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForAbstractSet() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForConcurrentSkipListSetNatural() { - return Collections.emptySet(); + return emptySet(); } protected Collection suppressForConcurrentSkipListSetWithComparator() { - return Collections.emptySet(); + return emptySet(); } public Test testsForEmptySet() { @@ -132,7 +136,7 @@ public Test testsForEmptySet() { new TestStringSetGenerator() { @Override public Set create(String[] elements) { - return Collections.emptySet(); + return emptySet(); } }) .named("emptySet") @@ -146,7 +150,7 @@ public Test testsForSingletonSet() { new TestStringSetGenerator() { @Override public Set create(String[] elements) { - return Collections.singleton(elements[0]); + return singleton(elements[0]); } }) .named("singleton") @@ -286,7 +290,7 @@ public Test testsForUnmodifiableSet() { public Set create(String[] elements) { Set innerSet = new HashSet<>(); Collections.addAll(innerSet, elements); - return Collections.unmodifiableSet(innerSet); + return unmodifiableSet(innerSet); } }) .named("unmodifiableSet/HashSet") @@ -347,7 +351,7 @@ public Test testsForAbstractSet() { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - final String[] deduped = dedupe(elements); + String[] deduped = dedupe(elements); return new AbstractSet() { @Override public int size() { @@ -434,7 +438,7 @@ private static String[] dedupe(String[] elements) { } static Comparator arbitraryNullFriendlyComparator() { - return new NullFriendlyComparator(); + return new NullFriendlyComparator<>(); } private static final class NullFriendlyComparator implements Comparator, Serializable { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/UnhashableObject.java b/android/guava-testlib/src/com/google/common/collect/testing/UnhashableObject.java index 69d263bda096..eda5aac70239 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/UnhashableObject.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/UnhashableObject.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing; import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.Nullable; /** * An unhashable object to be used in testing as values in our collections. @@ -32,7 +33,7 @@ public UnhashableObject(int value) { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof UnhashableObject) { UnhashableObject that = (UnhashableObject) object; return this.value == that.value; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionFeature.java index 0f0a1235f018..db1851306650 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionFeature.java @@ -16,8 +16,9 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -31,8 +32,7 @@ * * @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum CollectionFeature implements Feature { /** @@ -109,7 +109,7 @@ public enum CollectionFeature implements Feature { private final Set> implied; CollectionFeature(Feature... implied) { - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionSize.java b/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionSize.java index 6203e514862e..1ca44740a01a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionSize.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/CollectionSize.java @@ -16,14 +16,16 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static java.util.Collections.emptySet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; -import java.util.Collections; import java.util.Set; +import org.jspecify.annotations.Nullable; /** * When describing the features of the collection produced by a given generator (i.e. in a call to @@ -41,8 +43,7 @@ * * @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum CollectionSize implements Feature, Comparable { /** Test an empty collection. */ @@ -59,17 +60,17 @@ public enum CollectionSize implements Feature, Comparable> implied; - private final Integer numElements; + private final @Nullable Integer numElements; CollectionSize(int numElements) { - this.implied = Collections.emptySet(); + this.implied = emptySet(); this.numElements = numElements; } CollectionSize(Feature... implied) { // Keep the order here, so that PerCollectionSizeTestSuiteBuilder // gives a predictable order of test suites. - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); this.numElements = null; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/ConflictingRequirementsException.java b/android/guava-testlib/src/com/google/common/collect/testing/features/ConflictingRequirementsException.java index 9096a118173f..3a37dfdd1e67 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/ConflictingRequirementsException.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/ConflictingRequirementsException.java @@ -17,6 +17,8 @@ package com.google.common.collect.testing.features; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Set; /** @@ -49,5 +51,5 @@ public String getMessage() { return super.getMessage() + " (source: " + source + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java b/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java index 2d11a0863494..6c1fb6451598 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/FeatureUtil.java @@ -16,14 +16,17 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static java.util.Collections.disjoint; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -31,6 +34,7 @@ import java.util.Map; import java.util.Queue; import java.util.Set; +import org.jspecify.annotations.NullMarked; /** * Utilities for collecting and validating tester requirements from annotations. @@ -38,9 +42,9 @@ * @author George van den Driessche */ @GwtIncompatible -public class FeatureUtil { +public final class FeatureUtil { /** A cache of annotated objects (typically a Class or Method) to its set of annotations. */ - private static Map> annotationCache = new HashMap<>(); + private static final Map> annotationCache = new HashMap<>(); private static final Map, TesterRequirements> classTesterRequirementsCache = new HashMap<>(); @@ -55,6 +59,7 @@ public class FeatureUtil { * @param features the set of features to expand * @return the same set of features, expanded with all implied features */ + @CanIgnoreReturnValue public static Set> addImpliedFeatures(Set> features) { Queue> queue = new ArrayDeque<>(features); while (!queue.isEmpty()) { @@ -179,16 +184,14 @@ private static TesterRequirements buildTesterRequirements(Annotation testerAnnot Feature[] presentFeatures; Feature[] absentFeatures; try { - presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); - absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); + presentFeatures = (Feature[]) annotationClass.getMethod("value").invoke(testerAnnotation); + absentFeatures = (Feature[]) annotationClass.getMethod("absent").invoke(testerAnnotation); } catch (Exception e) { throw new IllegalArgumentException("Error extracting features from tester annotation.", e); } - Set> allPresentFeatures = - addImpliedFeatures(Helpers.>copyToSet(presentFeatures)); - Set> allAbsentFeatures = - addImpliedFeatures(Helpers.>copyToSet(absentFeatures)); - if (!Collections.disjoint(allPresentFeatures, allAbsentFeatures)) { + Set> allPresentFeatures = addImpliedFeatures(copyToSet(presentFeatures)); + Set> allAbsentFeatures = copyToSet(absentFeatures); + if (!disjoint(allPresentFeatures, allAbsentFeatures)) { throw new ConflictingRequirementsException( "Annotation explicitly or " + "implicitly requires one or more features to be both present " @@ -233,11 +236,16 @@ public static Iterable getTesterAnnotations(AnnotatedElement classOr if (annotations == null) { annotations = new ArrayList<>(); for (Annotation a : classOrMethod.getDeclaredAnnotations()) { - if (a.annotationType().isAnnotationPresent(TesterAnnotation.class)) { + /* + * We avoid reflecting on NullMarked because its @Target(..., MODULE) causes problems + * under JDK 8. + */ + if (!(a instanceof NullMarked) + && a.annotationType().isAnnotationPresent(TesterAnnotation.class)) { annotations.add(a); } } - annotations = Collections.unmodifiableList(annotations); + annotations = unmodifiableList(annotations); annotationCache.put(classOrMethod, annotations); } return annotations; @@ -254,6 +262,7 @@ public static Iterable getTesterAnnotations(AnnotatedElement classOr * @throws ConflictingRequirementsException if the additional requirements are inconsistent with * the existing requirements */ + @CanIgnoreReturnValue private static TesterRequirements incorporateRequirements( TesterRequirements requirements, TesterRequirements moreRequirements, Object source) throws ConflictingRequirementsException { @@ -276,7 +285,7 @@ private static void checkConflict( Set> newFeatures, Object source) throws ConflictingRequirementsException { - if (!Collections.disjoint(newFeatures, earlierFeatures)) { + if (!disjoint(newFeatures, earlierFeatures)) { throw new ConflictingRequirementsException( String.format( Locale.ROOT, @@ -289,10 +298,17 @@ private static void checkConflict( } } - /** Construct a new {@link java.util.Set} that is the intersection of the given sets. */ + /** + * Construct a new {@link java.util.Set} that is the intersection of the given sets. + * + * @deprecated Use {@link com.google.common.collect.Sets#intersection(Set, Set)} instead. + */ + @Deprecated public static Set intersection(Set set1, Set set2) { - Set result = Helpers.copyToSet(set1); + Set result = copyToSet(set1); result.retainAll(set2); return result; } + + private FeatureUtil() {} } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/ListFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/features/ListFeature.java index 1427efaf0d37..da0d1a4cbce5 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/ListFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/ListFeature.java @@ -16,8 +16,9 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,8 +30,7 @@ * * @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum ListFeature implements Feature { SUPPORTS_SET, @@ -49,7 +49,7 @@ public enum ListFeature implements Feature { private final Set> implied; ListFeature(Feature... implied) { - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/MapFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/features/MapFeature.java index dff7b124f1ef..14b78b5eb316 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/MapFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/MapFeature.java @@ -16,8 +16,9 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,8 +30,7 @@ * * @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum MapFeature implements Feature { /** @@ -76,7 +76,7 @@ public enum MapFeature implements Feature { private final Set> implied; MapFeature(Feature... implied) { - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); } @Override @@ -88,8 +88,8 @@ public Set> getImpliedFeatures() { @Inherited @TesterAnnotation public @interface Require { - public abstract MapFeature[] value() default {}; + MapFeature[] value() default {}; - public abstract MapFeature[] absent() default {}; + MapFeature[] absent() default {}; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/SetFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/features/SetFeature.java index 7b5dcd959ff5..0b01eb7919cb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/SetFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/SetFeature.java @@ -16,8 +16,9 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,8 +29,7 @@ * * @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum SetFeature implements Feature { GENERAL_PURPOSE(CollectionFeature.GENERAL_PURPOSE); @@ -37,7 +37,7 @@ public enum SetFeature implements Feature { private final Set> implied; SetFeature(Feature... implied) { - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); } @Override @@ -49,8 +49,8 @@ public Set> getImpliedFeatures() { @Inherited @TesterAnnotation public @interface Require { - public abstract SetFeature[] value() default {}; + SetFeature[] value() default {}; - public abstract SetFeature[] absent() default {}; + SetFeature[] absent() default {}; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/TesterAnnotation.java b/android/guava-testlib/src/com/google/common/collect/testing/features/TesterAnnotation.java index 1831e417f08e..247c62a7067a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/TesterAnnotation.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/TesterAnnotation.java @@ -16,6 +16,7 @@ import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -29,7 +30,7 @@ * * @author George van den Driessche */ -@Target(value = {java.lang.annotation.ElementType.ANNOTATION_TYPE}) +@Target(value = {ElementType.ANNOTATION_TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Documented @GwtCompatible diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/TesterRequirements.java b/android/guava-testlib/src/com/google/common/collect/testing/features/TesterRequirements.java index 4780b1bf50ff..6b848afa4586 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/features/TesterRequirements.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/TesterRequirements.java @@ -16,10 +16,14 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Collections; import java.util.Set; +import org.jspecify.annotations.Nullable; /** * Encapsulates the constraints that a class under test must satisfy in order for a tester method to @@ -33,8 +37,8 @@ public final class TesterRequirements { private final Set> absentFeatures; public TesterRequirements(Set> presentFeatures, Set> absentFeatures) { - this.presentFeatures = Helpers.copyToSet(presentFeatures); - this.absentFeatures = Helpers.copyToSet(absentFeatures); + this.presentFeatures = copyToSet(presentFeatures); + this.absentFeatures = copyToSet(absentFeatures); } public TesterRequirements(TesterRequirements tr) { @@ -54,7 +58,7 @@ public final Set> getAbsentFeatures() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -76,5 +80,5 @@ public String toString() { return "{TesterRequirements: present=" + presentFeatures + ", absent=" + absentFeatures + "}"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/features/package-info.java b/android/guava-testlib/src/com/google/common/collect/testing/features/package-info.java new file mode 100644 index 000000000000..88b611ca42a9 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/features/package-info.java @@ -0,0 +1,2 @@ +@com.google.errorprone.annotations.CheckReturnValue +package com.google.common.collect.testing.features; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractBiMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractBiMapTester.java index 85ddb447648b..a1e6ca4ae8e2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractBiMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractBiMapTester.java @@ -16,28 +16,37 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.BiMap; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** Skeleton for a tester of a {@code BiMap}. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public abstract class AbstractBiMapTester extends AbstractMapTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public abstract class AbstractBiMapTester + extends AbstractMapTester { @Override protected BiMap getMap() { return (BiMap) super.getMap(); } - static Entry reverseEntry(Entry entry) { - return Helpers.mapEntry(entry.getValue(), entry.getKey()); + static Entry reverseEntry( + Entry entry) { + return mapEntry(entry.getValue(), entry.getKey()); } @Override @@ -47,7 +56,7 @@ protected void expectContents(Collection> expected) { for (Entry entry : expected) { reversedEntries.add(reverseEntry(entry)); } - Helpers.assertEqualIgnoringOrder(getMap().inverse().entrySet(), reversedEntries); + assertEqualIgnoringOrder(getMap().inverse().entrySet(), reversedEntries); for (Entry entry : expected) { assertEquals( diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractListMultimapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractListMultimapTester.java index 7bce8b012072..0c4534e55468 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractListMultimapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractListMultimapTester.java @@ -15,11 +15,13 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertEqualInOrder; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; -import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -28,17 +30,20 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class AbstractListMultimapTester +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class AbstractListMultimapTester extends AbstractMultimapTester> { @Override protected void assertGet(K key, V... values) { - assertGet(key, Arrays.asList(values)); + assertGet(key, asList(values)); } @Override - protected void assertGet(K key, Collection values) { + protected void assertGet(K key, Collection values) { assertEqualInOrder(values, multimap().get(key)); if (!values.isEmpty()) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultimapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultimapTester.java index 7f649e757165..c12835009f9a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultimapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultimapTester.java @@ -17,16 +17,19 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; import com.google.common.collect.testing.AbstractContainerTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.SampleElements; -import java.util.Arrays; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.Iterator; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -35,8 +38,12 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public abstract class AbstractMultimapTester> +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public abstract class AbstractMultimapTester< + K extends @Nullable Object, V extends @Nullable Object, M extends Multimap> extends AbstractContainerTester> { private M multimap; @@ -45,21 +52,25 @@ protected M multimap() { return multimap; } - /** @return an array of the proper size with {@code null} as the key of the middle element. */ + /** + * @return an array of the proper size with {@code null} as the key of the middle element. + */ protected Entry[] createArrayWithNullKey() { Entry[] array = createSamplesArray(); int nullKeyLocation = getNullLocation(); Entry oldEntry = array[nullKeyLocation]; - array[nullKeyLocation] = Helpers.mapEntry(null, oldEntry.getValue()); + array[nullKeyLocation] = mapEntry(null, oldEntry.getValue()); return array; } - /** @return an array of the proper size with {@code null} as the value of the middle element. */ + /** + * @return an array of the proper size with {@code null} as the value of the middle element. + */ protected Entry[] createArrayWithNullValue() { Entry[] array = createSamplesArray(); int nullValueLocation = getNullLocation(); Entry oldEntry = array[nullValueLocation]; - array[nullValueLocation] = Helpers.mapEntry(oldEntry.getKey(), null); + array[nullValueLocation] = mapEntry(oldEntry.getKey(), null); return array; } @@ -70,7 +81,7 @@ protected Entry[] createArrayWithNullValue() { protected Entry[] createArrayWithNullKeyAndValue() { Entry[] array = createSamplesArray(); int nullValueLocation = getNullLocation(); - array[nullValueLocation] = Helpers.mapEntry(null, null); + array[nullValueLocation] = mapEntry(null, null); return array; } @@ -121,26 +132,30 @@ protected Collection> actualContents() { // TODO: dispose of this once collection is encapsulated. @Override + @CanIgnoreReturnValue protected M resetContainer(M newContents) { multimap = super.resetContainer(newContents); return multimap; } + @CanIgnoreReturnValue protected Multimap resetContainer(Entry... newContents) { multimap = super.resetContainer(getSubjectGenerator().create((Object[]) newContents)); return multimap; } - /** @see AbstractContainerTester#resetContainer() */ + /** + * @see AbstractContainerTester#resetContainer() + */ protected void resetCollection() { resetContainer(); } protected void assertGet(K key, V... values) { - assertGet(key, Arrays.asList(values)); + assertGet(key, asList(values)); } - protected void assertGet(K key, Collection values) { + protected void assertGet(K key, Collection values) { assertEqualIgnoringOrder(values, multimap().get(key)); if (!values.isEmpty()) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetSetCountTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetSetCountTester.java index a72fd9fba5e8..20c1f2d09641 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetSetCountTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetSetCountTester.java @@ -23,16 +23,18 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Multiset; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; @@ -47,7 +49,9 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public abstract class AbstractMultisetSetCountTester extends AbstractMultisetTester { /* * TODO: consider adding MultisetFeatures.SUPPORTS_SET_COUNT. Currently we @@ -188,26 +192,16 @@ public void testSetCount_zeroToOne_supported() { @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) public void testSetCountZeroToOneConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertSetCount(e3(), 1); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + Iterator iterator = collection.iterator(); + assertSetCount(e3(), 1); + assertThrows(ConcurrentModificationException.class, iterator::next); } @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) public void testSetCountZeroToOneConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMultiset().entrySet().iterator(); - assertSetCount(e3(), 1); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + Iterator> iterator = getMultiset().entrySet().iterator(); + assertSetCount(e3(), 1); + assertThrows(ConcurrentModificationException.class, iterator::next); } @CollectionFeature.Require(SUPPORTS_ADD) @@ -248,27 +242,17 @@ public void testSetCount_oneToZero_supported() { @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(absent = ZERO) public void testSetCountOneToZeroConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertSetCount(e0(), 0); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + Iterator iterator = collection.iterator(); + assertSetCount(e0(), 0); + assertThrows(ConcurrentModificationException.class, iterator::next); } @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(absent = ZERO) public void testSetCountOneToZeroConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMultiset().entrySet().iterator(); - assertSetCount(e0(), 0); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + Iterator> iterator = getMultiset().entrySet().iterator(); + assertSetCount(e0(), 0); + assertThrows(ConcurrentModificationException.class, iterator::next); } @CollectionSize.Require(SEVERAL) @@ -323,11 +307,7 @@ public void testSetCount_addNull_nullSupported() { @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES) public void testSetCount_addNull_nullUnsupported() { - try { - setCountNoCheckReturnValue(null, 1); - fail("adding null with setCount() should throw NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> setCountNoCheckReturnValue(null, 1)); } @CollectionFeature.Require(ALLOWS_NULL_VALUES) @@ -360,11 +340,7 @@ public void testSetCount_existingNoNopNull_nullSupported() { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_negative_removeSupported() { - try { - setCountNoCheckReturnValue(e3(), -1); - fail("calling setCount() with a negative count should throw IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> setCountNoCheckReturnValue(e3(), -1)); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @@ -384,14 +360,16 @@ public void testSetCount_negative_removeUnsupported() { * Returns {@link Method} instances for the {@code setCount()} tests that assume multisets support * duplicates so that the test of {@code Multisets.forSet()} can suppress them. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getSetCountDuplicateInitializingMethods() { - return Arrays.asList( + return asList( getMethod("testSetCount_threeToThree_removeSupported"), getMethod("testSetCount_threeToZero_supported"), getMethod("testSetCount_threeToOne_supported")); } + @J2ktIncompatible @GwtIncompatible // reflection private static Method getMethod(String methodName) { return Helpers.getMethod(AbstractMultisetSetCountTester.class, methodName); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetTester.java index 18ffcbca5126..5e0279ae1099 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/AbstractMultisetTester.java @@ -27,7 +27,9 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class AbstractMultisetTester extends AbstractCollectionTester { protected final Multiset getMultiset() { return (Multiset) collection; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapClearTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapClearTester.java index 4319a85068b9..fa6242ae0c6d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapClearTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapClearTester.java @@ -29,7 +29,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class BiMapClearTester extends AbstractBiMapTester { @MapFeature.Require(SUPPORTS_REMOVE) public void testClearClearsInverse() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapEntrySetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapEntrySetTester.java index 5c9a47989bf4..5d97e11636d8 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapEntrySetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapEntrySetTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionSize; @@ -29,7 +30,9 @@ /** Tester for {@code BiMap.entrySet} and methods on the entries in the set. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class BiMapEntrySetTester extends AbstractBiMapTester { @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) @@ -47,11 +50,7 @@ public void testSetValue_valueAbsent() { public void testSetValue_valuePresent() { for (Entry entry : getMap().entrySet()) { if (entry.getKey().equals(k0())) { - try { - entry.setValue(v1()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> entry.setValue(v1())); } } expectUnchanged(); @@ -61,11 +60,7 @@ public void testSetValue_valuePresent() { @CollectionSize.Require(absent = ZERO) public void testSetValueNullUnsupported() { for (Entry entry : getMap().entrySet()) { - try { - entry.setValue(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> entry.setValue(null)); expectUnchanged(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapGenerators.java index 13efdcd6d1be..634a1a424c03 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapGenerators.java @@ -16,13 +16,16 @@ package com.google.common.collect.testing.google; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Maps; -import java.util.Arrays; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * Generators of various {@link com.google.common.collect.BiMap}s and derived collections. @@ -31,12 +34,14 @@ * @author Hayward Chan */ @GwtCompatible +@NullMarked public class BiMapGenerators { public static class ImmutableBiMapGenerator extends TestStringBiMapGenerator { @Override protected BiMap create(Entry[] entries) { ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); for (Entry entry : entries) { + checkNotNull(entry); builder.put(entry.getKey(), entry.getValue()); } return builder.build(); @@ -57,7 +62,7 @@ protected BiMap create(Entry[] entries) { public static class ImmutableBiMapCopyOfEntriesGenerator extends TestStringBiMapGenerator { @Override protected BiMap create(Entry[] entries) { - return ImmutableBiMap.copyOf(Arrays.asList(entries)); + return ImmutableBiMap.copyOf(asList(entries)); } } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapInverseTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapInverseTester.java index 984558e2b72d..b8f372854489 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapInverseTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapInverseTester.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.BiMap; import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; @@ -39,7 +40,9 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class BiMapInverseTester extends AbstractBiMapTester { public void testInverseSame() { @@ -65,18 +68,20 @@ private static class BiMapPair implements Serializable { this.backward = original.inverse(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns {@link Method} instances for the tests that assume that the inverse will be the same * after serialization. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getInverseSameAfterSerializingMethods() { return Collections.singletonList(getMethod("testInverseSerialization")); } + @J2ktIncompatible @GwtIncompatible // reflection private static Method getMethod(String methodName) { return Helpers.getMethod(BiMapInverseTester.class, methodName); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapPutTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapPutTester.java index 4bb72a2f3888..5106c538e005 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapPutTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapPutTester.java @@ -16,40 +16,36 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import org.junit.Ignore; /** Tester for {@code BiMap.put} and {@code BiMap.forcePut}. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class BiMapPutTester extends AbstractBiMapTester { - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ZERO) public void testPutWithSameValueFails() { getMap().put(k0(), v0()); - try { - getMap().put(k1(), v0()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // success - } + assertThrows(IllegalArgumentException.class, () -> getMap().put(k1(), v0())); // verify that the bimap is unchanged expectAdded(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ZERO) public void testPutPresentKeyDifferentValue() { @@ -57,10 +53,9 @@ public void testPutPresentKeyDifferentValue() { getMap().put(k0(), v1()); // verify that the bimap is changed, and that the old inverse mapping // from v1 -> v0 is deleted - expectContents(Helpers.mapEntry(k0(), v1())); + expectContents(mapEntry(k0(), v1())); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ZERO) public void putDistinctKeysDistinctValues() { @@ -69,41 +64,37 @@ public void putDistinctKeysDistinctValues() { expectAdded(e0(), e1()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ONE) public void testForcePutKeyPresent() { getMap().forcePut(k0(), v1()); - expectContents(Helpers.mapEntry(k0(), v1())); + expectContents(mapEntry(k0(), v1())); assertFalse(getMap().containsValue(v0())); assertNull(getMap().inverse().get(v0())); assertEquals(1, getMap().size()); assertTrue(getMap().containsKey(k0())); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ONE) public void testForcePutValuePresent() { getMap().forcePut(k1(), v0()); - expectContents(Helpers.mapEntry(k1(), v0())); + expectContents(mapEntry(k1(), v0())); assertEquals(k1(), getMap().inverse().get(v0())); assertEquals(1, getMap().size()); assertFalse(getMap().containsKey(k0())); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(SEVERAL) public void testForcePutKeyAndValuePresent() { getMap().forcePut(k0(), v1()); - expectContents(Helpers.mapEntry(k0(), v1()), Helpers.mapEntry(k2(), v2())); + expectContents(mapEntry(k0(), v1()), mapEntry(k2(), v2())); assertEquals(2, getMap().size()); assertFalse(getMap().containsKey(k1())); assertFalse(getMap().containsValue(v0())); } - @SuppressWarnings("unchecked") @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) @CollectionSize.Require(ONE) public void testForcePutNullKeyPresent() { @@ -111,7 +102,7 @@ public void testForcePutNullKeyPresent() { getMap().forcePut(null, v1()); - expectContents(Helpers.mapEntry((K) null, v1())); + expectContents(mapEntry((K) null, v1())); assertFalse(getMap().containsValue(v0())); @@ -122,7 +113,6 @@ public void testForcePutNullKeyPresent() { assertEquals(1, getMap().size()); } - @SuppressWarnings("unchecked") @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) @CollectionSize.Require(ONE) public void testForcePutNullValuePresent() { @@ -130,7 +120,7 @@ public void testForcePutNullValuePresent() { getMap().forcePut(k1(), null); - expectContents(Helpers.mapEntry(k1(), (V) null)); + expectContents(mapEntry(k1(), (V) null)); assertFalse(getMap().containsKey(k0())); @@ -143,7 +133,6 @@ public void testForcePutNullValuePresent() { // nb: inverse is run through its own entire suite - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(ZERO) public void testInversePut() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapRemoveTester.java index e54256ad864b..9e8dee23d16a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapRemoveTester.java @@ -33,9 +33,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class BiMapRemoveTester extends AbstractBiMapTester { - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveKeyRemovesFromInverse() { @@ -43,7 +44,6 @@ public void testRemoveKeyRemovesFromInverse() { expectMissing(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveKeyFromKeySetRemovesFromInverse() { @@ -51,7 +51,6 @@ public void testRemoveKeyFromKeySetRemovesFromInverse() { expectMissing(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveFromValuesRemovesFromInverse() { @@ -59,7 +58,6 @@ public void testRemoveFromValuesRemovesFromInverse() { expectMissing(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveFromInverseRemovesFromForward() { @@ -67,7 +65,6 @@ public void testRemoveFromInverseRemovesFromForward() { expectMissing(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveFromInverseKeySetRemovesFromForward() { @@ -75,7 +72,6 @@ public void testRemoveFromInverseKeySetRemovesFromForward() { expectMissing(e0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveFromInverseValuesRemovesFromInverse() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapTestSuiteBuilder.java index 0821fb0b554e..28b5572e974f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/BiMapTestSuiteBuilder.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing.google; +import static java.util.Collections.emptySet; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.BiMap; import com.google.common.collect.testing.AbstractTester; @@ -32,7 +34,6 @@ import com.google.common.collect.testing.google.DerivedGoogleCollectionGenerators.MapGenerator; import com.google.common.collect.testing.testers.SetCreationTester; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; @@ -53,6 +54,7 @@ public static BiMapTestSuiteBuilder using(TestBiMapGenerator return new BiMapTestSuiteBuilder().usingGenerator(generator); } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { List> testers = new ArrayList<>(); @@ -69,7 +71,7 @@ enum NoRecurse implements Feature { @Override public Set> getImpliedFeatures() { - return Collections.emptySet(); + return emptySet(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/DerivedGoogleCollectionGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/DerivedGoogleCollectionGenerators.java index 35579a01d758..45376acba99b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/DerivedGoogleCollectionGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/DerivedGoogleCollectionGenerators.java @@ -16,10 +16,12 @@ package com.google.common.collect.testing.google; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.BiMap; import com.google.common.collect.testing.DerivedGenerator; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.TestMapGenerator; @@ -31,6 +33,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Derived suite generators for Guava collection interfaces, split out of the suite builders so that @@ -39,8 +43,10 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public final class DerivedGoogleCollectionGenerators { - public static class MapGenerator implements TestMapGenerator, DerivedGenerator { + public static class MapGenerator + implements TestMapGenerator, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> generator; @@ -87,7 +93,7 @@ public TestSubjectGenerator getInnerGenerator() { } } - public static class InverseBiMapGenerator + public static class InverseBiMapGenerator implements TestBiMapGenerator, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> generator; @@ -109,7 +115,8 @@ public SampleElements> samples() { } private Entry reverse(Entry entry) { - return Helpers.mapEntry(entry.getValue(), entry.getKey()); + checkNotNull(entry); + return mapEntry(entry.getValue(), entry.getKey()); } @SuppressWarnings("unchecked") @@ -125,7 +132,7 @@ public BiMap create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -151,7 +158,7 @@ public TestSubjectGenerator getInnerGenerator() { } } - public static class BiMapValueSetGenerator + public static class BiMapValueSetGenerator implements TestSetGenerator, DerivedGenerator { private final OneSizeTestContainerGenerator, Entry> mapGenerator; private final SampleElements samples; @@ -186,7 +193,7 @@ public Set create(Object... elements) { Collection> entries = new ArrayList<>(elements.length); int i = 0; for (Entry entry : originalEntries) { - entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++])); + entries.add(mapEntry(entry.getKey(), valuesArray[i++])); } return mapGenerator.create(entries.toArray()).values(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListGenerators.java index 0839f09188ae..fe7229ff8159 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListGenerators.java @@ -16,20 +16,21 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Lists.charactersOf; +import static java.lang.System.arraycopy; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import com.google.common.collect.testing.TestCharacterListGenerator; import com.google.common.collect.testing.TestListGenerator; import com.google.common.collect.testing.TestStringListGenerator; import com.google.common.collect.testing.TestUnhashableCollectionGenerator; import com.google.common.collect.testing.UnhashableObject; import com.google.common.primitives.Chars; -import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * Common generators of different types of lists. @@ -37,6 +38,7 @@ * @author Hayward Chan */ @GwtCompatible +@NullMarked public final class ListGenerators { private ListGenerators() {} @@ -80,8 +82,8 @@ public static class ImmutableListHeadSubListGenerator extends TestStringListGene protected List create(String[] elements) { String[] suffix = {"f", "g"}; String[] all = new String[elements.length + suffix.length]; - System.arraycopy(elements, 0, all, 0, elements.length); - System.arraycopy(suffix, 0, all, elements.length, suffix.length); + arraycopy(elements, 0, all, 0, elements.length); + arraycopy(suffix, 0, all, elements.length, suffix.length); return ImmutableList.copyOf(all).subList(0, elements.length); } } @@ -91,8 +93,8 @@ public static class ImmutableListTailSubListGenerator extends TestStringListGene protected List create(String[] elements) { String[] prefix = {"f", "g"}; String[] all = new String[elements.length + prefix.length]; - System.arraycopy(prefix, 0, all, 0, 2); - System.arraycopy(elements, 0, all, 2, elements.length); + arraycopy(prefix, 0, all, 0, 2); + arraycopy(elements, 0, all, 2, elements.length); return ImmutableList.copyOf(all).subList(2, elements.length + 2); } } @@ -104,9 +106,9 @@ protected List create(String[] elements) { String[] suffix = {"h", "i"}; String[] all = new String[2 + elements.length + 2]; - System.arraycopy(prefix, 0, all, 0, 2); - System.arraycopy(elements, 0, all, 2, elements.length); - System.arraycopy(suffix, 0, all, 2 + elements.length, 2); + arraycopy(prefix, 0, all, 0, 2); + arraycopy(elements, 0, all, 2, elements.length); + arraycopy(suffix, 0, all, 2 + elements.length, 2); return ImmutableList.copyOf(all).subList(2, elements.length + 2); } @@ -115,18 +117,18 @@ protected List create(String[] elements) { public static class CharactersOfStringGenerator extends TestCharacterListGenerator { @Override public List create(Character[] elements) { - char[] chars = Chars.toArray(Arrays.asList(elements)); - return Lists.charactersOf(String.copyValueOf(chars)); + char[] chars = Chars.toArray(asList(elements)); + return charactersOf(String.copyValueOf(chars)); } } public static class CharactersOfCharSequenceGenerator extends TestCharacterListGenerator { @Override public List create(Character[] elements) { - char[] chars = Chars.toArray(Arrays.asList(elements)); + char[] chars = Chars.toArray(asList(elements)); StringBuilder str = new StringBuilder(); str.append(chars); - return Lists.charactersOf(str); + return charactersOf(str); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapAsMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapAsMapTester.java index ca6f21af2c55..fc3ef5fe865d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapAsMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapAsMapTester.java @@ -14,24 +14,27 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.testing.EqualsTester; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -42,8 +45,12 @@ * @param The value type of the tested multimap. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class ListMultimapAsMapTester extends AbstractListMultimapTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class ListMultimapAsMapTester + extends AbstractListMultimapTester { public void testAsMapValuesImplementList() { for (Collection valueCollection : multimap().asMap().values()) { assertTrue(valueCollection instanceof List); @@ -67,8 +74,7 @@ public void testAsMapRemoveImplementsList() { @CollectionSize.Require(SEVERAL) public void testEquals() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); Map> expected = Maps.newHashMap(); expected.put(k0(), Lists.newArrayList(v0(), v3())); expected.put(k1(), Lists.newArrayList(v0())); @@ -77,22 +83,25 @@ public void testEquals() { @CollectionSize.Require(SEVERAL) public void testEntrySetEquals() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); - Set>> expected = Sets.newHashSet(); - expected.add(Helpers.mapEntry(k0(), (Collection) Lists.newArrayList(v0(), v3()))); - expected.add(Helpers.mapEntry(k1(), (Collection) Lists.newArrayList(v0()))); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); + Set>> expected = newHashSet(); + expected.add(mapEntry(k0(), (Collection) Lists.newArrayList(v0(), v3()))); + expected.add(mapEntry(k1(), (Collection) Lists.newArrayList(v0()))); new EqualsTester().addEqualityGroup(expected, multimap().asMap().entrySet()).testEquals(); } @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) + /* + * ListMultimap.asMap essentially returns a Map>; we just can't declare it that way. + * Thus, calls like asMap().values().remove(someList) are safe because they are comparing a list + * to a collection of other lists. + */ + @SuppressWarnings("CollectionUndefinedEquality") public void testValuesRemove() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); - assertTrue(multimap().asMap().values().remove(Collections.singletonList(v0()))); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); + assertTrue(multimap().asMap().values().remove(singletonList(v0()))); assertEquals(2, multimap().size()); - assertEquals( - Collections.singletonMap(k0(), Lists.newArrayList(v0(), v3())), multimap().asMap()); + assertEquals(singletonMap(k0(), Lists.newArrayList(v0(), v3())), multimap().asMap()); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapEqualsTester.java index 2a9b14489b9f..01c51fa2be75 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapEqualsTester.java @@ -14,11 +14,11 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.EqualsTester; import org.junit.Ignore; @@ -29,22 +29,18 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListMultimapEqualsTester extends AbstractListMultimapTester { @CollectionSize.Require(SEVERAL) public void testOrderingAffectsEqualsComparisons() { ListMultimap multimap1 = getSubjectGenerator() - .create( - Helpers.mapEntry(k0(), v0()), - Helpers.mapEntry(k0(), v1()), - Helpers.mapEntry(k0(), v0())); + .create(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v0())); ListMultimap multimap2 = getSubjectGenerator() - .create( - Helpers.mapEntry(k0(), v1()), - Helpers.mapEntry(k0(), v0()), - Helpers.mapEntry(k0(), v0())); + .create(mapEntry(k0(), v1()), mapEntry(k0(), v0()), mapEntry(k0(), v0())); new EqualsTester().addEqualityGroup(multimap1).addEqualityGroup(multimap2).testEquals(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutAllTester.java index 243361680335..046014b5e43d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutAllTester.java @@ -16,11 +16,11 @@ import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; import com.google.common.collect.testing.features.MapFeature; -import java.util.Arrays; import java.util.List; import org.junit.Ignore; @@ -30,12 +30,13 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListMultimapPutAllTester extends AbstractListMultimapTester { @MapFeature.Require(SUPPORTS_PUT) public void testPutAllAddsAtEndInOrder() { - @SuppressWarnings("unchecked") - List values = Arrays.asList(v3(), v1(), v4()); + List values = asList(v3(), v1(), v4()); for (K k : sampleKeys()) { resetContainer(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutTester.java index c459496825dd..f730826c5fbe 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapPutTester.java @@ -20,7 +20,6 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.List; @@ -33,7 +32,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListMultimapPutTester extends AbstractListMultimapTester { // MultimapPutTester tests non-duplicate values, but ignores ordering @@ -44,7 +45,7 @@ public void testPutAddsValueAtEnd() { resetContainer(); List values = multimap().get(key); - List expectedValues = Helpers.copyToList(values); + List expectedValues = copyToList(values); assertTrue(multimap().put(key, value)); expectedValues.add(value); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapRemoveTester.java index 04ac0a2bc5c3..d2a5263596b4 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapRemoveTester.java @@ -19,12 +19,12 @@ import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map.Entry; @@ -36,9 +36,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListMultimapRemoveTester extends AbstractListMultimapTester { - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testMultimapRemoveDeletesFirstOccurrence() { @@ -49,11 +50,10 @@ public void testMultimapRemoveDeletesFirstOccurrence() { assertContentsInOrder(list, v1(), v0()); } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testRemoveAtIndexFromGetPropagates() { - List values = Arrays.asList(v0(), v1(), v0()); + List values = asList(v0(), v1(), v0()); for (int i = 0; i < 3; i++) { resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v0())); @@ -66,11 +66,10 @@ public void testRemoveAtIndexFromGetPropagates() { } } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testRemoveAtIndexFromAsMapPropagates() { - List values = Arrays.asList(v0(), v1(), v0()); + List values = asList(v0(), v1(), v0()); for (int i = 0; i < 3; i++) { resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v0())); @@ -84,11 +83,10 @@ public void testRemoveAtIndexFromAsMapPropagates() { } } - @SuppressWarnings("unchecked") @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testRemoveAtIndexFromAsMapEntrySetPropagates() { - List values = Arrays.asList(v0(), v1(), v0()); + List values = asList(v0(), v1(), v0()); for (int i = 0; i < 3; i++) { resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v0())); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapReplaceValuesTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapReplaceValuesTester.java index b0701658fcbb..1f87e69f0748 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapReplaceValuesTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapReplaceValuesTester.java @@ -16,11 +16,11 @@ import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; import com.google.common.collect.testing.features.MapFeature; -import java.util.Arrays; import java.util.List; import org.junit.Ignore; @@ -30,12 +30,13 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListMultimapReplaceValuesTester extends AbstractListMultimapTester { @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testReplaceValuesPreservesOrder() { - @SuppressWarnings("unchecked") - List values = Arrays.asList(v3(), v1(), v4()); + List values = asList(v3(), v1(), v4()); for (K k : sampleKeys()) { resetContainer(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapTestSuiteBuilder.java index b55c7d648f10..27e7afd487ca 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ListMultimapTestSuiteBuilder.java @@ -16,11 +16,12 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.ListMultimap; import com.google.common.collect.testing.AbstractTester; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.TestListGenerator; @@ -52,9 +53,10 @@ public static ListMultimapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(ListMultimapAsMapTester.class); testers.add(ListMultimapEqualsTester.class); testers.add(ListMultimapPutTester.class); @@ -101,6 +103,9 @@ Set> computeMultimapGetFeatures(Set> multimapFeatures) { if (derivedFeatures.contains(CollectionFeature.SUPPORTS_ADD)) { derivedFeatures.add(ListFeature.SUPPORTS_ADD_WITH_INDEX); } + if (derivedFeatures.contains(CollectionFeature.SUPPORTS_REMOVE)) { + derivedFeatures.add(ListFeature.SUPPORTS_REMOVE_WITH_INDEX); + } if (derivedFeatures.contains(CollectionFeature.GENERAL_PURPOSE)) { derivedFeatures.add(ListFeature.GENERAL_PURPOSE); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MapGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MapGenerators.java index bb84ea15f931..28f55ca53034 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MapGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MapGenerators.java @@ -16,12 +16,14 @@ package com.google.common.collect.testing.google; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.testing.AnEnum; @@ -33,12 +35,12 @@ import com.google.common.collect.testing.TestStringMapGenerator; import com.google.common.collect.testing.TestUnhashableCollectionGenerator; import com.google.common.collect.testing.UnhashableObject; -import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * Generators of different types of map and related collections, such as keys, entries and values. @@ -46,15 +48,17 @@ * @author Hayward Chan */ @GwtCompatible +@NullMarked public class MapGenerators { public static class ImmutableMapGenerator extends TestStringMapGenerator { @Override protected Map create(Entry[] entries) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Entry entry : entries) { + checkNotNull(entry); builder.put(entry.getKey(), entry.getValue()); } - return builder.build(); + return builder.buildOrThrow(); } } @@ -72,7 +76,7 @@ protected Map create(Entry[] entries) { public static class ImmutableMapCopyOfEntriesGenerator extends TestStringMapGenerator { @Override protected Map create(Entry[] entries) { - return ImmutableMap.copyOf(Arrays.asList(entries)); + return ImmutableMap.copyOf(asList(entries)); } } @@ -86,7 +90,7 @@ public Collection create(UnhashableObject[] elements) { for (UnhashableObject value : elements) { builder.put(key++, value); } - return builder.build().values(); + return builder.buildOrThrow().values(); } } @@ -97,7 +101,7 @@ public List create(String[] elements) { for (int i = 0; i < elements.length; i++) { builder.put(elements[i], i); } - return builder.build().keySet().asList(); + return builder.buildOrThrow().keySet().asList(); } } @@ -108,7 +112,7 @@ public List create(String[] elements) { for (int i = 0; i < elements.length; i++) { builder.put(i, elements[i]); } - return builder.build().values().asList(); + return builder.buildOrThrow().values().asList(); } } @@ -128,7 +132,7 @@ public SampleElements> samples() { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -141,10 +145,10 @@ public List> create(Object... elements) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Object o : elements) { @SuppressWarnings("unchecked") - Entry entry = (Entry) o; + Entry entry = (Entry) checkNotNull(o); builder.put(entry); } - return builder.build().entrySet().asList(); + return builder.buildOrThrow().entrySet().asList(); } } @@ -153,7 +157,7 @@ public static class ImmutableEnumMapGenerator extends TestEnumMapGenerator { protected Map create(Entry[] entries) { Map map = Maps.newHashMap(); for (Entry entry : entries) { - // checkArgument(!map.containsKey(entry.getKey())); + checkNotNull(entry); map.put(entry.getKey(), entry.getValue()); } return Maps.immutableEnumMap(map); @@ -188,16 +192,11 @@ public static class ImmutableMapValuesAsSingletonSetGenerator @Override public SampleElements>> samples() { return new SampleElements<>( - mapEntry("one", collectionOf(10000)), - mapEntry("two", collectionOf(-2000)), - mapEntry("three", collectionOf(300)), - mapEntry("four", collectionOf(-40)), - mapEntry("five", collectionOf(5))); - } - - // javac7 can't infer the type parameters correctly in samples() - private static Collection collectionOf(int item) { - return ImmutableSet.of(item); + mapEntry("one", ImmutableSet.of(10000)), + mapEntry("two", ImmutableSet.of(-2000)), + mapEntry("three", ImmutableSet.of(300)), + mapEntry("four", ImmutableSet.of(-40)), + mapEntry("five", ImmutableSet.of(5))); } @Override @@ -207,10 +206,10 @@ public Map> create(Object... elements) { for (Object elem : elements) { @SuppressWarnings("unchecked") // safe by generator contract Entry> entry = (Entry>) elem; - Integer value = Iterables.getOnlyElement(entry.getValue()); + Integer value = getOnlyElement(entry.getValue()); builder.put(entry.getKey(), value); } - return builder.build().asMultimap().asMap(); + return builder.buildOrThrow().asMultimap().asMap(); } @Override @@ -232,7 +231,7 @@ public String[] createKeyArray(int length) { @Override @SuppressWarnings({"unchecked", "rawtypes"}) // needed for arrays - public ImmutableSet[] createValueArray(int length) { + public Collection[] createValueArray(int length) { return new ImmutableSet[length]; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapGetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapGetTester.java index dadb9a324535..c9be74710cac 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapGetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapGetTester.java @@ -18,16 +18,17 @@ import static com.google.common.collect.testing.Helpers.assertContentsAnyOrder; import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; @@ -39,14 +40,15 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapAsMapGetTester extends AbstractMultimapTester> { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) public void testPropagatesRemoveToMultimap() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3()), Helpers.mapEntry(k0(), v2())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3()), mapEntry(k0(), v2())); Collection result = multimap().asMap().get(k0()); assertTrue(result.remove(v0())); assertFalse(multimap().containsEntry(k0(), v0())); @@ -89,11 +91,7 @@ public void testRemoveNullValue() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testAddNullValueUnsupported() { Collection result = multimap().asMap().get(k0()); - try { - result.add(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> result.add(null)); } @CollectionSize.Require(absent = ZERO) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapTester.java index 23b2351672e8..df8a30d91e17 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapAsMapTester.java @@ -14,9 +14,11 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.testing.Helpers.assertContentsAnyOrder; import static com.google.common.collect.testing.Helpers.assertContentsInOrder; import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; @@ -24,11 +26,10 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -46,7 +47,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapAsMapTester extends AbstractMultimapTester> { public void testAsMapGet() { for (K key : sampleKeys()) { @@ -80,11 +83,7 @@ public void testAsMapGetNullKeyAbsent() { @MapFeature.Require(absent = ALLOWS_NULL_KEY_QUERIES) public void testAsMapGetNullKeyUnsupported() { - try { - multimap().asMap().get(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> multimap().asMap().get(null)); } @CollectionSize.Require(absent = ZERO) @@ -98,10 +97,10 @@ public void testAsMapRemove() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_PUT) public void testAsMapEntrySetReflectsPutSameKey() { - resetContainer(Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3())); Set>> asMapEntrySet = multimap().asMap().entrySet(); - Collection valueCollection = Iterables.getOnlyElement(asMapEntrySet).getValue(); + Collection valueCollection = getOnlyElement(asMapEntrySet).getValue(); assertContentsAnyOrder(valueCollection, v0(), v3()); assertTrue(multimap().put(k0(), v4())); assertContentsAnyOrder(valueCollection, v0(), v3(), v4()); @@ -110,7 +109,7 @@ public void testAsMapEntrySetReflectsPutSameKey() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_PUT) public void testAsMapEntrySetReflectsPutDifferentKey() { - resetContainer(Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3())); Set>> asMapEntrySet = multimap().asMap().entrySet(); assertTrue(multimap().put(k1(), v4())); @@ -120,9 +119,9 @@ public void testAsMapEntrySetReflectsPutDifferentKey() { @CollectionSize.Require(SEVERAL) @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testAsMapEntrySetRemovePropagatesToMultimap() { - resetContainer(Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3())); Set>> asMapEntrySet = multimap().asMap().entrySet(); - Entry> asMapEntry0 = Iterables.getOnlyElement(asMapEntrySet); + Entry> asMapEntry0 = getOnlyElement(asMapEntrySet); assertTrue(multimap().put(k1(), v4())); assertTrue(asMapEntrySet.remove(asMapEntry0)); assertEquals(1, multimap().size()); @@ -132,7 +131,7 @@ public void testAsMapEntrySetRemovePropagatesToMultimap() { @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) public void testAsMapEntrySetIteratorRemovePropagatesToMultimap() { - resetContainer(Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3())); Set>> asMapEntrySet = multimap().asMap().entrySet(); Iterator>> asMapEntryItr = asMapEntrySet.iterator(); asMapEntryItr.next(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapClearTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapClearTester.java index 6ce9907c14d9..907c99c66ac0 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapClearTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapClearTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.google.GoogleHelpers.assertEmpty; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; @@ -36,18 +37,18 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapClearTester extends AbstractMultimapTester> { @CollectionSize.Require(absent = ZERO) @MapFeature.Require(absent = SUPPORTS_REMOVE) public void testClearUnsupported() { - try { - multimap().clear(); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> multimap().clear()); } + // Empty multimaps *do* have defined equals semantics. + @SuppressWarnings("UndefinedEquals") private void assertCleared() { assertEquals(0, multimap().size()); assertEmpty(multimap()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsEntryTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsEntryTester.java index 9da7fe3f2ac8..b302f3aa3410 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsEntryTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsEntryTester.java @@ -21,6 +21,7 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; @@ -34,7 +35,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapContainsEntryTester extends AbstractMultimapTester> { @CollectionSize.Require(absent = ZERO) @@ -68,21 +71,11 @@ public void testContainsEntryNullNo() { @MapFeature.Require(absent = ALLOWS_NULL_KEY_QUERIES) public void testContainsEntryNullDisallowedBecauseKeyQueriesDisallowed() { - try { - multimap().containsEntry(null, v3()); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().containsEntry(null, v3())); } @MapFeature.Require(absent = ALLOWS_NULL_VALUE_QUERIES) public void testContainsEntryNullDisallowedBecauseValueQueriesDisallowed() { - try { - multimap().containsEntry(k3(), null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().containsEntry(k3(), null)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsKeyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsKeyTester.java index a7dcd6626e0f..dcd4d4e3c251 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsKeyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsKeyTester.java @@ -19,6 +19,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; @@ -32,7 +33,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapContainsKeyTester extends AbstractMultimapTester> { @CollectionSize.Require(absent = ZERO) public void testContainsKeyYes() { @@ -81,11 +84,6 @@ public void testContainsKeyNullAbsent() { @MapFeature.Require(absent = ALLOWS_NULL_KEY_QUERIES) public void testContainsKeyNullDisallowed() { - try { - multimap().containsKey(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().containsKey(null)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsValueTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsValueTester.java index 00ca12ad514b..fcb601364e4b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsValueTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapContainsValueTester.java @@ -19,6 +19,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; @@ -32,7 +33,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapContainsValueTester extends AbstractMultimapTester> { @CollectionSize.Require(absent = ZERO) @@ -58,11 +61,6 @@ public void testContainsNullValueNo() { @MapFeature.Require(absent = ALLOWS_NULL_VALUE_QUERIES) public void testContainsNullValueFails() { - try { - multimap().containsValue(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().containsValue(null)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEntriesTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEntriesTester.java index 9874b884cc4b..2fc19974a5a6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEntriesTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEntriesTester.java @@ -16,6 +16,7 @@ import static com.google.common.collect.testing.Helpers.assertContains; import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; @@ -24,14 +25,13 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; -import java.util.Collections; import java.util.Iterator; import java.util.Map.Entry; import org.junit.Ignore; @@ -42,7 +42,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapEntriesTester extends AbstractMultimapTester> { public void testEntries() { assertEqualIgnoringOrder(getSampleElements(), multimap().entries()); @@ -52,31 +54,31 @@ public void testEntries() { @MapFeature.Require(ALLOWS_NULL_KEYS) public void testContainsEntryWithNullKeyPresent() { initMultimapWithNullKey(); - assertContains(multimap().entries(), Helpers.mapEntry((K) null, getValueForNullKey())); + assertContains(multimap().entries(), mapEntry((K) null, getValueForNullKey())); } @MapFeature.Require(ALLOWS_NULL_KEY_QUERIES) public void testContainsEntryWithNullKeyAbsent() { - assertFalse(multimap().entries().contains(Helpers.mapEntry(null, v0()))); + assertFalse(multimap().entries().contains(mapEntry(null, v0()))); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(ALLOWS_NULL_VALUES) public void testContainsEntryWithNullValuePresent() { initMultimapWithNullValue(); - assertContains(multimap().entries(), Helpers.mapEntry(getKeyForNullValue(), (V) null)); + assertContains(multimap().entries(), mapEntry(getKeyForNullValue(), (V) null)); } @MapFeature.Require(ALLOWS_NULL_VALUE_QUERIES) public void testContainsEntryWithNullValueAbsent() { - assertFalse(multimap().entries().contains(Helpers.mapEntry(k0(), null))); + assertFalse(multimap().entries().contains(mapEntry(k0(), null))); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_REMOVE) public void testRemovePropagatesToMultimap() { - assertTrue(multimap().entries().remove(Helpers.mapEntry(k0(), v0()))); - expectMissing(Helpers.mapEntry(k0(), v0())); + assertTrue(multimap().entries().remove(mapEntry(k0(), v0()))); + expectMissing(mapEntry(k0(), v0())); assertEquals(getNumElements() - 1, multimap().size()); assertFalse(multimap().containsEntry(k0(), v0())); } @@ -84,17 +86,23 @@ public void testRemovePropagatesToMultimap() { @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_REMOVE) public void testRemoveAllPropagatesToMultimap() { - assertTrue(multimap().entries().removeAll(Collections.singleton(Helpers.mapEntry(k0(), v0())))); - expectMissing(Helpers.mapEntry(k0(), v0())); + assertTrue(multimap().entries().removeAll(singleton(mapEntry(k0(), v0())))); + expectMissing(mapEntry(k0(), v0())); assertEquals(getNumElements() - 1, multimap().size()); assertFalse(multimap().containsEntry(k0(), v0())); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_REMOVE) + /* + * We are comparing Multimaps of the same type, so as long as they have value collections that + * implement equals() (as with ListMultimap or SetMultimap, as opposed to a QueueMultimap or + * something), our equality check is value-based. + */ + @SuppressWarnings("UndefinedEquals") public void testRetainAllPropagatesToMultimap() { - multimap().entries().retainAll(Collections.singleton(Helpers.mapEntry(k0(), v0()))); - assertEquals(getSubjectGenerator().create(Helpers.mapEntry(k0(), v0())), multimap()); + multimap().entries().retainAll(singleton(mapEntry(k0(), v0()))); + assertEquals(getSubjectGenerator().create(mapEntry(k0(), v0())), multimap()); assertEquals(1, multimap().size()); assertTrue(multimap().containsEntry(k0(), v0())); } @@ -103,7 +111,7 @@ public void testRetainAllPropagatesToMultimap() { @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) public void testIteratorRemovePropagatesToMultimap() { Iterator> iterator = multimap().entries().iterator(); - assertEquals(Helpers.mapEntry(k0(), v0()), iterator.next()); + assertEquals(mapEntry(k0(), v0()), iterator.next()); iterator.remove(); assertTrue(multimap().isEmpty()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEqualsTester.java index 21163602eecc..9a56b0d56a2d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapEqualsTester.java @@ -14,19 +14,21 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.testing.EqualsTester; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -35,8 +37,12 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class MultimapEqualsTester extends AbstractMultimapTester> { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class MultimapEqualsTester + extends AbstractMultimapTester> { public void testEqualsTrue() { new EqualsTester() .addEqualityGroup(multimap(), getSubjectGenerator().create(getSampleElements().toArray())) @@ -45,7 +51,7 @@ public void testEqualsTrue() { public void testEqualsFalse() { List> targetEntries = new ArrayList<>(getSampleElements()); - targetEntries.add(Helpers.mapEntry(k0(), v3())); + targetEntries.add(mapEntry(k0(), v3())); new EqualsTester() .addEqualityGroup(multimap()) .addEqualityGroup(getSubjectGenerator().create(targetEntries.toArray())) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapFeature.java index 8c1bdaad6694..86db577c061f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapFeature.java @@ -16,9 +16,10 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToSet; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.Feature; import com.google.common.collect.testing.features.TesterAnnotation; import java.lang.annotation.Inherited; @@ -31,8 +32,7 @@ * * @author Louis Wasserman */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum MultimapFeature implements Feature { VALUE_COLLECTIONS_SUPPORT_ITERATOR_REMOVE; @@ -40,7 +40,7 @@ public enum MultimapFeature implements Feature { private final Set> implied; MultimapFeature(Feature... implied) { - this.implied = Helpers.copyToSet(implied); + this.implied = copyToSet(implied); } @Override @@ -52,8 +52,8 @@ public Set> getImpliedFeatures() { @Inherited @TesterAnnotation public @interface Require { - public abstract MultimapFeature[] value() default {}; + MultimapFeature[] value() default {}; - public abstract MultimapFeature[] absent() default {}; + MultimapFeature[] absent() default {}; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapGetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapGetTester.java index 6978473232fa..2e524f2b1016 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapGetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapGetTester.java @@ -18,6 +18,7 @@ import static com.google.common.collect.testing.Helpers.assertContains; import static com.google.common.collect.testing.Helpers.assertContentsAnyOrder; import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; @@ -25,14 +26,14 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; -import java.util.Collections; import org.junit.Ignore; /** @@ -41,7 +42,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapGetTester extends AbstractMultimapTester> { public void testGetEmpty() { Collection result = multimap().get(k3()); @@ -58,8 +61,7 @@ public void testGetNonEmpty() { @CollectionSize.Require(SEVERAL) public void testGetMultiple() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v1()), Helpers.mapEntry(k0(), v2())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v2())); assertGet(k0(), v0(), v1(), v2()); } @@ -70,8 +72,7 @@ public void testGetAbsentKey() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) public void testPropagatesRemoveToMultimap() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v3()), Helpers.mapEntry(k0(), v2())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v3()), mapEntry(k0(), v2())); Collection result = multimap().get(k0()); assertTrue(result.remove(v0())); assertFalse(multimap().containsEntry(k0(), v0())); @@ -98,7 +99,7 @@ public void testPropagatesAddToMultimap() { @MapFeature.Require(SUPPORTS_PUT) public void testPropagatesAddAllToMultimap() { Collection result = multimap().get(k0()); - assertTrue(result.addAll(Collections.singletonList(v3()))); + assertTrue(result.addAll(singletonList(v3()))); assertTrue(multimap().containsKey(k0())); assertEquals(getNumElements() + 1, multimap().size()); assertTrue(multimap().containsEntry(k0(), v3())); @@ -141,12 +142,7 @@ public void testGetNullAbsent() { @MapFeature.Require(absent = ALLOWS_NULL_KEY_QUERIES) public void testGetNullForbidden() { - try { - multimap().get(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().get(null)); } @MapFeature.Require(ALLOWS_NULL_VALUES) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeySetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeySetTester.java index 100b15bb760c..0e9905eafb85 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeySetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeySetTester.java @@ -35,7 +35,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapKeySetTester extends AbstractMultimapTester> { public void testKeySet() { for (Entry entry : getSampleElements()) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeysTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeysTester.java index 6b2a93ca3d02..56c18000faa6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeysTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapKeysTester.java @@ -15,18 +15,19 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertContainsAllOf; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.lang.Math.max; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import com.google.common.collect.Multisets; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -39,12 +40,13 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapKeysTester extends AbstractMultimapTester> { @CollectionSize.Require(SEVERAL) public void testKeys() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v1()), Helpers.mapEntry(k1(), v0())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k1(), v0())); Multiset keys = multimap().keys(); assertEquals(2, keys.count(k0())); assertEquals(1, keys.count(k1())); @@ -62,10 +64,7 @@ public void testKeysCountAbsentNullKey() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(ALLOWS_NULL_KEYS) public void testKeysWithNullKey() { - resetContainer( - Helpers.mapEntry((K) null, v0()), - Helpers.mapEntry((K) null, v1()), - Helpers.mapEntry(k1(), v0())); + resetContainer(mapEntry((K) null, v0()), mapEntry((K) null, v1()), mapEntry(k1(), v0())); Multiset keys = multimap().keys(); assertEquals(2, keys.count(null)); assertEquals(1, keys.count(k1())); @@ -82,7 +81,7 @@ public void testKeysElementSet() { @MapFeature.Require(SUPPORTS_REMOVE) public void testKeysRemove() { int original = multimap().keys().remove(k0(), 1); - assertEquals(Math.max(original - 1, 0), multimap().get(k0()).size()); + assertEquals(max(original - 1, 0), multimap().get(k0()).size()); } @CollectionSize.Require(ONE) @@ -98,8 +97,7 @@ public void testKeysEntrySetIteratorRemove() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) public void testKeysEntrySetRemove() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v1()), Helpers.mapEntry(k1(), v0())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k1(), v0())); assertTrue(multimap().keys().entrySet().remove(Multisets.immutableEntry(k0(), 2))); assertEquals(1, multimap().size()); assertTrue(multimap().containsEntry(k1(), v0())); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutAllMultimapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutAllMultimapTester.java index 92622933f8b7..2f7cddef1ec4 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutAllMultimapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutAllMultimapTester.java @@ -17,13 +17,14 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertContains; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; import org.junit.Ignore; @@ -34,19 +35,21 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapPutAllMultimapTester extends AbstractMultimapTester> { @MapFeature.Require(absent = SUPPORTS_PUT) public void testPutUnsupported() { - try { - multimap().putAll(getSubjectGenerator().create(Helpers.mapEntry(k3(), v3()))); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> multimap().putAll(getSubjectGenerator().create(mapEntry(k3(), v3())))); } @MapFeature.Require(SUPPORTS_PUT) + // Empty multimaps *do* have defined equals semantics. + @SuppressWarnings("UndefinedEquals") public void testPutAllIntoEmpty() { Multimap target = getSubjectGenerator().create(); assertEquals(!multimap().isEmpty(), target.putAll(multimap())); @@ -56,7 +59,7 @@ public void testPutAllIntoEmpty() { @MapFeature.Require(SUPPORTS_PUT) public void testPutAll() { Multimap source = - getSubjectGenerator().create(Helpers.mapEntry(k0(), v3()), Helpers.mapEntry(k3(), v3())); + getSubjectGenerator().create(mapEntry(k0(), v3()), mapEntry(k3(), v3())); assertTrue(multimap().putAll(source)); assertTrue(multimap().containsEntry(k0(), v3())); assertTrue(multimap().containsEntry(k3(), v3())); @@ -64,44 +67,36 @@ public void testPutAll() { @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) public void testPutAllWithNullValue() { - Multimap source = getSubjectGenerator().create(Helpers.mapEntry(k0(), null)); + Multimap source = getSubjectGenerator().create(mapEntry(k0(), null)); assertTrue(multimap().putAll(source)); assertTrue(multimap().containsEntry(k0(), null)); } @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) public void testPutAllWithNullKey() { - Multimap source = getSubjectGenerator().create(Helpers.mapEntry(null, v0())); + Multimap source = getSubjectGenerator().create(mapEntry(null, v0())); assertTrue(multimap().putAll(source)); assertTrue(multimap().containsEntry(null, v0())); } @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPutAllRejectsNullValue() { - Multimap source = getSubjectGenerator().create(Helpers.mapEntry(k0(), null)); - try { - multimap().putAll(source); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + Multimap source = getSubjectGenerator().create(mapEntry(k0(), null)); + assertThrows(NullPointerException.class, () -> multimap().putAll(source)); expectUnchanged(); } @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) public void testPutAllRejectsNullKey() { - Multimap source = getSubjectGenerator().create(Helpers.mapEntry(null, v0())); - try { - multimap().putAll(source); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + Multimap source = getSubjectGenerator().create(mapEntry(null, v0())); + assertThrows(NullPointerException.class, () -> multimap().putAll(source)); expectUnchanged(); } @MapFeature.Require(SUPPORTS_PUT) public void testPutAllPropagatesToGet() { Multimap source = - getSubjectGenerator().create(Helpers.mapEntry(k0(), v3()), Helpers.mapEntry(k3(), v3())); + getSubjectGenerator().create(mapEntry(k0(), v3()), mapEntry(k3(), v3())); Collection getCollection = multimap().get(k0()); int getCollectionSize = getCollection.size(); assertTrue(multimap().putAll(source)); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutIterableTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutIterableTester.java index b36037877a32..fed32a35473e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutIterableTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutIterableTester.java @@ -16,16 +16,17 @@ package com.google.common.collect.testing.google; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.testing.Helpers.assertContainsAllOf; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; -import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -40,68 +41,56 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +@SuppressWarnings({ + // @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. + "JUnit4ClassUsedInJUnit3", + // We use ::iterator so that we test passing a plain Iterable, not a Collection. + "UnnecessaryMethodReference", +}) public class MultimapPutIterableTester extends AbstractMultimapTester> { @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_PUT) public void testPutAllNonEmptyIterableOnPresentKey() { - assertTrue( - multimap() - .putAll( - k0(), - new Iterable() { - @Override - public Iterator iterator() { - return Lists.newArrayList(v3(), v4()).iterator(); - } - })); + assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4())::iterator)); assertGet(k0(), v0(), v3(), v4()); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_PUT) public void testPutAllNonEmptyCollectionOnPresentKey() { - assertTrue(multimap().putAll(k0(), Lists.newArrayList(v3(), v4()))); + assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4()))); assertGet(k0(), v0(), v3(), v4()); } @MapFeature.Require(SUPPORTS_PUT) public void testPutAllNonEmptyIterableOnAbsentKey() { - assertTrue( - multimap() - .putAll( - k3(), - new Iterable() { - @Override - public Iterator iterator() { - return Lists.newArrayList(v3(), v4()).iterator(); - } - })); + assertTrue(multimap().putAll(k3(), newArrayList(v3(), v4())::iterator)); assertGet(k3(), v3(), v4()); } @MapFeature.Require(SUPPORTS_PUT) public void testPutAllNonEmptyCollectionOnAbsentKey() { - assertTrue(multimap().putAll(k3(), Lists.newArrayList(v3(), v4()))); + assertTrue(multimap().putAll(k3(), newArrayList(v3(), v4()))); assertGet(k3(), v3(), v4()); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) public void testPutAllNullValueOnPresentKey_supported() { - assertTrue(multimap().putAll(k0(), Lists.newArrayList(v3(), null))); + assertTrue(multimap().putAll(k0(), newArrayList(v3(), null))); assertGet(k0(), v0(), v3(), null); } @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) public void testPutAllNullValueOnAbsentKey_supported() { - assertTrue(multimap().putAll(k3(), Lists.newArrayList(v3(), null))); + assertTrue(multimap().putAll(k3(), newArrayList(v3(), null))); assertGet(k3(), v3(), null); } @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPutAllNullValueSingle_unsupported() { - multimap().putAll(k1(), Lists.newArrayList((V) null)); + multimap().putAll(k1(), newArrayList((V) null)); expectUnchanged(); } @@ -111,20 +100,17 @@ public void testPutAllNullValueSingle_unsupported() { public void testPutAllNullValueNullLast_unsupported() { int size = getNumElements(); - try { - multimap().putAll(k3(), Lists.newArrayList(v3(), null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> multimap().putAll(k3(), newArrayList(v3(), null))); Collection values = multimap().get(k3()); if (values.size() == 0) { expectUnchanged(); // Be extra thorough in case internal state was corrupted by the expected null. - assertEquals(Lists.newArrayList(), Lists.newArrayList(values)); + assertEquals(newArrayList(), newArrayList(values)); assertEquals(size, multimap().size()); } else { - assertEquals(Lists.newArrayList(v3()), Lists.newArrayList(values)); + assertEquals(newArrayList(v3()), newArrayList(values)); assertEquals(size + 1, multimap().size()); } } @@ -133,11 +119,8 @@ public void testPutAllNullValueNullLast_unsupported() { public void testPutAllNullValueNullFirst_unsupported() { int size = getNumElements(); - try { - multimap().putAll(k3(), Lists.newArrayList(null, v3())); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> multimap().putAll(k3(), newArrayList(null, v3()))); /* * In principle, a Multimap implementation could add e3 first before failing on the null. But @@ -146,24 +129,19 @@ public void testPutAllNullValueNullFirst_unsupported() { */ expectUnchanged(); // Be extra thorough in case internal state was corrupted by the expected null. - assertEquals(Lists.newArrayList(), Lists.newArrayList(multimap().get(k3()))); + assertEquals(newArrayList(), newArrayList(multimap().get(k3()))); assertEquals(size, multimap().size()); } @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) public void testPutAllOnPresentNullKey() { - assertTrue(multimap().putAll(null, Lists.newArrayList(v3(), v4()))); + assertTrue(multimap().putAll(null, newArrayList(v3(), v4()))); assertGet(null, v3(), v4()); } - @MapFeature.Require(absent = ALLOWS_NULL_KEYS) + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) public void testPutAllNullForbidden() { - try { - multimap().putAll(null, Collections.singletonList(v3())); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().putAll(null, singletonList(v3()))); } @MapFeature.Require(SUPPORTS_PUT) @@ -174,15 +152,7 @@ public void testPutAllEmptyCollectionOnAbsentKey() { @MapFeature.Require(SUPPORTS_PUT) public void testPutAllEmptyIterableOnAbsentKey() { - Iterable iterable = - new Iterable() { - @Override - public Iterator iterator() { - return ImmutableSet.of().iterator(); - } - }; - - assertFalse(multimap().putAll(k3(), iterable)); + assertFalse(multimap().putAll(k3(), Collections::emptyIterator)); expectUnchanged(); } @@ -214,7 +184,7 @@ public Iterator iterator() { public void testPutAllPropagatesToGet() { Collection getCollection = multimap().get(k0()); int getCollectionSize = getCollection.size(); - assertTrue(multimap().putAll(k0(), Lists.newArrayList(v3(), v4()))); + assertTrue(multimap().putAll(k0(), newArrayList(v3(), v4()))); assertEquals(getCollectionSize + 2, getCollection.size()); assertContainsAllOf(getCollection, v3(), v4()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutTester.java index c108e8525ca4..b25763d4aa21 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapPutTester.java @@ -19,22 +19,26 @@ import static com.google.common.collect.testing.Helpers.assertContains; import static com.google.common.collect.testing.Helpers.assertEmpty; import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -43,15 +47,15 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class MultimapPutTester extends AbstractMultimapTester> { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class MultimapPutTester + extends AbstractMultimapTester> { @MapFeature.Require(absent = SUPPORTS_PUT) public void testPutUnsupported() { - try { - multimap().put(k3(), v3()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> multimap().put(k3(), v3())); } @MapFeature.Require(SUPPORTS_PUT) @@ -83,7 +87,7 @@ public void testPutPresent() { public void testPutTwoElements() { int size = getNumElements(); - List values = Helpers.copyToList(multimap().get(k0())); + List values = copyToList(multimap().get(k0())); assertTrue(multimap().put(k0(), v1())); assertTrue(multimap().put(k0(), v2())); @@ -107,11 +111,7 @@ public void testPutNullValue_supported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPutNullValue_unsupported() { - try { - multimap().put(k1(), null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> multimap().put(k1(), null)); expectUnchanged(); } @@ -139,31 +139,31 @@ public void testPutNotPresentKeyPropagatesToGet() { @MapFeature.Require(SUPPORTS_PUT) public void testPutNotPresentKeyPropagatesToEntries() { Collection> entries = multimap().entries(); - assertFalse(entries.contains(Helpers.mapEntry(k3(), v3()))); + assertFalse(entries.contains(mapEntry(k3(), v3()))); multimap().put(k3(), v3()); - assertContains(entries, Helpers.mapEntry(k3(), v3())); + assertContains(entries, mapEntry(k3(), v3())); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(SUPPORTS_PUT) public void testPutPresentKeyPropagatesToEntries() { Collection> entries = multimap().entries(); - assertFalse(entries.contains(Helpers.mapEntry(k0(), v3()))); + assertFalse(entries.contains(mapEntry(k0(), v3()))); multimap().put(k0(), v3()); - assertContains(entries, Helpers.mapEntry(k0(), v3())); + assertContains(entries, mapEntry(k0(), v3())); } @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testPutPresentKeyPropagatesToGet() { - List keys = Helpers.copyToList(multimap().keySet()); + List keys = copyToList(multimap().keySet()); for (K key : keys) { resetContainer(); int size = getNumElements(); Collection collection = multimap().get(key); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().put(key, v3()); expectedCollection.add(v3()); @@ -175,7 +175,7 @@ public void testPutPresentKeyPropagatesToGet() { @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testPutPresentKeyPropagatesToAsMapGet() { - List keys = Helpers.copyToList(multimap().keySet()); + List keys = copyToList(multimap().keySet()); for (K key : keys) { resetContainer(); @@ -183,7 +183,7 @@ public void testPutPresentKeyPropagatesToAsMapGet() { Collection collection = multimap().asMap().get(key); assertNotNull(collection); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().put(key, v3()); expectedCollection.add(v3()); @@ -195,7 +195,7 @@ public void testPutPresentKeyPropagatesToAsMapGet() { @MapFeature.Require(SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testPutPresentKeyPropagatesToAsMapEntrySet() { - List keys = Helpers.copyToList(multimap().keySet()); + List keys = copyToList(multimap().keySet()); for (K key : keys) { resetContainer(); @@ -211,7 +211,7 @@ public void testPutPresentKeyPropagatesToAsMapEntrySet() { } } assertNotNull(collection); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().put(key, v3()); expectedCollection.add(v3()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveAllTester.java index 185ba2c206cd..afce6a6e7027 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveAllTester.java @@ -18,6 +18,7 @@ import static com.google.common.collect.testing.Helpers.assertContentsAnyOrder; import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_ANY_NULL_QUERIES; @@ -27,7 +28,6 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; @@ -39,7 +39,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapRemoveAllTester extends AbstractMultimapTester> { @MapFeature.Require(SUPPORTS_REMOVE) public void testRemoveAllAbsentKey() { @@ -68,8 +70,7 @@ public void testRemoveAllPropagatesToGet() { @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) public void testRemoveAllMultipleValues() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k0(), v1()), Helpers.mapEntry(k0(), v2())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v2())); assertContentsAnyOrder(multimap().removeAll(k0()), v0(), v1(), v2()); assertEmpty(multimap()); @@ -82,7 +83,7 @@ public void testRemoveAllNullKeyPresent() { assertContentsAnyOrder(multimap().removeAll(null), getValueForNullKey()); - expectMissing(Helpers.mapEntry((K) null, getValueForNullKey())); + expectMissing(mapEntry((K) null, getValueForNullKey())); } @MapFeature.Require({SUPPORTS_REMOVE, ALLOWS_ANY_NULL_QUERIES}) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveEntryTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveEntryTester.java index 250a691f3f5a..20300f09bd62 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveEntryTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapRemoveEntryTester.java @@ -17,17 +17,19 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; @@ -42,7 +44,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapRemoveEntryTester extends AbstractMultimapTester> { @MapFeature.Require(SUPPORTS_REMOVE) public void testRemoveAbsent() { @@ -68,7 +72,7 @@ public void testRemoveNullKeyPresent() { assertTrue(multimap().remove(null, getValueForNullKey())); - expectMissing(Helpers.mapEntry((K) null, getValueForNullKey())); + expectMissing(mapEntry((K) null, getValueForNullKey())); assertGet(getKeyForNullValue(), ImmutableList.of()); } @@ -79,7 +83,7 @@ public void testRemoveNullValuePresent() { assertTrue(multimap().remove(getKeyForNullValue(), null)); - expectMissing(Helpers.mapEntry(getKeyForNullValue(), (V) null)); + expectMissing(mapEntry(getKeyForNullValue(), (V) null)); assertGet(getKeyForNullValue(), ImmutableList.of()); } @@ -97,30 +101,20 @@ public void testRemoveNullValueAbsent() { @MapFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_VALUE_QUERIES) public void testRemoveNullValueForbidden() { - try { - multimap().remove(k0(), null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().remove(k0(), null)); expectUnchanged(); } @MapFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_KEY_QUERIES) public void testRemoveNullKeyForbidden() { - try { - multimap().remove(null, v0()); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - // success - } + assertThrows(NullPointerException.class, () -> multimap().remove(null, v0())); expectUnchanged(); } @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemovePropagatesToGet() { - List> entries = Helpers.copyToList(multimap().entries()); + List> entries = copyToList(multimap().entries()); for (Entry entry : entries) { resetContainer(); @@ -128,7 +122,7 @@ public void testRemovePropagatesToGet() { V value = entry.getValue(); Collection collection = multimap().get(key); assertNotNull(collection); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().remove(key, value); expectedCollection.remove(value); @@ -141,7 +135,7 @@ public void testRemovePropagatesToGet() { @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemovePropagatesToAsMap() { - List> entries = Helpers.copyToList(multimap().entries()); + List> entries = copyToList(multimap().entries()); for (Entry entry : entries) { resetContainer(); @@ -149,7 +143,7 @@ public void testRemovePropagatesToAsMap() { V value = entry.getValue(); Collection collection = multimap().asMap().get(key); assertNotNull(collection); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().remove(key, value); expectedCollection.remove(value); @@ -162,7 +156,7 @@ public void testRemovePropagatesToAsMap() { @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemovePropagatesToAsMapEntrySet() { - List> entries = Helpers.copyToList(multimap().entries()); + List> entries = copyToList(multimap().entries()); for (Entry entry : entries) { resetContainer(); @@ -179,7 +173,7 @@ public void testRemovePropagatesToAsMapEntrySet() { } } assertNotNull(collection); - Collection expectedCollection = Helpers.copyToList(collection); + Collection expectedCollection = copyToList(collection); multimap().remove(key, value); expectedCollection.remove(value); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapReplaceValuesTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapReplaceValuesTester.java index 3e2597d8dbf0..afa9fb9f8823 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapReplaceValuesTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapReplaceValuesTester.java @@ -17,21 +17,23 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertContentsAnyOrder; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import org.junit.Ignore; @@ -41,22 +43,22 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapReplaceValuesTester extends AbstractMultimapTester> { @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_VALUES}) public void testReplaceValuesWithNullValue() { - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), null, v3()); + List values = asList(v0(), null, v3()); multimap().replaceValues(k0(), values); assertGet(k0(), values); } @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_KEYS}) public void testReplaceValuesWithNullKey() { - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), v2(), v3()); + List values = asList(v0(), v2(), v3()); multimap().replaceValues(null, values); assertGet(null, values); } @@ -64,8 +66,7 @@ public void testReplaceValuesWithNullKey() { @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testReplaceEmptyValues() { int size = multimap().size(); - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), v2(), v3()); + List values = asList(v0(), v2(), v3()); multimap().replaceValues(k3(), values); assertGet(k3(), values); assertEquals(size + values.size(), multimap().size()); @@ -75,8 +76,7 @@ public void testReplaceEmptyValues() { public void testReplaceValuesWithEmpty() { int size = multimap().size(); List oldValues = new ArrayList<>(multimap().get(k0())); - @SuppressWarnings("unchecked") - List values = Collections.emptyList(); + List values = emptyList(); assertEquals(oldValues, new ArrayList(multimap().replaceValues(k0(), values))); assertGet(k0()); assertEquals(size - oldValues.size(), multimap().size()); @@ -86,7 +86,7 @@ public void testReplaceValuesWithEmpty() { public void testReplaceValuesWithDuplicates() { int size = multimap().size(); List oldValues = new ArrayList<>(multimap().get(k0())); - List values = Arrays.asList(v0(), v3(), v0()); + List values = asList(v0(), v3(), v0()); assertEquals(oldValues, new ArrayList(multimap().replaceValues(k0(), values))); assertEquals(size - oldValues.size() + multimap().get(k0()).size(), multimap().size()); assertTrue(multimap().get(k0()).containsAll(values)); @@ -95,15 +95,14 @@ public void testReplaceValuesWithDuplicates() { @CollectionSize.Require(absent = ZERO) @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testReplaceNonEmptyValues() { - List keys = Helpers.copyToList(multimap().keySet()); - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), v2(), v3()); + List keys = copyToList(multimap().keySet()); + List values = asList(v0(), v2(), v3()); for (K k : keys) { resetContainer(); int size = multimap().size(); - Collection oldKeyValues = Helpers.copyToList(multimap().get(k)); + Collection oldKeyValues = copyToList(multimap().get(k)); multimap().replaceValues(k, values); assertGet(k, values); assertEquals(size + values.size() - oldKeyValues.size(), multimap().size()); @@ -113,8 +112,7 @@ public void testReplaceNonEmptyValues() { @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testReplaceValuesPropagatesToGet() { Collection getCollection = multimap().get(k0()); - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), v2(), v3()); + List values = asList(v0(), v2(), v3()); multimap().replaceValues(k0(), values); assertContentsAnyOrder(getCollection, v0(), v2(), v3()); } @@ -122,23 +120,13 @@ public void testReplaceValuesPropagatesToGet() { @MapFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testReplaceValuesRemoveNotSupported() { - List values = Collections.singletonList(v3()); - try { - multimap().replaceValues(k0(), values); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - // success - } + List values = singletonList(v3()); + assertThrows(UnsupportedOperationException.class, () -> multimap().replaceValues(k0(), values)); } @MapFeature.Require(absent = SUPPORTS_PUT) public void testReplaceValuesPutNotSupported() { - List values = Collections.singletonList(v3()); - try { - multimap().replaceValues(k0(), values); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - // success - } + List values = singletonList(v3()); + assertThrows(UnsupportedOperationException.class, () -> multimap().replaceValues(k0(), values)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapSizeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapSizeTester.java index 23d6bdf043e3..03d5e07d2f87 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapSizeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapSizeTester.java @@ -28,6 +28,8 @@ import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -36,8 +38,12 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class MultimapSizeTester extends AbstractMultimapTester> { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class MultimapSizeTester + extends AbstractMultimapTester> { public void testSize() { int expectedSize = getNumElements(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapTestSuiteBuilder.java index 97c24c98d277..ee755852117f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapTestSuiteBuilder.java @@ -17,7 +17,9 @@ package com.google.common.collect.testing.google; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.testing.Helpers.copyToSet; import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.ImmutableList; @@ -28,7 +30,6 @@ import com.google.common.collect.testing.CollectionTestSuiteBuilder; import com.google.common.collect.testing.DerivedGenerator; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.PerCollectionSizeTestSuiteBuilder; @@ -73,6 +74,7 @@ public static > MultimapTestSuiteBuilder } // Class parameters must be raw. + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { return ImmutableList.>of( @@ -207,7 +209,7 @@ TestSuite computeKeysTestSuite( } static Set> computeDerivedCollectionFeatures(Set> multimapFeatures) { - Set> derivedFeatures = Helpers.copyToSet(multimapFeatures); + Set> derivedFeatures = copyToSet(multimapFeatures); if (!derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS)) { derivedFeatures.remove(CollectionFeature.SERIALIZABLE); } @@ -249,14 +251,14 @@ static Set> computeKeysFeatures(Set> multimapFeatures) { private static Set> computeReserializedMultimapFeatures( Set> multimapFeatures) { - Set> derivedFeatures = Helpers.copyToSet(multimapFeatures); + Set> derivedFeatures = copyToSet(multimapFeatures); derivedFeatures.remove(CollectionFeature.SERIALIZABLE); derivedFeatures.remove(CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS); return derivedFeatures; } private static Set> computeAsMapFeatures(Set> multimapFeatures) { - Set> derivedFeatures = Helpers.copyToSet(multimapFeatures); + Set> derivedFeatures = copyToSet(multimapFeatures); derivedFeatures.remove(MapFeature.GENERAL_PURPOSE); derivedFeatures.remove(MapFeature.SUPPORTS_PUT); derivedFeatures.remove(MapFeature.ALLOWS_NULL_VALUES); @@ -283,7 +285,7 @@ private static Set> computeAsMapFeatures(Set> multimapFeat .build(); Set> computeMultimapGetFeatures(Set> multimapFeatures) { - Set> derivedFeatures = Helpers.copyToSet(multimapFeatures); + Set> derivedFeatures = copyToSet(multimapFeatures); for (Entry, Feature> entry : GET_FEATURE_MAP.entries()) { if (derivedFeatures.contains(entry.getKey())) { derivedFeatures.add(entry.getValue()); @@ -300,8 +302,7 @@ Set> computeMultimapGetFeatures(Set> multimapFeatures) { } Set> computeMultimapAsMapGetFeatures(Set> multimapFeatures) { - Set> derivedFeatures = - Helpers.copyToSet(computeMultimapGetFeatures(multimapFeatures)); + Set> derivedFeatures = copyToSet(computeMultimapGetFeatures(multimapFeatures)); if (derivedFeatures.remove(CollectionSize.ANY)) { derivedFeatures.addAll(CollectionSize.ANY.getImpliedFeatures()); } @@ -324,7 +325,7 @@ public TestSubjectGenerator getInnerGenerator() { private Collection createCollection(V v) { return ((TestMultimapGenerator) multimapGenerator.getInnerGenerator()) - .createCollection(Collections.singleton(v)); + .createCollection(singleton(v)); } @Override @@ -346,10 +347,16 @@ public Map> create(Object... elements) { Set keySet = new HashSet<>(); List> builder = new ArrayList<>(); for (Object o : elements) { - Entry> entry = (Entry>) o; - keySet.add(entry.getKey()); - for (V v : entry.getValue()) { - builder.add(mapEntry(entry.getKey(), v)); + Entry entry = (Entry) o; + // These come from Entry>> objects somewhere. + @SuppressWarnings("unchecked") + K key = (K) entry.getKey(); + keySet.add(key); + for (Object v : (Collection) entry.getValue()) { + // These come from Entry>> objects somewhere. + @SuppressWarnings("unchecked") + V value = (V) v; + builder.add(mapEntry(key, value)); } } checkArgument(keySet.size() == elements.length, "Duplicate keys"); @@ -359,7 +366,7 @@ public Map> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry>[] createArray(int length) { - return new Entry[length]; + return (Entry>[]) new Entry[length]; } @Override @@ -389,7 +396,7 @@ public K[] createKeyArray(int length) { @SuppressWarnings("unchecked") @Override public Collection[] createValueArray(int length) { - return new Collection[length]; + return (Collection[]) new Collection[length]; } } @@ -419,7 +426,7 @@ public Collection> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -448,14 +455,15 @@ public Collection create(Object... elements) { ((TestMultimapGenerator) multimapGenerator.getInnerGenerator()) .sampleKeys() .e0(); - Entry[] entries = new Entry[elements.length]; + Object[] entries = new Object[elements.length]; for (int i = 0; i < elements.length; i++) { - entries[i] = mapEntry(k, (V) elements[i]); + @SuppressWarnings("unchecked") // These come from Entry objects somewhere. + V value = (V) elements[i]; + entries[i] = mapEntry(k, value); } - return multimapGenerator.create((Object[]) entries).values(); + return multimapGenerator.create(entries).values(); } - @SuppressWarnings("unchecked") @Override public V[] createArray(int length) { return ((TestMultimapGenerator) multimapGenerator.getInnerGenerator()) @@ -505,17 +513,17 @@ public Multiset create(Object... elements) { * This is nasty and complicated, but it's the only way to make sure keys get mapped to enough * distinct values. */ - Entry[] entries = new Entry[elements.length]; + Entry[] entries = new Entry[elements.length]; Map> valueIterators = new HashMap<>(); for (int i = 0; i < elements.length; i++) { - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // These come from Entry objects somewhere. K key = (K) elements[i]; Iterator valueItr = valueIterators.get(key); if (valueItr == null) { valueIterators.put(key, valueItr = sampleValuesIterator()); } - entries[i] = mapEntry((K) elements[i], valueItr.next()); + entries[i] = mapEntry(key, valueItr.next()); } return multimapGenerator.create((Object[]) entries).keys(); } @@ -526,7 +534,6 @@ private Iterator sampleValuesIterator() { .iterator(); } - @SuppressWarnings("unchecked") @Override public K[] createArray(int length) { return ((TestMultimapGenerator) multimapGenerator.getInnerGenerator()) @@ -595,7 +602,9 @@ public Collection create(Object... elements) { .sampleKeys() .e0(); for (int i = 0; i < elements.length; i++) { - array[i] = mapEntry(k, (V) elements[i]); + @SuppressWarnings("unchecked") // These come from Entry objects somewhere. + V value = (V) elements[i]; + array[i] = mapEntry(k, value); } return multimapGenerator.create((Object[]) array).get(k); } @@ -617,7 +626,9 @@ public Collection create(Object... elements) { .sampleKeys() .e0(); for (int i = 0; i < elements.length; i++) { - array[i] = mapEntry(k, (V) elements[i]); + @SuppressWarnings("unchecked") // These come from Entry objects somewhere. + V value = (V) elements[i]; + array[i] = mapEntry(k, value); } return multimapGenerator.create((Object[]) array).asMap().get(k); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapToStringTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapToStringTester.java index 203f278b7f93..fb07a37ae478 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapToStringTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapToStringTester.java @@ -33,7 +33,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapToStringTester extends AbstractMultimapTester> { @CollectionSize.Require(ZERO) @CollectionFeature.Require(absent = NON_STANDARD_TOSTRING) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapValuesTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapValuesTester.java index ab7afce862d4..f776c470c4d1 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapValuesTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultimapValuesTester.java @@ -36,7 +36,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultimapValuesTester extends AbstractMultimapTester> { public void testValues() { List expected = Lists.newArrayList(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetAddTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetAddTester.java index fa8874dba760..b7ad8d8a3b25 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetAddTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetAddTester.java @@ -17,10 +17,11 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; -import java.util.Arrays; import java.util.Collections; import org.junit.Ignore; @@ -30,15 +31,13 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetAddTester extends AbstractMultisetTester { @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testAddUnsupported() { - try { - getMultiset().add(e0()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMultiset().add(e0())); } @CollectionFeature.Require(SUPPORTS_ADD) @@ -73,30 +72,18 @@ public void testAddSeveralTimes() { @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testAddOccurrences_unsupported() { - try { - getMultiset().add(e0(), 2); - fail("unsupported multiset.add(E, int) didn't throw exception"); - } catch (UnsupportedOperationException required) { - } + assertThrows(UnsupportedOperationException.class, () -> getMultiset().add(e0(), 2)); } @CollectionFeature.Require(SUPPORTS_ADD) public void testAddOccurrencesNegative() { - try { - getMultiset().add(e0(), -1); - fail("multiset.add(E, -1) didn't throw an exception"); - } catch (IllegalArgumentException required) { - } + assertThrows(IllegalArgumentException.class, () -> getMultiset().add(e0(), -1)); } @CollectionFeature.Require(SUPPORTS_ADD) public void testAddTooMany() { getMultiset().add(e3(), Integer.MAX_VALUE); - try { - getMultiset().add(e3()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> getMultiset().add(e3())); assertEquals(Integer.MAX_VALUE, getMultiset().count(e3())); assertEquals(Integer.MAX_VALUE, getMultiset().size()); } @@ -115,7 +102,7 @@ public void testAddAll_emptyMultiset() { @CollectionFeature.Require(SUPPORTS_ADD) public void testAddAll_nonEmptyList() { - assertTrue(getMultiset().addAll(Arrays.asList(e3(), e4(), e3()))); + assertTrue(getMultiset().addAll(asList(e3(), e4(), e3()))); expectAdded(e3(), e4(), e3()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetContainsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetContainsTester.java index bdbd090cc8ff..7cad976c2faf 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetContainsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetContainsTester.java @@ -15,10 +15,10 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Arrays; import org.junit.Ignore; /** @@ -27,7 +27,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetContainsTester extends AbstractMultisetTester { @CollectionSize.Require(absent = ZERO) public void testContainsAllMultisetIgnoresFrequency() { @@ -36,6 +38,6 @@ public void testContainsAllMultisetIgnoresFrequency() { @CollectionSize.Require(absent = ZERO) public void testContainsAllListIgnoresFrequency() { - assertTrue(getMultiset().containsAll(Arrays.asList(e0(), e0(), e0()))); + assertTrue(getMultiset().containsAll(asList(e0(), e0(), e0()))); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetCountTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetCountTester.java index 7c07cd33d06c..752cb09b798a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetCountTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetCountTester.java @@ -16,19 +16,21 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_QUERIES; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.WrongType; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import org.junit.Ignore; @@ -38,7 +40,9 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetCountTester extends AbstractMultisetTester { public void testCount_0() { @@ -63,11 +67,7 @@ public void testCount_nullAbsent() { @CollectionFeature.Require(absent = ALLOWS_NULL_QUERIES) public void testCount_null_forbidden() { - try { - getMultiset().count(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMultiset().count(null)); } @CollectionSize.Require(absent = ZERO) @@ -86,8 +86,9 @@ public void testCount_wrongType() { * Returns {@link Method} instances for the read tests that assume multisets support duplicates so * that the test of {@code Multisets.forSet()} can suppress them. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getCountDuplicateInitializingMethods() { - return Arrays.asList(Helpers.getMethod(MultisetCountTester.class, "testCount_3")); + return asList(getMethod(MultisetCountTester.class, "testCount_3")); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetElementSetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetElementSetTester.java index baa6071f84bf..872d3dc8936f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetElementSetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetElementSetTester.java @@ -17,19 +17,20 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Set; import org.junit.Ignore; @@ -40,7 +41,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetElementSetTester extends AbstractMultisetTester { @CollectionFeature.Require(SUPPORTS_ADD) public void testElementSetReflectsAddAbsent() { @@ -55,7 +58,7 @@ public void testElementSetReflectsAddAbsent() { public void testElementSetReflectsRemove() { Set elementSet = getMultiset().elementSet(); assertTrue(elementSet.contains(e0())); - getMultiset().removeAll(Collections.singleton(e0())); + getMultiset().removeAll(singleton(e0())); assertFalse(elementSet.contains(e0())); } @@ -99,10 +102,11 @@ public void testElementSetClear() { * Returns {@link Method} instances for the read tests that assume multisets support duplicates so * that the test of {@code Multisets.forSet()} can suppress them. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getElementSetDuplicateInitializingMethods() { - return Arrays.asList( - Helpers.getMethod( + return asList( + getMethod( MultisetElementSetTester.class, "testElementSetRemoveDuplicatePropagatesToMultiset")); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEntrySetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEntrySetTester.java index 3bec616aae11..a82efa8568f7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEntrySetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEntrySetTester.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; @@ -23,14 +24,13 @@ import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.google.MultisetFeature.ENTRIES_ARE_VIEWS; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.Iterables; import com.google.common.collect.Multiset; import com.google.common.collect.Multisets; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Collections; import java.util.Iterator; import org.junit.Ignore; @@ -40,7 +40,9 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetEntrySetTester extends AbstractMultisetTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -93,9 +95,7 @@ public void testEntrySet_removeAbsent() { public void testEntrySet_removeAllPresent() { assertTrue( "multiset.entrySet.removeAll(presentEntry) returned false", - getMultiset() - .entrySet() - .removeAll(Collections.singleton(Multisets.immutableEntry(e0(), 1)))); + getMultiset().entrySet().removeAll(singleton(Multisets.immutableEntry(e0(), 1)))); assertFalse("multiset contains element after removing its entry", getMultiset().contains(e0())); } @@ -104,9 +104,7 @@ public void testEntrySet_removeAllPresent() { public void testEntrySet_removeAllAbsent() { assertFalse( "multiset.entrySet.remove(missingEntry) returned true", - getMultiset() - .entrySet() - .removeAll(Collections.singleton(Multisets.immutableEntry(e0(), 2)))); + getMultiset().entrySet().removeAll(singleton(Multisets.immutableEntry(e0(), 2)))); assertTrue( "multiset didn't contain element after removing a missing entry", getMultiset().contains(e0())); @@ -117,9 +115,7 @@ public void testEntrySet_removeAllAbsent() { public void testEntrySet_retainAllPresent() { assertFalse( "multiset.entrySet.retainAll(presentEntry) returned false", - getMultiset() - .entrySet() - .retainAll(Collections.singleton(Multisets.immutableEntry(e0(), 1)))); + getMultiset().entrySet().retainAll(singleton(Multisets.immutableEntry(e0(), 1)))); assertTrue( "multiset doesn't contains element after retaining its entry", getMultiset().contains(e0())); @@ -130,9 +126,7 @@ public void testEntrySet_retainAllPresent() { public void testEntrySet_retainAllAbsent() { assertTrue( "multiset.entrySet.retainAll(missingEntry) returned true", - getMultiset() - .entrySet() - .retainAll(Collections.singleton(Multisets.immutableEntry(e0(), 2)))); + getMultiset().entrySet().retainAll(singleton(Multisets.immutableEntry(e0(), 2)))); assertFalse( "multiset contains element after retaining a different entry", getMultiset().contains(e0())); @@ -144,7 +138,7 @@ public void testEntrySet_retainAllAbsent() { public void testEntryViewReflectsRemove() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); assertTrue(getMultiset().remove(e0())); assertEquals(2, entry.getCount()); @@ -158,7 +152,7 @@ public void testEntryViewReflectsRemove() { public void testEntryReflectsIteratorRemove() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); Iterator itr = getMultiset().iterator(); itr.next(); @@ -177,7 +171,7 @@ public void testEntryReflectsIteratorRemove() { public void testEntryReflectsClear() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); getMultiset().clear(); assertEquals(0, entry.getCount()); @@ -189,7 +183,7 @@ public void testEntryReflectsClear() { public void testEntryReflectsEntrySetClear() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); getMultiset().entrySet().clear(); assertEquals(0, entry.getCount()); @@ -213,7 +207,7 @@ public void testEntryReflectsEntrySetIteratorRemove() { public void testEntryReflectsElementSetClear() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); getMultiset().elementSet().clear(); assertEquals(0, entry.getCount()); @@ -225,7 +219,7 @@ public void testEntryReflectsElementSetClear() { public void testEntryReflectsElementSetIteratorRemove() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); Iterator elementItr = getMultiset().elementSet().iterator(); elementItr.next(); @@ -239,7 +233,7 @@ public void testEntryReflectsElementSetIteratorRemove() { public void testEntryReflectsRemoveThenAdd() { initThreeCopies(); assertEquals(3, getMultiset().count(e0())); - Multiset.Entry entry = Iterables.getOnlyElement(getMultiset().entrySet()); + Multiset.Entry entry = getOnlyElement(getMultiset().entrySet()); assertEquals(3, entry.getCount()); assertTrue(getMultiset().remove(e0())); assertEquals(2, entry.getCount()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEqualsTester.java index 9d9fee0a1a15..a940e48b7f80 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetEqualsTester.java @@ -29,7 +29,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetEqualsTester extends AbstractMultisetTester { public void testEqualsSameContents() { new EqualsTester() diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetFeature.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetFeature.java index d05c560021a8..407a4b20db89 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetFeature.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetFeature.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing.google; +import static java.util.Collections.emptySet; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multiset; import com.google.common.collect.testing.features.Feature; @@ -23,7 +25,6 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collections; import java.util.Set; /** @@ -31,6 +32,7 @@ * * @author Louis Wasserman */ +@SuppressWarnings("rawtypes") // maybe avoidable if we rework the whole package? @GwtCompatible public enum MultisetFeature implements Feature { /** @@ -41,15 +43,15 @@ public enum MultisetFeature implements Feature { @Override public Set> getImpliedFeatures() { - return Collections.emptySet(); + return emptySet(); } @Retention(RetentionPolicy.RUNTIME) @Inherited @TesterAnnotation public @interface Require { - public abstract MultisetFeature[] value() default {}; + MultisetFeature[] value() default {}; - public abstract MultisetFeature[] absent() default {}; + MultisetFeature[] absent() default {}; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetIteratorTester.java index 34a8c725852d..5c6c6c6f4233 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetIteratorTester.java @@ -14,20 +14,23 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.features.CollectionFeature; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Iterator; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -37,15 +40,17 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class MultisetIteratorTester extends AbstractMultisetTester { - @SuppressWarnings("unchecked") +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class MultisetIteratorTester extends AbstractMultisetTester { @CollectionFeature.Require({SUPPORTS_ITERATOR_REMOVE, KNOWN_ORDER}) public void testRemovingIteratorKnownOrder() { new IteratorTester( 4, MODIFIABLE, - getSubjectGenerator().order(Arrays.asList(e0(), e1(), e1(), e2())), + getSubjectGenerator().order(asList(e0(), e1(), e1(), e2())), IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { @@ -54,14 +59,10 @@ protected Iterator newTargetIterator() { }.test(); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(value = SUPPORTS_ITERATOR_REMOVE, absent = KNOWN_ORDER) public void testRemovingIteratorUnknownOrder() { new IteratorTester( - 4, - MODIFIABLE, - Arrays.asList(e0(), e1(), e1(), e2()), - IteratorTester.KnownOrder.UNKNOWN_ORDER) { + 4, MODIFIABLE, asList(e0(), e1(), e1(), e2()), IteratorTester.KnownOrder.UNKNOWN_ORDER) { @Override protected Iterator newTargetIterator() { return getSubjectGenerator().create(e0(), e1(), e1(), e2()).iterator(); @@ -69,13 +70,12 @@ protected Iterator newTargetIterator() { }.test(); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(value = KNOWN_ORDER, absent = SUPPORTS_ITERATOR_REMOVE) public void testIteratorKnownOrder() { new IteratorTester( 4, UNMODIFIABLE, - getSubjectGenerator().order(Arrays.asList(e0(), e1(), e1(), e2())), + getSubjectGenerator().order(asList(e0(), e1(), e1(), e2())), IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { @@ -84,14 +84,10 @@ protected Iterator newTargetIterator() { }.test(); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(absent = {SUPPORTS_ITERATOR_REMOVE, KNOWN_ORDER}) public void testIteratorUnknownOrder() { new IteratorTester( - 4, - UNMODIFIABLE, - Arrays.asList(e0(), e1(), e1(), e2()), - IteratorTester.KnownOrder.UNKNOWN_ORDER) { + 4, UNMODIFIABLE, asList(e0(), e1(), e1(), e2()), IteratorTester.KnownOrder.UNKNOWN_ORDER) { @Override protected Iterator newTargetIterator() { return getSubjectGenerator().create(e0(), e1(), e1(), e2()).iterator(); @@ -103,12 +99,13 @@ protected Iterator newTargetIterator() { * Returns {@link Method} instances for the tests that assume multisets support duplicates so that * the test of {@code Multisets.forSet()} can suppress them. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getIteratorDuplicateInitializingMethods() { - return Arrays.asList( - Helpers.getMethod(MultisetIteratorTester.class, "testIteratorKnownOrder"), - Helpers.getMethod(MultisetIteratorTester.class, "testIteratorUnknownOrder"), - Helpers.getMethod(MultisetIteratorTester.class, "testRemovingIteratorKnownOrder"), - Helpers.getMethod(MultisetIteratorTester.class, "testRemovingIteratorUnknownOrder")); + return asList( + getMethod(MultisetIteratorTester.class, "testIteratorKnownOrder"), + getMethod(MultisetIteratorTester.class, "testIteratorUnknownOrder"), + getMethod(MultisetIteratorTester.class, "testRemovingIteratorKnownOrder"), + getMethod(MultisetIteratorTester.class, "testRemovingIteratorUnknownOrder")); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetNavigationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetNavigationTester.java index ce8e283a9c3e..563f1f866df3 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetNavigationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetNavigationTester.java @@ -22,18 +22,21 @@ import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.nCopies; +import static java.util.Collections.singletonList; +import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.BoundType; import com.google.common.collect.Iterators; -import com.google.common.collect.Multiset; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Multisets; import com.google.common.collect.SortedMultiset; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; @@ -45,7 +48,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetNavigationTester extends AbstractMultisetTester { private SortedMultiset sortedMultiset; private List entries; @@ -53,20 +58,15 @@ public class MultisetNavigationTester extends AbstractMultisetTester { private Entry b; private Entry c; - /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ - static SortedMultiset cast(Multiset iterable) { - return (SortedMultiset) iterable; - } - @Override public void setUp() throws Exception { super.setUp(); - sortedMultiset = cast(getMultiset()); + sortedMultiset = (SortedMultiset) getMultiset(); entries = copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, sortedMultiset.comparator()); + sort(entries, sortedMultiset.comparator()); // some tests assume SEVERAL == 3 if (entries.size() >= 1) { @@ -79,12 +79,11 @@ public void setUp() throws Exception { } /** Resets the contents of sortedMultiset to have entries a, c, for the navigation tests. */ - @SuppressWarnings("unchecked") // Needed to stop Eclipse whining private void resetWithHole() { List container = new ArrayList<>(); - container.addAll(Collections.nCopies(a.getCount(), a.getElement())); - container.addAll(Collections.nCopies(c.getCount(), c.getElement())); + container.addAll(nCopies(a.getCount(), a.getElement())); + container.addAll(nCopies(c.getCount(), c.getElement())); super.resetContainer(getSubjectGenerator().create(container.toArray())); sortedMultiset = (SortedMultiset) getMultiset(); } @@ -92,11 +91,7 @@ private void resetWithHole() { @CollectionSize.Require(ZERO) public void testEmptyMultisetFirst() { assertNull(sortedMultiset.firstEntry()); - try { - sortedMultiset.elementSet().first(); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> sortedMultiset.elementSet().first()); } @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -116,11 +111,8 @@ public void testEmptyMultisetNearby() { @CollectionSize.Require(ZERO) public void testEmptyMultisetLast() { assertNull(sortedMultiset.lastEntry()); - try { - assertNull(sortedMultiset.elementSet().last()); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows( + NoSuchElementException.class, () -> assertNull(sortedMultiset.elementSet().last())); } @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -167,21 +159,16 @@ public void testFirst() { assertEquals(a, sortedMultiset.firstEntry()); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testPollFirst() { assertEquals(a, sortedMultiset.pollFirstEntry()); - assertEquals(Arrays.asList(b, c), copyToList(sortedMultiset.entrySet())); + assertEquals(asList(b, c), copyToList(sortedMultiset.entrySet())); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testPollFirstUnsupported() { - try { - sortedMultiset.pollFirstEntry(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> sortedMultiset.pollFirstEntry()); } @CollectionSize.Require(SEVERAL) @@ -222,22 +209,17 @@ public void testLast() { assertEquals(c, sortedMultiset.lastEntry()); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testPollLast() { assertEquals(c, sortedMultiset.pollLastEntry()); - assertEquals(Arrays.asList(a, b), copyToList(sortedMultiset.entrySet())); + assertEquals(asList(a, b), copyToList(sortedMultiset.entrySet())); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testPollLastUnsupported() { - try { - sortedMultiset.pollLastEntry(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> sortedMultiset.pollLastEntry()); } @CollectionSize.Require(SEVERAL) @@ -264,7 +246,7 @@ void expectAddFailure(SortedMultiset multiset, Entry entry) { } try { - multiset.addAll(Collections.singletonList(entry.getElement())); + multiset.addAll(singletonList(entry.getElement())); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } @@ -453,9 +435,8 @@ public void testEmptyRangeSubMultiset(SortedMultiset multiset) { assertFalse(multiset.entrySet().iterator().hasNext()); } - @SuppressWarnings("unchecked") public void testEmptyRangeSubMultisetSupportingAdd(SortedMultiset multiset) { - for (Entry entry : Arrays.asList(a, b, c)) { + for (Entry entry : asList(a, b, c)) { expectAddFailure(multiset, entry); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetReadsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetReadsTester.java index 3a0cf59d3c50..78dfbd04af8c 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetReadsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetReadsTester.java @@ -33,7 +33,9 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetReadsTester extends AbstractMultisetTester { @CollectionSize.Require(absent = ZERO) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetRemoveTester.java index e6594c18a2d1..67719e987628 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetRemoveTester.java @@ -17,21 +17,24 @@ package com.google.common.collect.testing.google; import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_QUERIES; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.google.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.WrongType; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.junit.Ignore; @@ -42,25 +45,19 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetRemoveTester extends AbstractMultisetTester { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testRemoveNegative() { - try { - getMultiset().remove(e0(), -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> getMultiset().remove(e0(), -1)); expectUnchanged(); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testRemoveUnsupported() { - try { - getMultiset().remove(e0(), 2); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMultiset().remove(e0(), 2)); } @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -127,11 +124,7 @@ public void testRemove_occurrences_0() { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testRemove_occurrences_negative() { - try { - getMultiset().remove(e0(), -1); - fail("multiset.remove(E, -1) didn't throw an exception"); - } catch (IllegalArgumentException required) { - } + assertThrows(IllegalArgumentException.class, () -> getMultiset().remove(e0(), -1)); } @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -160,18 +153,14 @@ public void testRemove_nullAbsent() { @CollectionFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_QUERIES) public void testRemove_nullForbidden() { - try { - getMultiset().remove(null, 2); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMultiset().remove(null, 2)); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testRemoveAllIgnoresCount() { initThreeCopies(); - assertTrue(getMultiset().removeAll(Collections.singleton(e0()))); + assertTrue(getMultiset().removeAll(singleton(e0()))); assertEmpty(getMultiset()); } @@ -179,8 +168,8 @@ public void testRemoveAllIgnoresCount() { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testRetainAllIgnoresCount() { initThreeCopies(); - List contents = Helpers.copyToList(getMultiset()); - assertFalse(getMultiset().retainAll(Collections.singleton(e0()))); + List contents = copyToList(getMultiset()); + assertFalse(getMultiset().retainAll(singleton(e0()))); expectContents(contents); } @@ -188,9 +177,9 @@ public void testRetainAllIgnoresCount() { * Returns {@link Method} instances for the remove tests that assume multisets support duplicates * so that the test of {@code Multisets.forSet()} can suppress them. */ + @J2ktIncompatible @GwtIncompatible // reflection public static List getRemoveDuplicateInitializingMethods() { - return Arrays.asList( - Helpers.getMethod(MultisetRemoveTester.class, "testRemove_some_occurrences_present")); + return asList(getMethod(MultisetRemoveTester.class, "testRemove_some_occurrences_present")); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSerializationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSerializationTester.java index 03039706b5a5..1ef915a45a51 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSerializationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSerializationTester.java @@ -27,12 +27,14 @@ /** * A generic JUnit test which tests multiset-specific serialization. Can't be invoked directly; - * please see {@link com.google.common.collect.testing.MultisetTestSuiteBuilder}. + * please see {@link MultisetTestSuiteBuilder}. * * @author Louis Wasserman */ @GwtCompatible // but no-op -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetSerializationTester extends AbstractMultisetTester { @CollectionFeature.Require(SERIALIZABLE_INCLUDING_VIEWS) public void testEntrySetSerialization() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountConditionallyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountConditionallyTester.java index d5e69638f4f8..f3f8bd7d0e98 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountConditionallyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountConditionallyTester.java @@ -24,6 +24,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.junit.Ignore; /** @@ -33,7 +34,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetSetCountConditionallyTester extends AbstractMultisetSetCountTester { @Override void setCountCheckReturnValue(E element, int count) { @@ -47,6 +50,7 @@ void setCountNoCheckReturnValue(E element, int count) { setCount(element, count); } + @CanIgnoreReturnValue private boolean setCount(E element, int count) { return getMultiset().setCount(element, getMultiset().count(element), count); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountUnconditionallyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountUnconditionallyTester.java index ec5436ddac1a..9810f918be30 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountUnconditionallyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetSetCountUnconditionallyTester.java @@ -17,6 +17,7 @@ package com.google.common.collect.testing.google; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.junit.Ignore; /** @@ -26,7 +27,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MultisetSetCountUnconditionallyTester extends AbstractMultisetSetCountTester { @Override void setCountCheckReturnValue(E element, int count) { @@ -41,6 +44,7 @@ void setCountNoCheckReturnValue(E element, int count) { setCount(element, count); } + @CanIgnoreReturnValue private int setCount(E element, int count) { return getMultiset().setCount(element, count); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java index d6a665c14bd4..77f24ddf4712 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/MultisetTestSuiteBuilder.java @@ -17,6 +17,8 @@ package com.google.common.collect.testing.google; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.testing.Helpers.copyToList; +import static java.util.Collections.emptySet; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.Multiset; @@ -25,7 +27,6 @@ import com.google.common.collect.testing.AbstractCollectionTestSuiteBuilder; import com.google.common.collect.testing.AbstractTester; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -36,7 +37,6 @@ import com.google.common.testing.SerializableTester; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -64,13 +64,14 @@ public enum NoRecurse implements Feature { @Override public Set> getImpliedFeatures() { - return Collections.emptySet(); + return emptySet(); } } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(CollectionSerializationEqualTester.class); testers.add(MultisetAddTester.class); testers.add(MultisetContainsTester.class); @@ -232,7 +233,7 @@ public Set> create(Object... entries) { @SuppressWarnings("unchecked") @Override public Multiset.Entry[] createArray(int length) { - return new Multiset.Entry[length]; + return (Multiset.Entry[]) new Multiset.Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/ReflectionFreeAssertThrows.java b/android/guava-testlib/src/com/google/common/collect/testing/google/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..92da5482f879 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect.testing.google; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java index 0b55a2798ae1..a0364b604c0f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetGenerators.java @@ -18,11 +18,15 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newTreeSet; import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST; import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST_2; import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST; import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST_2; +import static java.lang.Math.max; +import static java.util.Arrays.asList; +import static java.util.Collections.sort; import static junit.framework.Assert.assertEquals; import com.google.common.annotations.GwtCompatible; @@ -35,7 +39,6 @@ import com.google.common.collect.Ordering; import com.google.common.collect.Range; import com.google.common.collect.Sets; -import com.google.common.collect.testing.TestCollectionGenerator; import com.google.common.collect.testing.TestCollidingSetGenerator; import com.google.common.collect.testing.TestIntegerSortedSetGenerator; import com.google.common.collect.testing.TestSetGenerator; @@ -44,12 +47,12 @@ import com.google.common.collect.testing.TestStringSortedSetGenerator; import com.google.common.collect.testing.TestUnhashableCollectionGenerator; import com.google.common.collect.testing.UnhashableObject; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; /** * Generators of different types of sets and derived collections from sets. @@ -59,6 +62,7 @@ * @author Hayward Chan */ @GwtCompatible(emulated = true) +@NullMarked public class SetGenerators { public static class ImmutableSetCopyOfGenerator extends TestStringSetGenerator { @@ -83,7 +87,7 @@ public static class ImmutableSetSizedBuilderGenerator extends TestStringSetGener @Override protected Set create(String[] elements) { ImmutableSet.Builder builder = - ImmutableSet.builderWithExpectedSize(Sets.newHashSet(elements).size()); + ImmutableSet.builderWithExpectedSize(newHashSet(elements).size()); for (String e : elements) { builder.add(e); } @@ -95,7 +99,7 @@ public static class ImmutableSetTooBigBuilderGenerator extends TestStringSetGene @Override protected Set create(String[] elements) { ImmutableSet.Builder builder = - ImmutableSet.builderWithExpectedSize(Sets.newHashSet(elements).size() + 1); + ImmutableSet.builderWithExpectedSize(newHashSet(elements).size() + 1); for (String e : elements) { builder.add(e); } @@ -107,7 +111,7 @@ public static class ImmutableSetTooSmallBuilderGenerator extends TestStringSetGe @Override protected Set create(String[] elements) { ImmutableSet.Builder builder = - ImmutableSet.builderWithExpectedSize(Math.max(0, Sets.newHashSet(elements).size() - 1)); + ImmutableSet.builderWithExpectedSize(max(0, newHashSet(elements).size() - 1)); for (String e : elements) { builder.add(e); } @@ -115,11 +119,7 @@ protected Set create(String[] elements) { } } - public static class ImmutableSetWithBadHashesGenerator extends TestCollidingSetGenerator - // Work around a GWT compiler bug. Not explicitly listing this will - // cause the createArray() method missing in the generated javascript. - // TODO: Remove this once the GWT bug is fixed. - implements TestCollectionGenerator { + public static class ImmutableSetWithBadHashesGenerator extends TestCollidingSetGenerator { @Override public Set create(Object... elements) { return ImmutableSet.copyOf(elements); @@ -127,12 +127,10 @@ public Set create(Object... elements) { } public static class DegeneratedImmutableSetGenerator extends TestStringSetGenerator { - // Make sure we get what we think we're getting, or else this test - // is pointless - @SuppressWarnings("cast") + @SuppressWarnings("DistinctVarargsChecker") // deliberately testing deduplication @Override protected Set create(String[] elements) { - return (ImmutableSet) ImmutableSet.of(elements[0], elements[0]); + return ImmutableSet.of(elements[0], elements[0]); } } @@ -188,9 +186,15 @@ protected SortedSet create(String[] elements) { return ImmutableSortedSet.orderedBy(STRING_REVERSED).add(elements).build(); } + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Callers of TestContainerGenerator.order need to be prepared for implementations to return a new + * collection. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder, Collections.reverseOrder()); + sort(insertionOrder, Collections.reverseOrder()); return insertionOrder; } } @@ -205,9 +209,10 @@ protected SortedSet create(String[] elements) { return new ImmutableSortedSet.Builder(COMPARABLE_REVERSED).add(elements).build(); } + @SuppressWarnings("CanIgnoreReturnValueSuggester") // see ImmutableSortedSetExplicitComparator @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder, Collections.reverseOrder()); + sort(insertionOrder, Collections.reverseOrder()); return insertionOrder; } } @@ -216,14 +221,13 @@ public static class ImmutableSortedSetReversedOrderGenerator extends TestStringS @Override protected SortedSet create(String[] elements) { - return ImmutableSortedSet.reverseOrder() - .addAll(Arrays.asList(elements).iterator()) - .build(); + return ImmutableSortedSet.reverseOrder().addAll(asList(elements).iterator()).build(); } + @SuppressWarnings("CanIgnoreReturnValueSuggester") // see ImmutableSortedSetExplicitComparator @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder, Collections.reverseOrder()); + sort(insertionOrder, Collections.reverseOrder()); return insertionOrder; } } @@ -246,7 +250,7 @@ public static class ImmutableSortedSetAsListGenerator extends TestStringListGene @Override protected List create(String[] elements) { Comparator comparator = createExplicitComparator(elements); - ImmutableSet set = ImmutableSortedSet.copyOf(comparator, Arrays.asList(elements)); + ImmutableSet set = ImmutableSortedSet.copyOf(comparator, asList(elements)); return set.asList(); } } @@ -317,7 +321,7 @@ private static Ordering createExplicitComparator(String[] elements) { Set elementsPlus = Sets.newLinkedHashSet(); elementsPlus.add(BEFORE_FIRST); elementsPlus.add(BEFORE_FIRST_2); - elementsPlus.addAll(Arrays.asList(elements)); + elementsPlus.addAll(asList(elements)); elementsPlus.add(AFTER_LAST); elementsPlus.add(AFTER_LAST_2); return Ordering.explicit(Lists.newArrayList(elementsPlus)); @@ -398,9 +402,10 @@ protected SortedSet create(Integer[] elements) { } /** Sorts the elements in reverse natural order. */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") // see ImmutableSortedSetExplicitComparator @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder, Ordering.natural().reverse()); + sort(insertionOrder, Ordering.natural().reverse()); return insertionOrder; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapAsMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapAsMapTester.java index 6e7995720846..669d5dd02554 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapAsMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapAsMapTester.java @@ -14,24 +14,27 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; -import com.google.common.collect.Sets; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import com.google.common.testing.EqualsTester; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -42,8 +45,12 @@ * @param The value type of the tested multimap. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class SetMultimapAsMapTester extends AbstractMultimapTester> { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class SetMultimapAsMapTester + extends AbstractMultimapTester> { public void testAsMapValuesImplementSet() { for (Collection valueCollection : multimap().asMap().values()) { assertTrue(valueCollection instanceof Set); @@ -67,31 +74,34 @@ public void testAsMapRemoveImplementsSet() { @CollectionSize.Require(SEVERAL) public void testEquals() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); Map> expected = Maps.newHashMap(); - expected.put(k0(), Sets.newHashSet(v0(), v3())); - expected.put(k1(), Sets.newHashSet(v0())); + expected.put(k0(), newHashSet(v0(), v3())); + expected.put(k1(), newHashSet(v0())); new EqualsTester().addEqualityGroup(expected, multimap().asMap()).testEquals(); } @CollectionSize.Require(SEVERAL) public void testEntrySetEquals() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); - Set>> expected = Sets.newHashSet(); - expected.add(Helpers.mapEntry(k0(), (Collection) Sets.newHashSet(v0(), v3()))); - expected.add(Helpers.mapEntry(k1(), (Collection) Sets.newHashSet(v0()))); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); + Set>> expected = newHashSet(); + expected.add(mapEntry(k0(), (Collection) newHashSet(v0(), v3()))); + expected.add(mapEntry(k1(), (Collection) newHashSet(v0()))); new EqualsTester().addEqualityGroup(expected, multimap().asMap().entrySet()).testEquals(); } @CollectionSize.Require(SEVERAL) @MapFeature.Require(SUPPORTS_REMOVE) + /* + * SetMultimap.asMap essentially returns a Map>; we just can't declare it that way. + * Thus, calls like asMap().values().remove(someSet) are safe because they are comparing a set to + * a collection of other sets. + */ + @SuppressWarnings("CollectionUndefinedEquality") public void testValuesRemove() { - resetContainer( - Helpers.mapEntry(k0(), v0()), Helpers.mapEntry(k1(), v0()), Helpers.mapEntry(k0(), v3())); - assertTrue(multimap().asMap().values().remove(Collections.singleton(v0()))); + resetContainer(mapEntry(k0(), v0()), mapEntry(k1(), v0()), mapEntry(k0(), v3())); + assertTrue(multimap().asMap().values().remove(singleton(v0()))); assertEquals(2, multimap().size()); - assertEquals(Collections.singletonMap(k0(), Sets.newHashSet(v0(), v3())), multimap().asMap()); + assertEquals(singletonMap(k0(), newHashSet(v0(), v3())), multimap().asMap()); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapEqualsTester.java index 18d3823eb78b..58af05033ee9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapEqualsTester.java @@ -14,11 +14,11 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.SetMultimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.EqualsTester; import org.junit.Ignore; @@ -29,22 +29,18 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetMultimapEqualsTester extends AbstractMultimapTester> { @CollectionSize.Require(SEVERAL) public void testOrderingDoesntAffectEqualsComparisons() { SetMultimap multimap1 = getSubjectGenerator() - .create( - Helpers.mapEntry(k0(), v0()), - Helpers.mapEntry(k0(), v1()), - Helpers.mapEntry(k0(), v4())); + .create(mapEntry(k0(), v0()), mapEntry(k0(), v1()), mapEntry(k0(), v4())); SetMultimap multimap2 = getSubjectGenerator() - .create( - Helpers.mapEntry(k0(), v1()), - Helpers.mapEntry(k0(), v0()), - Helpers.mapEntry(k0(), v4())); + .create(mapEntry(k0(), v1()), mapEntry(k0(), v0()), mapEntry(k0(), v4())); new EqualsTester().addEqualityGroup(multimap1, multimap2).testEquals(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutAllTester.java index ca02b560326f..3a74618260ce 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutAllTester.java @@ -16,11 +16,11 @@ import static com.google.common.collect.testing.Helpers.copyToSet; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.SetMultimap; import com.google.common.collect.testing.features.MapFeature; -import java.util.Arrays; import java.util.List; import java.util.Set; import org.junit.Ignore; @@ -31,13 +31,14 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetMultimapPutAllTester extends AbstractMultimapTester> { @MapFeature.Require(SUPPORTS_PUT) public void testPutAllHandlesDuplicates() { - @SuppressWarnings("unchecked") - List valuesToPut = Arrays.asList(v0(), v1(), v0()); + List valuesToPut = asList(v0(), v1(), v0()); for (K k : sampleKeys()) { resetContainer(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutTester.java index 7aaf9dce54ba..26a2180e249d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapPutTester.java @@ -35,7 +35,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetMultimapPutTester extends AbstractMultimapTester> { // Tests for non-duplicate values are in MultimapPutTester diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapReplaceValuesTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapReplaceValuesTester.java index 67b6aec86590..673ae501405f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapReplaceValuesTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapReplaceValuesTester.java @@ -16,11 +16,11 @@ import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.SetMultimap; import com.google.common.collect.testing.features.MapFeature; -import java.util.Arrays; import java.util.List; import org.junit.Ignore; @@ -30,14 +30,15 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetMultimapReplaceValuesTester extends AbstractMultimapTester> { @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) public void testReplaceValuesHandlesDuplicates() { - @SuppressWarnings("unchecked") - List values = Arrays.asList(v0(), v1(), v0()); + List values = asList(v0(), v1(), v0()); for (K k : sampleKeys()) { resetContainer(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapTestSuiteBuilder.java index 4368cee9bf83..18ab4dfdd823 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SetMultimapTestSuiteBuilder.java @@ -16,11 +16,12 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.SetMultimap; import com.google.common.collect.testing.AbstractTester; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.SetTestSuiteBuilder; import com.google.common.collect.testing.TestSetGenerator; @@ -50,9 +51,10 @@ public static SetMultimapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(SetMultimapAsMapTester.class); testers.add(SetMultimapEqualsTester.class); testers.add(SetMultimapPutTester.class); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMapGenerators.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMapGenerators.java index 424fbb17efe3..d82fd9ab6bd0 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMapGenerators.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMapGenerators.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableSortedMap; @@ -26,10 +27,10 @@ import com.google.common.collect.testing.TestListGenerator; import com.google.common.collect.testing.TestStringListGenerator; import com.google.common.collect.testing.TestStringSortedMapGenerator; -import java.util.Arrays; import java.util.List; import java.util.Map.Entry; import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; /** * Generators of sorted maps and derived collections. @@ -42,6 +43,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class SortedMapGenerators { public static class ImmutableSortedMapGenerator extends TestStringSortedMapGenerator { @Override @@ -59,7 +61,7 @@ public static class ImmutableSortedMapCopyOfEntriesGenerator extends TestStringSortedMapGenerator { @Override public SortedMap create(Entry[] entries) { - return ImmutableSortedMap.copyOf(Arrays.asList(entries)); + return ImmutableSortedMap.copyOf(asList(entries)); } } @@ -79,7 +81,7 @@ public SampleElements> samples() { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -97,7 +99,7 @@ public List> create(Object... elements) { ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); for (Object o : elements) { @SuppressWarnings("unchecked") - Entry entry = (Entry) o; + Entry entry = (Entry) checkNotNull(o); builder.put(entry); } return builder.build().entrySet().asList(); @@ -116,7 +118,7 @@ protected List create(String[] elements) { @Override public List order(List insertionOrder) { - return Ordering.natural().sortedCopy(insertionOrder); + return Ordering.natural().sortedCopy(insertionOrder); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java index b44494b81ddf..bed3dac9e5f5 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedMultisetTestSuiteBuilder.java @@ -16,10 +16,14 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS; +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Collections.sort; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.BoundType; @@ -29,16 +33,13 @@ import com.google.common.collect.SortedMultiset; import com.google.common.collect.testing.AbstractTester; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SetTestSuiteBuilder; import com.google.common.collect.testing.features.Feature; import com.google.common.testing.SerializableTester; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -71,9 +72,10 @@ public TestSuite createTestSuite() { return suite; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(MultisetNavigationTester.class); return testers; } @@ -101,7 +103,7 @@ enum NoRecurse implements Feature { @Override public Set> getImpliedFeatures() { - return Collections.emptySet(); + return emptySet(); } } @@ -154,11 +156,10 @@ private TestSuite createSubMultisetSuite( SortedMultiset emptyMultiset = (SortedMultiset) delegate.create(); Comparator comparator = emptyMultiset.comparator(); SampleElements samples = delegate.samples(); - @SuppressWarnings("unchecked") List samplesList = - Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); + asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4()); - Collections.sort(samplesList, comparator); + sort(samplesList, comparator); E firstInclusive = samplesList.get(0); E lastInclusive = samplesList.get(samplesList.size() - 1); @@ -171,10 +172,10 @@ public SortedMultiset create(Object... entries) { List extremeValues = (List) getExtremeValues(); @SuppressWarnings("unchecked") // map generators must past entry objects - List normalValues = (List) Arrays.asList(entries); + List normalValues = (List) asList(entries); // prepare extreme values to be filtered out of view - Collections.sort(extremeValues, comparator); + sort(extremeValues, comparator); E firstExclusive = extremeValues.get(1); E lastExclusive = extremeValues.get(2); if (from == Bound.NO_BOUND) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapAsMapTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapAsMapTester.java index e95b74f84b0c..4fa0263d9c2b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapAsMapTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapAsMapTester.java @@ -33,7 +33,9 @@ * @param The value type of the tested multimap. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SortedSetMultimapAsMapTester extends AbstractMultimapTester> { public void testAsMapValuesImplementSortedSet() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapGetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapGetTester.java index 5244b5747caa..17cee7cea609 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapGetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapGetTester.java @@ -26,7 +26,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SortedSetMultimapGetTester extends AbstractMultimapTester> { public void testValueComparator() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapTestSuiteBuilder.java b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapTestSuiteBuilder.java index 66c5a8ed09a6..a66e3d423137 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapTestSuiteBuilder.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/SortedSetMultimapTestSuiteBuilder.java @@ -16,11 +16,12 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.SetMultimap; import com.google.common.collect.testing.AbstractTester; import com.google.common.collect.testing.FeatureSpecificTestSuiteBuilder; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.OneSizeTestContainerGenerator; import com.google.common.collect.testing.SortedSetTestSuiteBuilder; import com.google.common.collect.testing.features.CollectionSize; @@ -49,9 +50,10 @@ public static SortedSetMultimapTestSuiteBuilder using( return result; } + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - List> testers = Helpers.copyToList(super.getTesters()); + List> testers = copyToList(super.getTesters()); testers.add(SetMultimapAsMapTester.class); testers.add(SetMultimapEqualsTester.class); testers.add(SetMultimapPutTester.class); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestBiMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestBiMapGenerator.java index 11c353b6bbce..af48b03606a3 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestBiMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestBiMapGenerator.java @@ -20,6 +20,8 @@ import com.google.common.collect.BiMap; import com.google.common.collect.testing.TestContainerGenerator; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates bimaps, containing sample entries, to be tested. @@ -27,7 +29,9 @@ * @author Louis Wasserman */ @GwtCompatible -public interface TestBiMapGenerator extends TestContainerGenerator, Entry> { +@NullMarked +public interface TestBiMapGenerator + extends TestContainerGenerator, Entry> { K[] createKeyArray(int length); V[] createValueArray(int length); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestEnumMultisetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestEnumMultisetGenerator.java index 851e221896c0..49e5b489edbb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestEnumMultisetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestEnumMultisetGenerator.java @@ -16,13 +16,15 @@ package com.google.common.collect.testing.google; +import static java.util.Collections.sort; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multiset; import com.google.common.collect.testing.AnEnum; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SampleElements.Enums; -import java.util.Collections; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * An abstract {@code TestMultisetGenerator} for generating multisets containing enum values. @@ -30,6 +32,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestEnumMultisetGenerator implements TestMultisetGenerator { @Override public SampleElements samples() { @@ -54,9 +57,15 @@ public AnEnum[] createArray(int length) { } /** Sorts the enums according to their natural ordering. */ + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Callers of TestContainerGenerator.order need to be prepared for implementations to return a new + * collection. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestListMultimapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestListMultimapGenerator.java index 1ab668fb92b0..26a1f2f57e09 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestListMultimapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestListMultimapGenerator.java @@ -18,6 +18,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A generator for {@code ListMultimap} implementations based on test data. @@ -25,5 +27,6 @@ * @author Louis Wasserman */ @GwtCompatible -public interface TestListMultimapGenerator +@NullMarked +public interface TestListMultimapGenerator extends TestMultimapGenerator> {} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultimapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultimapGenerator.java index 06ce43f306c9..d555f51c2c1d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultimapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultimapGenerator.java @@ -22,6 +22,8 @@ import com.google.common.collect.testing.TestContainerGenerator; import java.util.Collection; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates multimaps, containing sample elements, to be tested. @@ -29,7 +31,9 @@ * @author Louis Wasserman */ @GwtCompatible -public interface TestMultimapGenerator> +@NullMarked +public interface TestMultimapGenerator< + K extends @Nullable Object, V extends @Nullable Object, M extends Multimap> extends TestContainerGenerator> { K[] createKeyArray(int length); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultisetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultisetGenerator.java index d3b5acd49d7e..0a36c8946904 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultisetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestMultisetGenerator.java @@ -19,6 +19,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multiset; import com.google.common.collect.testing.TestCollectionGenerator; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Creates multisets, containing sample elements, to be tested. @@ -26,7 +28,9 @@ * @author Jared Levy */ @GwtCompatible -public interface TestMultisetGenerator extends TestCollectionGenerator { +@NullMarked +public interface TestMultisetGenerator + extends TestCollectionGenerator { @Override Multiset create(Object... elements); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringBiMapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringBiMapGenerator.java index d475397edd21..ee56438dac48 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringBiMapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringBiMapGenerator.java @@ -16,12 +16,14 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.BiMap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.SampleElements; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * Implementation helper for {@link TestBiMapGenerator} for use with bimaps of strings. @@ -32,22 +34,23 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public abstract class TestStringBiMapGenerator implements TestBiMapGenerator { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry("one", "January"), - Helpers.mapEntry("two", "February"), - Helpers.mapEntry("three", "March"), - Helpers.mapEntry("four", "April"), - Helpers.mapEntry("five", "May")); + mapEntry("one", "January"), + mapEntry("two", "February"), + mapEntry("three", "March"), + mapEntry("four", "April"), + mapEntry("five", "May")); } @Override public final BiMap create(Object... entries) { @SuppressWarnings("unchecked") - Entry[] array = new Entry[entries.length]; + Entry[] array = (Entry[]) new Entry[entries.length]; int i = 0; for (Object o : entries) { @SuppressWarnings("unchecked") @@ -62,7 +65,7 @@ public final BiMap create(Object... entries) { @Override @SuppressWarnings("unchecked") public final Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringListMultimapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringListMultimapGenerator.java index 64b33af0a6e7..790fe46b1dfe 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringListMultimapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringListMultimapGenerator.java @@ -16,13 +16,16 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ListMultimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.SampleElements; import java.util.Collection; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * A skeleton generator for a {@code ListMultimap} implementation. @@ -30,17 +33,18 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public abstract class TestStringListMultimapGenerator implements TestListMultimapGenerator { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry("one", "January"), - Helpers.mapEntry("two", "February"), - Helpers.mapEntry("three", "March"), - Helpers.mapEntry("four", "April"), - Helpers.mapEntry("five", "May")); + mapEntry("one", "January"), + mapEntry("two", "February"), + mapEntry("three", "March"), + mapEntry("four", "April"), + mapEntry("five", "May")); } @Override @@ -55,13 +59,13 @@ public SampleElements sampleValues() { @Override public Collection createCollection(Iterable values) { - return Helpers.copyToList(values); + return copyToList(values); } @Override public final ListMultimap create(Object... entries) { @SuppressWarnings("unchecked") - Entry[] array = new Entry[entries.length]; + Entry[] array = (Entry[]) new Entry[entries.length]; int i = 0; for (Object o : entries) { @SuppressWarnings("unchecked") @@ -76,7 +80,7 @@ public final ListMultimap create(Object... entries) { @Override @SuppressWarnings("unchecked") public final Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringMultisetGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringMultisetGenerator.java index eeacf5d1932c..8bdef2293382 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringMultisetGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringMultisetGenerator.java @@ -21,6 +21,7 @@ import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SampleElements.Strings; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * Create multisets of strings for tests. @@ -28,6 +29,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class TestStringMultisetGenerator implements TestMultisetGenerator { @Override public SampleElements samples() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringSetMultimapGenerator.java b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringSetMultimapGenerator.java index e49ccffed7a8..414860e414c5 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringSetMultimapGenerator.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/TestStringSetMultimapGenerator.java @@ -15,13 +15,16 @@ */ package com.google.common.collect.testing.google; +import static com.google.common.collect.testing.Helpers.copyToSet; +import static com.google.common.collect.testing.Helpers.mapEntry; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.SetMultimap; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.SampleElements; import java.util.Collection; import java.util.List; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; /** * A skeleton generator for a {@code SetMultimap} implementation. @@ -29,17 +32,18 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public abstract class TestStringSetMultimapGenerator implements TestSetMultimapGenerator { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry("one", "January"), - Helpers.mapEntry("two", "February"), - Helpers.mapEntry("three", "March"), - Helpers.mapEntry("four", "April"), - Helpers.mapEntry("five", "May")); + mapEntry("one", "January"), + mapEntry("two", "February"), + mapEntry("three", "March"), + mapEntry("four", "April"), + mapEntry("five", "May")); } @Override @@ -54,13 +58,13 @@ public SampleElements sampleValues() { @Override public Collection createCollection(Iterable values) { - return Helpers.copyToSet(values); + return copyToSet(values); } @Override public final SetMultimap create(Object... entries) { @SuppressWarnings("unchecked") - Entry[] array = new Entry[entries.length]; + Entry[] array = (Entry[]) new Entry[entries.length]; int i = 0; for (Object o : entries) { @SuppressWarnings("unchecked") @@ -75,7 +79,7 @@ public final SetMultimap create(Object... entries) { @Override @SuppressWarnings("unchecked") public final Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/UnmodifiableCollectionTests.java b/android/guava-testlib/src/com/google/common/collect/testing/google/UnmodifiableCollectionTests.java index 40b2c859d20c..ec64f375593e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/google/UnmodifiableCollectionTests.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/UnmodifiableCollectionTests.java @@ -16,25 +16,27 @@ package com.google.common.collect.testing.google; +import static com.google.common.collect.Maps.immutableEntry; +import static java.util.Collections.singleton; +import static java.util.Collections.unmodifiableList; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.fail; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Iterators; import com.google.common.collect.LinkedHashMultiset; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A series of tests that support asserting that collections cannot be modified, either through @@ -43,11 +45,15 @@ * @author Robert Konigsberg */ @GwtCompatible +@NullMarked public class UnmodifiableCollectionTests { public static void assertMapEntryIsUnmodifiable(Entry entry) { try { - entry.setValue(null); + // fine because the call is going to fail without modifying the entry + @SuppressWarnings("unchecked") + Entry nullableValueEntry = (Entry) entry; + nullableValueEntry.setValue(null); fail("setValue on unmodifiable Map.Entry succeeded"); } catch (UnsupportedOperationException expected) { } @@ -109,14 +115,13 @@ public static void assertIteratorsInOrder( * @param sampleElement an element of the same type as that contained by {@code collection}. * {@code collection} may or may not have {@code sampleElement} as a member. */ - public static void assertCollectionIsUnmodifiable(Collection collection, E sampleElement) { + public static void assertCollectionIsUnmodifiable( + Collection collection, E sampleElement) { Collection siblingCollection = new ArrayList<>(); siblingCollection.add(sampleElement); Collection copy = new ArrayList<>(); - // Avoid copy.addAll(collection), which runs afoul of an Android bug in older versions: - // http://b.android.com/72073 http://r.android.com/98929 - Iterators.addAll(copy, collection.iterator()); + copy.addAll(collection); try { collection.add(sampleElement); @@ -181,7 +186,8 @@ public static void assertCollectionIsUnmodifiable(Collection collection, * @param sampleElement an element of the same type as that contained by {@code set}. {@code set} * may or may not have {@code sampleElement} as a member. */ - public static void assertSetIsUnmodifiable(Set set, E sampleElement) { + public static void assertSetIsUnmodifiable( + Set set, E sampleElement) { assertCollectionIsUnmodifiable(set, sampleElement); } @@ -201,7 +207,8 @@ public static void assertSetIsUnmodifiable(Set set, E sampleElement) { * @param sampleElement an element of the same type as that contained by {@code multiset}. {@code * multiset} may or may not have {@code sampleElement} as a member. */ - public static void assertMultisetIsUnmodifiable(Multiset multiset, E sampleElement) { + public static void assertMultisetIsUnmodifiable( + Multiset multiset, E sampleElement) { Multiset copy = LinkedHashMultiset.create(multiset); assertCollectionsAreEquivalent(multiset, copy); @@ -263,14 +270,13 @@ public E getElement() { * @param sampleValue a key of the same type as that contained by {@code multimap}. {@code * multimap} may or may not have {@code sampleValue} as a key. */ - public static void assertMultimapIsUnmodifiable( - Multimap multimap, K sampleKey, V sampleValue) { - List> originalEntries = - Collections.unmodifiableList(Lists.newArrayList(multimap.entries())); + public static + void assertMultimapIsUnmodifiable(Multimap multimap, K sampleKey, V sampleValue) { + List> originalEntries = unmodifiableList(Lists.newArrayList(multimap.entries())); assertMultimapRemainsUnmodified(multimap, originalEntries); - Collection sampleValueAsCollection = Collections.singleton(sampleValue); + Collection sampleValueAsCollection = singleton(sampleValue); // Test #clear() try { @@ -283,7 +289,7 @@ public static void assertMultimapIsUnmodifiable( // Test asMap().entrySet() assertSetIsUnmodifiable( - multimap.asMap().entrySet(), Maps.immutableEntry(sampleKey, sampleValueAsCollection)); + multimap.asMap().entrySet(), immutableEntry(sampleKey, sampleValueAsCollection)); // Test #values() @@ -295,7 +301,7 @@ public static void assertMultimapIsUnmodifiable( } // Test #entries() - assertCollectionIsUnmodifiable(multimap.entries(), Maps.immutableEntry(sampleKey, sampleValue)); + assertCollectionIsUnmodifiable(multimap.entries(), immutableEntry(sampleKey, sampleValue)); assertMultimapRemainsUnmodified(multimap, originalEntries); // Iterate over every element in the entry set @@ -403,13 +409,13 @@ public static void assertMultimapIsUnmodifiable( assertMultimapRemainsUnmodified(multimap, originalEntries); } - private static void assertCollectionsAreEquivalent( + private static void assertCollectionsAreEquivalent( Collection expected, Collection actual) { assertIteratorsInOrder(expected.iterator(), actual.iterator()); } - private static void assertMultimapRemainsUnmodified( - Multimap expected, List> actual) { + private static + void assertMultimapRemainsUnmodified(Multimap expected, List> actual) { assertIteratorsInOrder(expected.entries().iterator(), actual.iterator()); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/google/package-info.java b/android/guava-testlib/src/com/google/common/collect/testing/google/package-info.java new file mode 100644 index 000000000000..57c1a21bb0b4 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/google/package-info.java @@ -0,0 +1,2 @@ +@com.google.errorprone.annotations.CheckReturnValue +package com.google.common.collect.testing.google; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/package-info.java b/android/guava-testlib/src/com/google/common/collect/testing/package-info.java new file mode 100644 index 000000000000..15086f3ae088 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/package-info.java @@ -0,0 +1,2 @@ +@com.google.errorprone.annotations.CheckReturnValue +package com.google.common.collect.testing; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListIndexOfTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListIndexOfTester.java index 46bd52942657..ef0b917fe69a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListIndexOfTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListIndexOfTester.java @@ -23,6 +23,7 @@ import com.google.common.collect.testing.WrongType; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -31,10 +32,12 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public abstract class AbstractListIndexOfTester extends AbstractListTester { /** Override to call {@code indexOf()} or {@code lastIndexOf()}. */ - protected abstract int find(Object o); + protected abstract int find(@Nullable Object o); /** Override to return "indexOf" or "lastIndexOf()" for use in failure messages. */ protected abstract String getMethodName(); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListTester.java index 5275acb250e0..0f8900c066ef 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractListTester.java @@ -16,11 +16,14 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import java.util.Collection; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -29,8 +32,11 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class AbstractListTester extends AbstractCollectionTester { +@NullMarked +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +public class AbstractListTester extends AbstractCollectionTester { /* * Previously we had a field named list that was initialized to the value of * collection in setUp(), but that caused problems when a tester changed the @@ -49,7 +55,7 @@ protected final List getList() { */ @Override protected void expectContents(Collection expectedCollection) { - List expectedList = Helpers.copyToList(expectedCollection); + List expectedList = copyToList(expectedCollection); // Avoid expectEquals() here to delay reason manufacture until necessary. if (getList().size() != expectedList.size()) { fail("size mismatch: " + reportContext(expectedList)); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractQueueTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractQueueTester.java index 7476340ea0d4..639e683d2cc8 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractQueueTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractQueueTester.java @@ -27,7 +27,9 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class AbstractQueueTester extends AbstractCollectionTester { protected final Queue getQueue() { return (Queue) collection; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractSetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractSetTester.java index b7409af8362e..32cb5f97775d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractSetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/AbstractSetTester.java @@ -21,9 +21,13 @@ import java.util.Set; import org.junit.Ignore; -/** @author George van den Driessche */ +/** + * @author George van den Driessche + */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class AbstractSetTester extends AbstractCollectionTester { /* * Previously we had a field named set that was initialized to the value of diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddAllTester.java index 5c21c9a93137..73e260fa3e59 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddAllTester.java @@ -16,17 +16,19 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -34,6 +36,7 @@ import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -43,10 +46,12 @@ * @author Chris Povirk * @author Kevin Bourrillion */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class CollectionAddAllTester extends AbstractCollectionTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +public class CollectionAddAllTester + extends AbstractCollectionTester { @CollectionFeature.Require(SUPPORTS_ADD) public void testAddAll_supportedNothing() { assertFalse("addAll(nothing) should return false", collection.addAll(emptyCollection())); @@ -72,11 +77,8 @@ public void testAddAll_supportedNonePresent() { @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testAddAll_unsupportedNonePresent() { - try { - collection.addAll(createDisjointCollection()); - fail("addAll(nonePresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> collection.addAll(createDisjointCollection())); expectUnchanged(); expectMissing(e3(), e4()); } @@ -94,25 +96,22 @@ public void testAddAll_supportedSomePresent() { @CollectionFeature.Require(absent = SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) public void testAddAll_unsupportedSomePresent() { - try { - collection.addAll(MinimalCollection.of(e3(), e0())); - fail("addAll(somePresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> collection.addAll(MinimalCollection.of(e3(), e0()))); expectUnchanged(); } @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(absent = ZERO) public void testAddAllConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertTrue(collection.addAll(MinimalCollection.of(e3(), e0()))); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + assertTrue(collection.addAll(MinimalCollection.of(e3(), e0()))); + iterator.next(); + }); } @CollectionFeature.Require(absent = SUPPORTS_ADD) @@ -143,11 +142,7 @@ public void testAddAll_nullSupported() { @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES) public void testAddAll_nullUnsupported() { List containsNull = singletonList(null); - try { - collection.addAll(containsNull); - fail("addAll(containsNull) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> collection.addAll(containsNull)); expectUnchanged(); expectNullMissingWhenNullUnsupported( "Should not contain null after unsupported addAll(containsNull)"); @@ -155,42 +150,43 @@ public void testAddAll_nullUnsupported() { @CollectionFeature.Require(SUPPORTS_ADD) public void testAddAll_nullCollectionReference() { - try { - collection.addAll(null); - fail("addAll(null) should throw NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> collection.addAll(null)); } /** * Returns the {@link Method} instance for {@link #testAddAll_nullUnsupported()} so that tests can * suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddAllNullUnsupportedMethod() { - return Helpers.getMethod(CollectionAddAllTester.class, "testAddAll_nullUnsupported"); + return getMethod(CollectionAddAllTester.class, "testAddAll_nullUnsupported"); } /** * Returns the {@link Method} instance for {@link #testAddAll_unsupportedNonePresent()} so that * tests can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} while we - * figure out what to do with {@code ConcurrentHashMap} support for - * {@code entrySet().add()}. + * figure out what to do with {@code + * ConcurrentHashMap} support for {@code entrySet().add()}. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddAllUnsupportedNonePresentMethod() { - return Helpers.getMethod(CollectionAddAllTester.class, "testAddAll_unsupportedNonePresent"); + return getMethod(CollectionAddAllTester.class, "testAddAll_unsupportedNonePresent"); } /** * Returns the {@link Method} instance for {@link #testAddAll_unsupportedSomePresent()} so that * tests can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} while we - * figure out what to do with {@code ConcurrentHashMap} support for - * {@code entrySet().add()}. + * figure out what to do with {@code + * ConcurrentHashMap} support for {@code entrySet().add()}. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddAllUnsupportedSomePresentMethod() { - return Helpers.getMethod(CollectionAddAllTester.class, "testAddAll_unsupportedSomePresent"); + return getMethod(CollectionAddAllTester.class, "testAddAll_unsupportedSomePresent"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddTester.java index bed257c97675..a54f16b8a6bb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionAddTester.java @@ -16,16 +16,18 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -40,9 +42,10 @@ * @author Chris Povirk * @author Kevin Bourrillion */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionAddTester extends AbstractCollectionTester { @CollectionFeature.Require(SUPPORTS_ADD) public void testAdd_supportedNotPresent() { @@ -52,11 +55,7 @@ public void testAdd_supportedNotPresent() { @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testAdd_unsupportedNotPresent() { - try { - collection.add(e3()); - fail("add(notPresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> collection.add(e3())); expectUnchanged(); expectMissing(e3()); } @@ -81,11 +80,7 @@ public void testAdd_nullSupported() { @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES) public void testAdd_nullUnsupported() { - try { - collection.add(null); - fail("add(null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> collection.add(null)); expectUnchanged(); expectNullMissingWhenNullUnsupported("Should not contain null after unsupported add(null)"); } @@ -93,49 +88,52 @@ public void testAdd_nullUnsupported() { @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(absent = ZERO) public void testAddConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertTrue(collection.add(e3())); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + assertTrue(collection.add(e3())); + iterator.next(); + }); } /** * Returns the {@link Method} instance for {@link #testAdd_nullSupported()} so that tests of * {@link java.util.Collections#checkedCollection(java.util.Collection, Class)} can suppress it * with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6409434 is fixed. - * It's unclear whether nulls were to be permitted or forbidden, but presumably the eventual fix - * will be to permit them, as it seems more likely that code would depend on that behavior than on - * the other. Thus, we say the bug is in add(), which fails to support null. + * href="https://bugs.openjdk.org/browse/JDK-6409434">JDK-6409434 is fixed. It's unclear + * whether nulls were to be permitted or forbidden, but presumably the eventual fix will be to + * permit them, as it seems more likely that code would depend on that behavior than on the other. + * Thus, we say the bug is in add(), which fails to support null. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddNullSupportedMethod() { - return Helpers.getMethod(CollectionAddTester.class, "testAdd_nullSupported"); + return getMethod(CollectionAddTester.class, "testAdd_nullSupported"); } /** * Returns the {@link Method} instance for {@link #testAdd_nullSupported()} so that tests of * {@link java.util.TreeSet} can suppress it with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddNullUnsupportedMethod() { - return Helpers.getMethod(CollectionAddTester.class, "testAdd_nullUnsupported"); + return getMethod(CollectionAddTester.class, "testAdd_nullUnsupported"); } /** * Returns the {@link Method} instance for {@link #testAdd_unsupportedNotPresent()} so that tests * can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} while we figure out - * what to do with {@code ConcurrentHashMap} support for {@code - * entrySet().add()}. + * what to do with {@code + * ConcurrentHashMap} support for {@code entrySet().add()}. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddUnsupportedNotPresentMethod() { - return Helpers.getMethod(CollectionAddTester.class, "testAdd_unsupportedNotPresent"); + return getMethod(CollectionAddTester.class, "testAdd_unsupportedNotPresent"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionClearTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionClearTester.java index 9b97fff73ee4..1dae1b6de689 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionClearTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionClearTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; @@ -36,7 +37,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionClearTester extends AbstractCollectionTester { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testClear() { @@ -49,13 +52,7 @@ public void testClear() { @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testClear_unsupported() { - try { - collection.clear(); - fail( - "clear() should throw UnsupportedOperation if a collection does " - + "not support it and is not empty."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> collection.clear()); expectUnchanged(); } @@ -72,17 +69,12 @@ public void testClear_unsupportedByEmptyCollection() { @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(SEVERAL) public void testClearConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - collection.clear(); - iterator.next(); - /* - * We prefer for iterators to fail immediately on hasNext, but ArrayList - * and LinkedList will notably return true on hasNext here! - */ - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + collection.clear(); + iterator.next(); + }); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsAllTester.java index 8d03271cb7f1..53792ed4ddb2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsAllTester.java @@ -37,9 +37,10 @@ * @author Kevin Bourrillion * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionContainsAllTester extends AbstractCollectionTester { public void testContainsAll_empty() { assertTrue( diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsTester.java index 47e0dd6cfffa..58c9f470cf15 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionContainsTester.java @@ -35,7 +35,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionContainsTester extends AbstractCollectionTester { @CollectionSize.Require(absent = ZERO) public void testContains_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionCreationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionCreationTester.java index 848fdd663263..50fff71570dc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionCreationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionCreationTester.java @@ -16,13 +16,15 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -36,7 +38,9 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionCreationTester extends AbstractCollectionTester { @CollectionFeature.Require(ALLOWS_NULL_VALUES) @CollectionSize.Require(absent = ZERO) @@ -51,20 +55,21 @@ public void testCreateWithNull_supported() { public void testCreateWithNull_unsupported() { E[] array = createArrayWithNullElement(); - try { - getSubjectGenerator().create(array); - fail("Creating a collection containing null should fail"); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> { + Object unused = getSubjectGenerator().create(array); + }); } /** * Returns the {@link Method} instance for {@link #testCreateWithNull_unsupported()} so that tests * can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getCreateWithNullUnsupportedMethod() { - return Helpers.getMethod(CollectionCreationTester.class, "testCreateWithNull_unsupported"); + return getMethod(CollectionCreationTester.class, "testCreateWithNull_unsupported"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionEqualsTester.java index e8276cb4372a..d303ff5d6366 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionEqualsTester.java @@ -26,20 +26,28 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionEqualsTester extends AbstractCollectionTester { - // TODO(cpovirk): Consider using EqualsTester from Guava. - @SuppressWarnings("SelfEquals") + @SuppressWarnings({ + "SelfEquals", // TODO(cpovirk): Consider using EqualsTester from Guava. + "UndefinedEquals", // Comparisons of an object to itself *are* defined. + }) public void testEquals_self() { assertTrue("An Object should be equal to itself.", collection.equals(collection)); } + // Comparisons to null *are* defined. + @SuppressWarnings("UndefinedEquals") public void testEquals_null() { // noinspection ObjectEqualsNull assertFalse("An object should not be equal to null.", collection.equals(null)); } + // A collection should essentially never be equal to a non-collection. + @SuppressWarnings("UndefinedEquals") public void testEquals_notACollection() { // noinspection EqualsBetweenInconvertibleTypes assertFalse( diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionForEachTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionForEachTester.java new file mode 100644 index 000000000000..b22fef9a4b84 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionForEachTester.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractCollectionTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.features.CollectionFeature; +import java.util.ArrayList; +import java.util.List; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@code forEach} operations on a collection. Can't be invoked + * directly; please see {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class CollectionForEachTester extends AbstractCollectionTester { + @CollectionFeature.Require(absent = KNOWN_ORDER) + public void testForEachUnknownOrder() { + List elements = new ArrayList<>(); + collection.forEach(elements::add); + Helpers.assertEqualIgnoringOrder(asList(createSamplesArray()), elements); + } + + @CollectionFeature.Require(KNOWN_ORDER) + public void testForEachKnownOrder() { + List elements = new ArrayList<>(); + collection.forEach(elements::add); + List expected = Helpers.copyToList(getOrderedElements()); + assertEquals("Different ordered iteration", expected, elements); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIsEmptyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIsEmptyTester.java index 419e049d8ef9..d543645cad44 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIsEmptyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIsEmptyTester.java @@ -30,7 +30,9 @@ * @author Kevin Bourrillion */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionIsEmptyTester extends AbstractCollectionTester { @CollectionSize.Require(ZERO) public void testIsEmpty_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java index 1699fe19dced..d9775c33019e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionIteratorTester.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; @@ -23,22 +25,23 @@ import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.IteratorFeature; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -48,14 +51,18 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class CollectionIteratorTester extends AbstractCollectionTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class CollectionIteratorTester + extends AbstractCollectionTester { public void testIterator() { List iteratorElements = new ArrayList<>(); for (E element : collection) { // uses iterator() iteratorElements.add(element); } - Helpers.assertEqualIgnoringOrder(Arrays.asList(createSamplesArray()), iteratorElements); + assertEqualIgnoringOrder(asList(createSamplesArray()), iteratorElements); } @CollectionFeature.Require(KNOWN_ORDER) @@ -64,7 +71,7 @@ public void testIterationOrdering() { for (E element : collection) { // uses iterator() iteratorElements.add(element); } - List expected = Helpers.copyToList(getOrderedElements()); + List expected = copyToList(getOrderedElements()); assertEquals("Different ordered iteration", expected, iteratorElements); } @@ -76,7 +83,7 @@ public void testIterator_nullElement() { for (E element : collection) { // uses iterator() iteratorElements.add(element); } - Helpers.assertEqualIgnoringOrder(asList(createArrayWithNullElement()), iteratorElements); + assertEqualIgnoringOrder(asList(createArrayWithNullElement()), iteratorElements); } @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) @@ -139,10 +146,6 @@ public void testIteratorNoSuchElementException() { iterator.next(); } - try { - iterator.next(); - fail("iterator.next() should throw NoSuchElementException"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, iterator::next); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveAllTester.java index 0d59097377be..581b718235b8 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveAllTester.java @@ -22,6 +22,8 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; @@ -29,7 +31,7 @@ import com.google.common.collect.testing.WrongType; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Collections; +import java.util.AbstractSet; import java.util.ConcurrentModificationException; import java.util.Iterator; import org.junit.Ignore; @@ -41,9 +43,10 @@ * @author George van den Driessche * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionRemoveAllTester extends AbstractCollectionTester { @CollectionFeature.Require(SUPPORTS_REMOVE) public void testRemoveAll_emptyCollection() { @@ -82,17 +85,16 @@ public void testRemoveAll_somePresent() { @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(SEVERAL) public void testRemoveAllSomePresentConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertTrue(collection.removeAll(MinimalCollection.of(e0(), e3()))); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + assertTrue(collection.removeAll(MinimalCollection.of(e0(), e3()))); + iterator.next(); + }); } - /** Trigger the {@code other.size() >= this.size()} case in {@link AbstractSet#removeAll()}. */ + /** Trigger the {@code other.size() >= this.size()} case in {@link AbstractSet#removeAll}. */ @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveAll_somePresentLargeCollectionToRemove() { @@ -129,11 +131,9 @@ public void testRemoveAll_unsupportedNonePresent() { @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveAll_unsupportedPresent() { - try { - collection.removeAll(MinimalCollection.of(e0())); - fail("removeAll(intersectingCollection) should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> collection.removeAll(MinimalCollection.of(e0()))); expectUnchanged(); assertTrue(collection.contains(e0())); } @@ -158,11 +158,7 @@ public void testRemoveAll_nullCollectionReferenceEmptySubject() { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemoveAll_nullCollectionReferenceNonEmptySubject() { - try { - collection.removeAll(null); - fail("removeAll(null) should throw NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> collection.removeAll(null)); } @CollectionFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_QUERIES) @@ -188,9 +184,7 @@ public void testRemoveAll_containsNullNoButAllowed() { @CollectionSize.Require(absent = ZERO) public void testRemoveAll_containsNullYes() { initCollectionWithNullElement(); - assertTrue( - "removeAll(containsNull) should return true", - collection.removeAll(Collections.singleton(null))); + assertTrue("removeAll(containsNull) should return true", collection.removeAll(singleton(null))); // TODO: make this work with MinimalCollection } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveIfTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveIfTester.java new file mode 100644 index 000000000000..7d0955a6bd06 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveIfTester.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; +import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; +import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractCollectionTester; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.function.Predicate; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Collection#removeIf}. Can't be invoked directly; please + * see {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class CollectionRemoveIfTester extends AbstractCollectionTester { + @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) + public void testRemoveIf_alwaysFalse() { + assertFalse("removeIf(x -> false) should return false", collection.removeIf(x -> false)); + expectUnchanged(); + } + + @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testRemoveIf_sometimesTrue() { + assertTrue( + "removeIf(isEqual(present)) should return true", + collection.removeIf(Predicate.isEqual(samples.e0()))); + expectMissing(samples.e0()); + } + + @CollectionFeature.Require(SUPPORTS_ITERATOR_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testRemoveIf_allPresent() { + assertTrue("removeIf(x -> true) should return true", collection.removeIf(x -> true)); + expectContents(); + } + + @CollectionFeature.Require({SUPPORTS_ITERATOR_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) + @CollectionSize.Require(SEVERAL) + public void testRemoveIfSomeMatchesConcurrentWithIteration() { + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + assertTrue(collection.removeIf(Predicate.isEqual(samples.e0()))); + iterator.next(); + }); + } + + @CollectionFeature.Require(absent = SUPPORTS_REMOVE) + @CollectionSize.Require(ZERO) + public void testRemoveIf_unsupportedEmptyCollection() { + try { + assertFalse( + "removeIf(Predicate) should return false or throw " + "UnsupportedOperationException", + collection.removeIf( + x -> { + throw new AssertionError("predicate should never be called"); + })); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @CollectionFeature.Require(absent = SUPPORTS_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testRemoveIf_alwaysTrueUnsupported() { + assertThrows(UnsupportedOperationException.class, () -> collection.removeIf(x -> true)); + expectUnchanged(); + assertTrue(collection.contains(samples.e0())); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveTester.java index 49568fc28044..74da5dd2f959 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRemoveTester.java @@ -22,6 +22,7 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; @@ -38,9 +39,10 @@ * * @author George van den Driessche */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionRemoveTester extends AbstractCollectionTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) @@ -57,14 +59,13 @@ public void testRemove_present() { @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(SEVERAL) public void testRemovePresentConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - assertTrue(collection.remove(e0())); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + assertTrue(collection.remove(e0())); + iterator.next(); + }); } @CollectionFeature.Require(SUPPORTS_REMOVE) @@ -90,11 +91,7 @@ public void testRemove_nullPresent() { @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemove_unsupported() { - try { - collection.remove(e0()); - fail("remove(present) should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> collection.remove(e0())); expectUnchanged(); assertTrue("remove(present) should not remove the element", collection.contains(e0())); } @@ -133,11 +130,7 @@ public void testRemove_nullAllowed() { public void testIteratorRemove_unsupported() { Iterator iterator = collection.iterator(); iterator.next(); - try { - iterator.remove(); - fail("iterator.remove() should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, iterator::remove); expectUnchanged(); assertTrue(collection.contains(e0())); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRetainAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRetainAllTester.java index db7aef13929d..85bb60683ee7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRetainAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionRetainAllTester.java @@ -20,13 +20,14 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -38,9 +39,10 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionRetainAllTester extends AbstractCollectionTester { /** A collection of elements to retain, along with a description for use in failure messages. */ @@ -79,11 +81,11 @@ public void setUp() throws Exception { * MinimalCollection, which throws NullPointerException on calls to * contains(null). */ - List disjointList = Arrays.asList(e3(), e4()); + List disjointList = asList(e3(), e4()); disjoint = new Target(disjointList, "disjoint"); superset = new Target(MinimalCollection.of(e0(), e1(), e2(), e3(), e4()), "superset"); nonEmptyProperSubset = new Target(MinimalCollection.of(e1()), "subset"); - sameElements = new Target(Arrays.asList(createSamplesArray()), "sameElements"); + sameElements = new Target(asList(createSamplesArray()), "sameElements"); containsDuplicates = new Target(MinimalCollection.of(e0(), e0(), e3(), e3()), "containsDuplicates"); partialOverlap = new Target(MinimalCollection.of(e2(), e3()), "partialOverlap"); @@ -292,11 +294,7 @@ public void testRetainAll_nullCollectionReferenceEmptySubject() { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRetainAll_nullCollectionReferenceNonEmptySubject() { - try { - collection.retainAll(null); - fail("retainAll(null) should throw NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> collection.retainAll(null)); } private void expectReturnsTrue(Target target) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationEqualTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationEqualTester.java index 38c770d2d00f..2220ab779e8b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationEqualTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationEqualTester.java @@ -31,9 +31,17 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionSerializationEqualTester extends AbstractCollectionTester { @CollectionFeature.Require(SERIALIZABLE) + /* + * As the class docs say, this test only makes sense for collections that define equals(). + * Accordingly, our testing framework adds it only when testing an implementation of List, Set, or + * Multiset. + */ + @SuppressWarnings("UndefinedEquals") public void testReserialize() { assertEquals(SerializableTester.reserialize(actualContents()), actualContents()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationTester.java index 18f8c6660f88..e177c6bfb020 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSerializationTester.java @@ -16,11 +16,11 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.testing.SerializableTester; import org.junit.Ignore; @@ -31,12 +31,13 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionSerializationTester extends AbstractCollectionTester { @CollectionFeature.Require(SERIALIZABLE) public void testReserialize() { // For a bare Collection, the most we can guarantee is that the elements are preserved. - Helpers.assertEqualIgnoringOrder( - actualContents(), SerializableTester.reserialize(actualContents())); + assertEqualIgnoringOrder(actualContents(), SerializableTester.reserialize(actualContents())); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSizeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSizeTester.java index 64d07a0cde8f..53bf99c0b9c9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSizeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSizeTester.java @@ -27,7 +27,9 @@ * @author Kevin Bourrillion */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionSizeTester extends AbstractCollectionTester { public void testSize() { assertEquals("size():", getNumElements(), collection.size()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSpliteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSpliteratorTester.java new file mode 100644 index 000000000000..62d986b41edf --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionSpliteratorTester.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; +import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.features.CollectionSize.ZERO; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.collect.testing.AbstractCollectionTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.SpliteratorTester; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import java.lang.reflect.Method; +import java.util.Spliterator; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@code spliterator} operations on a collection. Can't be invoked + * directly; please see {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class CollectionSpliteratorTester extends AbstractCollectionTester { + + @CollectionFeature.Require(absent = KNOWN_ORDER) + public void testSpliteratorUnknownOrder() { + SpliteratorTester.of(collection::spliterator).expect(getSampleElements()); + } + + @CollectionFeature.Require(KNOWN_ORDER) + public void testSpliteratorKnownOrder() { + SpliteratorTester.of(collection::spliterator).expect(getOrderedElements()).inOrder(); + } + + @CollectionFeature.Require(ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testSpliteratorNullable() { + initCollectionWithNullElement(); + assertFalse(collection.spliterator().hasCharacteristics(Spliterator.NONNULL)); + } + + @CollectionFeature.Require(SUPPORTS_ADD) + public void testSpliteratorNotImmutable_collectionAllowsAdd() { + // If add is supported, verify that IMMUTABLE is not reported. + assertFalse(collection.spliterator().hasCharacteristics(Spliterator.IMMUTABLE)); + } + + @CollectionFeature.Require(SUPPORTS_REMOVE) + public void testSpliteratorNotImmutable_collectionAllowsRemove() { + // If remove is supported, verify that IMMUTABLE is not reported. + assertFalse(collection.spliterator().hasCharacteristics(Spliterator.IMMUTABLE)); + } + + @J2ktIncompatible + @GwtIncompatible // reflection + public static Method getSpliteratorNotImmutableCollectionAllowsAddMethod() { + return Helpers.getMethod( + CollectionSpliteratorTester.class, "testSpliteratorNotImmutable_collectionAllowsAdd"); + } + + @J2ktIncompatible + @GwtIncompatible // reflection + public static Method getSpliteratorNotImmutableCollectionAllowsRemoveMethod() { + return Helpers.getMethod( + CollectionSpliteratorTester.class, "testSpliteratorNotImmutable_collectionAllowsRemove"); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionStreamTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionStreamTester.java new file mode 100644 index 000000000000..66b8f4e0a6f5 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionStreamTester.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractCollectionTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.features.CollectionFeature; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@code stream} operations on a collection. Can't be invoked + * directly; please see {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class CollectionStreamTester extends AbstractCollectionTester { + /* + * We're not really testing the implementation of Stream, only that we're getting a Stream + * that corresponds to the expected elements. + */ + + @CollectionFeature.Require(absent = KNOWN_ORDER) + public void testStreamToArrayUnknownOrder() { + Helpers.assertEqualIgnoringOrder(getSampleElements(), asList(collection.stream().toArray())); + } + + @CollectionFeature.Require(KNOWN_ORDER) + public void testStreamToArrayKnownOrder() { + assertEquals(getOrderedElements(), asList(collection.stream().toArray())); + } + + public void testStreamCount() { + assertEquals(getNumElements(), collection.stream().count()); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToArrayTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToArrayTester.java index b40fc3636943..3bd15e6669ef 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToArrayTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToArrayTester.java @@ -16,13 +16,17 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.WrongType; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -40,7 +44,9 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionToArrayTester extends AbstractCollectionTester { public void testToArray_noArgs() { Object[] array = collection.toArray(); @@ -130,7 +136,7 @@ public void testToArray_oversizedArray() { assertSame( "toArray(overSizedE[]) should return the given array", array, collection.toArray(array)); - List subArray = Arrays.asList(array).subList(0, getNumElements()); + List subArray = asList(array).subList(0, getNumElements()); E[] expectedSubArray = createSamplesArray(); for (int i = 0; i < getNumElements(); i++) { assertTrue( @@ -163,12 +169,12 @@ public void testToArray_oversizedArray_ordered() { @CollectionSize.Require(absent = ZERO) public void testToArray_emptyArrayOfWrongTypeForNonEmptyCollection() { - try { - WrongType[] array = new WrongType[0]; - collection.toArray(array); - fail("toArray(notAssignableTo[]) should throw"); - } catch (ArrayStoreException expected) { - } + assertThrows( + ArrayStoreException.class, + () -> { + WrongType[] array = new WrongType[0]; + collection.toArray(array); + }); } @CollectionSize.Require(ZERO) @@ -181,21 +187,22 @@ public void testToArray_emptyArrayOfWrongTypeForEmptyCollection() { } private void expectArrayContentsAnyOrder(Object[] expected, Object[] actual) { - Helpers.assertEqualIgnoringOrder(Arrays.asList(expected), Arrays.asList(actual)); + assertEqualIgnoringOrder(asList(expected), asList(actual)); } private void expectArrayContentsInOrder(List expected, Object[] actual) { - assertEquals("toArray() ordered contents: ", expected, Arrays.asList(actual)); + assertEquals("toArray() ordered contents: ", expected, asList(actual)); } /** * Returns the {@link Method} instance for {@link #testToArray_isPlainObjectArray()} so that tests * of {@link Arrays#asList(Object[])} can suppress it with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6260652 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-6260652">JDK-6260652 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getToArrayIsPlainObjectArrayMethod() { - return Helpers.getMethod(CollectionToArrayTester.class, "testToArray_isPlainObjectArray"); + return getMethod(CollectionToArrayTester.class, "testToArray_isPlainObjectArray"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToStringTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToStringTester.java index f138ccfa9192..a5c8f463e7bb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToStringTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/CollectionToStringTester.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.NON_STANDARD_TOSTRING; @@ -25,7 +26,6 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractCollectionTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import org.junit.Ignore; @@ -37,7 +37,9 @@ * @author Kevin Bourrillion */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class CollectionToStringTester extends AbstractCollectionTester { public void testToString_minimal() { assertNotNull("toString() should not return null", collection.toString()); @@ -61,7 +63,7 @@ public void testToString_size1() { @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(value = KNOWN_ORDER, absent = NON_STANDARD_TOSTRING) public void testToString_sizeSeveral() { - String expected = Helpers.copyToList(getOrderedElements()).toString(); + String expected = copyToList(getOrderedElements()).toString(); assertEquals("collection.toString() incorrect", expected, collection.toString()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapPutIfAbsentTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapPutIfAbsentTester.java index 6cf5be2327ea..78395adbc801 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapPutIfAbsentTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapPutIfAbsentTester.java @@ -20,13 +20,16 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; +import org.jspecify.annotations.NullMarked; import org.junit.Ignore; /** @@ -37,7 +40,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked public class ConcurrentMapPutIfAbsentTester extends AbstractMapTester { @Override protected ConcurrentMap getMap() { @@ -62,11 +68,7 @@ public void testPutIfAbsent_supportedPresent() { @MapFeature.Require(absent = SUPPORTS_PUT) public void testPutIfAbsent_unsupportedAbsent() { - try { - putIfAbsent(e3()); - fail("putIfAbsent(notPresent, value) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> putIfAbsent(e3())); expectUnchanged(); expectMissing(e3()); } @@ -96,11 +98,7 @@ public void testPutIfAbsent_unsupportedPresentDifferentValue() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) public void testPutIfAbsent_nullKeyUnsupported() { - try { - getMap().putIfAbsent(null, v3()); - fail("putIfAbsent(null, value) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMap().putIfAbsent(null, v3())); expectUnchanged(); expectNullKeyMissingWhenNullKeysUnsupported( "Should not contain null key after unsupported putIfAbsent(null, value)"); @@ -108,11 +106,7 @@ public void testPutIfAbsent_nullKeyUnsupported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPutIfAbsent_nullValueUnsupported() { - try { - getMap().putIfAbsent(k3(), null); - fail("putIfAbsent(key, null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMap().putIfAbsent(k3(), null)); expectUnchanged(); expectNullValueMissingWhenNullValuesUnsupported( "Should not contain null value after unsupported put(key, null)"); @@ -130,6 +124,7 @@ public void testPutIfAbsent_putWithNullValueUnsupported() { "Should not contain null after unsupported putIfAbsent(present, null)"); } + @CanIgnoreReturnValue private V putIfAbsent(Entry entry) { return getMap().putIfAbsent(entry.getKey(), entry.getValue()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapRemoveTester.java index 87cc319b3b18..2d5b1014e0c8 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapRemoveTester.java @@ -20,12 +20,14 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.concurrent.ConcurrentMap; +import org.jspecify.annotations.NullMarked; import org.junit.Ignore; /** @@ -35,7 +37,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked public class ConcurrentMapRemoveTester extends AbstractMapTester { @Override protected ConcurrentMap getMap() { @@ -90,11 +95,7 @@ public void testRemove_nullValueQueriesUnsupported() { @MapFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemove_unsupportedPresent() { - try { - getMap().remove(k0(), v0()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().remove(k0(), v0())); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceEntryTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceEntryTester.java index 57f631cd8b24..7cd1ba6693bb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceEntryTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceEntryTester.java @@ -20,12 +20,14 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.concurrent.ConcurrentMap; +import org.jspecify.annotations.NullMarked; import org.junit.Ignore; /** @@ -36,7 +38,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked public class ConcurrentMapReplaceEntryTester extends AbstractMapTester { @Override protected ConcurrentMap getMap() { @@ -73,11 +78,7 @@ public void testReplaceEntry_supportedAbsentKey() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) @CollectionSize.Require(absent = ZERO) public void testReplaceEntry_presentNullValueUnsupported() { - try { - getMap().replace(k0(), v0(), null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMap().replace(k0(), v0(), null)); expectUnchanged(); } @@ -121,11 +122,7 @@ public void testReplaceEntry_expectNullUnsupported() { @MapFeature.Require(absent = SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testReplaceEntry_unsupportedPresent() { - try { - getMap().replace(k0(), v0(), v3()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().replace(k0(), v0(), v3())); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceTester.java index f0bc16447239..f65d5fb326d0 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ConcurrentMapReplaceTester.java @@ -21,12 +21,14 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.concurrent.ConcurrentMap; +import org.jspecify.annotations.NullMarked; import org.junit.Ignore; /** @@ -37,7 +39,10 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked public class ConcurrentMapReplaceTester extends AbstractMapTester { @Override protected ConcurrentMap getMap() { @@ -67,11 +72,7 @@ public void testReplace_supportedAbsent() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) @CollectionSize.Require(absent = ZERO) public void testReplace_presentNullValueUnsupported() { - try { - getMap().replace(k0(), null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getMap().replace(k0(), null)); expectUnchanged(); } @@ -98,11 +99,7 @@ public void testReplace_absentNullKeyUnsupported() { @MapFeature.Require(absent = SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testReplace_unsupportedPresent() { - try { - getMap().replace(k0(), v3()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().replace(k0(), v3())); expectUnchanged(); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/IgnoreJRERequirement.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/IgnoreJRERequirement.java new file mode 100644 index 000000000000..cab1041e5953 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/IgnoreJRERequirement.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect.testing.testers; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; +import org.jspecify.annotations.NullMarked; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@NullMarked +@interface IgnoreJRERequirement {} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllAtIndexTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllAtIndexTester.java index c3e338f1a450..eb2077dbcd54 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllAtIndexTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllAtIndexTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_ADD_WITH_INDEX; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; @@ -36,9 +37,10 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListAddAllAtIndexTester extends AbstractListTester { @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) @CollectionSize.Require(absent = ZERO) @@ -52,11 +54,8 @@ public void testAddAllAtIndex_supportedAllPresent() { @ListFeature.Require(absent = SUPPORTS_ADD_WITH_INDEX) @CollectionSize.Require(absent = ZERO) public void testAddAllAtIndex_unsupportedAllPresent() { - try { - getList().addAll(0, MinimalCollection.of(e0())); - fail("addAll(n, allPresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> getList().addAll(0, MinimalCollection.of(e0()))); expectUnchanged(); } @@ -72,11 +71,9 @@ public void testAddAllAtIndex_supportedSomePresent() { @ListFeature.Require(absent = SUPPORTS_ADD_WITH_INDEX) @CollectionSize.Require(absent = ZERO) public void testAddAllAtIndex_unsupportedSomePresent() { - try { - getList().addAll(0, MinimalCollection.of(e0(), e3())); - fail("addAll(n, allPresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> getList().addAll(0, MinimalCollection.of(e0(), e3()))); expectUnchanged(); expectMissing(e3()); } @@ -121,11 +118,7 @@ public void testAddAllAtIndex_nullSupported() { @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES) public void testAddAllAtIndex_nullUnsupported() { List containsNull = singletonList(null); - try { - getList().addAll(0, containsNull); - fail("addAll(n, containsNull) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getList().addAll(0, containsNull)); expectUnchanged(); expectNullMissingWhenNullUnsupported( "Should not contain null after unsupported addAll(n, containsNull)"); @@ -151,32 +144,23 @@ public void testAddAllAtIndex_end() { @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAllAtIndex_nullCollectionReference() { - try { - getList().addAll(0, null); - fail("addAll(n, null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getList().addAll(0, null)); expectUnchanged(); } @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAllAtIndex_negative() { - try { - getList().addAll(-1, MinimalCollection.of(e3())); - fail("addAll(-1, e) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> getList().addAll(-1, MinimalCollection.of(e3()))); expectUnchanged(); expectMissing(e3()); } @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAllAtIndex_tooLarge() { - try { - getList().addAll(getNumElements() + 1, MinimalCollection.of(e3())); - fail("addAll(size + 1, e) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, + () -> getList().addAll(getNumElements() + 1, MinimalCollection.of(e3()))); expectUnchanged(); expectMissing(e3()); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllTester.java index 1504f1a4b195..9d2cf9fd7b6a 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAllTester.java @@ -18,6 +18,7 @@ import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.MinimalCollection; @@ -31,9 +32,10 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListAddAllTester extends AbstractListTester { @CollectionFeature.Require(SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) @@ -46,11 +48,8 @@ public void testAddAll_supportedAllPresent() { @CollectionFeature.Require(absent = SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) public void testAddAll_unsupportedAllPresent() { - try { - getList().addAll(MinimalCollection.of(e0())); - fail("addAll(allPresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> getList().addAll(MinimalCollection.of(e0()))); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAtIndexTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAtIndexTester.java index 32310b8d3815..70f36426380d 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAtIndexTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddAtIndexTester.java @@ -16,15 +16,17 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_ADD_WITH_INDEX; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.ListFeature; @@ -39,9 +41,10 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListAddAtIndexTester extends AbstractListTester { @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) @CollectionSize.Require(absent = ZERO) @@ -57,11 +60,7 @@ public void testAddAtIndex_supportedPresent() { * throw regardless, but it keeps the method name accurate. */ public void testAddAtIndex_unsupportedPresent() { - try { - getList().add(0, e0()); - fail("add(n, present) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getList().add(0, e0())); expectUnchanged(); } @@ -74,23 +73,18 @@ public void testAddAtIndex_supportedNotPresent() { @CollectionFeature.Require(FAILS_FAST_ON_CONCURRENT_MODIFICATION) @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAtIndexConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - getList().add(0, e3()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + getList().add(0, e3()); + iterator.next(); + }); } @ListFeature.Require(absent = SUPPORTS_ADD_WITH_INDEX) public void testAddAtIndex_unsupportedNotPresent() { - try { - getList().add(0, e3()); - fail("add(n, notPresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getList().add(0, e3())); expectUnchanged(); expectMissing(e3()); } @@ -119,33 +113,21 @@ public void testAddAtIndex_nullSupported() { @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES) public void testAddAtIndex_nullUnsupported() { - try { - getList().add(0, null); - fail("add(n, null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getList().add(0, null)); expectUnchanged(); expectNullMissingWhenNullUnsupported("Should not contain null after unsupported add(n, null)"); } @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAtIndex_negative() { - try { - getList().add(-1, e3()); - fail("add(-1, e) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().add(-1, e3())); expectUnchanged(); expectMissing(e3()); } @ListFeature.Require(SUPPORTS_ADD_WITH_INDEX) public void testAddAtIndex_tooLarge() { - try { - getList().add(getNumElements() + 1, e3()); - fail("add(size + 1, e) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().add(getNumElements() + 1, e3())); expectUnchanged(); expectMissing(e3()); } @@ -154,8 +136,9 @@ public void testAddAtIndex_tooLarge() { * Returns the {@link Method} instance for {@link #testAddAtIndex_nullSupported()} so that tests * can suppress it. See {@link CollectionAddTester#getAddNullSupportedMethod()} for details. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddNullSupportedMethod() { - return Helpers.getMethod(ListAddAtIndexTester.class, "testAddAtIndex_nullSupported"); + return getMethod(ListAddAtIndexTester.class, "testAddAtIndex_nullSupported"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddTester.java index 8559d3464d91..3dc1154b65dc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListAddTester.java @@ -16,13 +16,16 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -35,9 +38,10 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListAddTester extends AbstractListTester { @CollectionFeature.Require(SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) @@ -53,11 +57,7 @@ public void testAdd_supportedPresent() { * throw regardless, but it keeps the method name accurate. */ public void testAdd_unsupportedPresent() { - try { - getList().add(e0()); - fail("add(present) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getList().add(e0())); } @CollectionFeature.Require(value = {SUPPORTS_ADD, ALLOWS_NULL_VALUES}) @@ -67,7 +67,7 @@ public void testAdd_supportedNullPresent() { collection = getSubjectGenerator().create(array); assertTrue("add(nullPresent) should return true", getList().add(null)); - List expected = Helpers.copyToList(array); + List expected = copyToList(array); expected.add(null); expectContents(expected); } @@ -76,8 +76,9 @@ public void testAdd_supportedNullPresent() { * Returns the {@link Method} instance for {@link #testAdd_supportedNullPresent()} so that tests * can suppress it. See {@link CollectionAddTester#getAddNullSupportedMethod()} for details. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddSupportedNullPresentMethod() { - return Helpers.getMethod(ListAddTester.class, "testAdd_supportedNullPresent"); + return getMethod(ListAddTester.class, "testAdd_supportedNullPresent"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListCreationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListCreationTester.java index 9d0b77ab2de5..7d2ab03b90fa 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListCreationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListCreationTester.java @@ -33,7 +33,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListCreationTester extends AbstractListTester { @CollectionFeature.Require(absent = REJECTS_DUPLICATES_AT_CREATION) @CollectionSize.Require(absent = {ZERO, ONE}) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListEqualsTester.java index 3a09586f80a2..e692f7643236 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListEqualsTester.java @@ -33,7 +33,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListEqualsTester extends AbstractListTester { public void testEquals_otherListWithSameElements() { assertTrue( diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListGetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListGetTester.java index 2ca16c4950a1..8f67f03680e0 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListGetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListGetTester.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import org.junit.Ignore; @@ -26,7 +28,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListGetTester extends AbstractListTester { public void testGet_valid() { // This calls get() on each index and checks the result: @@ -34,18 +38,10 @@ public void testGet_valid() { } public void testGet_negative() { - try { - getList().get(-1); - fail("get(-1) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().get(-1)); } public void testGet_tooLarge() { - try { - getList().get(getNumElements()); - fail("get(size) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().get(getNumElements())); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListHashCodeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListHashCodeTester.java index 93a8526791d8..fd5a3ee69a43 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListHashCodeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListHashCodeTester.java @@ -16,9 +16,11 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import java.lang.reflect.Method; import org.junit.Ignore; @@ -28,7 +30,9 @@ * @author George van den Driessche */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListHashCodeTester extends AbstractListTester { public void testHashCode() { int expectedHashCode = 1; @@ -45,8 +49,9 @@ public void testHashCode() { * Returns the {@link Method} instance for {@link #testHashCode()} so that list tests on * unhashable objects can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()}. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getHashCodeMethod() { - return Helpers.getMethod(ListHashCodeTester.class, "testHashCode"); + return getMethod(ListHashCodeTester.class, "testHashCode"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListIndexOfTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListIndexOfTester.java index 7afb8c82812a..c79cb901a316 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListIndexOfTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListIndexOfTester.java @@ -32,7 +32,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListIndexOfTester extends AbstractListIndexOfTester { @Override protected int find(Object o) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListLastIndexOfTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListLastIndexOfTester.java index 19f7f1e123da..4120963469b2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListLastIndexOfTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListLastIndexOfTester.java @@ -32,7 +32,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListLastIndexOfTester extends AbstractListIndexOfTester { @Override protected int find(Object o) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListListIteratorTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListListIteratorTester.java index 0d4b13e68413..a16b523e5be8 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListListIteratorTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListListIteratorTester.java @@ -16,17 +16,20 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_ADD_WITH_INDEX; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_SET; import static com.google.common.collect.testing.testers.Platform.listListIteratorTesterNumIterations; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.IteratorFeature; import com.google.common.collect.testing.ListIteratorTester; import com.google.common.collect.testing.features.CollectionFeature; @@ -36,6 +39,8 @@ import java.util.ListIterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -46,8 +51,11 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class ListListIteratorTester extends AbstractListTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class ListListIteratorTester extends AbstractListTester { @CollectionFeature.Require(absent = SUPPORTS_REMOVE) @ListFeature.Require(absent = {SUPPORTS_SET, SUPPORTS_ADD_WITH_INDEX}) public void testListIterator_unmodifiable() { @@ -69,7 +77,7 @@ private void runListIteratorTest(Set features) { listListIteratorTesterNumIterations(), singleton(e4()), features, - Helpers.copyToList(getOrderedElements()), + copyToList(getOrderedElements()), 0) { @Override protected ListIterator newTargetIterator() { @@ -85,19 +93,12 @@ protected void verify(List elements) { } public void testListIterator_tooLow() { - try { - getList().listIterator(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().listIterator(-1)); } public void testListIterator_tooHigh() { - try { - getList().listIterator(getNumElements() + 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> getList().listIterator(getNumElements() + 1)); } public void testListIterator_atSize() { @@ -109,19 +110,21 @@ public void testListIterator_atSize() { * Returns the {@link Method} instance for {@link #testListIterator_fullyModifiable()} so that * tests of {@link CopyOnWriteArraySet} can suppress it with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6570575 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-6570575">JDK-6570575 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getListIteratorFullyModifiableMethod() { - return Helpers.getMethod(ListListIteratorTester.class, "testListIterator_fullyModifiable"); + return getMethod(ListListIteratorTester.class, "testListIterator_fullyModifiable"); } /** * Returns the {@link Method} instance for {@link #testListIterator_unmodifiable()} so that it can * be suppressed in GWT tests. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getListIteratorUnmodifiableMethod() { - return Helpers.getMethod(ListListIteratorTester.class, "testListIterator_unmodifiable"); + return getMethod(ListListIteratorTester.class, "testListIterator_unmodifiable"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAllTester.java index 513134cd446d..cbaf769b24be 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAllTester.java @@ -32,9 +32,10 @@ * * @author George van den Driessche */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListRemoveAllTester extends AbstractListTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = {ZERO, ONE}) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAtIndexTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAtIndexTester.java index 97142515272b..47a817ae0089 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAtIndexTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveAtIndexTester.java @@ -16,13 +16,14 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_REMOVE_WITH_INDEX; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.ListFeature; @@ -38,36 +39,26 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListRemoveAtIndexTester extends AbstractListTester { @ListFeature.Require(absent = SUPPORTS_REMOVE_WITH_INDEX) @CollectionSize.Require(absent = ZERO) public void testRemoveAtIndex_unsupported() { - try { - getList().remove(0); - fail("remove(i) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getList().remove(0)); expectUnchanged(); } @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX) public void testRemoveAtIndex_negative() { - try { - getList().remove(-1); - fail("remove(-1) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().remove(-1)); expectUnchanged(); } @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX) public void testRemoveAtIndex_tooLarge() { - try { - getList().remove(getNumElements()); - fail("remove(size) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().remove(getNumElements())); expectUnchanged(); } @@ -87,14 +78,13 @@ public void testRemoveAtIndex_middle() { @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX) @CollectionSize.Require(absent = ZERO) public void testRemoveAtIndexConcurrentWithIteration() { - try { - Iterator iterator = collection.iterator(); - getList().remove(getNumElements() / 2); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = collection.iterator(); + getList().remove(getNumElements() / 2); + iterator.next(); + }); } @ListFeature.Require(SUPPORTS_REMOVE_WITH_INDEX) @@ -108,7 +98,7 @@ private void runRemoveTest(int index) { Platform.format("remove(%d) should return the element at index %d", index, index), getList().get(index), getList().remove(index)); - List expected = Helpers.copyToList(createSamplesArray()); + List expected = copyToList(createSamplesArray()); expected.remove(index); expectContents(expected); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveTester.java index 9c2c688aba4c..383fbb86f25f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRemoveTester.java @@ -32,7 +32,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListRemoveTester extends AbstractListTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = {ZERO, ONE}) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java new file mode 100644 index 000000000000..2158f241ca96 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListReplaceAllTester.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_SET; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.ListFeature; +import java.util.Collections; +import java.util.List; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link List#replaceAll}. Can't be invoked directly; please see + * {@link com.google.common.collect.testing.ListTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class ListReplaceAllTester extends AbstractListTester { + @ListFeature.Require(SUPPORTS_SET) + public void testReplaceAll() { + getList().replaceAll(e -> samples.e3()); + expectContents(Collections.nCopies(getNumElements(), samples.e3())); + } + + @ListFeature.Require(SUPPORTS_SET) + public void testReplaceAll_changesSome() { + getList().replaceAll(e -> e.equals(samples.e0()) ? samples.e3() : e); + E[] expected = createSamplesArray(); + for (int i = 0; i < expected.length; i++) { + if (expected[i].equals(samples.e0())) { + expected[i] = samples.e3(); + } + } + expectContents(expected); + } + + @CollectionSize.Require(absent = ZERO) + @ListFeature.Require(absent = SUPPORTS_SET) + public void testReplaceAll_unsupported() { + assertThrows(UnsupportedOperationException.class, () -> getList().replaceAll(e -> e)); + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRetainAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRetainAllTester.java index 96bd3b40042e..6bc0deeead97 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRetainAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListRetainAllTester.java @@ -21,12 +21,12 @@ import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Arrays; import org.junit.Ignore; /** @@ -36,7 +36,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListRetainAllTester extends AbstractListTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = {ZERO, ONE}) @@ -50,7 +52,6 @@ public void testRetainAll_duplicatesKept() { expectContents(array); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testRetainAll_duplicatesRemoved() { @@ -63,12 +64,11 @@ public void testRetainAll_duplicatesRemoved() { expectContents(e2()); } - @SuppressWarnings("unchecked") @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testRetainAll_countIgnored() { resetContainer(getSubjectGenerator().create(e0(), e2(), e1(), e0())); - assertTrue(getList().retainAll(Arrays.asList(e0(), e1()))); + assertTrue(getList().retainAll(asList(e0(), e1()))); assertContentsInOrder(getList(), e0(), e1(), e0()); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSetTester.java index 844f1b4d6052..aeec44920cf0 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSetTester.java @@ -16,13 +16,15 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_SET; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.ListFeature; @@ -36,7 +38,9 @@ * @author George van den Driessche */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListSetTester extends AbstractListTester { @ListFeature.Require(SUPPORTS_SET) @CollectionSize.Require(absent = ZERO) @@ -76,33 +80,21 @@ private void doTestSet(E newValue) { @ListFeature.Require(SUPPORTS_SET) public void testSet_indexTooLow() { - try { - getList().set(-1, e3()); - fail("set(-1) should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().set(-1, e3())); expectUnchanged(); } @ListFeature.Require(SUPPORTS_SET) public void testSet_indexTooHigh() { int index = getNumElements(); - try { - getList().set(index, e3()); - fail("set(size) should throw IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().set(index, e3())); expectUnchanged(); } @CollectionSize.Require(absent = ZERO) @ListFeature.Require(absent = SUPPORTS_SET) public void testSet_unsupported() { - try { - getList().set(aValidIndex(), e3()); - fail("set() should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getList().set(aValidIndex(), e3())); expectUnchanged(); } @@ -112,7 +104,7 @@ public void testSet_unsupportedByEmptyList() { try { getList().set(0, e3()); fail("set() should throw UnsupportedOperationException or IndexOutOfBoundsException"); - } catch (UnsupportedOperationException | IndexOutOfBoundsException tolerated) { + } catch (UnsupportedOperationException | IndexOutOfBoundsException expected) { } expectUnchanged(); } @@ -121,11 +113,7 @@ public void testSet_unsupportedByEmptyList() { @ListFeature.Require(SUPPORTS_SET) @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES) public void testSet_nullUnsupported() { - try { - getList().set(aValidIndex(), null); - fail("set(null) should throw NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getList().set(aValidIndex(), null)); expectUnchanged(); } @@ -137,13 +125,14 @@ private int aValidIndex() { * Returns the {@link java.lang.reflect.Method} instance for {@link #testSet_null()} so that tests * of {@link java.util.Collections#checkedCollection(java.util.Collection, Class)} can suppress it * with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6409434 is fixed. - * It's unclear whether nulls were to be permitted or forbidden, but presumably the eventual fix - * will be to permit them, as it seems more likely that code would depend on that behavior than on - * the other. Thus, we say the bug is in set(), which fails to support null. + * href="https://bugs.openjdk.org/browse/JDK-6409434">JDK-6409434 is fixed. It's unclear + * whether nulls were to be permitted or forbidden, but presumably the eventual fix will be to + * permit them, as it seems more likely that code would depend on that behavior than on the other. + * Thus, we say the bug is in set(), which fails to support null. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSetNullSupportedMethod() { - return Helpers.getMethod(ListSetTester.class, "testSet_null"); + return getMethod(ListSetTester.class, "testSet_null"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSubListTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSubListTester.java index 553b693584a4..b603358646e6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSubListTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListSubListTester.java @@ -16,6 +16,7 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE_INCLUDING_VIEWS; import static com.google.common.collect.testing.features.CollectionSize.ONE; @@ -23,18 +24,19 @@ import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_ADD_WITH_INDEX; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_REMOVE_WITH_INDEX; import static com.google.common.collect.testing.features.ListFeature.SUPPORTS_SET; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.ListFeature; import com.google.common.testing.SerializableTester; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.junit.Ignore; @@ -45,24 +47,17 @@ * * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListSubListTester extends AbstractListTester { public void testSubList_startNegative() { - try { - getList().subList(-1, 0); - fail("subList(-1, 0) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().subList(-1, 0)); } public void testSubList_endTooLarge() { - try { - getList().subList(0, getNumElements() + 1); - fail("subList(0, size + 1) should throw"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> getList().subList(0, getNumElements() + 1)); } public void testSubList_startGreaterThanEnd() { @@ -75,7 +70,7 @@ public void testSubList_startGreaterThanEnd() { * The subList() docs claim that this should be an * IndexOutOfBoundsException, but many JDK implementations throw * IllegalArgumentException: - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4506427 + * https://bugs.openjdk.org/browse/JDK-4506427 */ } } @@ -96,7 +91,7 @@ public void testSubList_entireList() { public void testSubList_subListRemoveAffectsOriginal() { List subList = getList().subList(0, 1); subList.remove(0); - List expected = Arrays.asList(createSamplesArray()).subList(1, getNumElements()); + List expected = asList(createSamplesArray()).subList(1, getNumElements()); expectContents(expected); } @@ -105,7 +100,7 @@ public void testSubList_subListRemoveAffectsOriginal() { public void testSubList_subListClearAffectsOriginal() { List subList = getList().subList(0, 1); subList.clear(); - List expected = Arrays.asList(createSamplesArray()).subList(1, getNumElements()); + List expected = asList(createSamplesArray()).subList(1, getNumElements()); expectContents(expected); } @@ -121,7 +116,7 @@ public void testSubList_subListAddAffectsOriginal() { public void testSubList_subListSetAffectsOriginal() { List subList = getList().subList(0, 1); subList.set(0, e3()); - List expected = Helpers.copyToList(createSamplesArray()); + List expected = copyToList(createSamplesArray()); expected.set(0, e3()); expectContents(expected); } @@ -134,7 +129,7 @@ public void testSubList_originalListSetAffectsSubList() { assertEquals( "A set() call to a list after a sublist has been created " + "should be reflected in the sublist", - Collections.singletonList(e3()), + singletonList(e3()), subList); } @@ -143,7 +138,7 @@ public void testSubList_originalListSetAffectsSubList() { public void testSubList_subListRemoveAffectsOriginalLargeList() { List subList = getList().subList(1, 3); subList.remove(e2()); - List expected = Helpers.copyToList(createSamplesArray()); + List expected = copyToList(createSamplesArray()); expected.remove(2); expectContents(expected); } @@ -161,7 +156,7 @@ public void testSubList_subListAddAtIndexAffectsOriginalLargeList() { public void testSubList_subListSetAffectsOriginalLargeList() { List subList = getList().subList(1, 2); subList.set(0, e3()); - List expected = Helpers.copyToList(createSamplesArray()); + List expected = copyToList(createSamplesArray()); expected.set(1, e3()); expectContents(expected); } @@ -174,7 +169,7 @@ public void testSubList_originalListSetAffectsSubListLargeList() { assertEquals( "A set() call to a list after a sublist has been created " + "should be reflected in the sublist", - Arrays.asList(e3(), e2()), + asList(e3(), e2()), subList); } @@ -189,7 +184,7 @@ public void testSubList_ofSubListNonEmpty() { assertEquals( "subList(0, 2).subList(1, 2) " + "should be a single-element list of the element at index 1", - Collections.singletonList(getOrderedElements().get(1)), + singletonList(getOrderedElements().get(1)), subList); } @@ -209,7 +204,7 @@ public void testSubList_isEmpty() { List list = getList(); int size = getNumElements(); for (List subList : - Arrays.asList( + asList( list.subList(0, size), list.subList(0, size - 1), list.subList(1, size), @@ -232,13 +227,9 @@ public void testSubList_get() { assertEquals(list.get(size - 1), tail.get(size - 2)); assertEquals(list.get(0), head.get(0)); assertEquals(list.get(size - 2), head.get(size - 2)); - for (List subList : Arrays.asList(copy, head, tail)) { - for (int index : Arrays.asList(-1, subList.size())) { - try { - subList.get(index); - fail("expected IndexOutOfBoundsException"); - } catch (IndexOutOfBoundsException expected) { - } + for (List subList : asList(copy, head, tail)) { + for (int index : asList(-1, subList.size())) { + assertThrows(IndexOutOfBoundsException.class, () -> subList.get(index)); } } } @@ -317,8 +308,9 @@ public void testReserializeSubList() { * Returns the {@link Method} instance for {@link #testSubList_originalListSetAffectsSubList()} so * that tests of {@link CopyOnWriteArrayList} can suppress them with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6570631 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-6570631">JDK-6570631 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSubListOriginalListSetAffectsSubListMethod() { return getMethod(ListSubListTester.class, "testSubList_originalListSetAffectsSubList"); @@ -326,11 +318,12 @@ public static Method getSubListOriginalListSetAffectsSubListMethod() { /** * Returns the {@link Method} instance for {@link - * #testSubList_originalListSetAffectsSubListLargeList()} ()} so that tests of {@link + * #testSubList_originalListSetAffectsSubListLargeList()} so that tests of {@link * CopyOnWriteArrayList} can suppress them with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6570631 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-6570631">JDK-6570631 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSubListOriginalListSetAffectsSubListLargeListMethod() { return getMethod(ListSubListTester.class, "testSubList_originalListSetAffectsSubListLargeList"); @@ -341,8 +334,9 @@ public static Method getSubListOriginalListSetAffectsSubListLargeListMethod() { * #testSubList_subListRemoveAffectsOriginalLargeList()} so that tests of {@link * CopyOnWriteArrayList} can suppress it with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 6570575 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-6570575">JDK-6570575 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSubListSubListRemoveAffectsOriginalLargeListMethod() { return getMethod(ListSubListTester.class, "testSubList_subListRemoveAffectsOriginalLargeList"); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListToArrayTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListToArrayTester.java index 4c5eb091fe83..54be14ad313c 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/ListToArrayTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ListToArrayTester.java @@ -17,10 +17,10 @@ package com.google.common.collect.testing.testers; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Arrays; import org.junit.Ignore; /** @@ -30,7 +30,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class ListToArrayTester extends AbstractListTester { // CollectionToArrayTester tests everything except ordering. @@ -51,6 +53,6 @@ public void testToArray_largeEnough() { } private static void assertArrayEquals(String message, Object[] expected, Object[] actual) { - assertEquals(message, Arrays.asList(expected), Arrays.asList(actual)); + assertEquals(message, asList(expected), asList(actual)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapClearTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapClearTester.java index 64f5127e7f14..ab5c2f35163f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapClearTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapClearTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; @@ -38,7 +39,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapClearTester extends AbstractMapTester { @MapFeature.Require(SUPPORTS_REMOVE) public void testClear() { @@ -51,52 +54,43 @@ public void testClear() { @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testClearConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMap().entrySet().iterator(); - getMap().clear(); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator> iterator = getMap().entrySet().iterator(); + getMap().clear(); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testClearConcurrentWithKeySetIteration() { - try { - Iterator iterator = getMap().keySet().iterator(); - getMap().clear(); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().keySet().iterator(); + getMap().clear(); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testClearConcurrentWithValuesIteration() { - try { - Iterator iterator = getMap().values().iterator(); - getMap().clear(); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().values().iterator(); + getMap().clear(); + iterator.next(); + }); } @MapFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testClear_unsupported() { - try { - getMap().clear(); - fail( - "clear() should throw UnsupportedOperation if a map does " - + "not support it and is not empty."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().clear()); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfAbsentTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfAbsentTester.java new file mode 100644 index 000000000000..4ebe32cf4179 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfAbsentTester.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException; +import java.util.Map; +import junit.framework.AssertionFailedError; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#computeIfAbsent}. Can't be invoked directly; please + * see {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapComputeIfAbsentTester extends AbstractMapTester { + + @MapFeature.Require(SUPPORTS_PUT) + public void testComputeIfAbsent_supportedAbsent() { + assertEquals( + "computeIfAbsent(notPresent, function) should return new value", + v3(), + getMap() + .computeIfAbsent( + k3(), + k -> { + assertEquals(k3(), k); + return v3(); + })); + expectAdded(e3()); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfAbsent_supportedPresent() { + assertEquals( + "computeIfAbsent(present, function) should return existing value", + v0(), + getMap() + .computeIfAbsent( + k0(), + k -> { + throw new AssertionFailedError(); + })); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_PUT) + public void testComputeIfAbsent_functionReturnsNullNotInserted() { + assertNull( + "computeIfAbsent(absent, returnsNull) should return null", + getMap() + .computeIfAbsent( + k3(), + k -> { + assertEquals(k3(), k); + return null; + })); + expectUnchanged(); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfAbsent_nullTreatedAsAbsent() { + initMapWithNullValue(); + assertEquals( + "computeIfAbsent(presentAssignedToNull, function) should return newValue", + getValueForNullKey(), + getMap() + .computeIfAbsent( + getKeyForNullValue(), + k -> { + assertEquals(getKeyForNullValue(), k); + return getValueForNullKey(); + })); + expectReplacement(entry(getKeyForNullValue(), getValueForNullKey())); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) + public void testComputeIfAbsent_nullKeySupported() { + getMap() + .computeIfAbsent( + null, + k -> { + assertNull(k); + return v3(); + }); + expectAdded(entry(null, v3())); + } + + @MapFeature.Require(SUPPORTS_PUT) + public void testComputeIfAbsent_functionThrows() { + assertThrows( + SomeUncheckedException.class, + () -> + getMap() + .computeIfAbsent( + k3(), + k -> { + assertEquals(k3(), k); + throw new SomeUncheckedException(); + })); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testComputeIfAbsent_unsupportedAbsent() { + assertThrows( + UnsupportedOperationException.class, + () -> + getMap() + .computeIfAbsent( + k3(), + k -> { + // allowed to be called + assertEquals(k3(), k); + return v3(); + })); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfAbsent_unsupportedPresentExistingValue() { + try { + assertEquals( + "computeIfAbsent(present, returnsCurrentValue) should return present or throw", + v0(), + getMap() + .computeIfAbsent( + k0(), + k -> { + assertEquals(k0(), k); + return v0(); + })); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfAbsent_unsupportedPresentDifferentValue() { + try { + assertEquals( + "computeIfAbsent(present, returnsDifferentValue) should return present or throw", + v0(), + getMap() + .computeIfAbsent( + k0(), + k -> { + assertEquals(k0(), k); + return v3(); + })); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) + public void testComputeIfAbsent_nullKeyUnsupported() { + assertThrows( + NullPointerException.class, + () -> + getMap() + .computeIfAbsent( + null, + k -> { + assertNull(k); + return v3(); + })); + expectUnchanged(); + expectNullKeyMissingWhenNullKeysUnsupported( + "Should not contain null key after unsupported computeIfAbsent(null, function)"); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfPresentTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfPresentTester.java new file mode 100644 index 000000000000..711ac125a1dc --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeIfPresentTester.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException; +import java.util.Map; +import java.util.Map.Entry; +import junit.framework.AssertionFailedError; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#computeIfPresent}. Can't be invoked directly; please + * see {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapComputeIfPresentTester extends AbstractMapTester { + + @MapFeature.Require(SUPPORTS_PUT) + public void testComputeIfPresent_supportedAbsent() { + assertNull( + "computeIfPresent(notPresent, function) should return null", + getMap() + .computeIfPresent( + k3(), + (k, v) -> { + throw new AssertionFailedError(); + })); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_supportedPresent() { + assertEquals( + "computeIfPresent(present, function) should return new value", + v3(), + getMap() + .computeIfPresent( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + return v3(); + })); + expectReplacement(entry(k0(), v3())); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_functionReturnsNull() { + assertNull( + "computeIfPresent(present, returnsNull) should return null", + getMap() + .computeIfPresent( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + return null; + })); + expectMissing(e0()); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_nullTreatedAsAbsent() { + initMapWithNullValue(); + assertNull( + "computeIfPresent(presentAssignedToNull, function) should return null", + getMap() + .computeIfPresent( + getKeyForNullValue(), + (k, v) -> { + throw new AssertionFailedError(); + })); + expectReplacement(entry(getKeyForNullValue(), null)); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_functionThrows() { + assertThrows( + SomeUncheckedException.class, + () -> + getMap() + .computeIfPresent( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + throw new SomeUncheckedException(); + })); + expectUnchanged(); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_nullKeySupportedPresent() { + initMapWithNullKey(); + assertEquals( + "computeIfPresent(null, function) should return new value", + v3(), + getMap() + .computeIfPresent( + null, + (k, v) -> { + assertNull(k); + assertEquals(getValueForNullKey(), v); + return v3(); + })); + + Entry[] expected = createArrayWithNullKey(); + expected[getNullLocation()] = entry(null, v3()); + expectContents(expected); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) + public void testComputeIfPresent_nullKeySupportedAbsent() { + assertNull( + "computeIfPresent(null, function) should return null", + getMap() + .computeIfPresent( + null, + (k, v) -> { + throw new AssertionFailedError(); + })); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testComputeIfPresent_unsupportedAbsent() { + try { + getMap() + .computeIfPresent( + k3(), + (k, v) -> { + throw new AssertionFailedError(); + }); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testComputeIfPresent_unsupportedPresent() { + assertThrows( + UnsupportedOperationException.class, () -> getMap().computeIfPresent(k0(), (k, v) -> v3())); + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeTester.java new file mode 100644 index 000000000000..31cecda1247d --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapComputeTester.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException; +import java.util.Map; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#compute}. Can't be invoked directly; please see + * {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapComputeTester extends AbstractMapTester { + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + public void testCompute_absentToPresent() { + assertEquals( + "Map.compute(absent, functionReturningValue) should return value", + v3(), + getMap() + .compute( + k3(), + (k, v) -> { + assertEquals(k3(), k); + assertNull(v); + return v3(); + })); + expectAdded(e3()); + assertEquals(getNumElements() + 1, getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + public void testCompute_absentToAbsent() { + assertNull( + "Map.compute(absent, functionReturningNull) should return null", + getMap() + .compute( + k3(), + (k, v) -> { + assertEquals(k3(), k); + assertNull(v); + return null; + })); + expectUnchanged(); + assertEquals(getNumElements(), getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_presentToPresent() { + assertEquals( + "Map.compute(present, functionReturningValue) should return new value", + v3(), + getMap() + .compute( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + return v3(); + })); + expectReplacement(entry(k0(), v3())); + assertEquals(getNumElements(), getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_presentToAbsent() { + assertNull( + "Map.compute(present, functionReturningNull) should return null", + getMap() + .compute( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + return null; + })); + expectMissing(e0()); + expectMissingKeys(k0()); + assertEquals(getNumElements() - 1, getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_VALUES}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_presentNullToPresentNonnull() { + initMapWithNullValue(); + V value = getValueForNullKey(); + assertEquals( + "Map.compute(presentMappedToNull, functionReturningValue) should return new value", + value, + getMap() + .compute( + getKeyForNullValue(), + (k, v) -> { + assertEquals(getKeyForNullValue(), k); + assertNull(v); + return value; + })); + expectReplacement(entry(getKeyForNullValue(), value)); + assertEquals(getNumElements(), getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_VALUES}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_presentNullToNull() { + // The spec is somewhat ambiguous about this case, but the actual default implementation + // in Map will remove a present null. + initMapWithNullValue(); + assertNull( + "Map.compute(presentMappedToNull, functionReturningNull) should return null", + getMap() + .compute( + getKeyForNullValue(), + (k, v) -> { + assertEquals(getKeyForNullValue(), k); + assertNull(v); + return null; + })); + expectMissingKeys(getKeyForNullValue()); + assertEquals(getNumElements() - 1, getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE, ALLOWS_NULL_KEYS}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_nullKeyPresentToPresent() { + initMapWithNullKey(); + assertEquals( + "Map.compute(present, functionReturningValue) should return new value", + v3(), + getMap() + .compute( + null, + (k, v) -> { + assertNull(k); + assertEquals(getValueForNullKey(), v); + return v3(); + })); + assertEquals(getNumElements(), getMap().size()); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + @CollectionSize.Require(absent = ZERO) + public void testCompute_presentFunctionThrows() { + assertThrows( + SomeUncheckedException.class, + () -> + getMap() + .compute( + k0(), + (k, v) -> { + assertEquals(k0(), k); + assertEquals(v0(), v); + throw new SomeUncheckedException(); + })); + expectUnchanged(); + } + + @MapFeature.Require({SUPPORTS_PUT, SUPPORTS_REMOVE}) + public void testCompute_absentFunctionThrows() { + assertThrows( + SomeUncheckedException.class, + () -> + getMap() + .compute( + k3(), + (k, v) -> { + assertEquals(k3(), k); + assertNull(v); + throw new SomeUncheckedException(); + })); + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsKeyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsKeyTester.java index 3721db1174ed..aa9a779fa9eb 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsKeyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsKeyTester.java @@ -34,7 +34,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapContainsKeyTester extends AbstractMapTester { @CollectionSize.Require(absent = ZERO) public void testContains_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsValueTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsValueTester.java index 044562ab6e94..701d1d2ef5b2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsValueTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapContainsValueTester.java @@ -35,7 +35,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapContainsValueTester extends AbstractMapTester { @CollectionSize.Require(absent = ZERO) public void testContains_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapCreationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapCreationTester.java index 0810dea28a54..46a43505531e 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapCreationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapCreationTester.java @@ -16,20 +16,22 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.REJECTS_DUPLICATES_AT_CREATION; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import java.util.Map.Entry; import org.junit.Ignore; @@ -43,7 +45,9 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapCreationTester extends AbstractMapTester { @MapFeature.Require(ALLOWS_NULL_KEYS) @CollectionSize.Require(absent = ZERO) @@ -55,11 +59,7 @@ public void testCreateWithNullKeySupported() { @MapFeature.Require(absent = ALLOWS_NULL_KEYS) @CollectionSize.Require(absent = ZERO) public void testCreateWithNullKeyUnsupported() { - try { - initMapWithNullKey(); - fail("Creating a map containing a null key should fail"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> initMapWithNullKey()); } @MapFeature.Require(ALLOWS_NULL_VALUES) @@ -72,11 +72,7 @@ public void testCreateWithNullValueSupported() { @MapFeature.Require(absent = ALLOWS_NULL_VALUES) @CollectionSize.Require(absent = ZERO) public void testCreateWithNullValueUnsupported() { - try { - initMapWithNullValue(); - fail("Creating a map containing a null value should fail"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> initMapWithNullValue()); } @MapFeature.Require({ALLOWS_NULL_KEYS, ALLOWS_NULL_VALUES}) @@ -104,22 +100,14 @@ public void testCreateWithDuplicates_nonNullDuplicatesNotRejected() { @CollectionSize.Require(absent = {ZERO, ONE}) public void testCreateWithDuplicates_nullDuplicatesRejected() { Entry[] entries = getEntriesMultipleNullKeys(); - try { - resetMap(entries); - fail("Should reject duplicate null elements at creation"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> resetMap(entries)); } @MapFeature.Require(REJECTS_DUPLICATES_AT_CREATION) @CollectionSize.Require(absent = {ZERO, ONE}) public void testCreateWithDuplicates_nonNullDuplicatesRejected() { Entry[] entries = getEntriesMultipleNonNullKeys(); - try { - resetMap(entries); - fail("Should reject duplicate non-null elements at creation"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> resetMap(entries)); } private Entry[] getEntriesMultipleNullKeys() { @@ -137,18 +125,18 @@ private Entry[] getEntriesMultipleNonNullKeys() { private void expectFirstRemoved(Entry[] entries) { resetMap(entries); - List> expectedWithDuplicateRemoved = - Arrays.asList(entries).subList(1, getNumElements()); + List> expectedWithDuplicateRemoved = asList(entries).subList(1, getNumElements()); expectContents(expectedWithDuplicateRemoved); } /** * Returns the {@link Method} instance for {@link #testCreateWithNullKeyUnsupported()} so that * tests can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getCreateWithNullKeyUnsupportedMethod() { - return Helpers.getMethod(MapCreationTester.class, "testCreateWithNullKeyUnsupported"); + return getMethod(MapCreationTester.class, "testCreateWithNullKeyUnsupported"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java index 537f091b4a06..4c9c8bcd6cd2 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEntrySetTester.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ITERATOR_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; @@ -24,11 +26,12 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -46,7 +49,9 @@ * @param The value type of the map implementation under test. */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapEntrySetTester extends AbstractMapTester { private enum IncomparableType { INSTANCE; @@ -65,7 +70,7 @@ public void testEntrySetIteratorRemove() { public void testContainsEntryWithIncomparableKey() { try { - assertFalse(getMap().entrySet().contains(Helpers.mapEntry(IncomparableType.INSTANCE, v0()))); + assertFalse(getMap().entrySet().contains(mapEntry(IncomparableType.INSTANCE, v0()))); } catch (ClassCastException acceptable) { // allowed by the spec } @@ -73,7 +78,7 @@ public void testContainsEntryWithIncomparableKey() { public void testContainsEntryWithIncomparableValue() { try { - assertFalse(getMap().entrySet().contains(Helpers.mapEntry(k0(), IncomparableType.INSTANCE))); + assertFalse(getMap().entrySet().contains(mapEntry(k0(), IncomparableType.INSTANCE))); } catch (ClassCastException acceptable) { // allowed by the spec } @@ -81,26 +86,26 @@ public void testContainsEntryWithIncomparableValue() { @MapFeature.Require(ALLOWS_NULL_KEY_QUERIES) public void testContainsEntryWithNullKeyAbsent() { - assertFalse(getMap().entrySet().contains(Helpers.mapEntry(null, v0()))); + assertFalse(getMap().entrySet().contains(mapEntry(null, v0()))); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(ALLOWS_NULL_KEYS) public void testContainsEntryWithNullKeyPresent() { initMapWithNullKey(); - assertTrue(getMap().entrySet().contains(Helpers.mapEntry(null, getValueForNullKey()))); + assertTrue(getMap().entrySet().contains(mapEntry(null, getValueForNullKey()))); } @MapFeature.Require(ALLOWS_NULL_VALUE_QUERIES) public void testContainsEntryWithNullValueAbsent() { - assertFalse(getMap().entrySet().contains(Helpers.mapEntry(k0(), null))); + assertFalse(getMap().entrySet().contains(mapEntry(k0(), null))); } @CollectionSize.Require(absent = ZERO) @MapFeature.Require(ALLOWS_NULL_VALUES) public void testContainsEntryWithNullValuePresent() { initMapWithNullValue(); - assertTrue(getMap().entrySet().contains(Helpers.mapEntry(getKeyForNullValue(), null))); + assertTrue(getMap().entrySet().contains(mapEntry(getKeyForNullValue(), null))); } @MapFeature.Require(SUPPORTS_PUT) @@ -131,38 +136,39 @@ public void testSetValueWithNullValuesPresent() { @CollectionSize.Require(absent = ZERO) public void testSetValueWithNullValuesAbsent() { for (Entry entry : getMap().entrySet()) { - try { - entry.setValue(null); - fail("Expected NullPointerException"); - } catch (NullPointerException exception) { - break; - } + assertThrows(NullPointerException.class, () -> entry.setValue(null)); + break; } expectUnchanged(); } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getContainsEntryWithIncomparableKeyMethod() { - return Helpers.getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableKey"); + return getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableKey"); } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getContainsEntryWithIncomparableValueMethod() { - return Helpers.getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableValue"); + return getMethod(MapEntrySetTester.class, "testContainsEntryWithIncomparableValue"); } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSetValueMethod() { - return Helpers.getMethod(MapEntrySetTester.class, "testSetValue"); + return getMethod(MapEntrySetTester.class, "testSetValue"); } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSetValueWithNullValuesPresentMethod() { - return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesPresent"); + return getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesPresent"); } + @J2ktIncompatible @GwtIncompatible // reflection public static Method getSetValueWithNullValuesAbsentMethod() { - return Helpers.getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesAbsent"); + return getMethod(MapEntrySetTester.class, "testSetValueWithNullValuesAbsent"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEqualsTester.java index 2408ad41e2af..1c19de02cd2b 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapEqualsTester.java @@ -16,12 +16,12 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.util.Collection; @@ -37,7 +37,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapEqualsTester extends AbstractMapTester { public void testEquals_otherMapWithSameEntries() { assertTrue( @@ -116,11 +118,10 @@ public void testEquals_largerMap() { public void testEquals_list() { assertFalse( - "A List should never equal a Map.", - getMap().equals(Helpers.copyToList(getMap().entrySet()))); + "A List should never equal a Map.", getMap().equals(copyToList(getMap().entrySet()))); } - private static HashMap newHashMap( + private static Map newHashMap( Collection> entries) { HashMap map = new HashMap<>(); for (Entry entry : entries) { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapForEachTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapForEachTester.java new file mode 100644 index 000000000000..d8b12093a0fa --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapForEachTester.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#forEach}. Can't be invoked directly; please see + * {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapForEachTester extends AbstractMapTester { + @CollectionFeature.Require(KNOWN_ORDER) + public void testForEachKnownOrder() { + List> entries = new ArrayList<>(); + getMap().forEach((k, v) -> entries.add(entry(k, v))); + assertEquals(getOrderedElements(), entries); + } + + @CollectionFeature.Require(absent = KNOWN_ORDER) + public void testForEachUnknownOrder() { + List> entries = new ArrayList<>(); + getMap().forEach((k, v) -> entries.add(entry(k, v))); + Helpers.assertEqualIgnoringOrder(getSampleEntries(), entries); + } + + @MapFeature.Require(ALLOWS_NULL_KEYS) + @CollectionSize.Require(absent = ZERO) + public void testForEach_nullKeys() { + initMapWithNullKey(); + List> expectedEntries = asList(createArrayWithNullKey()); + List> entries = new ArrayList<>(); + getMap().forEach((k, v) -> entries.add(entry(k, v))); + Helpers.assertEqualIgnoringOrder(expectedEntries, entries); + } + + @MapFeature.Require(ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testForEach_nullValues() { + initMapWithNullValue(); + List> expectedEntries = asList(createArrayWithNullValue()); + List> entries = new ArrayList<>(); + getMap().forEach((k, v) -> entries.add(entry(k, v))); + Helpers.assertEqualIgnoringOrder(expectedEntries, entries); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetOrDefaultTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetOrDefaultTester.java new file mode 100644 index 000000000000..2170566c8e98 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetOrDefaultTester.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.WrongType; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.Map; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#getOrDefault}. Can't be invoked directly; please see + * {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapGetOrDefaultTester extends AbstractMapTester { + @CollectionSize.Require(absent = ZERO) + public void testGetOrDefault_present() { + assertEquals( + "getOrDefault(present, def) should return the associated value", + v0(), + getMap().getOrDefault(k0(), v3())); + } + + @CollectionSize.Require(absent = ZERO) + public void testGetOrDefault_presentNullDefault() { + assertEquals( + "getOrDefault(present, null) should return the associated value", + v0(), + getMap().getOrDefault(k0(), null)); + } + + public void testGetOrDefault_absent() { + assertEquals( + "getOrDefault(absent, def) should return the default value", + v3(), + getMap().getOrDefault(k3(), v3())); + } + + public void testGetOrDefault_absentNullDefault() { + assertNull("getOrDefault(absent, null) should return null", getMap().getOrDefault(k3(), null)); + } + + @MapFeature.Require(ALLOWS_NULL_KEY_QUERIES) + public void testGetOrDefault_absentNull() { + assertEquals( + "getOrDefault(null, def) should return the default value", + v3(), + getMap().getOrDefault(null, v3())); + } + + @MapFeature.Require(absent = ALLOWS_NULL_KEY_QUERIES) + public void testGetOrDefault_nullAbsentAndUnsupported() { + try { + assertEquals( + "getOrDefault(null, def) should return default or throw", + v3(), + getMap().getOrDefault(null, v3())); + } catch (NullPointerException tolerated) { + } + } + + @MapFeature.Require(ALLOWS_NULL_KEYS) + @CollectionSize.Require(absent = ZERO) + public void testGetOrDefault_nonNullWhenNullContained() { + initMapWithNullKey(); + assertEquals( + "getOrDefault(absent, default) should return default", + v3(), + getMap().getOrDefault(k3(), v3())); + } + + @MapFeature.Require(ALLOWS_NULL_KEYS) + @CollectionSize.Require(absent = ZERO) + public void testGetOrDefault_presentNull() { + initMapWithNullKey(); + assertEquals( + "getOrDefault(null, default) should return the associated value", + getValueForNullKey(), + getMap().getOrDefault(null, v3())); + } + + @MapFeature.Require(ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testGetOrDefault_presentMappedToNull() { + initMapWithNullValue(); + assertNull( + "getOrDefault(mappedToNull, default) should return null", + getMap().getOrDefault(getKeyForNullValue(), v3())); + } + + public void testGet_wrongType() { + try { + assertEquals( + "getOrDefault(wrongType, default) should return default or throw", + v3(), + getMap().getOrDefault(WrongType.VALUE, v3())); + } catch (ClassCastException tolerated) { + } + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetTester.java index 89610f26c768..31eba25a921f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapGetTester.java @@ -35,7 +35,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapGetTester extends AbstractMapTester { @CollectionSize.Require(absent = ZERO) public void testGet_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapHashCodeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapHashCodeTester.java index 97ad5c4d5c77..cf82d186f453 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapHashCodeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapHashCodeTester.java @@ -34,7 +34,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapHashCodeTester extends AbstractMapTester { public void testHashCode() { int expectedHashCode = 0; diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapIsEmptyTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapIsEmptyTester.java index 548ebe5fa5a8..46722d72d935 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapIsEmptyTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapIsEmptyTester.java @@ -30,7 +30,9 @@ * @author Kevin Bourrillion */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapIsEmptyTester extends AbstractMapTester { @CollectionSize.Require(ZERO) public void testIsEmpty_yes() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java new file mode 100644 index 000000000000..8db00d3d47cf --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException; +import java.lang.reflect.Method; +import java.util.Hashtable; +import java.util.Map; +import junit.framework.AssertionFailedError; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#merge}. Can't be invoked directly; please see {@link + * com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapMergeTester extends AbstractMapTester { + @MapFeature.Require(SUPPORTS_PUT) + public void testAbsent() { + assertEquals( + "Map.merge(absent, value, function) should return value", + v3(), + getMap() + .merge( + k3(), + v3(), + (oldV, newV) -> { + throw new AssertionFailedError( + "Should not call merge function if key was absent"); + })); + expectAdded(e3()); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) + @CollectionSize.Require(absent = ZERO) + public void testMappedToNull() { + initMapWithNullValue(); + assertEquals( + "Map.merge(keyMappedToNull, value, function) should return value", + v3(), + getMap() + .merge( + getKeyForNullValue(), + v3(), + (oldV, newV) -> { + throw new AssertionFailedError( + "Should not call merge function if key was mapped to null"); + })); + expectReplacement(entry(getKeyForNullValue(), v3())); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) + public void testMergeAbsentNullKey() { + assertEquals( + "Map.merge(null, value, function) should return value", + v3(), + getMap() + .merge( + null, + v3(), + (oldV, newV) -> { + throw new AssertionFailedError( + "Should not call merge function if key was absent"); + })); + expectAdded(entry(null, v3())); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testMergePresent() { + assertEquals( + "Map.merge(present, value, function) should return function result", + v4(), + getMap() + .merge( + k0(), + v3(), + (oldV, newV) -> { + assertEquals(v0(), oldV); + assertEquals(v3(), newV); + return v4(); + })); + expectReplacement(entry(k0(), v4())); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testMergeFunctionThrows() { + assertThrows( + SomeUncheckedException.class, + () -> + getMap() + .merge( + k0(), + v3(), + (oldV, newV) -> { + assertEquals(v0(), oldV); + assertEquals(v3(), newV); + throw new SomeUncheckedException(); + })); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testMergePresentToNull() { + assertNull( + "Map.merge(present, value, functionReturningNull) should return null", + getMap() + .merge( + k0(), + v3(), + (oldV, newV) -> { + assertEquals(v0(), oldV); + assertEquals(v3(), newV); + return null; + })); + expectMissing(e0()); + } + + public void testMergeNullValue() { + try { + getMap() + .merge( + k0(), + null, + (oldV, newV) -> { + throw new AssertionFailedError("Should not call merge function if value was null"); + }); + fail("Expected NullPointerException or UnsupportedOperationException"); + } catch (NullPointerException | UnsupportedOperationException expected) { + } + } + + public void testMergeNullFunction() { + try { + getMap().merge(k0(), v3(), null); + fail("Expected NullPointerException or UnsupportedOperationException"); + } catch (NullPointerException | UnsupportedOperationException expected) { + } + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testMergeUnsupported() { + assertThrows( + UnsupportedOperationException.class, + () -> + getMap() + .merge( + k3(), + v3(), + (oldV, newV) -> { + throw new AssertionFailedError(); + })); + } + + /** + * Returns the {@link Method} instance for {@link #testMergeNullValue()} so that tests of {@link + * Hashtable} can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()}. + */ + @J2ktIncompatible + @GwtIncompatible // reflection + public static Method getMergeNullValueMethod() { + return Helpers.getMethod(MapMergeTester.class, "testMergeNullValue"); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutAllTester.java index 21d89d8d68d7..16056a112523 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutAllTester.java @@ -16,28 +16,32 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; import java.lang.reflect.Method; -import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -47,10 +51,13 @@ * @author Chris Povirk * @author Kevin Bourrillion */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class MapPutAllTester extends AbstractMapTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class MapPutAllTester + extends AbstractMapTester { private List> containsNullKey; private List> containsNullValue; @@ -84,11 +91,7 @@ public void testPutAll_supportedNonePresent() { @MapFeature.Require(absent = SUPPORTS_PUT) public void testPutAll_unsupportedNonePresent() { - try { - putAll(createDisjointCollection()); - fail("putAll(nonePresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> putAll(createDisjointCollection())); expectUnchanged(); expectMissing(e3(), e4()); } @@ -103,24 +106,20 @@ public void testPutAll_supportedSomePresent() { @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) @CollectionSize.Require(absent = ZERO) public void testPutAllSomePresentConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMap().entrySet().iterator(); - putAll(MinimalCollection.of(e3(), e0())); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator> iterator = getMap().entrySet().iterator(); + putAll(MinimalCollection.of(e3(), e0())); + iterator.next(); + }); } @MapFeature.Require(absent = SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testPutAll_unsupportedSomePresent() { - try { - putAll(MinimalCollection.of(e3(), e0())); - fail("putAll(somePresent) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> putAll(MinimalCollection.of(e3(), e0()))); expectUnchanged(); } @@ -142,11 +141,7 @@ public void testPutAll_nullKeySupported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) public void testPutAll_nullKeyUnsupported() { - try { - putAll(containsNullKey); - fail("putAll(containsNullKey) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> putAll(containsNullKey)); expectUnchanged(); expectNullKeyMissingWhenNullKeysUnsupported( "Should not contain null key after unsupported putAll(containsNullKey)"); @@ -160,11 +155,7 @@ public void testPutAll_nullValueSupported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPutAll_nullValueUnsupported() { - try { - putAll(containsNullValue); - fail("putAll(containsNullValue) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> putAll(containsNullValue)); expectUnchanged(); expectNullValueMissingWhenNullValuesUnsupported( "Should not contain null value after unsupported putAll(containsNullValue)"); @@ -172,15 +163,7 @@ public void testPutAll_nullValueUnsupported() { @MapFeature.Require(SUPPORTS_PUT) public void testPutAll_nullCollectionReference() { - try { - getMap().putAll(null); - fail("putAll(null) should throw NullPointerException"); - } catch (NullPointerException expected) { - } - } - - private Map emptyMap() { - return Collections.emptyMap(); + assertThrows(NullPointerException.class, () -> getMap().putAll(null)); } private void putAll(Iterable> entries) { @@ -194,10 +177,11 @@ private void putAll(Iterable> entries) { /** * Returns the {@link Method} instance for {@link #testPutAll_nullKeyUnsupported()} so that tests * can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getPutAllNullKeyUnsupportedMethod() { - return Helpers.getMethod(MapPutAllTester.class, "testPutAll_nullKeyUnsupported"); + return getMethod(MapPutAllTester.class, "testPutAll_nullKeyUnsupported"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutIfAbsentTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutIfAbsentTester.java new file mode 100644 index 000000000000..e03cc219c72f --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutIfAbsentTester.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.Map; +import java.util.Map.Entry; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#putIfAbsent}. Can't be invoked directly; please see + * {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapPutIfAbsentTester extends AbstractMapTester { + + @MapFeature.Require(SUPPORTS_PUT) + public void testPutIfAbsent_supportedAbsent() { + assertNull( + "putIfAbsent(notPresent, value) should return null", getMap().putIfAbsent(k3(), v3())); + expectAdded(e3()); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testPutIfAbsent_supportedPresent() { + assertEquals( + "putIfAbsent(present, value) should return existing value", + v0(), + getMap().putIfAbsent(k0(), v3())); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testPutIfAbsent_unsupportedAbsent() { + assertThrows(UnsupportedOperationException.class, () -> getMap().putIfAbsent(k3(), v3())); + expectUnchanged(); + expectMissing(e3()); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testPutIfAbsent_unsupportedPresentExistingValue() { + try { + assertEquals( + "putIfAbsent(present, existingValue) should return present or throw", + v0(), + getMap().putIfAbsent(k0(), v0())); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testPutIfAbsent_unsupportedPresentDifferentValue() { + try { + getMap().putIfAbsent(k0(), v3()); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) + public void testPutIfAbsent_nullKeyUnsupported() { + assertThrows(NullPointerException.class, () -> getMap().putIfAbsent(null, v3())); + expectUnchanged(); + expectNullKeyMissingWhenNullKeysUnsupported( + "Should not contain null key after unsupported putIfAbsent(null, value)"); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) + public void testPutIfAbsent_nullValueUnsupportedAndKeyAbsent() { + assertThrows(NullPointerException.class, () -> getMap().putIfAbsent(k3(), null)); + expectUnchanged(); + expectNullValueMissingWhenNullValuesUnsupported( + "Should not contain null value after unsupported putIfAbsent(key, null)"); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testPutIfAbsent_nullValueUnsupportedAndKeyPresent() { + try { + getMap().putIfAbsent(k0(), null); + } catch (NullPointerException tolerated) { + } + expectUnchanged(); + expectNullValueMissingWhenNullValuesUnsupported( + "Should not contain null after unsupported putIfAbsent(present, null)"); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) + public void testPut_nullValueSupported() { + Entry nullValueEntry = entry(k3(), null); + assertNull( + "putIfAbsent(key, null) should return null", + getMap().putIfAbsent(nullValueEntry.getKey(), nullValueEntry.getValue())); + expectAdded(nullValueEntry); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutTester.java index c9a745d4addf..4185a06361dd 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapPutTester.java @@ -16,18 +16,21 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AbstractMapTester; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.Method; import java.util.ConcurrentModificationException; import java.util.Iterator; @@ -41,9 +44,10 @@ * @author Chris Povirk * @author Kevin Bourrillion */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapPutTester extends AbstractMapTester { private Entry nullKeyEntry; private Entry nullValueEntry; @@ -75,49 +79,42 @@ public void testPut_supportedNotPresent() { @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) @CollectionSize.Require(absent = ZERO) public void testPutAbsentConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMap().entrySet().iterator(); - put(e3()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator> iterator = getMap().entrySet().iterator(); + put(e3()); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) @CollectionSize.Require(absent = ZERO) public void testPutAbsentConcurrentWithKeySetIteration() { - try { - Iterator iterator = getMap().keySet().iterator(); - put(e3()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().keySet().iterator(); + put(e3()); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) @CollectionSize.Require(absent = ZERO) public void testPutAbsentConcurrentWithValueIteration() { - try { - Iterator iterator = getMap().values().iterator(); - put(e3()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().values().iterator(); + put(e3()); + iterator.next(); + }); } @MapFeature.Require(absent = SUPPORTS_PUT) public void testPut_unsupportedNotPresent() { - try { - put(e3()); - fail("put(notPresent, value) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> put(e3())); expectUnchanged(); expectMissing(e3()); } @@ -135,11 +132,7 @@ public void testPut_unsupportedPresentExistingValue() { @MapFeature.Require(absent = SUPPORTS_PUT) @CollectionSize.Require(absent = ZERO) public void testPut_unsupportedPresentDifferentValue() { - try { - getMap().put(k0(), v3()); - fail("put(present, differentValue) should throw"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().put(k0(), v3())); expectUnchanged(); } @@ -166,11 +159,7 @@ public void testPut_nullKeySupportedPresent() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) public void testPut_nullKeyUnsupported() { - try { - put(nullKeyEntry); - fail("put(null, value) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> put(nullKeyEntry)); expectUnchanged(); expectNullKeyMissingWhenNullKeysUnsupported( "Should not contain null key after unsupported put(null, value)"); @@ -184,11 +173,7 @@ public void testPut_nullValueSupported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) public void testPut_nullValueUnsupported() { - try { - put(nullValueEntry); - fail("put(key, null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> put(nullValueEntry)); expectUnchanged(); expectNullValueMissingWhenNullValuesUnsupported( "Should not contain null value after unsupported put(key, null)"); @@ -207,11 +192,7 @@ public void testPut_replaceWithNullValueSupported() { @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) @CollectionSize.Require(absent = ZERO) public void testPut_replaceWithNullValueUnsupported() { - try { - put(presentKeyNullValueEntry); - fail("put(present, null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> put(presentKeyNullValueEntry)); expectUnchanged(); expectNullValueMissingWhenNullValuesUnsupported( "Should not contain null after unsupported put(present, null)"); @@ -245,6 +226,7 @@ public void testPut_nullKeyAndValueSupported() { expectAdded(nullKeyValueEntry); } + @CanIgnoreReturnValue private V put(Entry entry) { return getMap().put(entry.getKey(), entry.getValue()); } @@ -253,10 +235,11 @@ private V put(Entry entry) { * Returns the {@link Method} instance for {@link #testPut_nullKeyUnsupported()} so that tests of * {@link java.util.TreeMap} can suppress it with {@code * FeatureSpecificTestSuiteBuilder.suppressing()} until Sun bug 5045147 is fixed. + * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147 is fixed. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getPutNullKeyUnsupportedMethod() { - return Helpers.getMethod(MapPutTester.class, "testPut_nullKeyUnsupported"); + return getMethod(MapPutTester.class, "testPut_nullKeyUnsupported"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveEntryTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveEntryTester.java new file mode 100644 index 000000000000..584f46cc1518 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveEntryTester.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.Map; +import org.junit.Ignore; + +/** + * Tester for {@link Map#remove(Object, Object)}. Can't be invoked directly; please see {@link + * com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapRemoveEntryTester extends AbstractMapTester { + @MapFeature.Require(SUPPORTS_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testRemove_supportedPresent() { + assertTrue(getMap().remove(k0(), v0())); + expectMissing(e0()); + } + + @MapFeature.Require(SUPPORTS_REMOVE) + public void testRemove_supportedPresentKeyWrongValue() { + assertFalse(getMap().remove(k0(), v3())); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_REMOVE) + public void testRemove_supportedWrongKeyPresentValue() { + assertFalse(getMap().remove(k3(), v0())); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_REMOVE) + public void testRemove_supportedAbsentKeyAbsentValue() { + assertFalse(getMap().remove(k3(), v3())); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_KEY_QUERIES) + public void testRemove_nullKeyQueriesUnsupported() { + try { + assertFalse(getMap().remove(null, v3())); + } catch (NullPointerException tolerated) { + // since the operation would be a no-op, the exception is not required + } + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_REMOVE, absent = ALLOWS_NULL_VALUE_QUERIES) + public void testRemove_nullValueQueriesUnsupported() { + try { + assertFalse(getMap().remove(k3(), null)); + } catch (NullPointerException tolerated) { + // since the operation would be a no-op, the exception is not required + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_REMOVE) + @CollectionSize.Require(absent = ZERO) + public void testRemove_unsupportedPresent() { + assertThrows(UnsupportedOperationException.class, () -> getMap().remove(k0(), v0())); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_REMOVE) + public void testRemove_unsupportedAbsent() { + try { + assertFalse(getMap().remove(k0(), v3())); + } catch (UnsupportedOperationException tolerated) { + // since the operation would be a no-op, the exception is not required + } + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveTester.java index 00c074e5e8fc..36623a655fb4 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapRemoveTester.java @@ -22,6 +22,7 @@ import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; @@ -40,9 +41,10 @@ * @author George van den Driessche * @author Chris Povirk */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapRemoveTester extends AbstractMapTester { @MapFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) @@ -57,40 +59,37 @@ public void testRemove_present() { @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testRemovePresentConcurrentWithEntrySetIteration() { - try { - Iterator> iterator = getMap().entrySet().iterator(); - getMap().remove(k0()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator> iterator = getMap().entrySet().iterator(); + getMap().remove(k0()); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testRemovePresentConcurrentWithKeySetIteration() { - try { - Iterator iterator = getMap().keySet().iterator(); - getMap().remove(k0()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().keySet().iterator(); + getMap().remove(k0()); + iterator.next(); + }); } @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_REMOVE}) @CollectionSize.Require(SEVERAL) public void testRemovePresentConcurrentWithValuesIteration() { - try { - Iterator iterator = getMap().values().iterator(); - getMap().remove(k0()); - iterator.next(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - // success - } + assertThrows( + ConcurrentModificationException.class, + () -> { + Iterator iterator = getMap().values().iterator(); + getMap().remove(k0()); + iterator.next(); + }); } @MapFeature.Require(SUPPORTS_REMOVE) @@ -117,11 +116,7 @@ public void testRemove_nullPresent() { @MapFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) public void testRemove_unsupported() { - try { - getMap().remove(k0()); - fail("remove(present) should throw UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> getMap().remove(k0())); expectUnchanged(); assertEquals("remove(present) should not remove the element", v0(), get(k0())); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceAllTester.java new file mode 100644 index 000000000000..c503addd1ab7 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceAllTester.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.Helpers; +import com.google.common.collect.testing.SampleElements; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@code replaceAll()} operations on a map. Can't be invoked + * directly; please see {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapReplaceAllTester extends AbstractMapTester { + private SampleElements keys() { + return new SampleElements<>(k0(), k1(), k2(), k3(), k4()); + } + + private SampleElements values() { + return new SampleElements<>(v0(), v1(), v2(), v3(), v4()); + } + + @MapFeature.Require(SUPPORTS_PUT) + public void testReplaceAllRotate() { + getMap() + .replaceAll( + (K k, V v) -> { + int index = keys().asList().indexOf(k); + return values().asList().get(index + 1); + }); + List> expectedEntries = new ArrayList<>(); + for (Entry entry : getSampleEntries()) { + int index = keys().asList().indexOf(entry.getKey()); + expectedEntries.add(Helpers.mapEntry(entry.getKey(), values().asList().get(index + 1))); + } + expectContents(expectedEntries); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionFeature.Require(KNOWN_ORDER) + public void testReplaceAllPreservesOrder() { + getMap() + .replaceAll( + (K k, V v) -> { + int index = keys().asList().indexOf(k); + return values().asList().get(index + 1); + }); + List> orderedEntries = getOrderedElements(); + int index = 0; + for (K key : getMap().keySet()) { + assertEquals(orderedEntries.get(index).getKey(), key); + index++; + } + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceAll_unsupported() { + assertThrows( + UnsupportedOperationException.class, + () -> + getMap() + .replaceAll( + (K k, V v) -> { + int index = keys().asList().indexOf(k); + return values().asList().get(index + 1); + })); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(ZERO) + public void testReplaceAll_unsupportedByEmptyCollection() { + try { + getMap() + .replaceAll( + (K k, V v) -> { + int index = keys().asList().indexOf(k); + return values().asList().get(index + 1); + }); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testReplaceAll_unsupportedNoOpFunction() { + try { + getMap().replaceAll((K k, V v) -> v); + } catch (UnsupportedOperationException tolerated) { + } + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceEntryTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceEntryTester.java new file mode 100644 index 000000000000..124a81bebdf3 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceEntryTester.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.Map; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#replace(Object, Object, Object)}. Can't be invoked + * directly; please see {@link com.google.common.collect.testing.MapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapReplaceEntryTester extends AbstractMapTester { + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_supportedPresent() { + try { + assertTrue(getMap().replace(k0(), v0(), v3())); + expectReplacement(entry(k0(), v3())); + } catch (ClassCastException tolerated) { // for ClassToInstanceMap + expectUnchanged(); + } + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_supportedPresentUnchanged() { + assertTrue(getMap().replace(k0(), v0(), v0())); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_supportedWrongValue() { + assertFalse(getMap().replace(k0(), v3(), v4())); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_PUT) + public void testReplaceEntry_supportedAbsentKey() { + assertFalse(getMap().replace(k3(), v3(), v4())); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_presentNullValueUnsupported() { + assertThrows(NullPointerException.class, () -> getMap().replace(k0(), v0(), null)); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUE_QUERIES) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_wrongValueNullValueUnsupported() { + try { + assertFalse(getMap().replace(k0(), v3(), null)); + } catch (NullPointerException tolerated) { + // the operation would be a no-op, so exceptions are allowed but not required + } + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUE_QUERIES) + public void testReplaceEntry_absentKeyNullValueUnsupported() { + try { + assertFalse(getMap().replace(k3(), v3(), null)); + } catch (NullPointerException tolerated) { + // the operation would be a no-op, so exceptions are allowed but not required + } + expectUnchanged(); + } + + @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUE_QUERIES}) + public void testReplaceEntry_nullDifferentFromAbsent() { + assertFalse(getMap().replace(k3(), null, v3())); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUE_QUERIES) + public void testReplaceEntry_expectNullUnsupported() { + try { + assertFalse(getMap().replace(k3(), null, v3())); + } catch (NullPointerException tolerated) { + // the operation would be a no-op, so exceptions are allowed but not required + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_unsupportedPresent() { + assertThrows(UnsupportedOperationException.class, () -> getMap().replace(k0(), v0(), v3())); + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplaceEntry_unsupportedWrongValue() { + try { + getMap().replace(k0(), v3(), v4()); + } catch (UnsupportedOperationException tolerated) { + // the operation would be a no-op, so exceptions are allowed but not required + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + public void testReplaceEntry_unsupportedAbsentKey() { + try { + getMap().replace(k3(), v3(), v4()); + } catch (UnsupportedOperationException tolerated) { + // the operation would be a no-op, so exceptions are allowed but not required + } + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceTester.java new file mode 100644 index 000000000000..e231b0f33f04 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapReplaceTester.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEY_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; +import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUE_QUERIES; +import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.AbstractMapTester; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.util.Map; +import org.junit.Ignore; + +/** + * A generic JUnit test which tests {@link Map#replace(Object, Object)}. Can't be invoked directly; + * please see {@link com.google.common.collect.testing.ConcurrentMapTestSuiteBuilder}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@IgnoreJRERequirement // We opt into library desugaring for our tests. +public class MapReplaceTester extends AbstractMapTester { + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplace_supportedPresent() { + try { + assertEquals(v0(), getMap().replace(k0(), v3())); + expectReplacement(entry(k0(), v3())); + } catch (ClassCastException tolerated) { // for ClassToInstanceMap + expectUnchanged(); + } + } + + @MapFeature.Require(SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplace_supportedPresentNoChange() { + assertEquals(v0(), getMap().replace(k0(), v0())); + expectUnchanged(); + } + + @MapFeature.Require(SUPPORTS_PUT) + public void testReplace_supportedAbsent() { + assertNull(getMap().replace(k3(), v3())); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) + @CollectionSize.Require(absent = ZERO) + public void testReplace_presentNullValueUnsupported() { + assertThrows(NullPointerException.class, () -> getMap().replace(k0(), null)); + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUE_QUERIES) + public void testReplace_absentNullValueUnsupported() { + try { + getMap().replace(k3(), null); + } catch (NullPointerException tolerated) { + // permitted not to throw because it would be a no-op + } + expectUnchanged(); + } + + @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEY_QUERIES) + public void testReplace_absentNullKeyUnsupported() { + try { + getMap().replace(null, v3()); + } catch (NullPointerException tolerated) { + // permitted not to throw because it would be a no-op + } + expectUnchanged(); + } + + @MapFeature.Require(absent = SUPPORTS_PUT) + @CollectionSize.Require(absent = ZERO) + public void testReplace_unsupportedPresent() { + try { + getMap().replace(k0(), v3()); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException expected) { + } catch (ClassCastException tolerated) { + // for ClassToInstanceMap + } + + expectUnchanged(); + } +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSerializationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSerializationTester.java index dfa0a1ee0c3b..54463605c482 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSerializationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSerializationTester.java @@ -32,7 +32,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapSerializationTester extends AbstractMapTester { @CollectionFeature.Require(SERIALIZABLE) public void testReserializeMap() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSizeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSizeTester.java index b35d64aca786..83b21c3a7489 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSizeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapSizeTester.java @@ -27,7 +27,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapSizeTester extends AbstractMapTester { public void testSize() { assertEquals("size():", getNumElements(), getMap().size()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapToStringTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapToStringTester.java index 429f8f495749..32fab911c1b9 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/MapToStringTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/MapToStringTester.java @@ -39,7 +39,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class MapToStringTester extends AbstractMapTester { public void testToString_minimal() { assertNotNull("toString() should not return null", getMap().toString()); diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableMapNavigationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableMapNavigationTester.java index ebb86b6156f8..e9a0de2f04ed 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableMapNavigationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableMapNavigationTester.java @@ -16,10 +16,13 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE; +import static java.util.Collections.sort; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.AbstractMapTester; @@ -41,7 +44,9 @@ * @author Louis Wasserman */ @GwtIncompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class NavigableMapNavigationTester extends AbstractMapTester { private NavigableMap navigableMap; @@ -55,10 +60,10 @@ public void setUp() throws Exception { super.setUp(); navigableMap = (NavigableMap) getMap(); entries = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, Helpers.entryComparator(navigableMap.comparator())); + sort(entries, Helpers.entryComparator(navigableMap.comparator())); // some tests assume SEVERAL == 3 if (entries.size() >= 1) { @@ -73,7 +78,7 @@ public void setUp() throws Exception { /** Resets the contents of navigableMap to have entries a, c, for the navigation tests. */ @SuppressWarnings("unchecked") // Needed to stop Eclipse whining private void resetWithHole() { - Entry[] entries = new Entry[] {a, c}; + Entry[] entries = (Entry[]) new Entry[] {a, c}; super.resetMap(entries); navigableMap = (NavigableMap) getMap(); } @@ -157,16 +162,12 @@ public void testFirst() { @CollectionSize.Require(SEVERAL) public void testPollFirst() { assertEquals(a, navigableMap.pollFirstEntry()); - assertEquals(entries.subList(1, entries.size()), Helpers.copyToList(navigableMap.entrySet())); + assertEquals(entries.subList(1, entries.size()), copyToList(navigableMap.entrySet())); } @MapFeature.Require(absent = SUPPORTS_REMOVE) public void testPollFirstUnsupported() { - try { - navigableMap.pollFirstEntry(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> navigableMap.pollFirstEntry()); } @CollectionSize.Require(SEVERAL) @@ -222,18 +223,13 @@ public void testLast() { @CollectionSize.Require(SEVERAL) public void testPollLast() { assertEquals(c, navigableMap.pollLastEntry()); - assertEquals( - entries.subList(0, entries.size() - 1), Helpers.copyToList(navigableMap.entrySet())); + assertEquals(entries.subList(0, entries.size() - 1), copyToList(navigableMap.entrySet())); } @MapFeature.Require(absent = SUPPORTS_REMOVE) @CollectionSize.Require(SEVERAL) public void testPollLastUnsupported() { - try { - navigableMap.pollLastEntry(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> navigableMap.pollLastEntry()); } @CollectionSize.Require(SEVERAL) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableSetNavigationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableSetNavigationTester.java index 8b056b4cb1a1..428610b2f1cc 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableSetNavigationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/NavigableSetNavigationTester.java @@ -16,13 +16,16 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static java.util.Collections.sort; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -42,7 +45,9 @@ * @author Louis Wasserman */ @GwtIncompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class NavigableSetNavigationTester extends AbstractSetTester { private NavigableSet navigableSet; @@ -56,10 +61,10 @@ public void setUp() throws Exception { super.setUp(); navigableSet = (NavigableSet) getSet(); values = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(values, navigableSet.comparator()); + sort(values, navigableSet.comparator()); // some tests assume SEVERAL == 3 if (values.size() >= 1) { @@ -123,16 +128,12 @@ public void testSingletonSetPollLast() { @CollectionSize.Require(SEVERAL) public void testPollFirst() { assertEquals(a, navigableSet.pollFirst()); - assertEquals(values.subList(1, values.size()), Helpers.copyToList(navigableSet)); + assertEquals(values.subList(1, values.size()), copyToList(navigableSet)); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testPollFirstUnsupported() { - try { - navigableSet.pollFirst(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> navigableSet.pollFirst()); } @CollectionSize.Require(SEVERAL) @@ -204,16 +205,12 @@ public void testHigher() { @CollectionSize.Require(SEVERAL) public void testPollLast() { assertEquals(c, navigableSet.pollLast()); - assertEquals(values.subList(0, values.size() - 1), Helpers.copyToList(navigableSet)); + assertEquals(values.subList(0, values.size() - 1), copyToList(navigableSet)); } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testPollLastUnsupported() { - try { - navigableSet.pollLast(); - fail(); - } catch (UnsupportedOperationException e) { - } + assertThrows(UnsupportedOperationException.class, () -> navigableSet.pollLast()); } @CollectionSize.Require(SEVERAL) @@ -249,10 +246,10 @@ public void testEmptySubSet() { */ public static Method[] getHoleMethods() { return new Method[] { - Helpers.getMethod(NavigableSetNavigationTester.class, "testLowerHole"), - Helpers.getMethod(NavigableSetNavigationTester.class, "testFloorHole"), - Helpers.getMethod(NavigableSetNavigationTester.class, "testCeilingHole"), - Helpers.getMethod(NavigableSetNavigationTester.class, "testHigherHole"), + getMethod(NavigableSetNavigationTester.class, "testLowerHole"), + getMethod(NavigableSetNavigationTester.class, "testFloorHole"), + getMethod(NavigableSetNavigationTester.class, "testCeilingHole"), + getMethod(NavigableSetNavigationTester.class, "testHigherHole"), }; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueElementTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueElementTester.java index 3275e43c6930..e60dfc80f143 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueElementTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueElementTester.java @@ -20,6 +20,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; @@ -34,15 +35,13 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class QueueElementTester extends AbstractQueueTester { @CollectionSize.Require(ZERO) public void testElement_empty() { - try { - getQueue().element(); - fail("emptyQueue.element() should throw"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> getQueue().element()); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueOfferTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueOfferTester.java index 3b17289538c5..4c766c22c958 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueOfferTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueOfferTester.java @@ -18,6 +18,7 @@ import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; @@ -29,9 +30,10 @@ * * @author Jared Levy */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class QueueOfferTester extends AbstractQueueTester { @CollectionFeature.Require(SUPPORTS_ADD) public void testOffer_supportedNotPresent() { @@ -47,11 +49,7 @@ public void testOffer_nullSupported() { @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES) public void testOffer_nullUnsupported() { - try { - getQueue().offer(null); - fail("offer(null) should throw"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getQueue().offer(null)); expectUnchanged(); expectNullMissingWhenNullUnsupported("Should not contain null after unsupported offer(null)"); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePeekTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePeekTester.java index 641f171cafd2..8fc5c3361972 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePeekTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePeekTester.java @@ -33,7 +33,9 @@ * @author Jared Levy */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class QueuePeekTester extends AbstractQueueTester { @CollectionSize.Require(ZERO) public void testPeek_empty() { diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePollTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePollTester.java index 544b14ab627b..a8003a823865 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePollTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueuePollTester.java @@ -33,9 +33,10 @@ * * @author Jared Levy */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class QueuePollTester extends AbstractQueueTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(ZERO) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueRemoveTester.java index 2b02cea6e7e5..da794ae35cb7 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/QueueRemoveTester.java @@ -21,6 +21,7 @@ import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; @@ -34,18 +35,15 @@ * * @author Jared Levy */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class QueueRemoveTester extends AbstractQueueTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(ZERO) public void testRemove_empty() { - try { - getQueue().remove(); - fail("emptyQueue.remove() should throw"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> getQueue().remove()); expectUnchanged(); } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/ReflectionFreeAssertThrows.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..d2e90ba6a007 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/ReflectionFreeAssertThrows.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect.testing.testers; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.testing.testers.TestExceptions.SomeCheckedException; +import com.google.common.collect.testing.testers.TestExceptions.SomeError; +import com.google.common.collect.testing.testers.TestExceptions.SomeOtherCheckedException; +import com.google.common.collect.testing.testers.TestExceptions.SomeUncheckedException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(SomeCheckedException.class, e -> e instanceof SomeCheckedException) + .put(SomeError.class, e -> e instanceof SomeError) + .put(SomeOtherCheckedException.class, e -> e instanceof SomeOtherCheckedException) + .put(SomeUncheckedException.class, e -> e instanceof SomeUncheckedException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddAllTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddAllTester.java index 8e917cde761a..757638ab1c16 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddAllTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddAllTester.java @@ -31,9 +31,10 @@ * * @author Kevin Bourrillion */ -@SuppressWarnings("unchecked") // too many "unchecked generic array creations" @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetAddAllTester extends AbstractSetTester { @CollectionFeature.Require(SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddTester.java index 197496827e01..c9cf43da8b17 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetAddTester.java @@ -16,13 +16,14 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -35,7 +36,9 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetAddTester extends AbstractSetTester { @CollectionFeature.Require(SUPPORTS_ADD) @CollectionSize.Require(absent = ZERO) @@ -57,8 +60,9 @@ public void testAdd_supportedNullPresent() { * Returns the {@link Method} instance for {@link #testAdd_supportedNullPresent()} so that tests * can suppress it. See {@link CollectionAddTester#getAddNullSupportedMethod()} for details. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method getAddSupportedNullPresentMethod() { - return Helpers.getMethod(SetAddTester.class, "testAdd_supportedNullPresent"); + return getMethod(SetAddTester.class, "testAdd_supportedNullPresent"); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetCreationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetCreationTester.java index 0c1be6b7abae..43f29a292ede 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetCreationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetCreationTester.java @@ -20,11 +20,12 @@ import static com.google.common.collect.testing.features.CollectionFeature.REJECTS_DUPLICATES_AT_CREATION; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Arrays; import java.util.List; import org.junit.Ignore; @@ -36,7 +37,9 @@ * @author Chris Povirk */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetCreationTester extends AbstractSetTester { @CollectionFeature.Require(value = ALLOWS_NULL_VALUES, absent = REJECTS_DUPLICATES_AT_CREATION) @CollectionSize.Require(absent = {ZERO, ONE}) @@ -45,7 +48,7 @@ public void testCreateWithDuplicates_nullDuplicatesNotRejected() { array[0] = null; collection = getSubjectGenerator().create(array); - List expectedWithDuplicateRemoved = Arrays.asList(array).subList(1, getNumElements()); + List expectedWithDuplicateRemoved = asList(array).subList(1, getNumElements()); expectContents(expectedWithDuplicateRemoved); } @@ -56,7 +59,7 @@ public void testCreateWithDuplicates_nonNullDuplicatesNotRejected() { array[1] = e0(); collection = getSubjectGenerator().create(array); - List expectedWithDuplicateRemoved = Arrays.asList(array).subList(1, getNumElements()); + List expectedWithDuplicateRemoved = asList(array).subList(1, getNumElements()); expectContents(expectedWithDuplicateRemoved); } @@ -65,11 +68,8 @@ public void testCreateWithDuplicates_nonNullDuplicatesNotRejected() { public void testCreateWithDuplicates_nullDuplicatesRejected() { E[] array = createArrayWithNullElement(); array[0] = null; - try { - collection = getSubjectGenerator().create(array); - fail("Should reject duplicate null elements at creation"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> collection = getSubjectGenerator().create(array)); } @CollectionFeature.Require(REJECTS_DUPLICATES_AT_CREATION) @@ -77,10 +77,7 @@ public void testCreateWithDuplicates_nullDuplicatesRejected() { public void testCreateWithDuplicates_nonNullDuplicatesRejected() { E[] array = createSamplesArray(); array[1] = e0(); - try { - collection = getSubjectGenerator().create(array); - fail("Should reject duplicate non-null elements at creation"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> collection = getSubjectGenerator().create(array)); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetEqualsTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetEqualsTester.java index 839e1737c787..a63bcbceb557 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetEqualsTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetEqualsTester.java @@ -16,10 +16,10 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MinimalSet; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -33,7 +33,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetEqualsTester extends AbstractSetTester { public void testEquals_otherSetWithSameElements() { assertTrue( @@ -91,6 +93,6 @@ public void testEquals_largerSet() { } public void testEquals_list() { - assertFalse("A List should never equal a Set.", getSet().equals(Helpers.copyToList(getSet()))); + assertFalse("A List should never equal a Set.", getSet().equals(copyToList(getSet()))); } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetHashCodeTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetHashCodeTester.java index 5f60327d47af..8708789a1a2f 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetHashCodeTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetHashCodeTester.java @@ -16,11 +16,12 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.getMethod; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.lang.reflect.Method; @@ -33,7 +34,9 @@ * @author George van den Driessche */ @GwtCompatible(emulated = true) -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetHashCodeTester extends AbstractSetTester { public void testHashCode() { int expectedHashCode = 0; @@ -69,11 +72,12 @@ public void testHashCode_containingNull() { * hashCode()} on the set values so that set tests on unhashable objects can suppress it with * {@code FeatureSpecificTestSuiteBuilder.suppressing()}. */ + @J2ktIncompatible @GwtIncompatible // reflection public static Method[] getHashCodeMethods() { return new Method[] { - Helpers.getMethod(SetHashCodeTester.class, "testHashCode"), - Helpers.getMethod(SetHashCodeTester.class, "testHashCode_containingNull") + getMethod(SetHashCodeTester.class, "testHashCode"), + getMethod(SetHashCodeTester.class, "testHashCode_containingNull") }; } } diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetRemoveTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetRemoveTester.java index 7f1f88dead3f..5e547ef49f45 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SetRemoveTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SetRemoveTester.java @@ -31,7 +31,9 @@ * @author George van den Driessche */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SetRemoveTester extends AbstractSetTester { @CollectionFeature.Require(SUPPORTS_REMOVE) @CollectionSize.Require(absent = ZERO) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedMapNavigationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedMapNavigationTester.java index 691fee139bcf..17a2908dbbd6 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedMapNavigationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedMapNavigationTester.java @@ -17,15 +17,17 @@ package com.google.common.collect.testing.testers; import static com.google.common.collect.testing.Helpers.assertEqualInOrder; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.AbstractMapTester; import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -42,7 +44,9 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") public class SortedMapNavigationTester extends AbstractMapTester { private SortedMap navigableMap; @@ -54,10 +58,10 @@ public void setUp() throws Exception { super.setUp(); navigableMap = (SortedMap) getMap(); List> entries = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, Helpers.entryComparator(navigableMap.comparator())); + sort(entries, Helpers.entryComparator(navigableMap.comparator())); // some tests assume SEVERAL == 3 if (entries.size() >= 1) { @@ -70,20 +74,12 @@ public void setUp() throws Exception { @CollectionSize.Require(ZERO) public void testEmptyMapFirst() { - try { - navigableMap.firstKey(); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> navigableMap.firstKey()); } @CollectionSize.Require(ZERO) public void testEmptyMapLast() { - try { - assertNull(navigableMap.lastKey()); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> assertNull(navigableMap.lastKey())); } @CollectionSize.Require(ONE) @@ -118,10 +114,10 @@ public void testTailMapInclusive() { public void testHeadMap() { List> entries = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, Helpers.entryComparator(navigableMap.comparator())); + sort(entries, Helpers.entryComparator(navigableMap.comparator())); for (int i = 0; i < entries.size(); i++) { assertEqualInOrder( entries.subList(0, i), navigableMap.headMap(entries.get(i).getKey()).entrySet()); @@ -130,10 +126,10 @@ public void testHeadMap() { public void testTailMap() { List> entries = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, Helpers.entryComparator(navigableMap.comparator())); + sort(entries, Helpers.entryComparator(navigableMap.comparator())); for (int i = 0; i < entries.size(); i++) { assertEqualInOrder( entries.subList(i, entries.size()), @@ -143,10 +139,10 @@ public void testTailMap() { public void testSubMap() { List> entries = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(entries, Helpers.entryComparator(navigableMap.comparator())); + sort(entries, Helpers.entryComparator(navigableMap.comparator())); for (int i = 0; i < entries.size(); i++) { for (int j = i + 1; j < entries.size(); j++) { assertEqualInOrder( @@ -158,16 +154,11 @@ public void testSubMap() { @CollectionSize.Require(SEVERAL) public void testSubMapIllegal() { - try { - navigableMap.subMap(c.getKey(), a.getKey()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> navigableMap.subMap(c.getKey(), a.getKey())); } @CollectionSize.Require(absent = ZERO) public void testOrderedByComparator() { - @SuppressWarnings("unchecked") Comparator comparator = navigableMap.comparator(); if (comparator == null) { comparator = diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedSetNavigationTester.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedSetNavigationTester.java index bf5ac223a9c5..7023cf47b980 100644 --- a/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedSetNavigationTester.java +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/SortedSetNavigationTester.java @@ -16,17 +16,20 @@ package com.google.common.collect.testing.testers; +import static com.google.common.collect.testing.Helpers.copyToList; import static com.google.common.collect.testing.features.CollectionSize.ONE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; +import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.features.CollectionSize; -import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.SortedSet; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.Ignore; /** @@ -37,24 +40,27 @@ * @author Louis Wasserman */ @GwtCompatible -@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. -public class SortedSetNavigationTester extends AbstractSetTester { +@Ignore("test runners must not instantiate and run this directly, only via suites we build") +// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@NullMarked +public class SortedSetNavigationTester extends AbstractSetTester { private SortedSet sortedSet; private List values; - private E a; - private E b; - private E c; + private @Nullable E a; + private @Nullable E b; + private @Nullable E c; @Override public void setUp() throws Exception { super.setUp(); sortedSet = (SortedSet) getSet(); values = - Helpers.copyToList( + copyToList( getSubjectGenerator() .getSampleElements(getSubjectGenerator().getCollectionSize().getNumElements())); - Collections.sort(values, sortedSet.comparator()); + sort(values, sortedSet.comparator()); // some tests assume SEVERAL == 3 if (values.size() >= 1) { @@ -68,20 +74,12 @@ public void setUp() throws Exception { @CollectionSize.Require(ZERO) public void testEmptySetFirst() { - try { - sortedSet.first(); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> sortedSet.first()); } @CollectionSize.Require(ZERO) public void testEmptySetLast() { - try { - sortedSet.last(); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> sortedSet.last()); } @CollectionSize.Require(ONE) diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/TestExceptions.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/TestExceptions.java new file mode 100644 index 000000000000..4fdceccda453 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/TestExceptions.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing.testers; + +import com.google.common.annotations.GwtCompatible; + +/** Exception classes for use in tests. */ +@GwtCompatible +final class TestExceptions { + static class SomeError extends Error {} + + static class SomeCheckedException extends Exception {} + + static class SomeOtherCheckedException extends Exception {} + + static class YetAnotherCheckedException extends Exception {} + + static class SomeUncheckedException extends RuntimeException {} + + static class SomeChainingException extends RuntimeException { + public SomeChainingException(Throwable cause) { + super(cause); + } + } + + private TestExceptions() {} +} diff --git a/android/guava-testlib/src/com/google/common/collect/testing/testers/package-info.java b/android/guava-testlib/src/com/google/common/collect/testing/testers/package-info.java new file mode 100644 index 000000000000..fa9439065965 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/collect/testing/testers/package-info.java @@ -0,0 +1,2 @@ +@com.google.errorprone.annotations.CheckReturnValue +package com.google.common.collect.testing.testers; diff --git a/android/guava-testlib/src/com/google/common/escape/testing/EscaperAsserts.java b/android/guava-testlib/src/com/google/common/escape/testing/EscaperAsserts.java index 3920afe4c137..31ac2015dd04 100644 --- a/android/guava-testlib/src/com/google/common/escape/testing/EscaperAsserts.java +++ b/android/guava-testlib/src/com/google/common/escape/testing/EscaperAsserts.java @@ -18,7 +18,6 @@ import static com.google.common.escape.Escapers.computeReplacement; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.escape.CharEscaper; import com.google.common.escape.Escaper; @@ -32,7 +31,6 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible public final class EscaperAsserts { private EscaperAsserts() {} diff --git a/android/guava-testlib/src/com/google/common/escape/testing/package-info.java b/android/guava-testlib/src/com/google/common/escape/testing/package-info.java index 869884734e45..6b50614695c1 100644 --- a/android/guava-testlib/src/com/google/common/escape/testing/package-info.java +++ b/android/guava-testlib/src/com/google/common/escape/testing/package-info.java @@ -17,7 +17,7 @@ /** * Testing utilities for use in tests of {@code com.google.common.escape}. * - *

This package is a part of the open-source Guava + *

This package is a part of the open-source Guava * library. */ @CheckReturnValue diff --git a/android/guava-testlib/src/com/google/common/testing/AbstractPackageSanityTests.java b/android/guava-testlib/src/com/google/common/testing/AbstractPackageSanityTests.java index 1e31bbfb408f..0f84e106e4e7 100644 --- a/android/guava-testlib/src/com/google/common/testing/AbstractPackageSanityTests.java +++ b/android/guava-testlib/src/com/google/common/testing/AbstractPackageSanityTests.java @@ -20,8 +20,8 @@ import static com.google.common.base.Predicates.not; import static com.google.common.testing.AbstractPackageSanityTests.Chopper.suffix; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Predicate; @@ -43,7 +43,6 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.junit.Test; @@ -101,10 +100,10 @@ * @author Ben Yu * @since 14.0 */ -@Beta // TODO: Switch to JUnit 4 and use @Parameterized and @BeforeClass // Note: @Test annotations are deliberate, as some subclasses specify @RunWith(JUnit4). @GwtIncompatible +@J2ktIncompatible @J2ObjCIncompatible // com.google.common.reflect.ClassPath public abstract class AbstractPackageSanityTests extends TestCase { @@ -233,6 +232,15 @@ public void testSerializable() throws Exception { @Test public void testNulls() throws Exception { for (Class classToTest : findClassesToTest(loadClassesInPackage(), NULL_TEST_METHOD_NAMES)) { + if (classToTest.getSimpleName().equals("ReflectionFreeAssertThrows")) { + /* + * These classes handle null properly but throw IllegalArgumentException for the default + * Class argument that this test uses. Normally we'd fix that by declaring a + * ReflectionFreeAssertThrowsTest with a testNulls method, but that's annoying to have to do + * for a package-private utility class. So we skip the class entirely instead. + */ + continue; + } try { tester.doTestNulls(classToTest, visibility); } catch (Throwable e) { @@ -307,7 +315,7 @@ protected final void ignoreClasses(Predicate> condition) { this.classFilter = and(this.classFilter, not(condition)); } - private static AssertionFailedError sanityError( + private static AssertionError sanityError( Class cls, List explicitTestNames, String description, Throwable e) { String message = String.format( @@ -318,14 +326,12 @@ private static AssertionFailedError sanityError( cls, explicitTestNames.get(0), cls.getName()); - AssertionFailedError error = new AssertionFailedError(message); - error.initCause(e); - return error; + return new AssertionError(message, e); } /** * Finds the classes not ending with a test suffix and not covered by an explicit test whose name - * is {@code explicitTestName}. + * is {@code explicitTestNames}. */ @VisibleForTesting List> findClassesToTest( diff --git a/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java b/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java index 871c5e7133b1..d426ea2b7b3e 100644 --- a/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java +++ b/android/guava-testlib/src/com/google/common/testing/ArbitraryInstances.java @@ -17,11 +17,13 @@ package com.google.common.testing; import static com.google.common.base.Preconditions.checkArgument; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.SECONDS; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.CharMatcher; -import com.google.common.base.Charsets; import com.google.common.base.Defaults; import com.google.common.base.Equivalence; import com.google.common.base.Joiner; @@ -74,6 +76,7 @@ import com.google.common.primitives.Primitives; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; +import com.google.errorprone.annotations.Keep; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -142,7 +145,8 @@ import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Supplies an arbitrary "default" instance for a wide range of types, often useful in testing @@ -166,8 +170,9 @@ * @author Ben Yu * @since 12.0 */ -@Beta @GwtIncompatible +@J2ktIncompatible +@NullMarked public final class ArbitraryInstances { private static final Ordering BY_FIELD_NAME = @@ -181,9 +186,9 @@ public int compare(Field left, Field right) { /** * Returns a new {@code MatchResult} that corresponds to a successful match. Apache Harmony (used * in Android) requires a successful match in order to generate a {@code MatchResult}: - * http://goo.gl/5VQFmC + * https://cs.android.com/android/platform/superproject/+/android-2.3.7_r1:libcore/luni/src/main/java/java/util/regex/Matcher.java;l=550;drc=5850271b4ab93ebc27c1d49169a348c6be3c7f04 */ - private static MatchResult newMatchResult() { + private static MatchResult createMatchResult() { Matcher matcher = Pattern.compile(".").matcher("X"); matcher.find(); return matcher.toMatchResult(); @@ -201,9 +206,9 @@ private static MatchResult newMatchResult() { .put(CharSequence.class, "") .put(String.class, "") .put(Pattern.class, Pattern.compile("")) - .put(MatchResult.class, newMatchResult()) - .put(TimeUnit.class, TimeUnit.SECONDS) - .put(Charset.class, Charsets.UTF_8) + .put(MatchResult.class, createMatchResult()) + .put(TimeUnit.class, SECONDS) + .put(Charset.class, UTF_8) .put(Currency.class, Currency.getInstance(Locale.US)) .put(Locale.class, Locale.US) .put(UUID.class, UUID.randomUUID()) @@ -234,7 +239,7 @@ private static MatchResult newMatchResult() { .put(ByteSource.class, ByteSource.empty()) .put(CharSource.class, CharSource.empty()) .put(ByteSink.class, NullByteSink.INSTANCE) - .put(CharSink.class, NullByteSink.INSTANCE.asCharSink(Charsets.UTF_8)) + .put(CharSink.class, NullByteSink.INSTANCE.asCharSink(UTF_8)) // All collections are immutable empty. So safe for any type parameter. .put(Iterator.class, ImmutableSet.of().iterator()) .put(PeekingIterator.class, Iterators.peekingIterator(ImmutableSet.of().iterator())) @@ -327,8 +332,7 @@ private static void setImplementation(Class type, Class impl } @SuppressWarnings("unchecked") // it's a subtype map - @CheckForNull - private static Class getImplementation(Class type) { + private static @Nullable Class getImplementation(Class type) { return (Class) implementations.get(type); } @@ -338,8 +342,7 @@ private static Class getImplementation(Class type) { * Returns an arbitrary instance for {@code type}, or {@code null} if no arbitrary instance can be * determined. */ - @CheckForNull - public static T get(Class type) { + public static @Nullable T get(Class type) { T defaultValue = DEFAULTS.getInstance(type); if (defaultValue != null) { return defaultValue; @@ -350,7 +353,7 @@ public static T get(Class type) { } if (type.isEnum()) { T[] enumConstants = type.getEnumConstants(); - return (enumConstants.length == 0) ? null : enumConstants[0]; + return (enumConstants == null || enumConstants.length == 0) ? null : enumConstants[0]; } if (type.isArray()) { return createEmptyArray(type); @@ -362,7 +365,7 @@ public static T get(Class type) { if (Modifier.isAbstract(type.getModifiers()) || !Modifier.isPublic(type.getModifiers())) { return arbitraryConstantInstanceOrNull(type); } - final Constructor constructor; + Constructor constructor; try { constructor = type.getConstructor(); } catch (NoSuchMethodException e) { @@ -371,14 +374,7 @@ public static T get(Class type) { constructor.setAccessible(true); // accessibility check is too slow try { return constructor.newInstance(); - /* - * Do not merge the 2 catch blocks below. javac would infer a type of - * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of - * Android don't *seem* to mind, but there might be edge cases of which we're unaware.) - */ - } catch (InstantiationException impossible) { - throw new AssertionError(impossible); - } catch (IllegalAccessException impossible) { + } catch (InstantiationException | IllegalAccessException impossible) { throw new AssertionError(impossible); } catch (InvocationTargetException e) { logger.log(Level.WARNING, "Exception while invoking default constructor.", e.getCause()); @@ -386,8 +382,7 @@ public static T get(Class type) { } } - @CheckForNull - private static T arbitraryConstantInstanceOrNull(Class type) { + private static @Nullable T arbitraryConstantInstanceOrNull(Class type) { Field[] fields = type.getDeclaredFields(); Arrays.sort(fields, BY_FIELD_NAME); for (Field field : fields) { @@ -411,7 +406,8 @@ private static T arbitraryConstantInstanceOrNull(Class type) { } private static T createEmptyArray(Class arrayType) { - return arrayType.cast(Array.newInstance(arrayType.getComponentType(), 0)); + // getComponentType() is non-null because we call createEmptyArray only with an array type. + return arrayType.cast(Array.newInstance(requireNonNull(arrayType.getComponentType()), 0)); } // Internal implementations of some classes, with public default constructor that get() needs. @@ -430,6 +426,7 @@ public InMemoryPrintWriter() { } public static final class DeterministicRandom extends Random { + @Keep public DeterministicRandom() { super(0); } @@ -497,11 +494,13 @@ private Object readResolve() { } // Always equal is a valid total ordering. And it works for any Object. - private static final class AlwaysEqual extends Ordering implements Serializable { + private static final class AlwaysEqual extends Ordering<@Nullable Object> + implements Serializable { private static final AlwaysEqual INSTANCE = new AlwaysEqual(); @Override - public int compare(Object o1, Object o2) { + @SuppressWarnings("UnusedVariable") // intentionally weird Comparator + public int compare(@Nullable Object o1, @Nullable Object o2) { return 0; } diff --git a/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java b/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java index 09317854571a..4941b1050e09 100644 --- a/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java +++ b/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java @@ -21,8 +21,8 @@ import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.testing.NullPointerTester.isNullable; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Objects; @@ -33,7 +33,6 @@ import com.google.common.collect.MutableClassToInstanceMap; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; -import com.google.common.primitives.Ints; import com.google.common.reflect.Invokable; import com.google.common.reflect.Parameter; import com.google.common.reflect.Reflection; @@ -41,6 +40,7 @@ import com.google.common.testing.NullPointerTester.Visibility; import com.google.common.testing.RelationshipTester.Item; import com.google.common.testing.RelationshipTester.ItemReporter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -50,9 +50,10 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; import junit.framework.Assert; import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tester that runs automated sanity tests for any given class. A typical use case is to test static @@ -78,8 +79,10 @@ * @author Ben Yu * @since 14.0 */ -@Beta @GwtIncompatible +@J2ktIncompatible +@NullUnmarked +@SuppressWarnings("nullness") public final class ClassSanityTester { private static final Ordering> BY_METHOD_NAME = @@ -102,7 +105,7 @@ public int compare(Invokable left, Invokable right) { new Ordering>() { @Override public int compare(Invokable left, Invokable right) { - return Ints.compare(left.getParameters().size(), right.getParameters().size()); + return Integer.compare(left.getParameters().size(), right.getParameters().size()); } }; @@ -133,6 +136,7 @@ public ClassSanityTester() { * Object#equals} because more than one sample instances are needed for testing inequality. To set * distinct values for equality testing, use {@link #setDistinctValues} instead. */ + @CanIgnoreReturnValue public ClassSanityTester setDefault(Class type, T value) { nullPointerTester.setDefault(type, value); defaultValues.putInstance(type, value); @@ -154,6 +158,7 @@ public ClassSanityTester setDefault(Class type, T value) { * @return this tester instance * @since 17.0 */ + @CanIgnoreReturnValue public ClassSanityTester setDistinctValues(Class type, T value1, T value2) { checkNotNull(type); checkNotNull(value1); @@ -198,7 +203,9 @@ public void testNulls(Class cls) { } void doTestNulls(Class cls, Visibility visibility) - throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException, + throws ParameterNotInstantiableException, + IllegalAccessException, + InvocationTargetException, FactoryMethodReturnsNullException { if (!Modifier.isAbstract(cls.getModifiers())) { nullPointerTester.testConstructors(cls, visibility); @@ -290,8 +297,11 @@ public void testEquals(Class cls) { } void doTestEquals(Class cls) - throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, - IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { + throws ParameterNotInstantiableException, + ParameterHasNoDistinctValueException, + IllegalAccessException, + InvocationTargetException, + FactoryMethodReturnsNullException { if (cls.isEnum()) { return; } @@ -334,13 +344,14 @@ void doTestEquals(Class cls) * @return The instantiated instance, or {@code null} if the class has no non-private constructor * or factory method to be constructed. */ - @CheckForNull - T instantiate(Class cls) - throws ParameterNotInstantiableException, IllegalAccessException, InvocationTargetException, + @Nullable T instantiate(Class cls) + throws ParameterNotInstantiableException, + IllegalAccessException, + InvocationTargetException, FactoryMethodReturnsNullException { if (cls.isEnum()) { T[] constants = cls.getEnumConstants(); - if (constants.length > 0) { + if (constants != null && constants.length > 0) { return constants[0]; } else { return null; @@ -383,8 +394,7 @@ T instantiate(Class cls) * class, preventing its methods from being accessible. * @throws InvocationTargetException if a static method threw exception. */ - @CheckForNull - private T instantiate(Invokable factory) + private @Nullable T instantiate(Invokable factory) throws ParameterNotInstantiableException, InvocationTargetException, IllegalAccessException { return invoke(factory, getDummyArguments(factory)); } @@ -429,6 +439,7 @@ private FactoryMethodReturnValueTester( * * @return this tester object */ + @CanIgnoreReturnValue public FactoryMethodReturnValueTester thatReturn(Class returnType) { this.returnTypeToTest = returnType; return this; @@ -442,6 +453,7 @@ public FactoryMethodReturnValueTester thatReturn(Class returnType) { * * @return this tester */ + @CanIgnoreReturnValue public FactoryMethodReturnValueTester testNulls() throws Exception { for (Invokable factory : getFactoriesToTest()) { Object instance = instantiate(factory); @@ -450,10 +462,7 @@ public FactoryMethodReturnValueTester testNulls() throws Exception { try { nullPointerTester.testAllPublicInstanceMethods(instance); } catch (AssertionError e) { - AssertionError error = - new AssertionFailedError("Null check failed on return value of " + factory); - error.initCause(e); - throw error; + throw new AssertionError("Null check failed on return value of " + factory, e); } } } @@ -470,6 +479,7 @@ public FactoryMethodReturnValueTester testNulls() throws Exception { * * @return this tester */ + @CanIgnoreReturnValue public FactoryMethodReturnValueTester testEquals() throws Exception { for (Invokable factory : getFactoriesToTest()) { try { @@ -489,17 +499,17 @@ public FactoryMethodReturnValueTester testEquals() throws Exception { * * @return this tester */ + @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { Object instance = instantiate(factory); if (instance != null) { try { SerializableTester.reserialize(instance); - } catch (RuntimeException e) { - AssertionError error = - new AssertionFailedError("Serialization failed on return value of " + factory); - error.initCause(e.getCause()); - throw error; + } catch (Exception e) { // sneaky checked exception + throw new AssertionError( + "Serialization failed on return value of " + factory, e.getCause()); } } } @@ -514,6 +524,8 @@ public FactoryMethodReturnValueTester testSerializable() throws Exception { * * @return this tester */ + @CanIgnoreReturnValue + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception { for (Invokable factory : getFactoriesToTest()) { try { @@ -525,17 +537,12 @@ public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Excepti if (instance != null) { try { SerializableTester.reserializeAndAssert(instance); - } catch (RuntimeException e) { - AssertionError error = - new AssertionFailedError("Serialization failed on return value of " + factory); - error.initCause(e.getCause()); - throw error; + } catch (Exception e) { // sneaky checked exception + throw new AssertionError( + "Serialization failed on return value of " + factory, e.getCause()); } catch (AssertionFailedError e) { - AssertionError error = - new AssertionFailedError( - "Return value of " + factory + " reserialized to an unequal value"); - error.initCause(e); - throw error; + throw new AssertionError( + "Return value of " + factory + " reserialized to an unequal value", e); } } } @@ -563,12 +570,15 @@ public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Excepti } } - private void testEqualsUsing(final Invokable factory) - throws ParameterNotInstantiableException, ParameterHasNoDistinctValueException, - IllegalAccessException, InvocationTargetException, FactoryMethodReturnsNullException { + private void testEqualsUsing(Invokable factory) + throws ParameterNotInstantiableException, + ParameterHasNoDistinctValueException, + IllegalAccessException, + InvocationTargetException, + FactoryMethodReturnsNullException { List params = factory.getParameters(); List argGenerators = Lists.newArrayListWithCapacity(params.size()); - List args = Lists.newArrayListWithCapacity(params.size()); + List<@Nullable Object> args = Lists.newArrayListWithCapacity(params.size()); for (Parameter param : params) { FreshValueGenerator generator = newFreshValueGenerator(); argGenerators.add(generator); @@ -577,7 +587,7 @@ private void testEqualsUsing(final Invokable factory) Object instance = createInstance(factory, args); List equalArgs = generateEqualFactoryArguments(factory, params, args); // Each group is a List of items, each item has a list of factory args. - final List>> argGroups = Lists.newArrayList(); + List>> argGroups = Lists.newArrayList(); argGroups.add(ImmutableList.of(args, equalArgs)); EqualsTester tester = new EqualsTester( @@ -615,8 +625,10 @@ String reportItem(Item item) { */ private List generateEqualFactoryArguments( Invokable factory, List params, List args) - throws ParameterNotInstantiableException, FactoryMethodReturnsNullException, - InvocationTargetException, IllegalAccessException { + throws ParameterNotInstantiableException, + FactoryMethodReturnsNullException, + InvocationTargetException, + IllegalAccessException { List equalArgs = Lists.newArrayList(args); for (int i = 0; i < args.size(); i++) { Parameter param = params.get(i); @@ -626,7 +638,7 @@ private List generateEqualFactoryArguments( Object shouldBeEqualArg = generateDummyArg(param, newFreshValueGenerator()); if (arg != shouldBeEqualArg && Objects.equal(arg, shouldBeEqualArg) - && hashCodeInsensitiveToArgReference(factory, args, i, shouldBeEqualArg) + && hashCodeInsensitiveToArgReference(factory, args, i, checkNotNull(shouldBeEqualArg)) && hashCodeInsensitiveToArgReference( factory, args, i, generateDummyArg(param, newFreshValueGenerator()))) { // If the implementation uses identityHashCode(), referential equality is @@ -654,7 +666,7 @@ private FreshValueGenerator newFreshValueGenerator() { FreshValueGenerator generator = new FreshValueGenerator() { @Override - Object interfaceMethodCalled(Class interfaceType, Method method) { + @Nullable Object interfaceMethodCalled(Class interfaceType, Method method) { return getDummyValue(TypeToken.of(interfaceType).method(method).getReturnType()); } }; @@ -664,8 +676,7 @@ Object interfaceMethodCalled(Class interfaceType, Method method) { return generator; } - @CheckForNull - private static Object generateDummyArg(Parameter param, FreshValueGenerator generator) + private static @Nullable Object generateDummyArg(Parameter param, FreshValueGenerator generator) throws ParameterNotInstantiableException { if (isNullable(param)) { return null; @@ -708,9 +719,9 @@ private static void throwFirst(List exceptions) throws for (Invokable factory : factories) { factory.setAccessible(true); } - // Sorts methods/constructors with least number of parameters first since it's likely easier to - // fill dummy parameter values for them. Ties are broken by name then by the string form of the - // parameter list. + // Sorts methods/constructors with the least number of parameters first since it's likely easier + // to fill dummy parameter values for them. Ties are broken by name then by the string form of + // the parameter list. return BY_NUMBER_OF_PARAMETERS .compound(BY_METHOD_NAME) .compound(BY_PARAMETERS) @@ -734,7 +745,7 @@ private List getDummyArguments(Invokable invokable) return args; } - private T getDummyValue(TypeToken type) { + private @Nullable T getDummyValue(TypeToken type) { Class rawType = type.getRawType(); @SuppressWarnings("unchecked") // Assume all default values are generics safe. T defaultValue = (T) defaultValues.getInstance(rawType); @@ -761,8 +772,7 @@ private static T createInstance(Invokable factory, List a return instance; } - @CheckForNull - private static T invoke(Invokable factory, List args) + private static @Nullable T invoke(Invokable factory, List args) throws InvocationTargetException, IllegalAccessException { T returnValue = factory.invoke(null, args.toArray()); if (returnValue == null) { @@ -828,7 +838,7 @@ R dummyReturnValue(TypeToken returnType) { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof SerializableDummyProxy; } diff --git a/android/guava-testlib/src/com/google/common/testing/ClusterException.java b/android/guava-testlib/src/com/google/common/testing/ClusterException.java index 7665ab1d435b..47232e8dd63b 100644 --- a/android/guava-testlib/src/com/google/common/testing/ClusterException.java +++ b/android/guava-testlib/src/com/google/common/testing/ClusterException.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import org.jspecify.annotations.NullMarked; /** * An {@link ClusterException} is a data structure that allows for some code to "throw multiple @@ -59,20 +60,21 @@ * @author Luiz-Otavio Zorzella */ @GwtCompatible +@NullMarked final class ClusterException extends RuntimeException { - public final Collection exceptions; + final Collection exceptions; private ClusterException(Collection exceptions) { super( exceptions.size() + " exceptions were thrown. The first exception is listed as a cause.", exceptions.iterator().next()); - ArrayList temp = new ArrayList<>(exceptions); + ArrayList temp = new ArrayList<>(exceptions); this.exceptions = Collections.unmodifiableCollection(temp); } - /** @see #create(Collection) */ - public static RuntimeException create(Throwable... exceptions) { + /** See {@link #create(Collection)}. */ + static RuntimeException create(Throwable... exceptions) { ArrayList temp = new ArrayList<>(Arrays.asList(exceptions)); return create(temp); } @@ -96,7 +98,7 @@ public static RuntimeException create(Throwable... exceptions) { * @throws NullPointerException if {@code exceptions} is null * @throws IllegalArgumentException if {@code exceptions} is empty */ - public static RuntimeException create(Collection exceptions) { + static RuntimeException create(Collection exceptions) { if (exceptions.size() == 0) { throw new IllegalArgumentException("Can't create an ExceptionCollection with no exceptions"); } diff --git a/android/guava-testlib/src/com/google/common/testing/DummyProxy.java b/android/guava-testlib/src/com/google/common/testing/DummyProxy.java index 85e229d51831..837ea36a4122 100644 --- a/android/guava-testlib/src/com/google/common/testing/DummyProxy.java +++ b/android/guava-testlib/src/com/google/common/testing/DummyProxy.java @@ -20,6 +20,7 @@ import static com.google.common.testing.NullPointerTester.isNullable; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.reflect.AbstractInvocationHandler; @@ -30,6 +31,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Generates a dummy interface proxy that simply returns a dummy value for each method. @@ -37,6 +40,8 @@ * @author Ben Yu */ @GwtIncompatible +@J2ktIncompatible +@NullMarked abstract class DummyProxy { /** @@ -59,7 +64,7 @@ final T newProxy(TypeToken interfaceType) { } /** Returns the dummy return value for {@code returnType}. */ - abstract R dummyReturnValue(TypeToken returnType); + abstract @Nullable R dummyReturnValue(TypeToken returnType); private class DummyHandler extends AbstractInvocationHandler implements Serializable { private final TypeToken interfaceType; @@ -69,7 +74,8 @@ private class DummyHandler extends AbstractInvocationHandler implements Serializ } @Override - protected Object handleInvocation(Object proxy, Method method, Object[] args) { + protected @Nullable Object handleInvocation( + Object proxy, Method method, @Nullable Object[] args) { Invokable invokable = interfaceType.method(method); ImmutableList params = invokable.getParameters(); for (int i = 0; i < args.length; i++) { @@ -87,7 +93,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof DummyHandler) { DummyHandler that = (DummyHandler) obj; return identity().equals(that.identity()); diff --git a/android/guava-testlib/src/com/google/common/testing/EqualsTester.java b/android/guava-testlib/src/com/google/common/testing/EqualsTester.java index c7fb3dfd106d..699acb38b568 100644 --- a/android/guava-testlib/src/com/google/common/testing/EqualsTester.java +++ b/android/guava-testlib/src/com/google/common/testing/EqualsTester.java @@ -20,13 +20,15 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Equivalence; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tester for equals() and hashCode() methods of a class. @@ -74,8 +76,8 @@ * @author Jige Yu * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public final class EqualsTester { private static final int REPETITIONS = 3; @@ -94,14 +96,34 @@ public EqualsTester() { /** * Adds {@code equalityGroup} with objects that are supposed to be equal to each other and not * equal to any other equality groups added to this tester. + * + *

The {@code @Nullable} annotations on the {@code equalityGroup} parameter imply that the + * objects, and the array itself, can be null. That is for programmer convenience, when the + * objects come from factory methods that are themselves {@code @Nullable}. In reality neither the + * array nor its contents can be null, but it is not useful to force the use of {@code + * requireNonNull} or the like just to assert that. + * + *

{@code EqualsTester} will always check that every object it is given returns false from + * {@code equals(null)}, so it is neither useful nor allowed to include a null value in any + * equality group. */ - public EqualsTester addEqualityGroup(Object... equalityGroup) { + @CanIgnoreReturnValue + public EqualsTester addEqualityGroup(@Nullable Object @Nullable ... equalityGroup) { checkNotNull(equalityGroup); - equalityGroups.add(ImmutableList.copyOf(equalityGroup)); + List list = new ArrayList<>(equalityGroup.length); + for (int i = 0; i < equalityGroup.length; i++) { + Object element = equalityGroup[i]; + if (element == null) { + throw new NullPointerException("at index " + i); + } + list.add(element); + } + equalityGroups.add(list); return this; } /** Run tests on equals method, throwing a failure on an invalid test */ + @CanIgnoreReturnValue public EqualsTester testEquals() { RelationshipTester delegate = new RelationshipTester<>( diff --git a/android/guava-testlib/src/com/google/common/testing/EquivalenceTester.java b/android/guava-testlib/src/com/google/common/testing/EquivalenceTester.java index 27c71764b31f..bf16785bc5f1 100644 --- a/android/guava-testlib/src/com/google/common/testing/EquivalenceTester.java +++ b/android/guava-testlib/src/com/google/common/testing/EquivalenceTester.java @@ -20,13 +20,14 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.testing.RelationshipTester.ItemReporter; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; +import org.jspecify.annotations.NullMarked; /** * Tester for {@link Equivalence} relationships between groups of objects. @@ -49,8 +50,8 @@ * @author Gregory Kick * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public final class EquivalenceTester { private static final int REPETITIONS = 3; @@ -71,11 +72,13 @@ public static EquivalenceTester of(Equivalence equivalence) { * Adds a group of objects that are supposed to be equivalent to each other and not equivalent to * objects in any other equivalence group added to this tester. */ + @CanIgnoreReturnValue public EquivalenceTester addEquivalenceGroup(T first, T... rest) { addEquivalenceGroup(Lists.asList(first, rest)); return this; } + @CanIgnoreReturnValue public EquivalenceTester addEquivalenceGroup(Iterable group) { delegate.addRelatedGroup(group); items.addAll(ImmutableList.copyOf(group)); @@ -83,6 +86,7 @@ public EquivalenceTester addEquivalenceGroup(Iterable group) { } /** Run tests on equivalence methods, throwing a failure on an invalid test */ + @CanIgnoreReturnValue public EquivalenceTester test() { for (int run = 0; run < REPETITIONS; run++) { testItems(); diff --git a/android/guava-testlib/src/com/google/common/testing/FakeTicker.java b/android/guava-testlib/src/com/google/common/testing/FakeTicker.java index 698db6a002d5..66ae972ca4d9 100644 --- a/android/guava-testlib/src/com/google/common/testing/FakeTicker.java +++ b/android/guava-testlib/src/com/google/common/testing/FakeTicker.java @@ -17,12 +17,18 @@ package com.google.common.testing; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ticker; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import org.jspecify.annotations.NullMarked; /** * A Ticker whose value can be advanced programmatically in test. @@ -35,7 +41,7 @@ * @author Jige Yu * @since 10.0 */ -@Beta +@NullMarked @GwtCompatible public class FakeTicker extends Ticker { @@ -44,17 +50,35 @@ public class FakeTicker extends Ticker { /** Advances the ticker value by {@code time} in {@code timeUnit}. */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @CanIgnoreReturnValue public FakeTicker advance(long time, TimeUnit timeUnit) { return advance(timeUnit.toNanos(time)); } /** Advances the ticker value by {@code nanoseconds}. */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @CanIgnoreReturnValue public FakeTicker advance(long nanoseconds) { nanos.addAndGet(nanoseconds); return this; } + /** + * Advances the ticker value by {@code duration}. + * + * @since 33.1.0 (but since 28.0 in the JRE flavor) + */ + @GwtIncompatible + @J2ktIncompatible + @CanIgnoreReturnValue + @SuppressWarnings("Java7ApiChecker") // guava-android can rely on library desugaring now. + @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents. + @Beta // TODO: b/288085449 - Remove @Beta after we're sure that Java 8 APIs are safe for Android + public FakeTicker advance(Duration duration) { + return advance(duration.toNanos()); + } + /** * Sets the increment applied to the ticker whenever it is queried. * @@ -62,12 +86,32 @@ public FakeTicker advance(long nanoseconds) { * queried. */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @CanIgnoreReturnValue public FakeTicker setAutoIncrementStep(long autoIncrementStep, TimeUnit timeUnit) { checkArgument(autoIncrementStep >= 0, "May not auto-increment by a negative amount"); this.autoIncrementStepNanos = timeUnit.toNanos(autoIncrementStep); return this; } + /** + * Sets the increment applied to the ticker whenever it is queried. + * + *

The default behavior is to auto increment by zero. i.e: The ticker is left unchanged when + * queried. + * + * @since 33.1.0 (but since 28.0 in the JRE flavor) + */ + @GwtIncompatible + @J2ktIncompatible + @CanIgnoreReturnValue + @SuppressWarnings("Java7ApiChecker") // guava-android can rely on library desugaring now. + @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents. + @Beta // TODO: b/288085449 - Remove @Beta after we're sure that Java 8 APIs are safe for Android + public FakeTicker setAutoIncrementStep(Duration autoIncrementStep) { + return setAutoIncrementStep(autoIncrementStep.toNanos(), NANOSECONDS); + } + @Override public long read() { return nanos.getAndAdd(autoIncrementStepNanos); diff --git a/android/guava-testlib/src/com/google/common/testing/ForwardingWrapperTester.java b/android/guava-testlib/src/com/google/common/testing/ForwardingWrapperTester.java index 33ab3797b13e..193c9fe41859 100644 --- a/android/guava-testlib/src/com/google/common/testing/ForwardingWrapperTester.java +++ b/android/guava-testlib/src/com/google/common/testing/ForwardingWrapperTester.java @@ -22,19 +22,22 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.reflect.AbstractInvocationHandler; import com.google.common.reflect.Reflection; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.AccessibleObject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tester to ensure forwarding wrapper works by delegating calls to the corresponding method with @@ -53,8 +56,9 @@ * @author Ben Yu * @since 14.0 */ -@Beta @GwtIncompatible +@J2ktIncompatible +@NullMarked public final class ForwardingWrapperTester { private boolean testsEquals = false; @@ -63,6 +67,7 @@ public final class ForwardingWrapperTester { * Asks for {@link Object#equals} and {@link Object#hashCode} to be tested. That is, forwarding * wrappers of equal instances should be equal. */ + @CanIgnoreReturnValue public ForwardingWrapperTester includingEquals() { this.testsEquals = true; return this; @@ -80,9 +85,9 @@ public void testForwarding( Method[] methods = getMostConcreteMethods(interfaceType); AccessibleObject.setAccessible(methods, true); for (Method method : methods) { - // Under java 8, interfaces can have default methods that aren't abstract. + // Interfaces can have default methods that aren't abstract. // No need to verify them. - // Can't check isDefault() for JDK 7 compatibility. + // Can't check isDefault() for Android compatibility. if (!Modifier.isAbstract(method.getModifiers())) { continue; } @@ -135,7 +140,7 @@ private static void testExceptionPropagation( interfaceType, new AbstractInvocationHandler() { @Override - protected Object handleInvocation(Object p, Method m, Object[] args) + protected Object handleInvocation(Object p, Method m, @Nullable Object[] args) throws Throwable { throw exception; } @@ -173,9 +178,9 @@ private static void testToString( wrapperFunction.apply(proxy).toString()); } - private static Object[] getParameterValues(Method method) { + private static @Nullable Object[] getParameterValues(Method method) { FreshValueGenerator paramValues = new FreshValueGenerator(); - List passedArgs = Lists.newArrayList(); + List<@Nullable Object> passedArgs = Lists.newArrayList(); for (Class paramType : method.getParameterTypes()) { passedArgs.add(paramValues.generateFresh(paramType)); } @@ -187,8 +192,8 @@ private static final class InteractionTester extends AbstractInvocationHandle private final Class interfaceType; private final Method method; - private final Object[] passedArgs; - private final Object returnValue; + private final @Nullable Object[] passedArgs; + private final @Nullable Object returnValue; private final AtomicInteger called = new AtomicInteger(); InteractionTester(Class interfaceType, Method method) { @@ -199,8 +204,8 @@ private static final class InteractionTester extends AbstractInvocationHandle } @Override - protected Object handleInvocation(Object p, Method calledMethod, Object[] args) - throws Throwable { + protected @Nullable Object handleInvocation( + Object p, Method calledMethod, @Nullable Object[] args) throws Throwable { assertEquals(method, calledMethod); assertEquals(method + " invoked more than once.", 0, called.get()); for (int i = 0; i < passedArgs.length; i++) { diff --git a/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java b/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java index adc497d69b99..82783809f746 100644 --- a/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java +++ b/android/guava-testlib/src/com/google/common/testing/FreshValueGenerator.java @@ -18,10 +18,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Throwables.throwIfUnchecked; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.CharMatcher; -import com.google.common.base.Charsets; import com.google.common.base.Equivalence; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -118,7 +120,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Generates fresh instances of types that are different from each other (if possible). @@ -126,6 +129,9 @@ * @author Ben Yu */ @GwtIncompatible +@J2ktIncompatible +@NullUnmarked +@SuppressWarnings("nullness") class FreshValueGenerator { private static final ImmutableMap, Method> GENERATORS; @@ -137,7 +143,7 @@ class FreshValueGenerator { builder.put(method.getReturnType(), method); } } - GENERATORS = builder.build(); + GENERATORS = builder.buildOrThrow(); } private static final ImmutableMap, Method> EMPTY_GENERATORS; @@ -149,7 +155,7 @@ class FreshValueGenerator { builder.put(method.getReturnType(), method); } } - EMPTY_GENERATORS = builder.build(); + EMPTY_GENERATORS = builder.buildOrThrow(); } private final AtomicInteger freshness = new AtomicInteger(1); @@ -175,8 +181,7 @@ final void addSampleInstances(Class type, Iterable instances *
  • null if no value can be generated. * */ - @CheckForNull - final Object generateFresh(TypeToken type) { + final @Nullable Object generateFresh(TypeToken type) { Object generated = generate(type); if (generated != null) { freshness.incrementAndGet(); @@ -184,12 +189,11 @@ final Object generateFresh(TypeToken type) { return generated; } - @CheckForNull - final T generateFresh(Class type) { + final @Nullable T generateFresh(Class type) { return Primitives.wrap(type).cast(generateFresh(TypeToken.of(type))); } - final T newFreshProxy(final Class interfaceType) { + final T newFreshProxy(Class interfaceType) { T proxy = newProxy(interfaceType); freshness.incrementAndGet(); return proxy; @@ -199,7 +203,7 @@ final T newFreshProxy(final Class interfaceType) { * Generates an instance for {@code type} using the current {@link #freshness}. The generated * instance may or may not be unique across different calls. */ - private Object generate(TypeToken type) { + private @Nullable Object generate(TypeToken type) { Class rawType = type.getRawType(); List samples = sampleInstances.get(rawType); Object sample = pickInstance(samples, null); @@ -210,7 +214,7 @@ private Object generate(TypeToken type) { return pickInstance(rawType.getEnumConstants(), null); } if (type.isArray()) { - TypeToken componentType = type.getComponentType(); + TypeToken componentType = requireNonNull(type.getComponentType()); Object array = Array.newInstance(componentType.getRawType(), 1); Array.set(array, 0, generate(componentType)); return array; @@ -256,7 +260,7 @@ private Object generate(TypeToken type) { return defaultGenerate(rawType); } - private T defaultGenerate(Class rawType) { + private @Nullable T defaultGenerate(Class rawType) { if (rawType.isInterface()) { // always create a new proxy return newProxy(rawType); @@ -264,7 +268,7 @@ private T defaultGenerate(Class rawType) { return ArbitraryInstances.get(rawType); } - private T newProxy(final Class interfaceType) { + private T newProxy(Class interfaceType) { return Reflection.newProxy(interfaceType, new FreshInvocationHandler(interfaceType)); } @@ -289,7 +293,8 @@ private final class FreshInvocationHandler extends AbstractInvocationHandler { } @Override - protected Object handleInvocation(Object proxy, Method method, Object[] args) { + protected @Nullable Object handleInvocation( + Object proxy, Method method, @Nullable Object[] args) { return interfaceMethodCalled(interfaceType, method); } @@ -299,7 +304,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof FreshInvocationHandler) { FreshInvocationHandler that = (FreshInvocationHandler) obj; return identity == that.identity; @@ -314,7 +319,7 @@ public String toString() { } /** Subclasses can override to provide different return value for proxied interface methods. */ - Object interfaceMethodCalled(Class interfaceType, Method method) { + @Nullable Object interfaceMethodCalled(Class interfaceType, Method method) { throw new UnsupportedOperationException(); } @@ -376,6 +381,7 @@ int generateInt() { return freshness.get(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Integer generateInteger() { return new Integer(generateInt()); @@ -386,6 +392,7 @@ long generateLong() { return generateInt(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Long generateLongObject() { return new Long(generateLong()); @@ -396,6 +403,7 @@ float generateFloat() { return generateInt(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Float generateFloatObject() { return new Float(generateFloat()); @@ -406,6 +414,7 @@ Float generateFloatObject() { return generateInt(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Double generateDoubleObject() { return new Double(generateDouble()); @@ -416,6 +425,7 @@ short generateShort() { return (short) generateInt(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Short generateShortObject() { return new Short(generateShort()); @@ -426,6 +436,7 @@ byte generateByte() { return (byte) generateInt(); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Byte generateByteObject() { return new Byte(generateByte()); @@ -436,6 +447,7 @@ char generateChar() { return generateString().charAt(0); } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Character generateCharacter() { return new Character(generateChar()); @@ -446,6 +458,7 @@ boolean generateBoolean() { return generateInt() % 2 == 0; } + @SuppressWarnings("removal") // b/321209431 -- maybe just use valueOf here? @Generates Boolean generateBooleanObject() { return new Boolean(generateBoolean()); @@ -493,7 +506,7 @@ Pattern generatePattern() { @Generates Charset generateCharset() { - return pickInstance(Charset.availableCharsets().values(), Charsets.UTF_8); + return pickInstance(Charset.availableCharsets().values(), UTF_8); } @Generates @@ -503,37 +516,7 @@ Locale generateLocale() { @Generates Currency generateCurrency() { - try { - Method method = Currency.class.getMethod("getAvailableCurrencies"); - @SuppressWarnings("unchecked") // getAvailableCurrencies() returns Set. - Set currencies = (Set) method.invoke(null); - return pickInstance(currencies, Currency.getInstance(Locale.US)); - /* - * Do not merge the 2 catch blocks below. javac would infer a type of - * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of - * Android don't *seem* to mind, but there might be edge cases of which we're unaware.) - */ - } catch (NoSuchMethodException notJava7) { - return preJava7FreshCurrency(); - } catch (InvocationTargetException notJava7) { - return preJava7FreshCurrency(); - } catch (IllegalAccessException impossible) { - throw new AssertionError(impossible); - } - } - - private Currency preJava7FreshCurrency() { - for (Set uselessLocales = Sets.newHashSet(); ; ) { - Locale locale = generateLocale(); - if (uselessLocales.contains(locale)) { // exhausted all locales - return Currency.getInstance(Locale.US); - } - try { - return Currency.getInstance(locale); - } catch (IllegalArgumentException e) { - uselessLocales.add(locale); - } - } + return pickInstance(Currency.getAvailableCurrencies(), Currency.getInstance(Locale.US)); } // common.base @@ -620,9 +603,10 @@ Comparator generateComparator() { } @Generates - Ordering generateOrdering() { + Ordering generateOrdering() { return new Ordering() { @Override + @SuppressWarnings("UnusedVariable") // intentionally weird Comparator public int compare(T left, T right) { return 0; } @@ -647,29 +631,29 @@ static > Range generateRange(C freshElement) { } @Generates - static Iterable generateIterable(@CheckForNull E freshElement) { + static Iterable generateIterable(@Nullable E freshElement) { return generateList(freshElement); } @Generates - static Collection generateCollection(@CheckForNull E freshElement) { + static Collection generateCollection(@Nullable E freshElement) { return generateList(freshElement); } @Generates - static List generateList(@CheckForNull E freshElement) { + static List generateList(@Nullable E freshElement) { return generateArrayList(freshElement); } @Generates - static ArrayList generateArrayList(@CheckForNull E freshElement) { + static ArrayList generateArrayList(@Nullable E freshElement) { ArrayList list = Lists.newArrayList(); list.add(freshElement); return list; } @Generates - static LinkedList generateLinkedList(@CheckForNull E freshElement) { + static LinkedList generateLinkedList(@Nullable E freshElement) { LinkedList list = Lists.newLinkedList(); list.add(freshElement); return list; @@ -686,17 +670,17 @@ static ImmutableCollection generateImmutableCollection(E freshElement) { } @Generates - static Set generateSet(@CheckForNull E freshElement) { + static Set generateSet(@Nullable E freshElement) { return generateHashSet(freshElement); } @Generates - static HashSet generateHashSet(@CheckForNull E freshElement) { + static HashSet generateHashSet(@Nullable E freshElement) { return generateLinkedHashSet(freshElement); } @Generates - static LinkedHashSet generateLinkedHashSet(@CheckForNull E freshElement) { + static LinkedHashSet generateLinkedHashSet(@Nullable E freshElement) { LinkedHashSet set = Sets.newLinkedHashSet(); set.add(freshElement); return set; @@ -731,19 +715,19 @@ static > ImmutableSortedSet generateImmutable } @Generates - static Multiset generateMultiset(@CheckForNull E freshElement) { + static Multiset generateMultiset(@Nullable E freshElement) { return generateHashMultiset(freshElement); } @Generates - static HashMultiset generateHashMultiset(@CheckForNull E freshElement) { + static HashMultiset generateHashMultiset(@Nullable E freshElement) { HashMultiset multiset = HashMultiset.create(); multiset.add(freshElement); return multiset; } @Generates - static LinkedHashMultiset generateLinkedHashMultiset(@CheckForNull E freshElement) { + static LinkedHashMultiset generateLinkedHashMultiset(@Nullable E freshElement) { LinkedHashMultiset multiset = LinkedHashMultiset.create(); multiset.add(freshElement); return multiset; @@ -773,18 +757,17 @@ static > ImmutableSortedMultiset generateImmutableSor } @Generates - static Map generateMap(@CheckForNull K key, @CheckForNull V value) { + static Map generateMap(@Nullable K key, @Nullable V value) { return generateHashdMap(key, value); } @Generates - static HashMap generateHashdMap(@CheckForNull K key, @CheckForNull V value) { + static HashMap generateHashdMap(@Nullable K key, @Nullable V value) { return generateLinkedHashMap(key, value); } @Generates - static LinkedHashMap generateLinkedHashMap( - @CheckForNull K key, @CheckForNull V value) { + static LinkedHashMap generateLinkedHashMap(@Nullable K key, @Nullable V value) { LinkedHashMap map = Maps.newLinkedHashMap(); map.put(key, value); return map; @@ -809,19 +792,19 @@ static ConcurrentMap generateConcurrentMap(K key, V value) { @Generates static , V> SortedMap generateSortedMap( - K key, @CheckForNull V value) { + K key, @Nullable V value) { return generateNavigableMap(key, value); } @Generates static , V> NavigableMap generateNavigableMap( - K key, @CheckForNull V value) { + K key, @Nullable V value) { return generateTreeMap(key, value); } @Generates static , V> TreeMap generateTreeMap( - K key, @CheckForNull V value) { + K key, @Nullable V value) { TreeMap map = Maps.newTreeMap(); map.put(key, value); return map; @@ -834,7 +817,7 @@ static , V> ImmutableSortedMap generateImm } @Generates - static Multimap generateMultimap(@CheckForNull K key, @CheckForNull V value) { + static Multimap generateMultimap(@Nullable K key, @Nullable V value) { return generateListMultimap(key, value); } @@ -844,14 +827,13 @@ static ImmutableMultimap generateImmutableMultimap(K key, V value) } @Generates - static ListMultimap generateListMultimap( - @CheckForNull K key, @CheckForNull V value) { + static ListMultimap generateListMultimap(@Nullable K key, @Nullable V value) { return generateArrayListMultimap(key, value); } @Generates static ArrayListMultimap generateArrayListMultimap( - @CheckForNull K key, @CheckForNull V value) { + @Nullable K key, @Nullable V value) { ArrayListMultimap multimap = ArrayListMultimap.create(); multimap.put(key, value); return multimap; @@ -863,13 +845,12 @@ static ImmutableListMultimap generateImmutableListMultimap(K key, V } @Generates - static SetMultimap generateSetMultimap(@CheckForNull K key, @CheckForNull V value) { + static SetMultimap generateSetMultimap(@Nullable K key, @Nullable V value) { return generateLinkedHashMultimap(key, value); } @Generates - static HashMultimap generateHashMultimap( - @CheckForNull K key, @CheckForNull V value) { + static HashMultimap generateHashMultimap(@Nullable K key, @Nullable V value) { HashMultimap multimap = HashMultimap.create(); multimap.put(key, value); return multimap; @@ -877,7 +858,7 @@ static HashMultimap generateHashMultimap( @Generates static LinkedHashMultimap generateLinkedHashMultimap( - @CheckForNull K key, @CheckForNull V value) { + @Nullable K key, @Nullable V value) { LinkedHashMultimap multimap = LinkedHashMultimap.create(); multimap.put(key, value); return multimap; @@ -889,12 +870,12 @@ static ImmutableSetMultimap generateImmutableSetMultimap(K key, V v } @Generates - static BiMap generateBimap(@CheckForNull K key, @CheckForNull V value) { + static BiMap generateBimap(@Nullable K key, @Nullable V value) { return generateHashBiMap(key, value); } @Generates - static HashBiMap generateHashBiMap(@CheckForNull K key, @CheckForNull V value) { + static HashBiMap generateHashBiMap(@Nullable K key, @Nullable V value) { HashBiMap bimap = HashBiMap.create(); bimap.put(key, value); return bimap; @@ -906,14 +887,12 @@ static ImmutableBiMap generateImmutableBimap(K key, V value) { } @Generates - static Table generateTable( - @CheckForNull R row, @CheckForNull C column, @CheckForNull V value) { + static Table generateTable(R row, C column, V value) { return generateHashBasedTable(row, column, value); } @Generates - static HashBasedTable generateHashBasedTable( - @CheckForNull R row, @CheckForNull C column, @CheckForNull V value) { + static HashBasedTable generateHashBasedTable(R row, C column, V value) { HashBasedTable table = HashBasedTable.create(); table.put(row, column, value); return table; diff --git a/android/guava-testlib/src/com/google/common/testing/GcFinalization.java b/android/guava-testlib/src/com/google/common/testing/GcFinalization.java index 7d04077899a2..ec5a05a802d2 100644 --- a/android/guava-testlib/src/com/google/common/testing/GcFinalization.java +++ b/android/guava-testlib/src/com/google/common/testing/GcFinalization.java @@ -16,10 +16,11 @@ package com.google.common.testing; +import static java.lang.Math.max; import static java.util.concurrent.TimeUnit.SECONDS; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.DoNotMock; import com.google.j2objc.annotations.J2ObjCIncompatible; import java.lang.ref.WeakReference; @@ -29,6 +30,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.NullMarked; /** * Testing utilities relating to garbage collection finalization. @@ -103,9 +105,10 @@ * @author Martin Buchholz * @since 11.0 */ -@Beta @GwtIncompatible +@J2ktIncompatible @J2ObjCIncompatible // gc +@NullMarked public final class GcFinalization { private GcFinalization() {} @@ -125,7 +128,7 @@ private static long timeoutSeconds() { // // TODO(user): Consider scaling by number of mutator threads, // e.g. using Thread#activeCount() - return Math.max(10L, Runtime.getRuntime().totalMemory() / (32L * 1024L * 1024L)); + return max(10L, Runtime.getRuntime().totalMemory() / (32L * 1024L * 1024L)); } /** @@ -134,6 +137,7 @@ private static long timeoutSeconds() { * * @throws RuntimeException if timed out or interrupted while waiting */ + @SuppressWarnings("removal") // b/260137033 public static void awaitDone(Future future) { if (future.isDone()) { return; @@ -166,6 +170,7 @@ public static void awaitDone(Future future) { * * @throws RuntimeException if timed out or interrupted while waiting */ + @SuppressWarnings("removal") // b/260137033 public static void awaitDone(FinalizationPredicate predicate) { if (predicate.isDone()) { return; @@ -194,6 +199,7 @@ public static void awaitDone(FinalizationPredicate predicate) { * * @throws RuntimeException if timed out or interrupted while waiting */ + @SuppressWarnings("removal") // b/260137033 public static void await(CountDownLatch latch) { if (latch.getCount() == 0) { return; @@ -223,12 +229,14 @@ public static void await(CountDownLatch latch) { * separate method to make it somewhat more likely to be unreachable. */ private static void createUnreachableLatchFinalizer(CountDownLatch latch) { - new Object() { - @Override - protected void finalize() { - latch.countDown(); - } - }; + Object unused = + new Object() { + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 + @Override + protected void finalize() { + latch.countDown(); + } + }; } /** @@ -264,13 +272,7 @@ public interface FinalizationPredicate { * @throws RuntimeException if timed out or interrupted while waiting */ public static void awaitClear(WeakReference ref) { - awaitDone( - new FinalizationPredicate() { - @Override - public boolean isDone() { - return ref.get() == null; - } - }); + awaitDone(() -> ref.get() == null); } /** @@ -295,6 +297,7 @@ public boolean isDone() { * @throws RuntimeException if timed out or interrupted while waiting * @since 12.0 */ + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 public static void awaitFullGc() { CountDownLatch finalizerRan = new CountDownLatch(1); WeakReference ref = diff --git a/android/guava-testlib/src/com/google/common/testing/IgnoreJRERequirement.java b/android/guava-testlib/src/com/google/common/testing/IgnoreJRERequirement.java new file mode 100644 index 000000000000..2203162bf2b7 --- /dev/null +++ b/android/guava-testlib/src/com/google/common/testing/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.testing; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava-testlib/src/com/google/common/testing/NullPointerTester.java b/android/guava-testlib/src/com/google/common/testing/NullPointerTester.java index d1d1674bdc9c..178d98427af1 100644 --- a/android/guava-testlib/src/com/google/common/testing/NullPointerTester.java +++ b/android/guava-testlib/src/com/google/common/testing/NullPointerTester.java @@ -18,9 +18,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.stream; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Stream.concat; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.base.Objects; import com.google.common.collect.ClassToInstanceMap; @@ -33,8 +36,9 @@ import com.google.common.reflect.Parameter; import com.google.common.reflect.Reflection; import com.google.common.reflect.TypeToken; +import com.google.common.util.concurrent.AbstractFuture; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; @@ -45,15 +49,15 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentMap; -import javax.annotation.CheckForNull; import junit.framework.Assert; -import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A test utility that verifies that your methods and constructors throw {@link * NullPointerException} or {@link UnsupportedOperationException} whenever null is passed to a - * parameter that isn't annotated with an annotation with the simple name {@code Nullable}, {@code - * CheckForNull}, {@link NullableType}, or {@link NullableDecl}. + * parameter whose declaration or type isn't annotated with an annotation with the simple name + * {@code Nullable}, {@code CheckForNull}, {@code NullableType}, or {@code NullableDecl}. * *

    The tested methods and constructors are invoked -- each time with one parameter being null and * the rest not null -- and the test fails if no expected exception is thrown. {@code @@ -66,8 +70,9 @@ * @author Kevin Bourrillion * @since 10.0 */ -@Beta @GwtIncompatible +@J2ktIncompatible +@NullMarked public final class NullPointerTester { private final ClassToInstanceMap defaults = MutableClassToInstanceMap.create(); @@ -75,10 +80,53 @@ public final class NullPointerTester { private ExceptionTypePolicy policy = ExceptionTypePolicy.NPE_OR_UOE; + /* + * Requiring desugaring for guava-*testlib* is likely safe, at least for the reflection-based + * NullPointerTester. But if you are a user who is reading this because this change caused you + * trouble, please let us know: https://github.com/google/guava/issues/new + */ + @IgnoreJRERequirement + public NullPointerTester() { + try { + /* + * Converter.apply has a non-nullable parameter type but doesn't throw for null arguments. For + * more information, see the comments in that class. + * + * We already know that that's how it behaves, and subclasses of Converter can't change that + * behavior. So there's no sense in making all subclass authors exclude the method from any + * NullPointerTester tests that they have. + */ + ignoredMembers.add(Converter.class.getMethod("apply", Object.class)); + } catch (NoSuchMethodException shouldBeImpossible) { + // Fine: If it doesn't exist, then there's no chance that we're going to be asked to test it. + } + + /* + * These methods "should" call checkNotNull. However, I'm wary of accidentally introducing + * anything that might slow down execution on such a hot path. Given that the methods are only + * package-private, I feel OK with just not testing them for NPE. + * + * Note that testing casValue is particularly dangerous because it uses Unsafe under some + * versions of Java, and apparently Unsafe can cause SIGSEGV instead of NPE—almost as if it's + * not safe. + */ + concat( + stream(AbstractFuture.class.getDeclaredMethods()), + stream(requireNonNull(AbstractFuture.class.getSuperclass()).getDeclaredMethods())) + .filter( + m -> + m.getName().equals("getDoneValue") + || m.getName().equals("casValue") + || m.getName().equals("casListeners") + || m.getName().equals("gasListeners")) + .forEach(ignoredMembers::add); + } + /** * Sets a default value that can be used for any parameter of type {@code type}. Returns this * object. */ + @CanIgnoreReturnValue public NullPointerTester setDefault(Class type, T value) { defaults.putInstance(type, checkNotNull(value)); return this; @@ -89,6 +137,7 @@ public NullPointerTester setDefault(Class type, T value) { * * @since 13.0 */ + @CanIgnoreReturnValue public NullPointerTester ignore(Method method) { ignoredMembers.add(checkNotNull(method)); return this; @@ -99,6 +148,7 @@ public NullPointerTester ignore(Method method) { * * @since 22.0 */ + @CanIgnoreReturnValue public NullPointerTester ignore(Constructor constructor) { ignoredMembers.add(checkNotNull(constructor)); return this; @@ -176,7 +226,7 @@ public void testAllPublicInstanceMethods(Object instance) { * * @param instance the instance to invoke {@code method} on, or null if {@code method} is static */ - public void testMethod(@CheckForNull Object instance, Method method) { + public void testMethod(@Nullable Object instance, Method method) { Class[] types = method.getParameterTypes(); for (int nullIndex = 0; nullIndex < types.length; nullIndex++) { testMethodParameter(instance, method, nullIndex); @@ -207,8 +257,7 @@ public void testConstructor(Constructor ctor) { * * @param instance the instance to invoke {@code method} on, or null if {@code method} is static */ - public void testMethodParameter( - @CheckForNull final Object instance, final Method method, int paramIndex) { + public void testMethodParameter(@Nullable Object instance, Method method, int paramIndex) { method.setAccessible(true); testParameter(instance, invokable(instance, method), paramIndex, method.getDeclaringClass()); } @@ -306,7 +355,7 @@ private static final class Signature { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Signature) { Signature that = (Signature) obj; return name.equals(that.name) && parameterTypes.equals(that.parameterTypes); @@ -329,11 +378,18 @@ public int hashCode() { * static */ private void testParameter( - Object instance, Invokable invokable, int paramIndex, Class testedClass) { + @Nullable Object instance, Invokable invokable, int paramIndex, Class testedClass) { + /* + * com.google.common is starting to rely on type-use annotations, which aren't visible under + * Android VMs and in open-source guava-android. So we skip testing there. + */ + if (Reflection.getPackageName(testedClass).startsWith("com.google.common")) { + return; + } if (isPrimitiveOrNullable(invokable.getParameters().get(paramIndex))) { return; // there's nothing to test } - Object[] params = buildParamList(invokable, paramIndex); + @Nullable Object[] params = buildParamList(invokable, paramIndex); try { @SuppressWarnings("unchecked") // We'll get a runtime exception if the type is wrong. Invokable unsafe = (Invokable) invokable; @@ -351,27 +407,26 @@ private void testParameter( if (policy.isExpectedType(cause)) { return; } - AssertionFailedError error = - new AssertionFailedError( - String.format( - "wrong exception thrown from %s when passing null to %s parameter at index %s.%n" - + "Full parameters: %s%n" - + "Actual exception message: %s", - invokable, - invokable.getParameters().get(paramIndex).getType(), - paramIndex, - Arrays.toString(params), - cause)); - error.initCause(cause); - throw error; + throw new AssertionError( + String.format( + "wrong exception thrown from %s when passing null to %s parameter at index %s.%n" + + "Full parameters: %s%n" + + "Actual exception message: %s", + invokable, + invokable.getParameters().get(paramIndex).getType(), + paramIndex, + Arrays.toString(params), + cause), + cause); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } - private Object[] buildParamList(Invokable invokable, int indexOfParamToSetToNull) { + private @Nullable Object[] buildParamList( + Invokable invokable, int indexOfParamToSetToNull) { ImmutableList params = invokable.getParameters(); - Object[] args = new Object[params.size()]; + @Nullable Object[] args = new Object[params.size()]; for (int i = 0; i < args.length; i++) { Parameter param = params.get(i); @@ -387,7 +442,7 @@ private Object[] buildParamList(Invokable invokable, int indexOfParamToSet return args; } - private T getDefaultValue(TypeToken type) { + private @Nullable T getDefaultValue(TypeToken type) { // We assume that all defaults are generics-safe, even if they aren't, // we take the risk. @SuppressWarnings("unchecked") @@ -426,7 +481,7 @@ private T getDefaultValue(TypeToken type) { } private Converter defaultConverter( - final TypeToken convertFromType, final TypeToken convertToType) { + TypeToken convertFromType, TypeToken convertToType) { return new Converter() { @Override protected T doForward(F a) { @@ -452,16 +507,16 @@ private static TypeToken getFirstTypeParameter(Type type) { } } - private T newDefaultReturningProxy(final TypeToken type) { + private T newDefaultReturningProxy(TypeToken type) { return new DummyProxy() { @Override - R dummyReturnValue(TypeToken returnType) { + @Nullable R dummyReturnValue(TypeToken returnType) { return getDefaultValue(returnType); } }.newProxy(type); } - private static Invokable invokable(@CheckForNull Object instance, Method method) { + private static Invokable invokable(@Nullable Object instance, Method method) { if (instance == null) { return Invokable.from(method); } else { @@ -474,11 +529,18 @@ static boolean isPrimitiveOrNullable(Parameter param) { } private static final ImmutableSet NULLABLE_ANNOTATION_SIMPLE_NAMES = - ImmutableSet.of( - "CheckForNull", "Nullable", "NullableDecl", "NullableType", "ParametricNullness"); + ImmutableSet.of("CheckForNull", "Nullable", "NullableDecl", "NullableType"); + + static boolean isNullable(Invokable invokable) { + return NULLNESS_ANNOTATION_READER.isNullable(invokable); + } + + static boolean isNullable(Parameter param) { + return NULLNESS_ANNOTATION_READER.isNullable(param); + } - static boolean isNullable(AnnotatedElement e) { - for (Annotation annotation : e.getAnnotations()) { + private static boolean containsNullable(Annotation[] annotations) { + for (Annotation annotation : annotations) { if (NULLABLE_ANNOTATION_SIMPLE_NAMES.contains(annotation.annotationType().getSimpleName())) { return true; } @@ -549,4 +611,62 @@ public boolean isExpectedType(Throwable cause) { public abstract boolean isExpectedType(Throwable cause); } + + private static boolean annotatedTypeExists() { + try { + Class.forName("java.lang.reflect.AnnotatedType"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + + private static final NullnessAnnotationReader NULLNESS_ANNOTATION_READER = + annotatedTypeExists() + ? NullnessAnnotationReader.FROM_DECLARATION_AND_TYPE_USE_ANNOTATIONS + : NullnessAnnotationReader.FROM_DECLARATION_ANNOTATIONS_ONLY; + + /** + * Looks for declaration nullness annotations and, if supported, type-use nullness annotations. + * + *

    Under Android VMs, the methods for retrieving type-use annotations don't exist. This means + * that {@link NullPointerTester} may misbehave under Android when used on classes that rely on + * type-use annotations. + * + *

    Under j2objc, the necessary APIs exist, but some (perhaps all) return stub values, like + * empty arrays. Presumably {@link NullPointerTester} could likewise misbehave under j2objc, but I + * don't know that anyone uses it there, anyway. + */ + private enum NullnessAnnotationReader { + @SuppressWarnings("Java7ApiChecker") + FROM_DECLARATION_AND_TYPE_USE_ANNOTATIONS { + @Override + boolean isNullable(Invokable invokable) { + return FROM_DECLARATION_ANNOTATIONS_ONLY.isNullable(invokable) + ; + // TODO(cpovirk): Should we also check isNullableTypeVariable? + } + + @Override + boolean isNullable(Parameter param) { + return FROM_DECLARATION_ANNOTATIONS_ONLY.isNullable(param) + ; + } + }, + FROM_DECLARATION_ANNOTATIONS_ONLY { + @Override + boolean isNullable(Invokable invokable) { + return containsNullable(invokable.getAnnotations()); + } + + @Override + boolean isNullable(Parameter param) { + return containsNullable(param.getAnnotations()); + } + }; + + abstract boolean isNullable(Invokable invokable); + + abstract boolean isNullable(Parameter param); + } } diff --git a/android/guava-testlib/src/com/google/common/testing/Platform.java b/android/guava-testlib/src/com/google/common/testing/Platform.java index b107966ec97a..0352b5aa9ee5 100644 --- a/android/guava-testlib/src/com/google/common/testing/Platform.java +++ b/android/guava-testlib/src/com/google/common/testing/Platform.java @@ -17,6 +17,7 @@ package com.google.common.testing; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import java.io.ByteArrayInputStream; @@ -24,6 +25,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import org.jspecify.annotations.NullMarked; /** * Methods factored out so that they can be emulated differently in GWT. @@ -31,6 +33,7 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) +@NullMarked final class Platform { /** Serializes and deserializes the specified object. */ @SuppressWarnings("unchecked") @@ -41,7 +44,7 @@ static T reserialize(T object) { ObjectOutputStream out = new ObjectOutputStream(bytes); out.writeObject(object); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())); - return (T) in.readObject(); + return (T) requireNonNull(in.readObject()); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } diff --git a/android/guava-testlib/src/com/google/common/testing/RelationshipTester.java b/android/guava-testlib/src/com/google/common/testing/RelationshipTester.java index d5b9b6a18693..fdfa23242daf 100644 --- a/android/guava-testlib/src/com/google/common/testing/RelationshipTester.java +++ b/android/guava-testlib/src/com/google/common/testing/RelationshipTester.java @@ -22,8 +22,10 @@ import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; /** * Implementation helper for {@link EqualsTester} and {@link EquivalenceTester} that tests for @@ -32,6 +34,7 @@ * @author Gregory Kick */ @GwtCompatible +@NullMarked final class RelationshipTester { static class ItemReporter { @@ -66,6 +69,7 @@ String reportItem(Item item) { } // TODO(cpovirk): should we reject null items, since the tests already check null automatically? + @CanIgnoreReturnValue public RelationshipTester addRelatedGroup(Iterable group) { groups.add(ImmutableList.copyOf(group)); return this; diff --git a/android/guava-testlib/src/com/google/common/testing/SerializableTester.java b/android/guava-testlib/src/com/google/common/testing/SerializableTester.java index 62980764d65f..7be2f47586e3 100644 --- a/android/guava-testlib/src/com/google/common/testing/SerializableTester.java +++ b/android/guava-testlib/src/com/google/common/testing/SerializableTester.java @@ -16,10 +16,11 @@ package com.google.common.testing; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import junit.framework.Assert; import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; /** * Tests serialization and deserialization of an object, optionally asserting that the resulting @@ -32,8 +33,8 @@ * @author Mike Bostock * @since 10.0 */ -@Beta @GwtCompatible // but no-op! +@NullMarked public final class SerializableTester { private SerializableTester() {} @@ -52,7 +53,7 @@ private SerializableTester() {} * @throws RuntimeException if the specified object was not successfully serialized or * deserialized */ - @SuppressWarnings("unchecked") + @CanIgnoreReturnValue public static T reserialize(T object) { return Platform.reserialize(object); } @@ -84,6 +85,7 @@ public static T reserialize(T object) { * @throws AssertionFailedError if the re-serialized object is not equal to the original object, * or if the hashcodes are different. */ + @CanIgnoreReturnValue public static T reserializeAndAssert(T object) { T copy = reserialize(object); new EqualsTester().addEqualityGroup(object, copy).testEquals(); diff --git a/android/guava-testlib/src/com/google/common/testing/SloppyTearDown.java b/android/guava-testlib/src/com/google/common/testing/SloppyTearDown.java index 95ff34e33dda..1790499ca801 100644 --- a/android/guava-testlib/src/com/google/common/testing/SloppyTearDown.java +++ b/android/guava-testlib/src/com/google/common/testing/SloppyTearDown.java @@ -16,10 +16,10 @@ package com.google.common.testing; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.NullMarked; /** * Simple utility for when you want to create a {@link TearDown} that may throw an exception but @@ -30,8 +30,8 @@ * @author Luiz-Otavio Zorzella * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public abstract class SloppyTearDown implements TearDown { private static final Logger logger = Logger.getLogger(SloppyTearDown.class.getName()); diff --git a/android/guava-testlib/src/com/google/common/testing/TearDown.java b/android/guava-testlib/src/com/google/common/testing/TearDown.java index 50485348f99f..dbd04e2090ca 100644 --- a/android/guava-testlib/src/com/google/common/testing/TearDown.java +++ b/android/guava-testlib/src/com/google/common/testing/TearDown.java @@ -16,8 +16,8 @@ package com.google.common.testing; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullMarked; /** * An object that can perform a {@link #tearDown} operation. @@ -25,8 +25,8 @@ * @author Kevin Bourrillion * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public interface TearDown { /** * Performs a single tear-down operation. See test-libraries-for-java's {@code diff --git a/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java b/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java index bad1f1997867..ed514d2d4b1b 100644 --- a/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java +++ b/android/guava-testlib/src/com/google/common/testing/TearDownAccepter.java @@ -16,9 +16,9 @@ package com.google.common.testing; -import com.google.common.annotations.Beta; -import com.google.errorprone.annotations.DoNotMock; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.DoNotMock; +import org.jspecify.annotations.NullMarked; /** * Any object which can accept registrations of {@link TearDown} instances. @@ -26,9 +26,9 @@ * @author Kevin Bourrillion * @since 10.0 */ -@Beta @DoNotMock("Implement with a lambda") @GwtCompatible +@NullMarked public interface TearDownAccepter { /** * Registers a TearDown implementor which will be run after the test proper. diff --git a/android/guava-testlib/src/com/google/common/testing/TearDownStack.java b/android/guava-testlib/src/com/google/common/testing/TearDownStack.java index bab025a61fc2..4b6729011e2d 100644 --- a/android/guava-testlib/src/com/google/common/testing/TearDownStack.java +++ b/android/guava-testlib/src/com/google/common/testing/TearDownStack.java @@ -18,15 +18,17 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.google.errorprone.annotations.concurrent.GuardedBy; +import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; +import java.util.Deque; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import org.jspecify.annotations.NullMarked; /** * A {@code TearDownStack} contains a stack of {@link TearDown} instances. @@ -36,13 +38,15 @@ * @author Kevin Bourrillion * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public class TearDownStack implements TearDownAccepter { private static final Logger logger = Logger.getLogger(TearDownStack.class.getName()); - @GuardedBy("stack") - final LinkedList stack = new LinkedList<>(); + @VisibleForTesting final Object lock = new Object(); + + @GuardedBy("lock") + final Deque stack = new ArrayDeque<>(); private final boolean suppressThrows; @@ -56,7 +60,7 @@ public TearDownStack(boolean suppressThrows) { @Override public final void addTearDown(TearDown tearDown) { - synchronized (stack) { + synchronized (lock) { stack.addFirst(checkNotNull(tearDown)); } } @@ -65,7 +69,7 @@ public final void addTearDown(TearDown tearDown) { public final void runTearDown() { List exceptions = new ArrayList<>(); List stackCopy; - synchronized (stack) { + synchronized (lock) { stackCopy = Lists.newArrayList(stack); stack.clear(); } diff --git a/android/guava-testlib/src/com/google/common/testing/TestLogHandler.java b/android/guava-testlib/src/com/google/common/testing/TestLogHandler.java index 467edd44518f..f21a49d77695 100644 --- a/android/guava-testlib/src/com/google/common/testing/TestLogHandler.java +++ b/android/guava-testlib/src/com/google/common/testing/TestLogHandler.java @@ -16,14 +16,15 @@ package com.google.common.testing; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.concurrent.GuardedBy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Handler; import java.util.logging.LogRecord; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests may use this to intercept messages that are logged by the code under test. Example: @@ -52,16 +53,23 @@ * @author Kevin Bourrillion * @since 10.0 */ -@Beta @GwtCompatible +@NullMarked public class TestLogHandler extends Handler { + private final Object lock = new Object(); + /** We will keep a private list of all logged records */ + @GuardedBy("lock") private final List list = new ArrayList<>(); /** Adds the most recently logged record to our list. */ @Override - public synchronized void publish(@CheckForNull LogRecord record) { - list.add(record); + public void publish(@Nullable LogRecord record) { + synchronized (lock) { + if (record != null) { + list.add(record); + } + } } @Override @@ -70,8 +78,10 @@ public void flush() {} @Override public void close() {} - public synchronized void clear() { - list.clear(); + public void clear() { + synchronized (lock) { + list.clear(); + } } /** Returns a snapshot of the logged records. */ @@ -82,8 +92,10 @@ public synchronized void clear() { * TODO(cpovirk): consider renaming this method to reflect that it takes a snapshot (and/or return * an ImmutableList) */ - public synchronized List getStoredLogRecords() { - List result = new ArrayList<>(list); - return Collections.unmodifiableList(result); + public List getStoredLogRecords() { + synchronized (lock) { + List result = new ArrayList<>(list); + return Collections.unmodifiableList(result); + } } } diff --git a/android/guava-testlib/src/com/google/common/testing/package-info.java b/android/guava-testlib/src/com/google/common/testing/package-info.java index e6762f98c693..138d074788a6 100644 --- a/android/guava-testlib/src/com/google/common/testing/package-info.java +++ b/android/guava-testlib/src/com/google/common/testing/package-info.java @@ -15,8 +15,12 @@ */ /** - * This package contains testing utilities. It is a part of the open-source Guava library. + * Testing utilities. This package is a part of the open-source Guava library. */ -@javax.annotation.ParametersAreNonnullByDefault +@CheckReturnValue +@NullMarked package com.google.common.testing; + +import com.google.errorprone.annotations.CheckReturnValue; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractListenableFutureTest.java b/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractListenableFutureTest.java index 36fa16cd70c2..2baae7e9eb71 100644 --- a/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractListenableFutureTest.java +++ b/android/guava-testlib/src/com/google/common/util/concurrent/testing/AbstractListenableFutureTest.java @@ -16,7 +16,10 @@ package com.google.common.util.concurrent.testing; -import com.google.common.annotations.Beta; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; + import com.google.common.annotations.GwtIncompatible; import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.CancellationException; @@ -28,6 +31,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.Nullable; /** * Abstract test case parent for anything implementing {@link ListenableFuture}. Tests the two get @@ -36,7 +40,6 @@ * @author Sven Mawson * @since 10.0 */ -@Beta @GwtIncompatible public abstract class AbstractListenableFutureTest extends TestCase { @@ -60,7 +63,7 @@ protected void tearDown() throws Exception { /** Constructs a listenable future with a value available after the latch has counted down. */ protected abstract ListenableFuture createListenableFuture( - V value, Exception except, CountDownLatch waitOn); + V value, @Nullable Exception except, CountDownLatch waitOn); /** Tests that the {@link Future#get()} method blocks until a value is available. */ public void testGetBlocksUntilValueAvailable() throws Throwable { @@ -68,29 +71,17 @@ public void testGetBlocksUntilValueAvailable() throws Throwable { assertFalse(future.isDone()); assertFalse(future.isCancelled()); - CountDownLatch successLatch = new CountDownLatch(1); - Throwable[] badness = new Throwable[1]; + ExecutorService executor = Executors.newSingleThreadExecutor(); - // Wait on the future in a separate thread. - new Thread( - () -> { - try { - assertSame(Boolean.TRUE, future.get()); - successLatch.countDown(); - } catch (Throwable t) { - t.printStackTrace(); - badness[0] = t; - } - }) - .start(); - - // Release the future value. - latch.countDown(); + try { + Future getResult = executor.submit(() -> future.get()); - assertTrue(successLatch.await(10, TimeUnit.SECONDS)); + // Release the future value. + latch.countDown(); - if (badness[0] != null) { - throw badness[0]; + assertTrue(getResult.get(10, SECONDS)); + } finally { + executor.shutdownNow(); } assertTrue(future.isDone()); @@ -102,7 +93,7 @@ public void testTimeoutOnGetWorksCorrectly() throws InterruptedException, Execut // The task thread waits for the latch, so we expect a timeout here. try { - future.get(20, TimeUnit.MILLISECONDS); + future.get(20, MILLISECONDS); fail("Should have timed out trying to get the value."); } catch (TimeoutException expected) { } finally { @@ -125,13 +116,8 @@ public void testCanceledFutureThrowsCancellation() throws Exception { // Run cancellation in a separate thread as an extra thread-safety test. new Thread( () -> { - try { - future.get(); - } catch (CancellationException expected) { - successLatch.countDown(); - } catch (Exception ignored) { - // All other errors are ignored, we expect a cancellation. - } + assertThrows(CancellationException.class, future::get); + successLatch.countDown(); }) .start(); @@ -143,7 +129,7 @@ public void testCanceledFutureThrowsCancellation() throws Exception { assertTrue(future.isDone()); assertTrue(future.isCancelled()); - assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS)); + assertTrue(successLatch.await(200, MILLISECONDS)); latch.countDown(); } @@ -158,13 +144,8 @@ public void testListenersNotifiedOnError() throws Exception { new Thread( () -> { - try { - future.get(); - } catch (CancellationException expected) { - successLatch.countDown(); - } catch (Exception ignored) { - // No success latch count down. - } + assertThrows(CancellationException.class, future::get); + successLatch.countDown(); }) .start(); @@ -173,13 +154,13 @@ public void testListenersNotifiedOnError() throws Exception { assertTrue(future.isCancelled()); assertTrue(future.isDone()); - assertTrue(successLatch.await(200, TimeUnit.MILLISECONDS)); - assertTrue(listenerLatch.await(200, TimeUnit.MILLISECONDS)); + assertTrue(successLatch.await(200, MILLISECONDS)); + assertTrue(listenerLatch.await(200, MILLISECONDS)); latch.countDown(); exec.shutdown(); - exec.awaitTermination(100, TimeUnit.MILLISECONDS); + exec.awaitTermination(100, MILLISECONDS); } /** @@ -209,9 +190,9 @@ public void testAllListenersCompleteSuccessfully() assertSame(Boolean.TRUE, future.get()); // Wait for the listener latch to complete. - listenerLatch.await(500, TimeUnit.MILLISECONDS); + listenerLatch.await(500, MILLISECONDS); exec.shutdown(); - exec.awaitTermination(500, TimeUnit.MILLISECONDS); + exec.awaitTermination(500, MILLISECONDS); } } diff --git a/android/guava-testlib/src/com/google/common/util/concurrent/testing/MockFutureListener.java b/android/guava-testlib/src/com/google/common/util/concurrent/testing/MockFutureListener.java index fc3ed21f1ba9..87eb73aa9163 100644 --- a/android/guava-testlib/src/com/google/common/util/concurrent/testing/MockFutureListener.java +++ b/android/guava-testlib/src/com/google/common/util/concurrent/testing/MockFutureListener.java @@ -17,13 +17,12 @@ package com.google.common.util.concurrent.testing; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import junit.framework.Assert; /** @@ -32,7 +31,6 @@ * @author Nishant Thakkar * @since 10.0 */ -@Beta @GwtIncompatible public class MockFutureListener implements Runnable { private final CountDownLatch countDownLatch; @@ -59,7 +57,7 @@ public void run() { */ public void assertSuccess(Object expectedData) throws Throwable { // Verify that the listener executed in a reasonable amount of time. - Assert.assertTrue(countDownLatch.await(1L, TimeUnit.SECONDS)); + Assert.assertTrue(countDownLatch.await(1L, SECONDS)); try { Assert.assertEquals(expectedData, future.get()); @@ -75,7 +73,7 @@ public void assertSuccess(Object expectedData) throws Throwable { */ public void assertException(Throwable expectedCause) throws Exception { // Verify that the listener executed in a reasonable amount of time. - Assert.assertTrue(countDownLatch.await(1L, TimeUnit.SECONDS)); + Assert.assertTrue(countDownLatch.await(1L, SECONDS)); try { future.get(); @@ -88,6 +86,6 @@ public void assertException(Throwable expectedCause) throws Exception { public void assertTimeout() throws Exception { // Verify that the listener does not get called in a reasonable amount of // time. - Assert.assertFalse(countDownLatch.await(1L, TimeUnit.SECONDS)); + Assert.assertFalse(countDownLatch.await(1L, SECONDS)); } } diff --git a/android/guava-testlib/src/com/google/common/util/concurrent/testing/SameThreadScheduledExecutorService.java b/android/guava-testlib/src/com/google/common/util/concurrent/testing/SameThreadScheduledExecutorService.java index 25b7ef791c00..4891e9a29474 100644 --- a/android/guava-testlib/src/com/google/common/util/concurrent/testing/SameThreadScheduledExecutorService.java +++ b/android/guava-testlib/src/com/google/common/util/concurrent/testing/SameThreadScheduledExecutorService.java @@ -31,6 +31,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -135,7 +136,7 @@ public void execute(Runnable command) { public ListenableScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { Preconditions.checkNotNull(command, "command must not be null"); Preconditions.checkNotNull(unit, "unit must not be null!"); - return schedule(java.util.concurrent.Executors.callable(command), delay, unit); + return schedule(Executors.callable(command), delay, unit); } @Override @@ -147,11 +148,9 @@ public ListenableScheduledFuture schedule( return new ImmediateScheduledFuture<>(delegateFuture); } - private static class ImmediateScheduledFuture extends SimpleForwardingListenableFuture + private static final class ImmediateScheduledFuture extends SimpleForwardingListenableFuture implements ListenableScheduledFuture { - private ExecutionException exception; - - protected ImmediateScheduledFuture(ListenableFuture future) { + ImmediateScheduledFuture(ListenableFuture future) { super(future); } diff --git a/android/guava-testlib/src/com/google/common/util/concurrent/testing/TestingExecutors.java b/android/guava-testlib/src/com/google/common/util/concurrent/testing/TestingExecutors.java index 261d2844bd33..068122588312 100644 --- a/android/guava-testlib/src/com/google/common/util/concurrent/testing/TestingExecutors.java +++ b/android/guava-testlib/src/com/google/common/util/concurrent/testing/TestingExecutors.java @@ -16,10 +16,10 @@ package com.google.common.util.concurrent.testing; -import com.google.common.annotations.Beta; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Longs; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.AbstractListeningExecutorService; import com.google.common.util.concurrent.ListenableScheduledFuture; @@ -28,7 +28,10 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; import java.util.concurrent.TimeUnit; /** @@ -37,7 +40,6 @@ * @author Chris Nokleberg * @since 14.0 */ -@Beta @GwtIncompatible public final class TestingExecutors { private TestingExecutors() {} @@ -86,9 +88,9 @@ public static ListeningScheduledExecutorService noOpScheduledExecutor() { * invokeAll/invokeAny} throwing RejectedExecutionException, although a subset of the tasks may * already have been executed. * - * @since 15.0 + * @since 32.0.0 (taking the place of a method with a different return type from 15.0) */ - public static SameThreadScheduledExecutorService sameThreadScheduledExecutor() { + public static ListeningScheduledExecutorService sameThreadScheduledExecutor() { return new SameThreadScheduledExecutorService(); } @@ -163,7 +165,7 @@ public long getDelay(TimeUnit unit) { @Override public int compareTo(Delayed other) { - return Longs.compare(getDelay(TimeUnit.NANOSECONDS), other.getDelay(TimeUnit.NANOSECONDS)); + return Long.compare(getDelay(NANOSECONDS), other.getDelay(NANOSECONDS)); } } } diff --git a/android/guava/src/com/google/common/base/ElementTypesAreNonnullByDefault.java b/android/guava-testlib/test/com/google/common/collect/testing/AndroidIncompatible.java similarity index 53% rename from android/guava/src/com/google/common/base/ElementTypesAreNonnullByDefault.java rename to android/guava-testlib/test/com/google/common/collect/testing/AndroidIncompatible.java index 890e3a36062a..bbd60d7a2206 100644 --- a/android/guava/src/com/google/common/base/ElementTypesAreNonnullByDefault.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/AndroidIncompatible.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Guava Authors + * Copyright (C) 2015 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,28 +14,28 @@ * limitations under the License. */ -package com.google.common.base; +package com.google.common.collect.testing; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; /** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. + * Signifies that a test should not be run under Android. This annotation is respected only by our + * Google-internal Android suite generators. Note that those generators also suppress any test + * annotated with MediumTest or LargeTest. + * + *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the + * documentation on another copy of this annotation}. */ +@Retention(CLASS) +@Target({ANNOTATION_TYPE, CONSTRUCTOR, FIELD, METHOD, TYPE}) @GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} +@interface AndroidIncompatible {} diff --git a/android/guava-testlib/test/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilderTest.java b/android/guava-testlib/test/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilderTest.java index dc0fe37b4471..682fd909d4ad 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilderTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/FeatureSpecificTestSuiteBuilderTest.java @@ -22,37 +22,23 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; -import org.junit.Ignore; -/** @author Max Ross */ +/** + * @author Max Ross + */ +@AndroidIncompatible // test-suite builders public class FeatureSpecificTestSuiteBuilderTest extends TestCase { - - static boolean testWasRun; - - @Override - protected void setUp() throws Exception { - super.setUp(); - testWasRun = false; - } - - @Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. - public static final class MyAbstractTester extends AbstractTester { - public void testNothing() { - testWasRun = true; - } - } - private static final class MyTestSuiteBuilder extends FeatureSpecificTestSuiteBuilder { - + @SuppressWarnings("rawtypes") // class literals @Override protected List> getTesters() { - return Collections.>singletonList(MyAbstractTester.class); + return Collections.>singletonList(MyTester.class); } } public void testLifecycle() { - final boolean setUp[] = {false}; + boolean[] setUp = {false}; Runnable setUpRunnable = new Runnable() { @Override @@ -61,7 +47,7 @@ public void run() { } }; - final boolean tearDown[] = {false}; + boolean[] tearDown = {false}; Runnable tearDownRunnable = new Runnable() { @Override @@ -80,8 +66,9 @@ public void run() { .withTearDown(tearDownRunnable) .createTestSuite(); TestResult result = new TestResult(); + int timesMyTesterWasRunBeforeSuite = MyTester.timesTestClassWasRun; test.run(result); - assertTrue(testWasRun); + assertEquals(timesMyTesterWasRunBeforeSuite + 1, MyTester.timesTestClassWasRun); assertTrue(setUp[0]); assertTrue(tearDown[0]); } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/HelpersTest.java b/android/guava-testlib/test/com/google/common/collect/testing/HelpersTest.java index 41f4bfb27501..b70a36d6abd6 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/HelpersTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/HelpersTest.java @@ -17,12 +17,18 @@ package com.google.common.collect.testing; import static com.google.common.collect.testing.Helpers.NullsBeforeB; +import static com.google.common.collect.testing.Helpers.assertContains; +import static com.google.common.collect.testing.Helpers.assertContainsAllOf; +import static com.google.common.collect.testing.Helpers.assertContentsInOrder; +import static com.google.common.collect.testing.Helpers.assertEmpty; +import static com.google.common.collect.testing.Helpers.assertEqualInOrder; import static com.google.common.collect.testing.Helpers.testComparator; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -43,27 +49,27 @@ public void testNullsBeforeB() { public void testIsEmpty_iterable() { List list = new ArrayList<>(); - Helpers.assertEmpty(list); - Helpers.assertEmpty( + assertEmpty(list); + assertEmpty( new Iterable() { @Override public Iterator iterator() { - return Collections.emptyList().iterator(); + return emptyList().iterator(); } }); list.add("a"); try { - Helpers.assertEmpty(list); + assertEmpty(list); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertEmpty( + assertEmpty( new Iterable() { @Override public Iterator iterator() { - return Collections.singleton("a").iterator(); + return singleton("a").iterator(); } }); throw new Error(); @@ -73,110 +79,110 @@ public Iterator iterator() { public void testIsEmpty_map() { Map map = new HashMap<>(); - Helpers.assertEmpty(map); + assertEmpty(map); map.put("a", "b"); try { - Helpers.assertEmpty(map); + assertEmpty(map); throw new Error(); } catch (AssertionFailedError expected) { } } public void testAssertEqualInOrder() { - List list = Arrays.asList("a", "b", "c"); - Helpers.assertEqualInOrder(list, list); + List list = asList("a", "b", "c"); + assertEqualInOrder(list, list); - List fewer = Arrays.asList("a", "b"); + List fewer = asList("a", "b"); try { - Helpers.assertEqualInOrder(list, fewer); + assertEqualInOrder(list, fewer); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertEqualInOrder(fewer, list); + assertEqualInOrder(fewer, list); throw new Error(); } catch (AssertionFailedError expected) { } - List differentOrder = Arrays.asList("a", "c", "b"); + List differentOrder = asList("a", "c", "b"); try { - Helpers.assertEqualInOrder(list, differentOrder); + assertEqualInOrder(list, differentOrder); throw new Error(); } catch (AssertionFailedError expected) { } - List differentContents = Arrays.asList("a", "b", "C"); + List differentContents = asList("a", "b", "C"); try { - Helpers.assertEqualInOrder(list, differentContents); + assertEqualInOrder(list, differentContents); throw new Error(); } catch (AssertionFailedError expected) { } } public void testAssertContentsInOrder() { - List list = Arrays.asList("a", "b", "c"); - Helpers.assertContentsInOrder(list, "a", "b", "c"); + List list = asList("a", "b", "c"); + assertContentsInOrder(list, "a", "b", "c"); try { - Helpers.assertContentsInOrder(list, "a", "b"); + assertContentsInOrder(list, "a", "b"); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertContentsInOrder(list, "a", "b", "c", "d"); + assertContentsInOrder(list, "a", "b", "c", "d"); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertContentsInOrder(list, "a", "c", "b"); + assertContentsInOrder(list, "a", "c", "b"); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertContentsInOrder(list, "a", "B", "c"); + assertContentsInOrder(list, "a", "B", "c"); throw new Error(); } catch (AssertionFailedError expected) { } } public void testAssertContains() { - List list = Arrays.asList("a", "b"); - Helpers.assertContains(list, "a"); - Helpers.assertContains(list, "b"); + List list = asList("a", "b"); + assertContains(list, "a"); + assertContains(list, "b"); try { - Helpers.assertContains(list, "c"); + assertContains(list, "c"); throw new Error(); } catch (AssertionFailedError expected) { } } public void testAssertContainsAllOf() { - List list = Arrays.asList("a", "a", "b", "c"); - Helpers.assertContainsAllOf(list, "a"); - Helpers.assertContainsAllOf(list, "a", "a"); - Helpers.assertContainsAllOf(list, "a", "b", "c"); - Helpers.assertContainsAllOf(list, "a", "b", "c", "a"); + List list = asList("a", "a", "b", "c"); + assertContainsAllOf(list, "a"); + assertContainsAllOf(list, "a", "a"); + assertContainsAllOf(list, "a", "b", "c"); + assertContainsAllOf(list, "a", "b", "c", "a"); try { - Helpers.assertContainsAllOf(list, "d"); + assertContainsAllOf(list, "d"); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertContainsAllOf(list, "a", "b", "c", "d"); + assertContainsAllOf(list, "a", "b", "c", "d"); throw new Error(); } catch (AssertionFailedError expected) { } try { - Helpers.assertContainsAllOf(list, "a", "a", "a"); + assertContainsAllOf(list, "a", "a", "a"); throw new Error(); } catch (AssertionFailedError expected) { } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/IteratorTesterTest.java b/android/guava-testlib/test/com/google/common/collect/testing/IteratorTesterTest.java index 283f51efe66a..d7df4d3e117e 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/IteratorTesterTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/IteratorTesterTest.java @@ -26,7 +26,6 @@ import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; -import junit.framework.AssertionFailedError; import junit.framework.TestCase; /** @@ -104,13 +103,13 @@ protected Iterator newTargetIterator() { * to remove() will incorrectly throw an IllegalStateException, instead of removing the last * element returned. * - *

    See Sun bug 6529795 + *

    See JDK-6529795 */ - static class IteratorWithSunJavaBug6529795 implements Iterator { + static class IteratorWithJdkBug6529795 implements Iterator { Iterator iterator; boolean nextThrewException; - IteratorWithSunJavaBug6529795(Iterator iterator) { + IteratorWithJdkBug6529795(Iterator iterator) { this.iterator = iterator; } @@ -138,7 +137,7 @@ public void remove() { } } - public void testCanCatchSunJavaBug6529795InTargetIterator() { + public void testCanCatchJdkBug6529795InTargetIterator() { try { /* Choose 4 steps to get sequence [next, next, next, remove] */ new IteratorTester( @@ -146,10 +145,10 @@ public void testCanCatchSunJavaBug6529795InTargetIterator() { @Override protected Iterator newTargetIterator() { Iterator iterator = Lists.newArrayList(1, 2).iterator(); - return new IteratorWithSunJavaBug6529795<>(iterator); + return new IteratorWithJdkBug6529795<>(iterator); } }.test(); - } catch (AssertionFailedError e) { + } catch (AssertionError e) { return; } fail("Should have caught jdk6 bug in target iterator"); @@ -201,18 +200,18 @@ protected Iterator newTargetIterator() { @Override protected void verify(List elements) { - throw new AssertionFailedError(message); + throw new AssertionError(message); } }; - AssertionFailedError actual = null; + AssertionError actual = null; try { tester.test(); - } catch (AssertionFailedError e) { + } catch (AssertionError e) { actual = e; } assertNotNull("verify() should be able to cause test failure", actual); assertTrue( - "AssertionFailedError should have info about why test failed", + "AssertionError should have info about why test failed", actual.getCause().getMessage().contains(message)); } @@ -233,7 +232,7 @@ public void remove() { @Override public Integer next() { // We should throw here, but we won't! - return null; + return 0; } @Override @@ -323,7 +322,7 @@ public boolean hasNext() { private static void assertFailure(IteratorTester tester) { try { tester.test(); - } catch (AssertionFailedError expected) { + } catch (AssertionError expected) { return; } fail(); diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java b/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java index a4d216d1503c..d2e544016933 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/MapTestSuiteBuilderTests.java @@ -45,12 +45,14 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.Nullable; /** * Tests {@link MapTestSuiteBuilder} by using it against maps that have various negative behaviors. * * @author George van den Driessche */ +@AndroidIncompatible // test-suite builders public final class MapTestSuiteBuilderTests extends TestCase { private MapTestSuiteBuilderTests() {} @@ -106,7 +108,7 @@ public Set> entrySet() { } @Override - public String put(String key, String value) { + public @Nullable String put(String key, String value) { checkNotNull(key); return map.put(key, value); } @@ -138,7 +140,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return map.equals(o); } @@ -148,7 +150,7 @@ public String toString() { } @Override - public String remove(Object key) { + public @Nullable String remove(Object key) { return map.remove(key); } @@ -194,7 +196,7 @@ public String getKey() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return next.equals(obj); } @@ -238,7 +240,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return map.entrySet().equals(o); } @@ -249,7 +251,7 @@ public String toString() { } @Override - public String put(String key, String value) { + public @Nullable String put(String key, String value) { checkNotNull(value); return map.put(key, value); } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MinimalCollectionTest.java b/android/guava-testlib/test/com/google/common/collect/testing/MinimalCollectionTest.java index 38cadf3bff76..83489295a478 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/MinimalCollectionTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/MinimalCollectionTest.java @@ -27,6 +27,7 @@ * * @author Kevin Bourrillion */ +@AndroidIncompatible // test-suite builders public class MinimalCollectionTest extends TestCase { public static Test suite() { return CollectionTestSuiteBuilder.using( diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MinimalIterableTest.java b/android/guava-testlib/test/com/google/common/collect/testing/MinimalIterableTest.java index f6fae3105646..e9a9e0a4f4e4 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/MinimalIterableTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/MinimalIterableTest.java @@ -16,6 +16,9 @@ package com.google.common.collect.testing; +import static com.google.common.collect.testing.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.singleton; + import com.google.common.annotations.GwtCompatible; import java.util.Collections; import java.util.Iterator; @@ -34,16 +37,8 @@ public void testOf_empty() { Iterable iterable = MinimalIterable.of(); Iterator iterator = iterable.iterator(); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } - try { - iterable.iterator(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IllegalStateException.class, () -> iterable.iterator()); } public void testOf_one() { @@ -52,54 +47,26 @@ public void testOf_one() { assertTrue(iterator.hasNext()); assertEquals("a", iterator.next()); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } - try { - iterable.iterator(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IllegalStateException.class, () -> iterable.iterator()); } public void testFrom_empty() { Iterable iterable = MinimalIterable.from(Collections.emptySet()); Iterator iterator = iterable.iterator(); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } - try { - iterable.iterator(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IllegalStateException.class, () -> iterable.iterator()); } public void testFrom_one() { - Iterable iterable = MinimalIterable.from(Collections.singleton("a")); + Iterable iterable = MinimalIterable.from(singleton("a")); Iterator iterator = iterable.iterator(); assertTrue(iterator.hasNext()); assertEquals("a", iterator.next()); - try { - iterator.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } - try { - iterable.iterator(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IllegalStateException.class, () -> iterable.iterator()); } } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MinimalSetTest.java b/android/guava-testlib/test/com/google/common/collect/testing/MinimalSetTest.java index 51cc4c9561a9..037473508d83 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/MinimalSetTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/MinimalSetTest.java @@ -27,6 +27,7 @@ * * @author Regina O'Dell */ +@AndroidIncompatible // test-suite builders public class MinimalSetTest extends TestCase { public static Test suite() { return SetTestSuiteBuilder.using( diff --git a/android/guava-testlib/test/com/google/common/collect/testing/MyTester.java b/android/guava-testlib/test/com/google/common/collect/testing/MyTester.java new file mode 100644 index 000000000000..b82dc8b29a6e --- /dev/null +++ b/android/guava-testlib/test/com/google/common/collect/testing/MyTester.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing; + +import org.jspecify.annotations.Nullable; +import org.junit.Ignore; + +/** Support class added to a suite as part of {@link FeatureSpecificTestSuiteBuilderTest}. */ +/* + * @Ignore affects the Android test runner (and only the Android test runner): It respects JUnit 4 + * annotations even on JUnit 3 tests. + * + * TODO(b/225350400): Remove @Ignore, which doesn't seem like it should be necessary and probably + * soon won't be. + */ +@SuppressWarnings("JUnit4ClassUsedInJUnit3") +@Ignore +public final class MyTester extends AbstractTester<@Nullable Void> { + static int timesTestClassWasRun = 0; + + public void testNothing() { + timesTestClassWasRun++; + } +} diff --git a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6ListTests.java b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6ListTests.java index 7c5118e0b233..3ba5482c4ea3 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6ListTests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6ListTests.java @@ -19,11 +19,11 @@ import static com.google.common.collect.testing.testers.CollectionToArrayTester.getToArrayIsPlainObjectArrayMethod; import static com.google.common.collect.testing.testers.ListAddTester.getAddSupportedNullPresentMethod; import static com.google.common.collect.testing.testers.ListSetTester.getSetNullSupportedMethod; +import static java.util.Arrays.asList; import com.google.common.collect.testing.testers.CollectionAddTester; import com.google.common.collect.testing.testers.ListAddAtIndexTester; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.List; import junit.framework.Test; @@ -34,6 +34,7 @@ * * @author Kevin Bourrillion */ +@AndroidIncompatible // test-suite builders public class OpenJdk6ListTests extends TestsForListsInJavaUtil { public static Test suite() { return new OpenJdk6ListTests().allTests(); @@ -41,12 +42,12 @@ public static Test suite() { @Override protected Collection suppressForArraysAsList() { - return Arrays.asList(getToArrayIsPlainObjectArrayMethod()); + return asList(getToArrayIsPlainObjectArrayMethod()); } @Override protected Collection suppressForCheckedList() { - return Arrays.asList( + return asList( CollectionAddTester.getAddNullSupportedMethod(), getAddSupportedNullPresentMethod(), ListAddAtIndexTester.getAddNullSupportedMethod(), diff --git a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6MapTests.java b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6MapTests.java index 8e18d654b5aa..54231a9def8b 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6MapTests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6MapTests.java @@ -26,9 +26,9 @@ import static com.google.common.collect.testing.testers.MapEntrySetTester.getContainsEntryWithIncomparableValueMethod; import static com.google.common.collect.testing.testers.MapPutAllTester.getPutAllNullKeyUnsupportedMethod; import static com.google.common.collect.testing.testers.MapPutTester.getPutNullKeyUnsupportedMethod; +import static java.util.Arrays.asList; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -40,10 +40,8 @@ * * @author Kevin Bourrillion */ -/* - * TODO(cpovirk): consider renaming this class in light of our now running it - * under JDK7 - */ +// TODO(cpovirk): consider renaming this class in light of our now running it under newer JDKs. +@AndroidIncompatible // test-suite builders public class OpenJdk6MapTests extends TestsForMapsInJavaUtil { public static Test suite() { return new OpenJdk6MapTests().allTests(); @@ -51,7 +49,7 @@ public static Test suite() { @Override protected Collection suppressForTreeMapNatural() { - return Arrays.asList( + return asList( getPutNullKeyUnsupportedMethod(), getPutAllNullKeyUnsupportedMethod(), getCreateWithNullKeyUnsupportedMethod(), @@ -66,14 +64,14 @@ protected Collection suppressForConcurrentHashMap() { * The entrySet() of ConcurrentHashMap, unlike that of other Map * implementations, supports add() under JDK8. This seems problematic, but I * didn't see that discussed in the review, which included many other - * changes: http://goo.gl/okTTdr + * changes: https://mail.openjdk.org/pipermail/core-libs-dev/2013-May/thread.html#17367 * * TODO(cpovirk): decide what the best long-term action here is: force users * to suppress (as we do now), stop testing entrySet().add() at all, make * entrySet().add() tests tolerant of either behavior, introduce a map * feature for entrySet() that supports add(), or something else */ - return Arrays.asList( + return asList( getAddUnsupportedNotPresentMethod(), getAddAllUnsupportedNonePresentMethod(), getAddAllUnsupportedSomePresentMethod()); diff --git a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6QueueTests.java b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6QueueTests.java index f56fee2923ce..002b519f338a 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6QueueTests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6QueueTests.java @@ -17,9 +17,9 @@ package com.google.common.collect.testing; import static com.google.common.collect.testing.testers.CollectionCreationTester.getCreateWithNullUnsupportedMethod; +import static java.util.Arrays.asList; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Queue; @@ -31,13 +31,13 @@ * * @author Kevin Bourrillion */ +@AndroidIncompatible // test-suite builders public class OpenJdk6QueueTests extends TestsForQueuesInJavaUtil { public static Test suite() { return new OpenJdk6QueueTests().allTests(); } - private static final List PQ_SUPPRESS = - Arrays.asList(getCreateWithNullUnsupportedMethod()); + private static final List PQ_SUPPRESS = asList(getCreateWithNullUnsupportedMethod()); @Override protected Collection suppressForPriorityBlockingQueue() { diff --git a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6SetTests.java b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6SetTests.java index 67ef67bf0552..d5ccce9146bd 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6SetTests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6SetTests.java @@ -21,9 +21,9 @@ import static com.google.common.collect.testing.testers.CollectionAddTester.getAddNullUnsupportedMethod; import static com.google.common.collect.testing.testers.CollectionCreationTester.getCreateWithNullUnsupportedMethod; import static com.google.common.collect.testing.testers.SetAddTester.getAddSupportedNullPresentMethod; +import static java.util.Arrays.asList; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.Set; import junit.framework.Test; @@ -34,6 +34,7 @@ * * @author Kevin Bourrillion */ +@AndroidIncompatible // test-suite builders public class OpenJdk6SetTests extends TestsForSetsInJavaUtil { public static Test suite() { return new OpenJdk6SetTests().allTests(); @@ -41,7 +42,7 @@ public static Test suite() { @Override protected Collection suppressForTreeSetNatural() { - return Arrays.asList( + return asList( getAddNullUnsupportedMethod(), getAddAllNullUnsupportedMethod(), getCreateWithNullUnsupportedMethod()); @@ -49,6 +50,6 @@ protected Collection suppressForTreeSetNatural() { @Override protected Collection suppressForCheckedSet() { - return Arrays.asList(getAddNullSupportedMethod(), getAddSupportedNullPresentMethod()); + return asList(getAddNullSupportedMethod(), getAddSupportedNullPresentMethod()); } } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6Tests.java b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6Tests.java index db60982ca779..c97d2bf92f92 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6Tests.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/OpenJdk6Tests.java @@ -27,6 +27,7 @@ * * @author Kevin Bourrillion */ +@AndroidIncompatible // test-suite builders public class OpenJdk6Tests extends TestCase { public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-testlib/test/com/google/common/collect/testing/ReserializedSafeTreeMapMapInterfaceTest.java b/android/guava-testlib/test/com/google/common/collect/testing/ReserializedSafeTreeMapMapInterfaceTest.java new file mode 100644 index 000000000000..46d76a3532b3 --- /dev/null +++ b/android/guava-testlib/test/com/google/common/collect/testing/ReserializedSafeTreeMapMapInterfaceTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect.testing; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.testing.SerializableTester; +import java.util.NavigableMap; +import java.util.SortedMap; + +@GwtIncompatible // SerializableTester +public class ReserializedSafeTreeMapMapInterfaceTest + extends SortedMapInterfaceTest { + public ReserializedSafeTreeMapMapInterfaceTest() { + super(false, true, true, true, true); + } + + @Override + protected SortedMap makePopulatedMap() { + NavigableMap map = new SafeTreeMap<>(); + map.put("one", 1); + map.put("two", 2); + map.put("three", 3); + return SerializableTester.reserialize(map); + } + + @Override + protected SortedMap makeEmptyMap() throws UnsupportedOperationException { + NavigableMap map = new SafeTreeMap<>(); + return SerializableTester.reserialize(map); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeMapTest.java b/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeMapTest.java index f03ff0a09fe1..4bd81700586a 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeMapTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeMapTest.java @@ -42,6 +42,7 @@ * @author Louis Wasserman */ public class SafeTreeMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SafeTreeMapTest.class); @@ -110,36 +111,4 @@ public void testViewSerialization() { Lists.newArrayList(map.values()), Lists.newArrayList(SerializableTester.reserialize(map.values()))); } - - @GwtIncompatible // SerializableTester - public static class ReserializedMapTests extends SortedMapInterfaceTest { - public ReserializedMapTests() { - super(false, true, true, true, true); - } - - @Override - protected SortedMap makePopulatedMap() { - NavigableMap map = new SafeTreeMap<>(); - map.put("one", 1); - map.put("two", 2); - map.put("three", 3); - return SerializableTester.reserialize(map); - } - - @Override - protected SortedMap makeEmptyMap() throws UnsupportedOperationException { - NavigableMap map = new SafeTreeMap<>(); - return SerializableTester.reserialize(map); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } - } } diff --git a/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeSetTest.java b/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeSetTest.java index ea5a7840adb1..173e656e8016 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeSetTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/SafeTreeSetTest.java @@ -16,6 +16,8 @@ package com.google.common.collect.testing; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.Lists; @@ -24,7 +26,6 @@ import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.SerializableTester; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -36,6 +37,7 @@ import junit.framework.TestSuite; public class SafeTreeSetTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SafeTreeSetTest.class); @@ -44,7 +46,7 @@ public static Test suite() { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return new SafeTreeSet<>(Arrays.asList(elements)); + return new SafeTreeSet<>(asList(elements)); } @Override diff --git a/android/guava-testlib/test/com/google/common/collect/testing/features/AndroidIncompatible.java b/android/guava-testlib/test/com/google/common/collect/testing/features/AndroidIncompatible.java index 46c212307716..efcaa6727eee 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/features/AndroidIncompatible.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/features/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureEnumTest.java b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureEnumTest.java index 983a4954f125..793efd942237 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureEnumTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureEnumTest.java @@ -52,8 +52,7 @@ private static void assertGoodTesterAnnotation(Class annot try { method = annotationClass.getMethod(propertyName); } catch (NoSuchMethodException e) { - fail( - rootLocaleFormat("%s must have a property named '%s'.", annotationClass, propertyName)); + throw new AssertionError("Annotation is missing required method", e); } final Class returnType = method.getReturnType(); assertTrue( diff --git a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java index 37af90bdab89..a77820938b37 100644 --- a/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java +++ b/android/guava-testlib/test/com/google/common/collect/testing/features/FeatureUtilTest.java @@ -16,256 +16,297 @@ package com.google.common.collect.testing.features; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.testing.features.FeatureEnumTest.assertGoodFeatureEnum; +import static com.google.common.collect.testing.features.FeatureUtil.addImpliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtil.buildDeclaredTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.buildTesterRequirements; +import static com.google.common.collect.testing.features.FeatureUtil.getTesterAnnotations; +import static com.google.common.collect.testing.features.FeatureUtil.impliedFeatures; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_BAR; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO; +import static com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; import static com.google.common.truth.Truth.assertThat; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.NotTesterAnnotation; +import com.google.common.collect.testing.features.FeatureUtilTest.ExampleFeature.Require; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; -import java.util.Collections; import java.util.Set; import junit.framework.TestCase; -/** @author George van den Driessche */ -// Enum values use constructors with generic varargs. -@SuppressWarnings("unchecked") +/** + * @author George van den Driessche + */ public class FeatureUtilTest extends TestCase { - interface ExampleBaseInterface { - void behave(); - } - - interface ExampleDerivedInterface extends ExampleBaseInterface { - void misbehave(); - } - - enum ExampleBaseFeature implements Feature { - BASE_FEATURE_1, - BASE_FEATURE_2; + enum ExampleFeature implements Feature { + FOO, + IMPLIES_FOO, + IMPLIES_IMPLIES_FOO, + BAR, + IMPLIES_BAR, + IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR; @Override - public Set> getImpliedFeatures() { - return Collections.emptySet(); + public ImmutableSet> getImpliedFeatures() { + switch (this) { + case IMPLIES_FOO: + return ImmutableSet.of(FOO); + case IMPLIES_IMPLIES_FOO: + return ImmutableSet.of(IMPLIES_FOO); + case IMPLIES_BAR: + return ImmutableSet.of(BAR); + case IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR: + return ImmutableSet.of(IMPLIES_FOO, IMPLIES_BAR); + default: + return ImmutableSet.of(); + } } - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) @Inherited @TesterAnnotation @interface Require { - ExampleBaseFeature[] value() default {}; + ExampleFeature[] value() default {}; - ExampleBaseFeature[] absent() default {}; + ExampleFeature[] absent() default {}; + } + + @Retention(RUNTIME) + @Inherited + @interface NotTesterAnnotation { + ExampleFeature[] value() default {}; + + ExampleFeature[] absent() default {}; } } - enum ExampleDerivedFeature implements Feature { - DERIVED_FEATURE_1, - DERIVED_FEATURE_2(ExampleBaseFeature.BASE_FEATURE_1), - DERIVED_FEATURE_3, + public void testTestFeatureEnums() { + // Haha! Let's test our own test rig! + assertGoodFeatureEnum(ExampleFeature.class); + } - COMPOUND_DERIVED_FEATURE( - DERIVED_FEATURE_1, DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_2); + public void testAddImpliedFeatures_returnsSameSetInstance() { + Set> features = newHashSet(FOO); + assertThat(addImpliedFeatures(features)).isSameInstanceAs(features); + } - private Set> implied; + public void testAddImpliedFeatures_addsImpliedFeatures() { + assertThat(addImpliedFeatures(newHashSet(FOO))).containsExactly(FOO); - ExampleDerivedFeature(Feature... implied) { - this.implied = ImmutableSet.copyOf(implied); - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); - @Override - public Set> getImpliedFeatures() { - return implied; - } + assertThat(addImpliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + } - @Retention(RetentionPolicy.RUNTIME) - @Inherited - @TesterAnnotation - @interface Require { - ExampleDerivedFeature[] value() default {}; + public void testImpliedFeatures_returnsNewSetInstance() { + Set> features = newHashSet(IMPLIES_FOO); + assertThat(impliedFeatures(features)).isNotSameInstanceAs(features); + } - ExampleDerivedFeature[] absent() default {}; - } + public void testImpliedFeatures_returnsImpliedFeatures() { + assertThat(impliedFeatures(newHashSet(FOO))).isEmpty(); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO))).containsExactly(IMPLIES_FOO, FOO); + + assertThat(impliedFeatures(newHashSet(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR))) + .containsExactly(IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); } - @Retention(RetentionPolicy.RUNTIME) - @interface NonTesterAnnotation {} + public void testBuildTesterRequirements_class_notAnnotated() throws Exception { + class Tester {} - @ExampleBaseFeature.Require({ExampleBaseFeature.BASE_FEATURE_1}) - private abstract static class ExampleBaseInterfaceTester extends TestCase { - protected final void doNotActuallyRunThis() { - fail("Nobody's meant to actually run this!"); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @AndroidIncompatible // Android attempts to run directly - @NonTesterAnnotation - @ExampleDerivedFeature.Require({ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ExampleDerivedInterfaceTester extends ExampleBaseInterfaceTester { - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2 - }) - public void testRequiringTwoExplicitDerivedFeatures() throws Exception { - doNotActuallyRunThis(); - } + public void testBuildTesterRequirements_class_empty() throws Exception { + @Require + class Tester {} - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleDerivedFeature.Require({ - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_3 - }) - public void testRequiringAllThreeDerivedFeatures() { - doNotActuallyRunThis(); - } + TesterRequirements requirements = buildTesterRequirements(Tester.class); - // Exists to test that our framework doesn't run it: - @SuppressWarnings("unused") - @ExampleBaseFeature.Require(absent = {ExampleBaseFeature.BASE_FEATURE_1}) - public void testRequiringConflictingFeatures() throws Exception { - doNotActuallyRunThis(); - } + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - @ExampleDerivedFeature.Require(absent = {ExampleDerivedFeature.DERIVED_FEATURE_2}) - private static class ConflictingRequirementsExampleDerivedInterfaceTester - extends ExampleBaseInterfaceTester {} + public void testBuildTesterRequirements_class_present() throws Exception { + @Require({IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} - public void testTestFeatureEnums() throws Exception { - // Haha! Let's test our own test rig! - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleBaseFeature.class); - FeatureEnumTest.assertGoodFeatureEnum(FeatureUtilTest.ExampleDerivedFeature.class); + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testAddImpliedFeatures_returnsSameSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertSame(features, FeatureUtil.addImpliedFeatures(features)); + public void testBuildTesterRequirements_class_absent() throws Exception { + @Require(absent = {IMPLIES_IMPLIES_FOO, IMPLIES_BAR}) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); } - public void testAddImpliedFeatures_addsImpliedFeatures() throws Exception { - Set> features; - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .contains(ExampleDerivedFeature.DERIVED_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_2, ExampleBaseFeature.BASE_FEATURE_1); - - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.addImpliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + public void testBuildTesterRequirements_class_present_and_absent() throws Exception { + @Require(value = IMPLIES_FOO, absent = IMPLIES_IMPLIES_FOO) + class Tester {} + + TesterRequirements requirements = buildTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO); } - public void testImpliedFeatures_returnsNewSetInstance() throws Exception { - Set> features = Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1); - assertNotSame(features, FeatureUtil.impliedFeatures(features)); + public void testBuildTesterRequirements_class_present_method_present() throws Exception { + @Require(IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO, IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); } - public void testImpliedFeatures_returnsImpliedFeatures() throws Exception { - Set> features; + public void testBuildTesterRequirements_class_absent_method_absent() throws Exception { + @Require(absent = IMPLIES_BAR) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()).isEmpty(); + assertThat(requirements.getAbsentFeatures()).containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_BAR); + } + + public void testBuildTesterRequirements_class_present_method_absent() throws Exception { + @Require(IMPLIES_IMPLIES_FOO) + class Tester { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + public void test() {} + } + + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); + + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_1); - assertTrue(FeatureUtil.impliedFeatures(features).isEmpty()); + public void testBuildTesterRequirements_class_absent_method_present() throws Exception { + @Require(absent = IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR) + class Tester { + @Require(IMPLIES_IMPLIES_FOO) + public void test() {} + } - features = Sets.>newHashSet(ExampleDerivedFeature.DERIVED_FEATURE_2); - assertThat(FeatureUtil.impliedFeatures(features)).contains(ExampleBaseFeature.BASE_FEATURE_1); + TesterRequirements requirements = buildTesterRequirements(Tester.class.getMethod("test")); - features = Sets.>newHashSet(ExampleDerivedFeature.COMPOUND_DERIVED_FEATURE); - assertThat(FeatureUtil.impliedFeatures(features)) - .containsExactly( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleBaseFeature.BASE_FEATURE_1, - ExampleBaseFeature.BASE_FEATURE_2); + assertThat(requirements.getPresentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO, IMPLIES_FOO, FOO); + assertThat(requirements.getAbsentFeatures()) + .containsExactly(IMPLIES_IMPLIES_FOO_AND_IMPLIES_BAR); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_class() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleBaseInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet(ExampleBaseFeature.BASE_FEATURE_1), - Collections.>emptySet())); - - assertEquals( - FeatureUtil.buildTesterRequirements(ExampleDerivedInterfaceTester.class), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); + public void testBuildTesterRequirements_classClassConflict() { + @Require(value = FOO, absent = FOO) + class Tester {} + + ConflictingRequirementsException e = + assertThrows( + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_method() throws Exception { - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2), - Collections.>emptySet())); - assertEquals( - FeatureUtil.buildTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod("testRequiringAllThreeDerivedFeatures")), - new TesterRequirements( - Sets.>newHashSet( - ExampleBaseFeature.BASE_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2, - ExampleDerivedFeature.DERIVED_FEATURE_3), - Collections.>emptySet())); + public void testBuildTesterRequirements_classClassConflict_inherited() { + @Require(FOO) + abstract class BaseTester {} + @Require(absent = FOO) + class Tester extends BaseTester {} + + ConflictingRequirementsException e = + assertThrows( + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists - public void testBuildTesterRequirements_classClassConflict() throws Exception { - try { - FeatureUtil.buildTesterRequirements( - ConflictingRequirementsExampleDerivedInterfaceTester.class); - fail("Expected ConflictingRequirementsException"); - } catch (ConflictingRequirementsException e) { - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(ConflictingRequirementsExampleDerivedInterfaceTester.class, e.getSource()); - } + public void testBuildTesterRequirements_classClassConflict_implied() { + @Require(value = IMPLIES_FOO, absent = FOO) + class Tester {} + + ConflictingRequirementsException e = + assertThrows( + ConflictingRequirementsException.class, () -> buildTesterRequirements(Tester.class)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(Tester.class.getAnnotation(Require.class)); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists public void testBuildTesterRequirements_methodClassConflict() throws Exception { - final Method method = - ExampleDerivedInterfaceTester.class.getMethod("testRequiringConflictingFeatures"); - try { - FeatureUtil.buildTesterRequirements(method); - fail("Expected ConflictingRequirementsException"); - } catch (ConflictingRequirementsException e) { - assertThat(e.getConflicts()).contains(ExampleBaseFeature.BASE_FEATURE_1); - assertEquals(method, e.getSource()); + @Require(IMPLIES_FOO) + class Tester { + @Require(absent = FOO) + public void test() {} } + + Method method = Tester.class.getMethod("test"); + ConflictingRequirementsException e = + assertThrows(ConflictingRequirementsException.class, () -> buildTesterRequirements(method)); + assertThat(e.getConflicts()).containsExactly(FOO); + assertThat(e.getSource()).isEqualTo(method); } - @AndroidIncompatible // Android runs ExampleDerivedInterfaceTester directly if it exists public void testBuildDeclaredTesterRequirements() throws Exception { - assertEquals( - FeatureUtil.buildDeclaredTesterRequirements( - ExampleDerivedInterfaceTester.class.getMethod( - "testRequiringTwoExplicitDerivedFeatures")), - new TesterRequirements( - FeatureUtil.addImpliedFeatures( - Sets.>newHashSet( - ExampleDerivedFeature.DERIVED_FEATURE_1, - ExampleDerivedFeature.DERIVED_FEATURE_2)), - Collections.>emptySet())); + @Require(IMPLIES_FOO) + abstract class BaseTester {} + @Require(IMPLIES_BAR) + class Tester extends BaseTester {} + + TesterRequirements requirements = buildDeclaredTesterRequirements(Tester.class); + + assertThat(requirements.getPresentFeatures()).containsExactly(IMPLIES_BAR, BAR); + assertThat(requirements.getAbsentFeatures()).isEmpty(); + } + + public void testGetTesterAnnotations_class() { + @Require + @NotTesterAnnotation + class Tester {} + + assertThat(getTesterAnnotations(Tester.class)) + .containsExactly(Tester.class.getAnnotation(Require.class)); + } + + public void testGetTesterAnnotations_method() throws Exception { + class Tester { + @Require + @NotTesterAnnotation + public void test() {} + } + Method method = Tester.class.getMethod("test"); + + assertThat(getTesterAnnotations(method)).containsExactly(method.getAnnotation(Require.class)); } } diff --git a/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java b/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java index cb6ba6abf457..9712d8aa0112 100644 --- a/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java +++ b/android/guava-testlib/test/com/google/common/testing/AbstractPackageSanityTestsTest.java @@ -23,12 +23,14 @@ import java.util.Arrays; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link AbstractPackageSanityTests}. * * @author Ben Yu */ +@NullUnmarked public class AbstractPackageSanityTestsTest extends TestCase { /* * This is a public type so that the Android test runner can create an instance directly as it @@ -108,6 +110,7 @@ static class EmptyTestSuite {} static class Foo {} + @SuppressWarnings("IdentifierName") // We're testing that we ignore classes with underscores. static class Foo_Bar {} public static class PublicFoo {} diff --git a/android/guava-testlib/test/com/google/common/testing/AndroidIncompatible.java b/android/guava-testlib/test/com/google/common/testing/AndroidIncompatible.java index 326d7b86e2e2..d1a3721e50d6 100644 --- a/android/guava-testlib/test/com/google/common/testing/AndroidIncompatible.java +++ b/android/guava-testlib/test/com/google/common/testing/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java b/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java index b47672f83dc6..bbd5c6404476 100644 --- a/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java +++ b/android/guava-testlib/test/com/google/common/testing/ArbitraryInstancesTest.java @@ -17,9 +17,11 @@ package com.google.common.testing; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.CharMatcher; -import com.google.common.base.Charsets; import com.google.common.base.Equivalence; import com.google.common.base.Joiner; import com.google.common.base.Predicate; @@ -127,12 +129,15 @@ import java.util.regex.MatchResult; import java.util.regex.Pattern; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link ArbitraryInstances}. * * @author Ben Yu */ +@NullUnmarked public class ArbitraryInstancesTest extends TestCase { public void testGet_primitives() { @@ -152,18 +157,18 @@ public void testGet_primitives() { assertEquals(Long.valueOf(0), ArbitraryInstances.get(Long.class)); assertEquals(Float.valueOf(0), ArbitraryInstances.get(float.class)); assertEquals(Float.valueOf(0), ArbitraryInstances.get(Float.class)); - assertEquals(Double.valueOf(0), ArbitraryInstances.get(double.class)); - assertEquals(Double.valueOf(0), ArbitraryInstances.get(Double.class)); + assertThat(ArbitraryInstances.get(double.class)).isEqualTo(Double.valueOf(0)); + assertThat(ArbitraryInstances.get(Double.class)).isEqualTo(Double.valueOf(0)); assertEquals(UnsignedInteger.ZERO, ArbitraryInstances.get(UnsignedInteger.class)); assertEquals(UnsignedLong.ZERO, ArbitraryInstances.get(UnsignedLong.class)); assertEquals(0, ArbitraryInstances.get(BigDecimal.class).intValue()); assertEquals(0, ArbitraryInstances.get(BigInteger.class).intValue()); assertEquals("", ArbitraryInstances.get(String.class)); assertEquals("", ArbitraryInstances.get(CharSequence.class)); - assertEquals(TimeUnit.SECONDS, ArbitraryInstances.get(TimeUnit.class)); + assertEquals(SECONDS, ArbitraryInstances.get(TimeUnit.class)); assertNotNull(ArbitraryInstances.get(Object.class)); assertEquals(0, ArbitraryInstances.get(Number.class)); - assertEquals(Charsets.UTF_8, ArbitraryInstances.get(Charset.class)); + assertEquals(UTF_8, ArbitraryInstances.get(Charset.class)); assertNotNull(ArbitraryInstances.get(UUID.class)); } @@ -277,11 +282,7 @@ public void testGet_comparable() { Comparable comparable = ArbitraryInstances.get(Comparable.class); assertEquals(0, comparable.compareTo(comparable)); assertTrue(comparable.compareTo("") > 0); - try { - comparable.compareTo(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> comparable.compareTo(null)); } public void testGet_array() { @@ -473,7 +474,7 @@ private WithGenericConstant() {} } public static class WithNullConstant { - public static final WithNullConstant NULL = null; + public static final @Nullable WithNullConstant NULL = null; private WithNullConstant() {} } @@ -496,7 +497,7 @@ private static class WithPublicConstants { private static class FirstConstantIsNull { // To test that null constant is ignored @SuppressWarnings("unused") - public static final FirstConstantIsNull FIRST = null; + public static final @Nullable FirstConstantIsNull FIRST = null; public static final FirstConstantIsNull SECOND = new FirstConstantIsNull(); } diff --git a/android/guava-testlib/test/com/google/common/testing/ClassSanityTesterTest.java b/android/guava-testlib/test/com/google/common/testing/ClassSanityTesterTest.java deleted file mode 100644 index be1752d4793a..000000000000 --- a/android/guava-testlib/test/com/google/common/testing/ClassSanityTesterTest.java +++ /dev/null @@ -1,1350 +0,0 @@ -/* - * Copyright (C) 2012 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.testing; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.base.Functions; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; -import com.google.common.testing.ClassSanityTester.FactoryMethodReturnsNullException; -import com.google.common.testing.ClassSanityTester.ParameterHasNoDistinctValueException; -import com.google.common.testing.ClassSanityTester.ParameterNotInstantiableException; -import com.google.common.testing.NullPointerTester.Visibility; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; -import junit.framework.AssertionFailedError; -import junit.framework.TestCase; - -/** - * Unit tests for {@link ClassSanityTester}. - * - * @author Ben Yu - */ -public class ClassSanityTesterTest extends TestCase { - - private final ClassSanityTester tester = new ClassSanityTester(); - - public void testEqualsOnReturnValues_good() throws Exception { - tester.forAllPublicStaticMethods(GoodEqualsFactory.class).testEquals(); - } - - public static class GoodEqualsFactory { - public static Object good( - String a, - int b, - // oneConstantOnly doesn't matter since it's not nullable and can be only 1 value. - @SuppressWarnings("unused") OneConstantEnum oneConstantOnly, - // noConstant doesn't matter since it can only be null - @SuppressWarnings("unused") @CheckForNull NoConstantEnum noConstant) { - return new GoodEquals(a, b); - } - // instance method ignored - public Object badIgnored() { - return new BadEquals(); - } - // primitive ignored - public int returnsInt() { - throw new UnsupportedOperationException(); - } - // void ignored - public void voidMethod() { - throw new UnsupportedOperationException(); - } - // non-public method ignored - static Object badButNotPublic() { - return new BadEquals(); - } - } - - public void testForAllPublicStaticMethods_noPublicStaticMethods() throws Exception { - try { - tester.forAllPublicStaticMethods(NoPublicStaticMethods.class).testEquals(); - } catch (AssertionFailedError expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo( - "No public static methods that return java.lang.Object or subtype are found in " - + NoPublicStaticMethods.class - + "."); - return; - } - fail(); - } - - public void testEqualsOnReturnValues_bad() throws Exception { - try { - tester.forAllPublicStaticMethods(BadEqualsFactory.class).testEquals(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - private static class BadEqualsFactory { - /** oneConstantOnly matters now since it can be either null or the constant. */ - @SuppressWarnings("unused") // Called by reflection - public static Object bad(String a, int b, @CheckForNull OneConstantEnum oneConstantOnly) { - return new GoodEquals(a, b); - } - } - - public void testNullsOnReturnValues_good() throws Exception { - tester.forAllPublicStaticMethods(GoodNullsFactory.class).testNulls(); - } - - private static class GoodNullsFactory { - @SuppressWarnings("unused") // Called by reflection - public static Object good(String s) { - return new GoodNulls(s); - } - } - - public void testNullsOnReturnValues_bad() throws Exception { - try { - tester.forAllPublicStaticMethods(BadNullsFactory.class).thatReturn(Object.class).testNulls(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public void testNullsOnReturnValues_returnTypeFiltered() throws Exception { - try { - tester - .forAllPublicStaticMethods(BadNullsFactory.class) - .thatReturn(Iterable.class) - .testNulls(); - } catch (AssertionFailedError expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo( - "No public static methods that return java.lang.Iterable or subtype are found in " - + BadNullsFactory.class - + "."); - return; - } - fail(); - } - - public static class BadNullsFactory { - public static Object bad(@SuppressWarnings("unused") String a) { - return new BadNulls(); - } - } - - @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException... ClassSanityTesterTest$AnInterface - public void testSerializableOnReturnValues_good() throws Exception { - tester.forAllPublicStaticMethods(GoodSerializableFactory.class).testSerializable(); - } - - public static class GoodSerializableFactory { - public static Object good(Runnable r) { - return r; - } - - public static Object good(AnInterface i) { - return i; - } - } - - public void testSerializableOnReturnValues_bad() throws Exception { - try { - tester.forAllPublicStaticMethods(BadSerializableFactory.class).testSerializable(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public static class BadSerializableFactory { - public static Object bad() { - return new Serializable() { - @SuppressWarnings("unused") - private final Object notSerializable = new Object(); - }; - } - } - - public void testEqualsAndSerializableOnReturnValues_equalsIsGoodButNotSerializable() - throws Exception { - try { - tester.forAllPublicStaticMethods(GoodEqualsFactory.class).testEqualsAndSerializable(); - } catch (AssertionFailedError expected) { - return; - } - fail("should have failed"); - } - - public void testEqualsAndSerializableOnReturnValues_serializableButNotEquals() throws Exception { - try { - tester.forAllPublicStaticMethods(GoodSerializableFactory.class).testEqualsAndSerializable(); - } catch (AssertionFailedError expected) { - return; - } - fail("should have failed"); - } - - @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException... ClassSanityTesterTest$AnInterface - public void testEqualsAndSerializableOnReturnValues_good() throws Exception { - tester - .forAllPublicStaticMethods(GoodEqualsAndSerialiableFactory.class) - .testEqualsAndSerializable(); - } - - public static class GoodEqualsAndSerialiableFactory { - public static Object good(AnInterface s) { - return Functions.constant(s); - } - } - - public void testEqualsForReturnValues_factoryReturnsNullButNotAnnotated() throws Exception { - try { - tester.forAllPublicStaticMethods(FactoryThatReturnsNullButNotAnnotated.class).testEquals(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public void testNullsForReturnValues_factoryReturnsNullButNotAnnotated() throws Exception { - try { - tester.forAllPublicStaticMethods(FactoryThatReturnsNullButNotAnnotated.class).testNulls(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public void testSerializableForReturnValues_factoryReturnsNullButNotAnnotated() throws Exception { - try { - tester - .forAllPublicStaticMethods(FactoryThatReturnsNullButNotAnnotated.class) - .testSerializable(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public void testEqualsAndSerializableForReturnValues_factoryReturnsNullButNotAnnotated() - throws Exception { - try { - tester - .forAllPublicStaticMethods(FactoryThatReturnsNullButNotAnnotated.class) - .testEqualsAndSerializable(); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public static class FactoryThatReturnsNullButNotAnnotated { - public static Object bad() { - return null; - } - } - - public void testEqualsForReturnValues_factoryReturnsNullAndAnnotated() throws Exception { - tester.forAllPublicStaticMethods(FactoryThatReturnsNullAndAnnotated.class).testEquals(); - } - - public void testNullsForReturnValues_factoryReturnsNullAndAnnotated() throws Exception { - tester.forAllPublicStaticMethods(FactoryThatReturnsNullAndAnnotated.class).testNulls(); - } - - public void testSerializableForReturnValues_factoryReturnsNullAndAnnotated() throws Exception { - tester.forAllPublicStaticMethods(FactoryThatReturnsNullAndAnnotated.class).testSerializable(); - } - - public void testEqualsAndSerializableForReturnValues_factoryReturnsNullAndAnnotated() - throws Exception { - tester - .forAllPublicStaticMethods(FactoryThatReturnsNullAndAnnotated.class) - .testEqualsAndSerializable(); - } - - public static class FactoryThatReturnsNullAndAnnotated { - @CheckForNull - public static Object bad() { - return null; - } - } - - public void testGoodEquals() throws Exception { - tester.testEquals(GoodEquals.class); - } - - public void testEquals_interface() { - tester.testEquals(AnInterface.class); - } - - public void testEquals_abstractClass() { - tester.testEquals(AnAbstractClass.class); - } - - public void testEquals_enum() { - tester.testEquals(OneConstantEnum.class); - } - - public void testBadEquals() throws Exception { - try { - tester.testEquals(BadEquals.class); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains("create(null)"); - return; - } - fail("should have failed"); - } - - public void testBadEquals_withParameterizedType() throws Exception { - try { - tester.testEquals(BadEqualsWithParameterizedType.class); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains("create([[1]])"); - return; - } - fail("should have failed"); - } - - public void testBadEquals_withSingleParameterValue() throws Exception { - try { - tester.doTestEquals(ConstructorParameterWithOptionalNotInstantiable.class); - fail(); - } catch (ParameterHasNoDistinctValueException expected) { - } - } - - public void testGoodReferentialEqualityComparison() throws Exception { - tester.testEquals(UsesEnum.class); - tester.testEquals(UsesReferentialEquality.class); - tester.testEquals(SameListInstance.class); - } - - @AndroidIncompatible // problem with equality of Type objects? - public void testEqualsUsingReferentialEquality() throws Exception { - assertBadUseOfReferentialEquality(SameIntegerInstance.class); - assertBadUseOfReferentialEquality(SameLongInstance.class); - assertBadUseOfReferentialEquality(SameFloatInstance.class); - assertBadUseOfReferentialEquality(SameDoubleInstance.class); - assertBadUseOfReferentialEquality(SameShortInstance.class); - assertBadUseOfReferentialEquality(SameByteInstance.class); - assertBadUseOfReferentialEquality(SameCharacterInstance.class); - assertBadUseOfReferentialEquality(SameBooleanInstance.class); - assertBadUseOfReferentialEquality(SameObjectInstance.class); - assertBadUseOfReferentialEquality(SameStringInstance.class); - assertBadUseOfReferentialEquality(SameInterfaceInstance.class); - } - - private void assertBadUseOfReferentialEquality(Class cls) throws Exception { - try { - tester.testEquals(cls); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains(cls.getSimpleName() + "("); - return; - } - fail("should have failed for " + cls); - } - - public void testParameterNotInstantiableForEqualsTest() throws Exception { - try { - tester.doTestEquals(ConstructorParameterNotInstantiable.class); - fail("should have failed"); - } catch (ParameterNotInstantiableException expected) { - } - } - - public void testNoDistinctValueForEqualsTest() throws Exception { - try { - tester.doTestEquals(ConstructorParameterSingleValue.class); - fail("should have failed"); - } catch (ParameterHasNoDistinctValueException expected) { - } - } - - public void testConstructorThrowsForEqualsTest() throws Exception { - try { - tester.doTestEquals(ConstructorThrows.class); - fail("should have failed"); - } catch (InvocationTargetException expected) { - } - } - - public void testFactoryMethodReturnsNullForEqualsTest() throws Exception { - try { - tester.doTestEquals(FactoryMethodReturnsNullAndAnnotated.class); - fail("should have failed"); - } catch (FactoryMethodReturnsNullException expected) { - } - } - - public void testFactoryMethodReturnsNullButNotAnnotatedInEqualsTest() throws Exception { - try { - tester.testEquals(FactoryMethodReturnsNullButNotAnnotated.class); - } catch (AssertionFailedError expected) { - return; - } - fail("should have failed"); - } - - public void testNoEqualsChecksOnEnum() throws Exception { - tester.testEquals(OneConstantEnum.class); - tester.testEquals(NoConstantEnum.class); - tester.testEquals(TimeUnit.class); - } - - public void testNoEqualsChecksOnInterface() throws Exception { - tester.testEquals(Runnable.class); - } - - public void testNoEqualsChecksOnAnnotation() throws Exception { - tester.testEquals(MyAnnotation.class); - } - - public void testGoodNulls() throws Exception { - tester.testNulls(GoodNulls.class); - } - - public void testNoNullCheckNeededDespitNotInstantiable() throws Exception { - tester.doTestNulls(NoNullCheckNeededDespitNotInstantiable.class, Visibility.PACKAGE); - } - - public void testNulls_interface() { - tester.testNulls(AnInterface.class); - } - - public void testNulls_abstractClass() { - tester.testNulls(AnAbstractClass.class); - } - - public void testNulls_enum() throws Exception { - tester.testNulls(OneConstantEnum.class); - tester.testNulls(NoConstantEnum.class); - tester.testNulls(TimeUnit.class); - } - - public void testNulls_parameterOptionalNotInstantiable() throws Exception { - tester.testNulls(ConstructorParameterWithOptionalNotInstantiable.class); - } - - public void testEnumFailsToCheckNull() throws Exception { - try { - tester.testNulls(EnumFailsToCheckNull.class); - } catch (AssertionFailedError expected) { - return; - } - fail("should have failed"); - } - - public void testNoNullChecksOnInterface() throws Exception { - tester.testNulls(Runnable.class); - } - - public void testNoNullChecksOnAnnotation() throws Exception { - tester.testNulls(MyAnnotation.class); - } - - public void testBadNulls() throws Exception { - try { - tester.testNulls(BadNulls.class); - } catch (AssertionFailedError expected) { - return; - } - fail("should have failed"); - } - - public void testInstantiate_factoryMethodReturnsNullButNotAnnotated() throws Exception { - try { - tester.instantiate(FactoryMethodReturnsNullButNotAnnotated.class); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains("@Nullable"); - return; - } - fail("should have failed"); - } - - public void testInstantiate_factoryMethodReturnsNullAndAnnotated() throws Exception { - try { - tester.instantiate(FactoryMethodReturnsNullAndAnnotated.class); - fail("should have failed"); - } catch (FactoryMethodReturnsNullException expected) { - } - } - - public void testInstantiate_factoryMethodAcceptsNull() throws Exception { - assertNull(tester.instantiate(FactoryMethodAcceptsNull.class).name); - } - - public void testInstantiate_factoryMethodDoesNotAcceptNull() throws Exception { - assertNotNull(tester.instantiate(FactoryMethodDoesNotAcceptNull.class).name); - } - - public void testInstantiate_constructorAcceptsNull() throws Exception { - assertNull(tester.instantiate(ConstructorAcceptsNull.class).name); - } - - public void testInstantiate_constructorDoesNotAcceptNull() throws Exception { - assertNotNull(tester.instantiate(ConstructorDoesNotAcceptNull.class).name); - } - - public void testInstantiate_notInstantiable() throws Exception { - assertNull(tester.instantiate(NotInstantiable.class)); - } - - public void testInstantiate_noConstantEnum() throws Exception { - assertNull(tester.instantiate(NoConstantEnum.class)); - } - - public void testInstantiate_oneConstantEnum() throws Exception { - assertEquals(OneConstantEnum.A, tester.instantiate(OneConstantEnum.class)); - } - - public void testInstantiate_interface() throws Exception { - assertNull(tester.instantiate(Runnable.class)); - } - - public void testInstantiate_abstractClass() throws Exception { - assertNull(tester.instantiate(AbstractList.class)); - } - - public void testInstantiate_annotation() throws Exception { - assertNull(tester.instantiate(MyAnnotation.class)); - } - - public void testInstantiate_setDefault() throws Exception { - NotInstantiable x = new NotInstantiable(); - tester.setDefault(NotInstantiable.class, x); - assertNotNull(tester.instantiate(ConstructorParameterNotInstantiable.class)); - } - - public void testSetDistinctValues_equalInstances() { - try { - tester.setDistinctValues(String.class, "", ""); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - public void testInstantiate_setDistinctValues() throws Exception { - NotInstantiable x = new NotInstantiable(); - NotInstantiable y = new NotInstantiable(); - tester.setDistinctValues(NotInstantiable.class, x, y); - assertNotNull(tester.instantiate(ConstructorParameterNotInstantiable.class)); - tester.testEquals(ConstructorParameterMapOfNotInstantiable.class); - } - - public void testInstantiate_constructorThrows() throws Exception { - try { - tester.instantiate(ConstructorThrows.class); - fail(); - } catch (InvocationTargetException expected) { - } - } - - public void testInstantiate_factoryMethodThrows() throws Exception { - try { - tester.instantiate(FactoryMethodThrows.class); - fail(); - } catch (InvocationTargetException expected) { - } - } - - public void testInstantiate_constructorParameterNotInstantiable() throws Exception { - try { - tester.instantiate(ConstructorParameterNotInstantiable.class); - fail(); - } catch (ParameterNotInstantiableException expected) { - } - } - - public void testInstantiate_factoryMethodParameterNotInstantiable() throws Exception { - try { - tester.instantiate(FactoryMethodParameterNotInstantiable.class); - fail(); - } catch (ParameterNotInstantiableException expected) { - } - } - - public void testInstantiate_instantiableFactoryMethodChosen() throws Exception { - assertEquals("good", tester.instantiate(InstantiableFactoryMethodChosen.class).name); - } - - @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException... ClassSanityTesterTest$AnInterface - public void testInterfaceProxySerializable() throws Exception { - SerializableTester.reserializeAndAssert(tester.instantiate(HasAnInterface.class)); - } - - public void testReturnValuesFromAnotherPackageIgnoredForNullTests() throws Exception { - new ClassSanityTester().forAllPublicStaticMethods(JdkObjectFactory.class).testNulls(); - } - - /** String doesn't check nulls as we expect. But the framework should ignore. */ - private static class JdkObjectFactory { - @SuppressWarnings("unused") // Called by reflection - public static Object create() { - return new ArrayList<>(); - } - } - - static class HasAnInterface implements Serializable { - private final AnInterface i; - - public HasAnInterface(AnInterface i) { - this.i = i; - } - - @Override - public boolean equals(@CheckForNull Object obj) { - if (obj instanceof HasAnInterface) { - HasAnInterface that = (HasAnInterface) obj; - return i.equals(that.i); - } else { - return false; - } - } - - @Override - public int hashCode() { - return i.hashCode(); - } - } - - static class InstantiableFactoryMethodChosen { - final String name; - - private InstantiableFactoryMethodChosen(String name) { - this.name = name; - } - - public InstantiableFactoryMethodChosen(NotInstantiable x) { - checkNotNull(x); - this.name = "x1"; - } - - public static InstantiableFactoryMethodChosen create(NotInstantiable x) { - return new InstantiableFactoryMethodChosen(x); - } - - public static InstantiableFactoryMethodChosen create(String s) { - checkNotNull(s); - return new InstantiableFactoryMethodChosen("good"); - } - } - - public void testInstantiate_instantiableConstructorChosen() throws Exception { - assertEquals("good", tester.instantiate(InstantiableConstructorChosen.class).name); - } - - public void testEquals_setOfNonInstantiable() throws Exception { - try { - new ClassSanityTester().doTestEquals(SetWrapper.class); - fail(); - } catch (ParameterNotInstantiableException expected) { - } - } - - private abstract static class Wrapper { - private final Object wrapped; - - Wrapper(Object wrapped) { - this.wrapped = checkNotNull(wrapped); - } - - @Override - public boolean equals(@CheckForNull Object obj) { - // In general getClass().isInstance() is bad for equals. - // But here we fully control the subclasses to ensure symmetry. - if (getClass().isInstance(obj)) { - Wrapper that = (Wrapper) obj; - return wrapped.equals(that.wrapped); - } - return false; - } - - @Override - public int hashCode() { - return wrapped.hashCode(); - } - - @Override - public String toString() { - return wrapped.toString(); - } - } - - private static class SetWrapper extends Wrapper { - public SetWrapper(Set wrapped) { - super(wrapped); - } - } - - static class InstantiableConstructorChosen { - final String name; - - public InstantiableConstructorChosen(String name) { - checkNotNull(name); - this.name = "good"; - } - - public InstantiableConstructorChosen(NotInstantiable x) { - checkNotNull(x); - this.name = "x1"; - } - - public static InstantiableFactoryMethodChosen create(NotInstantiable x) { - return new InstantiableFactoryMethodChosen(x); - } - } - - static class GoodEquals { - - private final String a; - private final int b; - - private GoodEquals(String a, int b) { - this.a = checkNotNull(a); - this.b = b; - } - - // ignored by testEquals() - GoodEquals(@SuppressWarnings("unused") NotInstantiable x) { - this.a = "x"; - this.b = -1; - } - - // will keep trying - public GoodEquals(@SuppressWarnings("unused") NotInstantiable x, int b) { - this.a = "x"; - this.b = b; - } - - // keep trying - @SuppressWarnings("unused") - static GoodEquals create(int a, int b) { - throw new RuntimeException(); - } - - // Good! - static GoodEquals create(String a, int b) { - return new GoodEquals(a, b); - } - - // keep trying - @SuppressWarnings("unused") - @CheckForNull - public static GoodEquals createMayReturnNull(int a, int b) { - return null; - } - - @Override - public boolean equals(@CheckForNull Object obj) { - if (obj instanceof GoodEquals) { - GoodEquals that = (GoodEquals) obj; - return a.equals(that.a) && b == that.b; - } else { - return false; - } - } - - @Override - public int hashCode() { - return 0; - } - } - - static class BadEquals { - - public BadEquals() {} // ignored by testEquals() since it has less parameters. - - public static BadEquals create(@SuppressWarnings("unused") @CheckForNull String s) { - return new BadEquals(); - } - - @Override - public boolean equals(@CheckForNull Object obj) { - return obj instanceof BadEquals; - } - - @Override - public int hashCode() { - return 0; - } - } - - static class SameIntegerInstance { - private final Integer i; - - public SameIntegerInstance(Integer i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameIntegerInstance) { - SameIntegerInstance that = (SameIntegerInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameLongInstance { - private final Long i; - - public SameLongInstance(Long i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameLongInstance) { - SameLongInstance that = (SameLongInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameFloatInstance { - private final Float i; - - public SameFloatInstance(Float i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameFloatInstance) { - SameFloatInstance that = (SameFloatInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameDoubleInstance { - private final Double i; - - public SameDoubleInstance(Double i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameDoubleInstance) { - SameDoubleInstance that = (SameDoubleInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameShortInstance { - private final Short i; - - public SameShortInstance(Short i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameShortInstance) { - SameShortInstance that = (SameShortInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameByteInstance { - private final Byte i; - - public SameByteInstance(Byte i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - @SuppressWarnings("NumericEquality") - public boolean equals(Object obj) { - if (obj instanceof SameByteInstance) { - SameByteInstance that = (SameByteInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameCharacterInstance { - private final Character i; - - public SameCharacterInstance(Character i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameCharacterInstance) { - SameCharacterInstance that = (SameCharacterInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameBooleanInstance { - private final Boolean i; - - public SameBooleanInstance(Boolean i) { - this.i = checkNotNull(i); - } - - @Override - public int hashCode() { - return i.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameBooleanInstance) { - SameBooleanInstance that = (SameBooleanInstance) obj; - return i == that.i; - } - return false; - } - } - - static class SameStringInstance { - private final String s; - - public SameStringInstance(String s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameStringInstance) { - SameStringInstance that = (SameStringInstance) obj; - return s == that.s; - } - return false; - } - } - - static class SameObjectInstance { - private final Object s; - - public SameObjectInstance(Object s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameObjectInstance) { - SameObjectInstance that = (SameObjectInstance) obj; - return s == that.s; - } - return false; - } - } - - static class SameInterfaceInstance { - private final Runnable s; - - public SameInterfaceInstance(Runnable s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameInterfaceInstance) { - SameInterfaceInstance that = (SameInterfaceInstance) obj; - return s == that.s; - } - return false; - } - } - - static class SameListInstance { - private final List s; - - public SameListInstance(List s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return System.identityHashCode(s); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof SameListInstance) { - SameListInstance that = (SameListInstance) obj; - return s == that.s; - } - return false; - } - } - - static class UsesReferentialEquality { - private final ReferentialEquality s; - - public UsesReferentialEquality(ReferentialEquality s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof UsesReferentialEquality) { - UsesReferentialEquality that = (UsesReferentialEquality) obj; - return s == that.s; - } - return false; - } - } - - static class UsesEnum { - private final TimeUnit s; - - public UsesEnum(TimeUnit s) { - this.s = checkNotNull(s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof UsesEnum) { - UsesEnum that = (UsesEnum) obj; - return s == that.s; - } - return false; - } - } - - public static class ReferentialEquality { - public ReferentialEquality() {} - } - - static class BadEqualsWithParameterizedType { - - // ignored by testEquals() since it has less parameters. - public BadEqualsWithParameterizedType() {} - - public static BadEqualsWithParameterizedType create( - @SuppressWarnings("unused") ImmutableList> s) { - return new BadEqualsWithParameterizedType(); - } - - @Override - public boolean equals(@CheckForNull Object obj) { - return obj instanceof BadEqualsWithParameterizedType; - } - - @Override - public int hashCode() { - return 0; - } - } - - static class GoodNulls { - public GoodNulls(String s) { - checkNotNull(s); - } - - public void rejectNull(String s) { - checkNotNull(s); - } - } - - public static class BadNulls { - public void failsToRejectNull(@SuppressWarnings("unused") String s) {} - } - - public static class NoNullCheckNeededDespitNotInstantiable { - - public NoNullCheckNeededDespitNotInstantiable(NotInstantiable x) { - checkNotNull(x); - } - - @SuppressWarnings("unused") // reflected - void primitiveOnly(int i) {} - - @SuppressWarnings("unused") // reflected - void nullableOnly(@CheckForNull String s) {} - - public void noParameter() {} - - @SuppressWarnings("unused") // reflected - void primitiveAndNullable(@CheckForNull String s, int i) {} - } - - static class FactoryMethodReturnsNullButNotAnnotated { - private FactoryMethodReturnsNullButNotAnnotated() {} - - static FactoryMethodReturnsNullButNotAnnotated returnsNull() { - return null; - } - } - - static class FactoryMethodReturnsNullAndAnnotated { - private FactoryMethodReturnsNullAndAnnotated() {} - - @CheckForNull - public static FactoryMethodReturnsNullAndAnnotated returnsNull() { - return null; - } - } - - static class FactoryMethodAcceptsNull { - - final String name; - - private FactoryMethodAcceptsNull(String name) { - this.name = name; - } - - static FactoryMethodAcceptsNull create(@CheckForNull String name) { - return new FactoryMethodAcceptsNull(name); - } - } - - static class FactoryMethodDoesNotAcceptNull { - - final String name; - - private FactoryMethodDoesNotAcceptNull(String name) { - this.name = checkNotNull(name); - } - - public static FactoryMethodDoesNotAcceptNull create(String name) { - return new FactoryMethodDoesNotAcceptNull(name); - } - } - - static class ConstructorAcceptsNull { - - final String name; - - public ConstructorAcceptsNull(@CheckForNull String name) { - this.name = name; - } - } - - static class ConstructorDoesNotAcceptNull { - - final String name; - - ConstructorDoesNotAcceptNull(String name) { - this.name = checkNotNull(name); - } - } - - static class ConstructorParameterNotInstantiable { - public ConstructorParameterNotInstantiable(@SuppressWarnings("unused") NotInstantiable x) {} - } - - static class ConstructorParameterMapOfNotInstantiable { - private final Map m; - - public ConstructorParameterMapOfNotInstantiable(Map m) { - this.m = checkNotNull(m); - } - - @Override - public boolean equals(@CheckForNull Object obj) { - if (obj instanceof ConstructorParameterMapOfNotInstantiable) { - return m.equals(((ConstructorParameterMapOfNotInstantiable) obj).m); - } else { - return false; - } - } - - @Override - public int hashCode() { - return m.hashCode(); - } - } - - // Test that we should get a distinct parameter error when doing equals test. - static class ConstructorParameterWithOptionalNotInstantiable { - public ConstructorParameterWithOptionalNotInstantiable(Optional x) { - checkNotNull(x); - } - - @Override - public boolean equals(@CheckForNull Object obj) { - throw new UnsupportedOperationException(); - } - - @Override - public int hashCode() { - throw new UnsupportedOperationException(); - } - } - - static class ConstructorParameterSingleValue { - public ConstructorParameterSingleValue(@SuppressWarnings("unused") Singleton s) {} - - @Override - public boolean equals(Object obj) { - return obj instanceof ConstructorParameterSingleValue; - } - - @Override - public int hashCode() { - return 1; - } - - public static class Singleton { - public static final Singleton INSTANCE = new Singleton(); - - private Singleton() {} - } - } - - static class FactoryMethodParameterNotInstantiable { - - private FactoryMethodParameterNotInstantiable() {} - - static FactoryMethodParameterNotInstantiable create( - @SuppressWarnings("unused") NotInstantiable x) { - return new FactoryMethodParameterNotInstantiable(); - } - } - - static class ConstructorThrows { - public ConstructorThrows() { - throw new RuntimeException(); - } - } - - static class FactoryMethodThrows { - private FactoryMethodThrows() {} - - public static FactoryMethodThrows create() { - throw new RuntimeException(); - } - } - - static class NotInstantiable { - private NotInstantiable() {} - } - - private enum NoConstantEnum {} - - private enum OneConstantEnum { - A - } - - private enum EnumFailsToCheckNull { - A; - - @SuppressWarnings("unused") - public void failToCheckNull(String s) {} - } - - private interface AnInterface {} - - private abstract static class AnAbstractClass { - @SuppressWarnings("unused") - public AnAbstractClass(String s) {} - - @SuppressWarnings("unused") - public void failsToCheckNull(String s) {} - } - - private static class NoPublicStaticMethods { - @SuppressWarnings("unused") // To test non-public factory isn't used. - static String notPublic() { - return ""; - } - } - - @interface MyAnnotation {} -} diff --git a/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java b/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java index d615af6633a2..303f452734d1 100644 --- a/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java +++ b/android/guava-testlib/test/com/google/common/testing/EqualsTesterTest.java @@ -16,13 +16,19 @@ package com.google.common.testing; +import static com.google.common.testing.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Set; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@link EqualsTester}. @@ -31,6 +37,7 @@ */ @GwtCompatible @SuppressWarnings("MissingTestCall") +@NullUnmarked public class EqualsTesterTest extends TestCase { private ValidTestObject reference; private EqualsTester equalsTester; @@ -50,29 +57,21 @@ public void setUp() throws Exception { /** Test null reference yields error */ public void testAddNullReference() { - try { - equalsTester.addEqualityGroup((Object) null); - fail("Should fail on null reference"); - } catch (NullPointerException e) { - } + assertThrows(NullPointerException.class, () -> equalsTester.addEqualityGroup((Object) null)); } /** Test equalObjects after adding multiple instances at once with a null */ public void testAddTwoEqualObjectsAtOnceWithNull() { - try { - equalsTester.addEqualityGroup(reference, equalObject1, null); - fail("Should fail on null equal object"); - } catch (NullPointerException e) { - } + assertThrows( + NullPointerException.class, + () -> equalsTester.addEqualityGroup(reference, equalObject1, null)); } /** Test adding null equal object yields error */ public void testAddNullEqualObject() { - try { - equalsTester.addEqualityGroup(reference, (Object[]) null); - fail("Should fail on null equal object"); - } catch (NullPointerException e) { - } + assertThrows( + NullPointerException.class, + () -> equalsTester.addEqualityGroup(reference, (Object[]) null)); } /** @@ -114,7 +113,7 @@ public void testTestEqualsEqualsObjects() { } /** Test proper handling of case where an object is not equal to itself */ - public void testNonreflexiveEquals() { + public void testNonReflexiveEquals() { Object obj = new NonReflexiveObject(); equalsTester.addEqualityGroup(obj); try { @@ -194,21 +193,14 @@ public void testInvalidHashCode() { public void testNullEqualityGroup() { EqualsTester tester = new EqualsTester(); - try { - tester.addEqualityGroup((Object[]) null); - fail(); - } catch (NullPointerException e) { - } + assertThrows(NullPointerException.class, () -> tester.addEqualityGroup((Object[]) null)); } public void testNullObjectInEqualityGroup() { EqualsTester tester = new EqualsTester(); - try { - tester.addEqualityGroup(1, null, 3); - fail(); - } catch (NullPointerException e) { - assertErrorMessage(e, "at index 1"); - } + NullPointerException e = + assertThrows(NullPointerException.class, () -> tester.addEqualityGroup(1, null, 3)); + assertErrorMessage(e, "at index 1"); } public void testSymmetryBroken() { @@ -273,12 +265,12 @@ public void testEqualityGroups() { } public void testEqualityBasedOnToString() { - try { - new EqualsTester().addEqualityGroup(new EqualsBasedOnToString("foo")).testEquals(); - fail(); - } catch (AssertionFailedError e) { - assertTrue(e.getMessage().contains("toString representation")); - } + AssertionFailedError e = + assertThrows( + AssertionFailedError.class, + () -> + new EqualsTester().addEqualityGroup(new EqualsBasedOnToString("foo")).testEquals()); + assertThat(e).hasMessageThat().contains("toString representation"); } private static void assertErrorMessage(Throwable e, String message) { @@ -302,7 +294,7 @@ private static class ValidTestObject { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ValidTestObject)) { return false; } @@ -337,7 +329,7 @@ private static class InvalidHashCodeObject { @SuppressWarnings("EqualsHashCode") @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof InvalidHashCodeObject)) { return false; } @@ -356,7 +348,7 @@ public boolean equals(Object o) { private static class NonReflexiveObject { @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return false; } @@ -370,7 +362,7 @@ public int hashCode() { private static class InvalidEqualsNullObject { @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o == this || o == null; } @@ -384,7 +376,7 @@ public int hashCode() { private static class InvalidEqualsIncompatibleClassObject { @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o != null; } @@ -407,13 +399,14 @@ private static class NamedObject { this.name = Preconditions.checkNotNull(name); } + @CanIgnoreReturnValue NamedObject addPeers(String... names) { peerNames.addAll(ImmutableList.copyOf(names)); return this; } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NamedObject) { NamedObject that = (NamedObject) obj; return name.equals(that.name) || peerNames.contains(that.name); @@ -440,7 +433,7 @@ private EqualsBasedOnToString(String s) { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj != null && obj.toString().equals(toString()); } diff --git a/android/guava-testlib/test/com/google/common/testing/EquivalenceTesterTest.java b/android/guava-testlib/test/com/google/common/testing/EquivalenceTesterTest.java index d612b2c2c876..77a35d04ece2 100644 --- a/android/guava-testlib/test/com/google/common/testing/EquivalenceTesterTest.java +++ b/android/guava-testlib/test/com/google/common/testing/EquivalenceTesterTest.java @@ -17,6 +17,7 @@ package com.google.common.testing; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.testing.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; @@ -26,6 +27,7 @@ import com.google.common.collect.ImmutableTable; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link EquivalenceTester}. @@ -33,6 +35,7 @@ * @author Gregory Kick */ @GwtCompatible +@NullUnmarked public class EquivalenceTesterTest extends TestCase { private EquivalenceTester tester; private MockEquivalence equivalenceMock; @@ -45,15 +48,11 @@ public void setUp() throws Exception { } /** Test null reference yields error */ - public void testOf_NullPointerException() { - try { - EquivalenceTester.of(null); - fail("Should fail on null reference"); - } catch (NullPointerException expected) { - } + public void testOf_nullPointerException() { + assertThrows(NullPointerException.class, () -> EquivalenceTester.of(null)); } - public void testTest_NoData() { + public void testTest_noData() { tester.test(); } @@ -104,7 +103,8 @@ public void testTest_symmetric() { try { tester.addEquivalenceGroup(group1Item1, group1Item2).test(); } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()) + assertThat(expected) + .hasMessageThat() .contains( "TestObject{group=1, item=2} [group 1, item 2] must be equivalent to " + "TestObject{group=1, item=1} [group 1, item 1]"); @@ -113,7 +113,7 @@ public void testTest_symmetric() { fail(); } - public void testTest_trasitive() { + public void testTest_transitive() { Object group1Item1 = new TestObject(1, 1); Object group1Item2 = new TestObject(1, 2); Object group1Item3 = new TestObject(1, 3); @@ -134,7 +134,8 @@ public void testTest_trasitive() { try { tester.addEquivalenceGroup(group1Item1, group1Item2, group1Item3).test(); } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()) + assertThat(expected) + .hasMessageThat() .contains( "TestObject{group=1, item=2} [group 1, item 2] must be equivalent to " + "TestObject{group=1, item=3} [group 1, item 3]"); @@ -158,7 +159,8 @@ public void testTest_inequivalence() { try { tester.addEquivalenceGroup(group1Item1).addEquivalenceGroup(group2Item1).test(); } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()) + assertThat(expected) + .hasMessageThat() .contains( "TestObject{group=1, item=1} [group 1, item 1] must not be equivalent to " + "TestObject{group=2, item=1} [group 2, item 1]"); @@ -236,8 +238,8 @@ void expectHash(Object object, int hash) { void replay() { checkRecording(); - equivalentExpectations = equivalentExpectationsBuilder.build(); - hashExpectations = hashExpectationsBuilder.build(); + equivalentExpectations = equivalentExpectationsBuilder.buildOrThrow(); + hashExpectations = hashExpectationsBuilder.buildOrThrow(); } @Override diff --git a/android/guava-testlib/test/com/google/common/testing/FakeTickerTest.java b/android/guava-testlib/test/com/google/common/testing/FakeTickerTest.java index 654304b1902b..8529f8f14a66 100644 --- a/android/guava-testlib/test/com/google/common/testing/FakeTickerTest.java +++ b/android/guava-testlib/test/com/google/common/testing/FakeTickerTest.java @@ -16,9 +16,14 @@ package com.google.common.testing; +import static com.google.common.testing.ReflectionFreeAssertThrows.assertThrows; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import java.util.EnumSet; +import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -26,6 +31,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link FakeTicker}. @@ -33,6 +40,9 @@ * @author Jige Yu */ @GwtCompatible(emulated = true) +// We also want to test the TimeUnit overload (especially under GWT, where it's the only option). +@SuppressWarnings("SetAutoIncrementStep_Nanos") +@NullUnmarked public class FakeTickerTest extends TestCase { @GwtIncompatible // NullPointerTester @@ -41,48 +51,61 @@ public void testNullPointerExceptions() { tester.testAllPublicInstanceMethods(new FakeTicker()); } + @GwtIncompatible // java.time.Duration + @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents. public void testAdvance() { FakeTicker ticker = new FakeTicker(); assertEquals(0, ticker.read()); assertSame(ticker, ticker.advance(10)); assertEquals(10, ticker.read()); - ticker.advance(1, TimeUnit.MILLISECONDS); + ticker.advance(1, MILLISECONDS); assertEquals(1000010L, ticker.read()); + ticker.advance(Duration.ofMillis(1)); + assertEquals(2000010L, ticker.read()); } public void testAutoIncrementStep_returnsSameInstance() { FakeTicker ticker = new FakeTicker(); - assertSame(ticker, ticker.setAutoIncrementStep(10, TimeUnit.NANOSECONDS)); + assertSame(ticker, ticker.setAutoIncrementStep(10, NANOSECONDS)); } public void testAutoIncrementStep_nanos() { - FakeTicker ticker = new FakeTicker().setAutoIncrementStep(10, TimeUnit.NANOSECONDS); + FakeTicker ticker = new FakeTicker().setAutoIncrementStep(10, NANOSECONDS); assertEquals(0, ticker.read()); assertEquals(10, ticker.read()); assertEquals(20, ticker.read()); } public void testAutoIncrementStep_millis() { - FakeTicker ticker = new FakeTicker().setAutoIncrementStep(1, TimeUnit.MILLISECONDS); + FakeTicker ticker = new FakeTicker().setAutoIncrementStep(1, MILLISECONDS); assertEquals(0, ticker.read()); assertEquals(1000000, ticker.read()); assertEquals(2000000, ticker.read()); } public void testAutoIncrementStep_seconds() { - FakeTicker ticker = new FakeTicker().setAutoIncrementStep(3, TimeUnit.SECONDS); + FakeTicker ticker = new FakeTicker().setAutoIncrementStep(3, SECONDS); assertEquals(0, ticker.read()); assertEquals(3000000000L, ticker.read()); assertEquals(6000000000L, ticker.read()); } + @GwtIncompatible // java.time.Duration + @IgnoreJRERequirement // TODO: b/288085449 - Remove this once we use library-desugaring scents. + public void testAutoIncrementStep_duration() { + FakeTicker ticker = new FakeTicker().setAutoIncrementStep(Duration.ofMillis(1)); + assertEquals(0, ticker.read()); + assertEquals(1000000, ticker.read()); + assertEquals(2000000, ticker.read()); + } + public void testAutoIncrementStep_resetToZero() { - FakeTicker ticker = new FakeTicker().setAutoIncrementStep(10, TimeUnit.NANOSECONDS); + FakeTicker ticker = new FakeTicker().setAutoIncrementStep(10, NANOSECONDS); assertEquals(0, ticker.read()); assertEquals(10, ticker.read()); assertEquals(20, ticker.read()); - for (TimeUnit timeUnit : EnumSet.allOf(TimeUnit.class)) { + for (TimeUnit timeUnit : TimeUnit.values()) { ticker.setAutoIncrementStep(0, timeUnit); assertEquals( "Expected no auto-increment when setting autoIncrementStep to 0 " + timeUnit, @@ -93,11 +116,8 @@ public void testAutoIncrementStep_resetToZero() { public void testAutoIncrement_negative() { FakeTicker ticker = new FakeTicker(); - try { - ticker.setAutoIncrementStep(-1, TimeUnit.NANOSECONDS); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> ticker.setAutoIncrementStep(-1, NANOSECONDS)); } @GwtIncompatible // concurrency @@ -108,9 +128,9 @@ public void testConcurrentAdvance() throws Exception { int numberOfThreads = 64; runConcurrentTest( numberOfThreads, - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { // adds two nanoseconds to the ticker ticker.advance(1L); Thread.sleep(10); @@ -126,16 +146,15 @@ public Void call() throws Exception { public void testConcurrentAutoIncrementStep() throws Exception { int incrementByNanos = 3; - final FakeTicker ticker = - new FakeTicker().setAutoIncrementStep(incrementByNanos, TimeUnit.NANOSECONDS); + final FakeTicker ticker = new FakeTicker().setAutoIncrementStep(incrementByNanos, NANOSECONDS); int numberOfThreads = 64; runConcurrentTest( numberOfThreads, - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { - ticker.read(); + public @Nullable Void call() throws Exception { + long unused = ticker.read(); return null; } }); @@ -145,7 +164,7 @@ public Void call() throws Exception { /** Runs {@code callable} concurrently {@code numberOfThreads} times. */ @GwtIncompatible // concurrency - private void runConcurrentTest(int numberOfThreads, final Callable callable) + private void runConcurrentTest(int numberOfThreads, final Callable<@Nullable Void> callable) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads); final CountDownLatch startLatch = new CountDownLatch(numberOfThreads); @@ -154,9 +173,9 @@ private void runConcurrentTest(int numberOfThreads, final Callable callabl @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = executorService.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { startLatch.countDown(); startLatch.await(); callable.call(); diff --git a/android/guava-testlib/test/com/google/common/testing/FreshValueGeneratorTest.java b/android/guava-testlib/test/com/google/common/testing/FreshValueGeneratorTest.java index b20517cadb15..f3eb11694bbf 100644 --- a/android/guava-testlib/test/com/google/common/testing/FreshValueGeneratorTest.java +++ b/android/guava-testlib/test/com/google/common/testing/FreshValueGeneratorTest.java @@ -106,12 +106,14 @@ import java.util.regex.MatchResult; import java.util.regex.Pattern; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link FreshValueGenerator}. * * @author Ben Yu */ +@NullUnmarked public class FreshValueGeneratorTest extends TestCase { @AndroidIncompatible // problem with equality of Type objects? diff --git a/android/guava-testlib/test/com/google/common/testing/GcFinalizationTest.java b/android/guava-testlib/test/com/google/common/testing/GcFinalizationTest.java index 164ac5a0c0a5..7257bce99a25 100644 --- a/android/guava-testlib/test/com/google/common/testing/GcFinalizationTest.java +++ b/android/guava-testlib/test/com/google/common/testing/GcFinalizationTest.java @@ -17,6 +17,7 @@ package com.google.common.testing; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.testing.GcFinalization.FinalizationPredicate; import com.google.common.util.concurrent.SettableFuture; @@ -25,6 +26,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link GcFinalization}. @@ -32,52 +35,57 @@ * @author Martin Buchholz * @author mike nonemacher */ +@AndroidIncompatible // depends on details of gc +@NullUnmarked public class GcFinalizationTest extends TestCase { // ---------------------------------------------------------------- // Ordinary tests of successful method execution // ---------------------------------------------------------------- - public void testAwait_CountDownLatch() { + public void testAwait_countDownLatch() { final CountDownLatch latch = new CountDownLatch(1); - Object x = + Object unused = new Object() { + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { latch.countDown(); } }; - x = null; // Hint to the JIT that x is unreachable + unused = null; // Hint to the JIT that unused is unreachable GcFinalization.await(latch); assertEquals(0, latch.getCount()); } - public void testAwaitDone_Future() { - final SettableFuture future = SettableFuture.create(); - Object x = + public void testAwaitDone_future() { + final SettableFuture<@Nullable Void> future = SettableFuture.create(); + Object unused = new Object() { + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { future.set(null); } }; - x = null; // Hint to the JIT that x is unreachable + unused = null; // Hint to the JIT that unused is unreachable GcFinalization.awaitDone(future); assertTrue(future.isDone()); assertFalse(future.isCancelled()); } - public void testAwaitDone_Future_Cancel() { - final SettableFuture future = SettableFuture.create(); - Object x = + public void testAwaitDone_future_cancel() { + final SettableFuture<@Nullable Void> future = SettableFuture.create(); + Object unused = new Object() { + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { future.cancel(false); } }; - x = null; // Hint to the JIT that x is unreachable + unused = null; // Hint to the JIT that unused is unreachable GcFinalization.awaitDone(future); assertTrue(future.isDone()); assertTrue(future.isCancelled()); @@ -89,11 +97,12 @@ public void testAwaitClear() { assertNull(ref.get()); } - public void testAwaitDone_FinalizationPredicate() { + public void testAwaitDone_finalizationPredicate() { final WeakHashMap map = new WeakHashMap<>(); map.put(new Object(), Boolean.TRUE); GcFinalization.awaitDone( new FinalizationPredicate() { + @Override public boolean isDone() { return map.isEmpty(); } @@ -113,9 +122,11 @@ class Interruptenator extends Thread { this(interruptee, new AtomicBoolean(false)); } + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) { super( new Runnable() { + @Override public void run() { while (!shutdown.get()) { interruptee.interrupt(); @@ -127,6 +138,7 @@ public void run() { start(); } + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. void shutdown() { shutdown.set(true); while (this.isAlive()) { @@ -140,68 +152,60 @@ void assertWrapsInterruptedException(RuntimeException e) { assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class); } - public void testAwait_CountDownLatch_Interrupted() { + public void testAwait_countDownLatch_interrupted() { Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); try { final CountDownLatch latch = new CountDownLatch(1); - try { - GcFinalization.await(latch); - fail("should throw"); - } catch (RuntimeException expected) { - assertWrapsInterruptedException(expected); - } + RuntimeException expected = + assertThrows(RuntimeException.class, () -> GcFinalization.await(latch)); + assertWrapsInterruptedException(expected); } finally { interruptenator.shutdown(); Thread.interrupted(); } } - public void testAwaitDone_Future_Interrupted_Interrupted() { + public void testAwaitDone_future_interrupted_interrupted() { Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); try { - final SettableFuture future = SettableFuture.create(); - try { - GcFinalization.awaitDone(future); - fail("should throw"); - } catch (RuntimeException expected) { - assertWrapsInterruptedException(expected); - } + final SettableFuture<@Nullable Void> future = SettableFuture.create(); + RuntimeException expected = + assertThrows(RuntimeException.class, () -> GcFinalization.awaitDone(future)); + assertWrapsInterruptedException(expected); } finally { interruptenator.shutdown(); Thread.interrupted(); } } - public void testAwaitClear_Interrupted() { + public void testAwaitClear_interrupted() { Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); try { final WeakReference ref = new WeakReference(Boolean.TRUE); - try { - GcFinalization.awaitClear(ref); - fail("should throw"); - } catch (RuntimeException expected) { - assertWrapsInterruptedException(expected); - } + RuntimeException expected = + assertThrows(RuntimeException.class, () -> GcFinalization.awaitClear(ref)); + assertWrapsInterruptedException(expected); } finally { interruptenator.shutdown(); Thread.interrupted(); } } - public void testAwaitDone_FinalizationPredicate_Interrupted() { + public void testAwaitDone_finalizationPredicate_interrupted() { Interruptenator interruptenator = new Interruptenator(Thread.currentThread()); try { - try { - GcFinalization.awaitDone( - new FinalizationPredicate() { - public boolean isDone() { - return false; - } - }); - fail("should throw"); - } catch (RuntimeException expected) { - assertWrapsInterruptedException(expected); - } + RuntimeException expected = + assertThrows( + RuntimeException.class, + () -> + GcFinalization.awaitDone( + new FinalizationPredicate() { + @Override + public boolean isDone() { + return false; + } + })); + assertWrapsInterruptedException(expected); } finally { interruptenator.shutdown(); Thread.interrupted(); @@ -218,6 +222,7 @@ public void testAwaitFullGc() { final WeakReference ref = new WeakReference( new Object() { + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { finalizerRan.countDown(); @@ -228,8 +233,8 @@ protected void finalize() { // Use e.g. awaitClear or await(CountDownLatch) instead. GcFinalization.awaitFullGc(); - // If this test turns out to be flaky, add a second call to awaitFullGc() - // GcFinalization.awaitFullGc(); + // Attempt to help with some flakiness that we've seen: b/387521512. + GcFinalization.awaitFullGc(); assertEquals(0, finalizerRan.getCount()); assertNull(ref.get()); diff --git a/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java b/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java deleted file mode 100644 index 218edef0a632..000000000000 --- a/android/guava-testlib/test/com/google/common/testing/NullPointerTesterTest.java +++ /dev/null @@ -1,1434 +0,0 @@ -/* - * Copyright (C) 2005 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.testing; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.base.Converter; -import com.google.common.base.Function; -import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableMultiset; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.ImmutableTable; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multiset; -import com.google.common.collect.Table; -import com.google.common.reflect.TypeToken; -import com.google.common.testing.NullPointerTester.Visibility; -import com.google.common.testing.anotherpackage.SomeClassThatDoesNotUseNullable; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import javax.annotation.CheckForNull; -import junit.framework.AssertionFailedError; -import junit.framework.TestCase; - -/** - * Unit test for {@link NullPointerTester}. - * - * @author Kevin Bourrillion - * @author Mick Killianey - */ -@SuppressWarnings("CheckReturnValue") -public class NullPointerTesterTest extends TestCase { - - /** Non-NPE RuntimeException. */ - public static class FooException extends RuntimeException { - private static final long serialVersionUID = 1L; - } - - /** - * Class for testing all permutations of static/non-static one-argument methods using - * methodParameter(). - */ - @SuppressWarnings("unused") // used by reflection - public static class OneArg { - - public static void staticOneArgCorrectlyThrowsNpe(String s) { - checkNotNull(s); // expect NPE here on null - } - - public static void staticOneArgThrowsOtherThanNpe(String s) { - throw new FooException(); // should catch as failure - } - - public static void staticOneArgShouldThrowNpeButDoesnt(String s) { - // should catch as failure - } - - public static void staticOneArgCheckForNullCorrectlyDoesNotThrowNPE( - @javax.annotation.CheckForNull String s) { - // null? no problem - } - - public static void staticOneArgJsr305NullableCorrectlyDoesNotThrowNPE(@CheckForNull String s) { - // null? no problem - } - - public static void staticOneArgNullableCorrectlyDoesNotThrowNPE(@CheckForNull String s) { - // null? no problem - } - - public static void staticOneArgCheckForNullCorrectlyThrowsOtherThanNPE( - @javax.annotation.CheckForNull String s) { - throw new FooException(); // ok, as long as it's not NullPointerException - } - - public static void staticOneArgNullableCorrectlyThrowsOtherThanNPE(@CheckForNull String s) { - throw new FooException(); // ok, as long as it's not NullPointerException - } - - public static void staticOneArgCheckForNullThrowsNPE(@javax.annotation.CheckForNull String s) { - checkNotNull(s); // doesn't check if you said you'd accept null, but you don't - } - - public static void staticOneArgNullableThrowsNPE(@CheckForNull String s) { - checkNotNull(s); // doesn't check if you said you'd accept null, but you don't - } - - public void oneArgCorrectlyThrowsNpe(String s) { - checkNotNull(s); // expect NPE here on null - } - - public void oneArgThrowsOtherThanNpe(String s) { - throw new FooException(); // should catch as failure - } - - public void oneArgShouldThrowNpeButDoesnt(String s) { - // should catch as failure - } - - public void oneArgCheckForNullCorrectlyDoesNotThrowNPE( - @javax.annotation.CheckForNull String s) { - // null? no problem - } - - public void oneArgNullableCorrectlyDoesNotThrowNPE(@CheckForNull String s) { - // null? no problem - } - - public void oneArgCheckForNullCorrectlyThrowsOtherThanNPE( - @javax.annotation.CheckForNull String s) { - throw new FooException(); // ok, as long as it's not NullPointerException - } - - public void oneArgNullableCorrectlyThrowsOtherThanNPE(@CheckForNull String s) { - throw new FooException(); // ok, as long as it's not NullPointerException - } - - public void oneArgCheckForNullThrowsNPE(@javax.annotation.CheckForNull String s) { - checkNotNull(s); // doesn't check if you said you'd accept null, but you don't - } - - public void oneArgNullableThrowsNPE(@CheckForNull String s) { - checkNotNull(s); // doesn't check if you said you'd accept null, but you don't - } - } - - private static final String[] STATIC_ONE_ARG_METHODS_SHOULD_PASS = { - "staticOneArgCorrectlyThrowsNpe", - "staticOneArgCheckForNullCorrectlyDoesNotThrowNPE", - "staticOneArgCheckForNullCorrectlyThrowsOtherThanNPE", - "staticOneArgCheckForNullThrowsNPE", - "staticOneArgNullableCorrectlyDoesNotThrowNPE", - "staticOneArgNullableCorrectlyThrowsOtherThanNPE", - "staticOneArgNullableThrowsNPE", - }; - private static final String[] STATIC_ONE_ARG_METHODS_SHOULD_FAIL = { - "staticOneArgThrowsOtherThanNpe", "staticOneArgShouldThrowNpeButDoesnt", - }; - private static final String[] NONSTATIC_ONE_ARG_METHODS_SHOULD_PASS = { - "oneArgCorrectlyThrowsNpe", - "oneArgCheckForNullCorrectlyDoesNotThrowNPE", - "oneArgCheckForNullCorrectlyThrowsOtherThanNPE", - "oneArgCheckForNullThrowsNPE", - "oneArgNullableCorrectlyDoesNotThrowNPE", - "oneArgNullableCorrectlyThrowsOtherThanNPE", - "oneArgNullableThrowsNPE", - }; - private static final String[] NONSTATIC_ONE_ARG_METHODS_SHOULD_FAIL = { - "oneArgThrowsOtherThanNpe", "oneArgShouldThrowNpeButDoesnt", - }; - - private static class ThrowsIae { - public static void christenPoodle(String name) { - checkArgument(name != null); - } - } - - private static class ThrowsNpe { - public static void christenPoodle(String name) { - checkNotNull(name); - } - } - - private static class ThrowsUoe { - public static void christenPoodle(String name) { - throw new UnsupportedOperationException(); - } - } - - private static class ThrowsSomethingElse { - public static void christenPoodle(String name) { - throw new RuntimeException(); - } - } - - public void testDontAcceptIae() { - NullPointerTester tester = new NullPointerTester(); - tester.testAllPublicStaticMethods(ThrowsNpe.class); - tester.testAllPublicStaticMethods(ThrowsUoe.class); - try { - tester.testAllPublicStaticMethods(ThrowsIae.class); - } catch (AssertionFailedError expected) { - return; - } - fail(); - } - - public void testStaticOneArgMethodsThatShouldPass() throws Exception { - for (String methodName : STATIC_ONE_ARG_METHODS_SHOULD_PASS) { - Method method = OneArg.class.getMethod(methodName, String.class); - try { - new NullPointerTester().testMethodParameter(new OneArg(), method, 0); - } catch (AssertionFailedError unexpected) { - fail("Should not have flagged method " + methodName); - } - } - } - - public void testStaticOneArgMethodsThatShouldFail() throws Exception { - for (String methodName : STATIC_ONE_ARG_METHODS_SHOULD_FAIL) { - Method method = OneArg.class.getMethod(methodName, String.class); - boolean foundProblem = false; - try { - new NullPointerTester().testMethodParameter(new OneArg(), method, 0); - } catch (AssertionFailedError expected) { - foundProblem = true; - } - assertTrue("Should report error in method " + methodName, foundProblem); - } - } - - public void testNonStaticOneArgMethodsThatShouldPass() throws Exception { - OneArg foo = new OneArg(); - for (String methodName : NONSTATIC_ONE_ARG_METHODS_SHOULD_PASS) { - Method method = OneArg.class.getMethod(methodName, String.class); - try { - new NullPointerTester().testMethodParameter(foo, method, 0); - } catch (AssertionFailedError unexpected) { - fail("Should not have flagged method " + methodName); - } - } - } - - public void testNonStaticOneArgMethodsThatShouldFail() throws Exception { - OneArg foo = new OneArg(); - for (String methodName : NONSTATIC_ONE_ARG_METHODS_SHOULD_FAIL) { - Method method = OneArg.class.getMethod(methodName, String.class); - boolean foundProblem = false; - try { - new NullPointerTester().testMethodParameter(foo, method, 0); - } catch (AssertionFailedError expected) { - foundProblem = true; - } - assertTrue("Should report error in method " + methodName, foundProblem); - } - } - - public void testMessageOtherException() throws Exception { - Method method = OneArg.class.getMethod("staticOneArgThrowsOtherThanNpe", String.class); - boolean foundProblem = false; - try { - new NullPointerTester().testMethodParameter(new OneArg(), method, 0); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains("index 0"); - assertThat(expected.getMessage()).contains("[null]"); - foundProblem = true; - } - assertTrue("Should report error when different exception is thrown", foundProblem); - } - - public void testMessageNoException() throws Exception { - Method method = OneArg.class.getMethod("staticOneArgShouldThrowNpeButDoesnt", String.class); - boolean foundProblem = false; - try { - new NullPointerTester().testMethodParameter(new OneArg(), method, 0); - } catch (AssertionFailedError expected) { - assertThat(expected.getMessage()).contains("index 0"); - assertThat(expected.getMessage()).contains("[null]"); - foundProblem = true; - } - assertTrue("Should report error when no exception is thrown", foundProblem); - } - - /** - * Class for testing all permutations of nullable/non-nullable two-argument methods using - * testMethod(). - * - *
      - *
    • normalNormal: two params, neither is Nullable - *
    • nullableNormal: only first param is Nullable - *
    • normalNullable: only second param is Nullable - *
    • nullableNullable: both params are Nullable - *
    - */ - public static class TwoArg { - /** Action to take on a null param. */ - public enum Action { - THROW_A_NPE { - @Override - public void act() { - throw new NullPointerException(); - } - }, - THROW_OTHER { - @Override - public void act() { - throw new FooException(); - } - }, - JUST_RETURN { - @Override - public void act() {} - }; - - public abstract void act(); - } - - Action actionWhenFirstParamIsNull; - Action actionWhenSecondParamIsNull; - - public TwoArg(Action actionWhenFirstParamIsNull, Action actionWhenSecondParamIsNull) { - this.actionWhenFirstParamIsNull = actionWhenFirstParamIsNull; - this.actionWhenSecondParamIsNull = actionWhenSecondParamIsNull; - } - - /** Method that decides how to react to parameters. */ - public void reactToNullParameters(Object first, Object second) { - if (first == null) { - actionWhenFirstParamIsNull.act(); - } - if (second == null) { - actionWhenSecondParamIsNull.act(); - } - } - - /** Two-arg method with no Nullable params. */ - @SuppressWarnings("GoodTime") // false positive; b/122617528 - public void normalNormal(String first, Integer second) { - reactToNullParameters(first, second); - } - - /** Two-arg method with the second param Nullable. */ - @SuppressWarnings("GoodTime") // false positive; b/122617528 - public void normalNullable(String first, @CheckForNull Integer second) { - reactToNullParameters(first, second); - } - - /** Two-arg method with the first param Nullable. */ - @SuppressWarnings("GoodTime") // false positive; b/122617528 - public void nullableNormal(@CheckForNull String first, Integer second) { - reactToNullParameters(first, second); - } - - /** Two-arg method with the both params Nullable. */ - @SuppressWarnings("GoodTime") // false positive; b/122617528 - public void nullableNullable(@CheckForNull String first, @CheckForNull Integer second) { - reactToNullParameters(first, second); - } - - /** To provide sanity during debugging. */ - @Override - public String toString() { - return rootLocaleFormat( - "Bar(%s, %s)", actionWhenFirstParamIsNull, actionWhenSecondParamIsNull); - } - } - - public void verifyBarPass(Method method, TwoArg bar) { - try { - new NullPointerTester().testMethod(bar, method); - } catch (AssertionFailedError incorrectError) { - String errorMessage = - rootLocaleFormat("Should not have flagged method %s for %s", method.getName(), bar); - assertNull(errorMessage, incorrectError); - } - } - - public void verifyBarFail(Method method, TwoArg bar) { - try { - new NullPointerTester().testMethod(bar, method); - } catch (AssertionFailedError expected) { - return; // good...we wanted a failure - } - String errorMessage = - rootLocaleFormat("Should have flagged method %s for %s", method.getName(), bar); - fail(errorMessage); - } - - public void testTwoArgNormalNormal() throws Exception { - Method method = TwoArg.class.getMethod("normalNormal", String.class, Integer.class); - for (TwoArg.Action first : TwoArg.Action.values()) { - for (TwoArg.Action second : TwoArg.Action.values()) { - TwoArg bar = new TwoArg(first, second); - if (first.equals(TwoArg.Action.THROW_A_NPE) && second.equals(TwoArg.Action.THROW_A_NPE)) { - verifyBarPass(method, bar); // require both params to throw NPE - } else { - verifyBarFail(method, bar); - } - } - } - } - - public void testTwoArgNormalNullable() throws Exception { - Method method = TwoArg.class.getMethod("normalNullable", String.class, Integer.class); - for (TwoArg.Action first : TwoArg.Action.values()) { - for (TwoArg.Action second : TwoArg.Action.values()) { - TwoArg bar = new TwoArg(first, second); - if (first.equals(TwoArg.Action.THROW_A_NPE)) { - verifyBarPass(method, bar); // only pass if 1st param throws NPE - } else { - verifyBarFail(method, bar); - } - } - } - } - - public void testTwoArgNullableNormal() throws Exception { - Method method = TwoArg.class.getMethod("nullableNormal", String.class, Integer.class); - for (TwoArg.Action first : TwoArg.Action.values()) { - for (TwoArg.Action second : TwoArg.Action.values()) { - TwoArg bar = new TwoArg(first, second); - if (second.equals(TwoArg.Action.THROW_A_NPE)) { - verifyBarPass(method, bar); // only pass if 2nd param throws NPE - } else { - verifyBarFail(method, bar); - } - } - } - } - - public void testTwoArgNullableNullable() throws Exception { - Method method = TwoArg.class.getMethod("nullableNullable", String.class, Integer.class); - for (TwoArg.Action first : TwoArg.Action.values()) { - for (TwoArg.Action second : TwoArg.Action.values()) { - TwoArg bar = new TwoArg(first, second); - verifyBarPass(method, bar); // All args nullable: anything goes! - } - } - } - - /* - * This next part consists of several sample classes that provide - * demonstrations of conditions that cause NullPointerTester - * to succeed/fail. - */ - - /** Lots of well-behaved methods. */ - @SuppressWarnings("unused") // used by reflection - private static class PassObject extends SomeClassThatDoesNotUseNullable { - public static void doThrow(Object arg) { - if (arg == null) { - throw new FooException(); - } - } - - public void noArg() {} - - public void oneArg(String s) { - checkNotNull(s); - } - - void packagePrivateOneArg(String s) { - checkNotNull(s); - } - - protected void protectedOneArg(String s) { - checkNotNull(s); - } - - public void oneNullableArg(@CheckForNull String s) {} - - public void oneNullableArgThrows(@CheckForNull String s) { - doThrow(s); - } - - public void twoArg(String s, Integer i) { - checkNotNull(s); - i.intValue(); - } - - public void twoMixedArgs(String s, @CheckForNull Integer i) { - checkNotNull(s); - } - - public void twoMixedArgs(@CheckForNull Integer i, String s) { - checkNotNull(s); - } - - public void twoMixedArgsThrows(String s, @CheckForNull Integer i) { - checkNotNull(s); - doThrow(i); - } - - public void twoMixedArgsThrows(@CheckForNull Integer i, String s) { - checkNotNull(s); - doThrow(i); - } - - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) {} - - public void twoNullableArgsThrowsFirstArg(@CheckForNull String s, @CheckForNull Integer i) { - doThrow(s); - } - - public void twoNullableArgsThrowsSecondArg(@CheckForNull String s, @CheckForNull Integer i) { - doThrow(i); - } - - public static void staticOneArg(String s) { - checkNotNull(s); - } - - public static void staticOneNullableArg(@CheckForNull String s) {} - - public static void staticOneNullableArgThrows(@CheckForNull String s) { - doThrow(s); - } - } - - public void testGoodClass() { - shouldPass(new PassObject()); - } - - private static class FailOneArgDoesntThrowNPE extends PassObject { - @Override - public void oneArg(String s) { - // Fail: missing NPE for s - } - } - - public void testFailOneArgDoesntThrowNpe() { - shouldFail(new FailOneArgDoesntThrowNPE()); - } - - private static class FailOneArgThrowsWrongType extends PassObject { - @Override - public void oneArg(String s) { - doThrow(s); // Fail: throwing non-NPE exception for null s - } - } - - public void testFailOneArgThrowsWrongType() { - shouldFail(new FailOneArgThrowsWrongType()); - } - - private static class PassOneNullableArgThrowsNPE extends PassObject { - @Override - public void oneNullableArg(@CheckForNull String s) { - checkNotNull(s); // ok to throw NPE - } - } - - public void testPassOneNullableArgThrowsNPE() { - shouldPass(new PassOneNullableArgThrowsNPE()); - } - - private static class FailTwoArgsFirstArgDoesntThrowNPE extends PassObject { - @Override - public void twoArg(String s, Integer i) { - // Fail: missing NPE for s - i.intValue(); - } - } - - public void testFailTwoArgsFirstArgDoesntThrowNPE() { - shouldFail(new FailTwoArgsFirstArgDoesntThrowNPE()); - } - - private static class FailTwoArgsFirstArgThrowsWrongType extends PassObject { - @Override - public void twoArg(String s, Integer i) { - doThrow(s); // Fail: throwing non-NPE exception for null s - i.intValue(); - } - } - - public void testFailTwoArgsFirstArgThrowsWrongType() { - shouldFail(new FailTwoArgsFirstArgThrowsWrongType()); - } - - private static class FailTwoArgsSecondArgDoesntThrowNPE extends PassObject { - @Override - public void twoArg(String s, Integer i) { - checkNotNull(s); - // Fail: missing NPE for i - } - } - - public void testFailTwoArgsSecondArgDoesntThrowNPE() { - shouldFail(new FailTwoArgsSecondArgDoesntThrowNPE()); - } - - private static class FailTwoArgsSecondArgThrowsWrongType extends PassObject { - @Override - public void twoArg(String s, Integer i) { - checkNotNull(s); - doThrow(i); // Fail: throwing non-NPE exception for null i - } - } - - public void testFailTwoArgsSecondArgThrowsWrongType() { - shouldFail(new FailTwoArgsSecondArgThrowsWrongType()); - } - - private static class FailTwoMixedArgsFirstArgDoesntThrowNPE extends PassObject { - @Override - public void twoMixedArgs(String s, @CheckForNull Integer i) { - // Fail: missing NPE for s - } - } - - public void testFailTwoMixedArgsFirstArgDoesntThrowNPE() { - shouldFail(new FailTwoMixedArgsFirstArgDoesntThrowNPE()); - } - - private static class FailTwoMixedArgsFirstArgThrowsWrongType extends PassObject { - @Override - public void twoMixedArgs(String s, @CheckForNull Integer i) { - doThrow(s); // Fail: throwing non-NPE exception for null s - } - } - - public void testFailTwoMixedArgsFirstArgThrowsWrongType() { - shouldFail(new FailTwoMixedArgsFirstArgThrowsWrongType()); - } - - private static class PassTwoMixedArgsNullableArgThrowsNPE extends PassObject { - @Override - public void twoMixedArgs(String s, @CheckForNull Integer i) { - checkNotNull(s); - i.intValue(); // ok to throw NPE? - } - } - - public void testPassTwoMixedArgsNullableArgThrowsNPE() { - shouldPass(new PassTwoMixedArgsNullableArgThrowsNPE()); - } - - private static class PassTwoMixedArgSecondNullableArgThrowsOther extends PassObject { - @Override - public void twoMixedArgs(String s, @CheckForNull Integer i) { - checkNotNull(s); - doThrow(i); // ok to throw non-NPE exception for null i - } - } - - public void testPassTwoMixedArgSecondNullableArgThrowsOther() { - shouldPass(new PassTwoMixedArgSecondNullableArgThrowsOther()); - } - - private static class FailTwoMixedArgsSecondArgDoesntThrowNPE extends PassObject { - @Override - public void twoMixedArgs(@CheckForNull Integer i, String s) { - // Fail: missing NPE for null s - } - } - - public void testFailTwoMixedArgsSecondArgDoesntThrowNPE() { - shouldFail(new FailTwoMixedArgsSecondArgDoesntThrowNPE()); - } - - private static class FailTwoMixedArgsSecondArgThrowsWrongType extends PassObject { - @Override - public void twoMixedArgs(@CheckForNull Integer i, String s) { - doThrow(s); // Fail: throwing non-NPE exception for null s - } - } - - public void testFailTwoMixedArgsSecondArgThrowsWrongType() { - shouldFail(new FailTwoMixedArgsSecondArgThrowsWrongType()); - } - - private static class PassTwoNullableArgsFirstThrowsNPE extends PassObject { - @Override - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) { - checkNotNull(s); // ok to throw NPE? - } - } - - public void testPassTwoNullableArgsFirstThrowsNPE() { - shouldPass(new PassTwoNullableArgsFirstThrowsNPE()); - } - - private static class PassTwoNullableArgsFirstThrowsOther extends PassObject { - @Override - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) { - doThrow(s); // ok to throw non-NPE exception for null s - } - } - - public void testPassTwoNullableArgsFirstThrowsOther() { - shouldPass(new PassTwoNullableArgsFirstThrowsOther()); - } - - private static class PassTwoNullableArgsSecondThrowsNPE extends PassObject { - @Override - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) { - i.intValue(); // ok to throw NPE? - } - } - - public void testPassTwoNullableArgsSecondThrowsNPE() { - shouldPass(new PassTwoNullableArgsSecondThrowsNPE()); - } - - private static class PassTwoNullableArgsSecondThrowsOther extends PassObject { - @Override - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) { - doThrow(i); // ok to throw non-NPE exception for null i - } - } - - public void testPassTwoNullableArgsSecondThrowsOther() { - shouldPass(new PassTwoNullableArgsSecondThrowsOther()); - } - - private static class PassTwoNullableArgsNeitherThrowsAnything extends PassObject { - @Override - public void twoNullableArgs(@CheckForNull String s, @CheckForNull Integer i) { - // ok to do nothing - } - } - - public void testPassTwoNullableArgsNeitherThrowsAnything() { - shouldPass(new PassTwoNullableArgsNeitherThrowsAnything()); - } - - @SuppressWarnings("unused") // for NullPointerTester - private abstract static class BaseClassThatFailsToThrow { - public void oneArg(String s) {} - } - - private static class SubclassWithBadSuperclass extends BaseClassThatFailsToThrow {} - - public void testSubclassWithBadSuperclass() { - shouldFail(new SubclassWithBadSuperclass()); - } - - @SuppressWarnings("unused") // for NullPointerTester - private abstract static class BaseClassThatFailsToThrowForPackagePrivate { - void packagePrivateOneArg(String s) {} - } - - private static class SubclassWithBadSuperclassForPackagePrivate - extends BaseClassThatFailsToThrowForPackagePrivate {} - - public void testSubclassWithBadSuperclassForPackagePrivateMethod() { - shouldFail(new SubclassWithBadSuperclassForPackagePrivate(), Visibility.PACKAGE); - } - - @SuppressWarnings("unused") // for NullPointerTester - private abstract static class BaseClassThatFailsToThrowForProtected { - protected void protectedOneArg(String s) {} - } - - private static class SubclassWithBadSuperclassForProtected - extends BaseClassThatFailsToThrowForProtected {} - - public void testSubclassWithBadSuperclassForPackageProtectedMethod() { - shouldFail(new SubclassWithBadSuperclassForProtected(), Visibility.PROTECTED); - } - - private static class SubclassThatOverridesBadSuperclassMethod extends BaseClassThatFailsToThrow { - @Override - public void oneArg(@CheckForNull String s) {} - } - - public void testSubclassThatOverridesBadSuperclassMethod() { - shouldPass(new SubclassThatOverridesBadSuperclassMethod()); - } - - @SuppressWarnings("unused") // for NullPointerTester - private static class SubclassOverridesTheWrongMethod extends BaseClassThatFailsToThrow { - public void oneArg(@CheckForNull CharSequence s) {} - } - - public void testSubclassOverridesTheWrongMethod() { - shouldFail(new SubclassOverridesTheWrongMethod()); - } - - @SuppressWarnings("unused") // for NullPointerTester - private static class ClassThatFailsToThrowForStatic { - static void staticOneArg(String s) {} - } - - public void testClassThatFailsToThrowForStatic() { - shouldFail(ClassThatFailsToThrowForStatic.class); - } - - private static class SubclassThatFailsToThrowForStatic extends ClassThatFailsToThrowForStatic {} - - public void testSubclassThatFailsToThrowForStatic() { - shouldFail(SubclassThatFailsToThrowForStatic.class); - } - - private static class SubclassThatTriesToOverrideBadStaticMethod - extends ClassThatFailsToThrowForStatic { - static void staticOneArg(@CheckForNull String s) {} - } - - public void testSubclassThatTriesToOverrideBadStaticMethod() { - shouldFail(SubclassThatTriesToOverrideBadStaticMethod.class); - } - - private static final class HardToCreate { - private HardToCreate(HardToCreate x) {} - } - - @SuppressWarnings("unused") // used by reflection - private static class CanCreateDefault { - public void foo(@CheckForNull HardToCreate ignored, String required) { - checkNotNull(required); - } - } - - public void testCanCreateDefault() { - shouldPass(new CanCreateDefault()); - } - - @SuppressWarnings("unused") // used by reflection - private static class CannotCreateDefault { - public void foo(HardToCreate ignored, String required) { - checkNotNull(ignored); - checkNotNull(required); - } - } - - public void testCannotCreateDefault() { - shouldFail(new CannotCreateDefault()); - } - - private static void shouldPass(Object instance, Visibility visibility) { - new NullPointerTester().testInstanceMethods(instance, visibility); - } - - private static void shouldPass(Object instance) { - shouldPass(instance, Visibility.PACKAGE); - shouldPass(instance, Visibility.PROTECTED); - shouldPass(instance, Visibility.PUBLIC); - } - - // TODO(cpovirk): eliminate surprising Object/Class overloading of shouldFail - - private static void shouldFail(Object instance, Visibility visibility) { - try { - new NullPointerTester().testInstanceMethods(instance, visibility); - } catch (AssertionFailedError expected) { - return; - } - fail("Should detect problem in " + instance.getClass().getSimpleName()); - } - - private static void shouldFail(Object instance) { - shouldFail(instance, Visibility.PACKAGE); - shouldFail(instance, Visibility.PROTECTED); - shouldFail(instance, Visibility.PUBLIC); - } - - private static void shouldFail(Class cls, Visibility visibility) { - try { - new NullPointerTester().testStaticMethods(cls, visibility); - } catch (AssertionFailedError expected) { - return; - } - fail("Should detect problem in " + cls.getSimpleName()); - } - - private static void shouldFail(Class cls) { - shouldFail(cls, Visibility.PACKAGE); - } - - @SuppressWarnings("unused") // used by reflection - private static class PrivateClassWithPrivateConstructor { - private PrivateClassWithPrivateConstructor(@CheckForNull Integer argument) {} - } - - public void testPrivateClass() { - NullPointerTester tester = new NullPointerTester(); - for (Constructor constructor : - PrivateClassWithPrivateConstructor.class.getDeclaredConstructors()) { - tester.testConstructor(constructor); - } - } - - private interface Foo { - void doSomething(T bar, Integer baz); - } - - private static class StringFoo implements Foo { - - @Override - public void doSomething(String bar, Integer baz) { - checkNotNull(bar); - checkNotNull(baz); - } - } - - public void testBridgeMethodIgnored() { - new NullPointerTester().testAllPublicInstanceMethods(new StringFoo()); - } - - private abstract static class DefaultValueChecker { - - private final Map arguments = Maps.newHashMap(); - - final DefaultValueChecker runTester() { - new NullPointerTester().testInstanceMethods(this, Visibility.PACKAGE); - return this; - } - - final void assertNonNullValues(Object... expectedValues) { - assertEquals(expectedValues.length, arguments.size()); - for (int i = 0; i < expectedValues.length; i++) { - assertEquals("Default value for parameter #" + i, expectedValues[i], arguments.get(i)); - } - } - - final Object getDefaultParameterValue(int position) { - return arguments.get(position); - } - - final void calledWith(Object... args) { - for (int i = 0; i < args.length; i++) { - if (args[i] != null) { - arguments.put(i, args[i]); - } - } - for (Object arg : args) { - checkNotNull(arg); // to fulfill null check - } - } - } - - private enum Gender { - MALE, - FEMALE - } - - private static class AllDefaultValuesChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkDefaultValuesForTheseTypes( - Gender gender, - Integer integer, - int i, - String string, - CharSequence charSequence, - List list, - ImmutableList immutableList, - Map map, - ImmutableMap immutableMap, - Set set, - ImmutableSet immutableSet, - SortedSet sortedSet, - ImmutableSortedSet immutableSortedSet, - Multiset multiset, - ImmutableMultiset immutableMultiset, - Multimap multimap, - ImmutableMultimap immutableMultimap, - Table table, - ImmutableTable immutableTable) { - calledWith( - gender, - integer, - i, - string, - charSequence, - list, - immutableList, - map, - immutableMap, - set, - immutableSet, - sortedSet, - immutableSortedSet, - multiset, - immutableMultiset, - multimap, - immutableMultimap, - table, - immutableTable); - } - - final void check() { - runTester() - .assertNonNullValues( - Gender.MALE, - Integer.valueOf(0), - 0, - "", - "", - ImmutableList.of(), - ImmutableList.of(), - ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of(), - ImmutableSet.of(), - ImmutableSortedSet.of(), - ImmutableSortedSet.of(), - ImmutableMultiset.of(), - ImmutableMultiset.of(), - ImmutableMultimap.of(), - ImmutableMultimap.of(), - ImmutableTable.of(), - ImmutableTable.of()); - } - } - - public void testDefaultValues() { - new AllDefaultValuesChecker().check(); - } - - private static class ObjectArrayDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(Object[] array, String s) { - calledWith(array, s); - } - - void check() { - runTester(); - Object[] defaultArray = (Object[]) getDefaultParameterValue(0); - assertThat(defaultArray).isEmpty(); - } - } - - public void testObjectArrayDefaultValue() { - new ObjectArrayDefaultValueChecker().check(); - } - - private static class StringArrayDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(String[] array, String s) { - calledWith(array, s); - } - - void check() { - runTester(); - String[] defaultArray = (String[]) getDefaultParameterValue(0); - assertThat(defaultArray).isEmpty(); - } - } - - public void testStringArrayDefaultValue() { - new StringArrayDefaultValueChecker().check(); - } - - private static class IntArrayDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(int[] array, String s) { - calledWith(array, s); - } - - void check() { - runTester(); - int[] defaultArray = (int[]) getDefaultParameterValue(0); - assertEquals(0, defaultArray.length); - } - } - - public void testIntArrayDefaultValue() { - new IntArrayDefaultValueChecker().check(); - } - - private enum EmptyEnum {} - - private static class EmptyEnumDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(EmptyEnum object, String s) { - calledWith(object, s); - } - - void check() { - try { - runTester(); - } catch (AssertionFailedError expected) { - return; - } - fail("Should have failed because enum has no constant"); - } - } - - public void testEmptyEnumDefaultValue() { - new EmptyEnumDefaultValueChecker().check(); - } - - private static class GenericClassTypeDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(Class> cls, String s) { - calledWith(cls, s); - } - - void check() { - runTester(); - Class defaultClass = (Class) getDefaultParameterValue(0); - assertEquals(List.class, defaultClass); - } - } - - public void testGenericClassDefaultValue() { - new GenericClassTypeDefaultValueChecker().check(); - } - - private static class NonGenericClassTypeDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(@SuppressWarnings("rawtypes") Class cls, String s) { - calledWith(cls, s); - } - - void check() { - runTester(); - Class defaultClass = (Class) getDefaultParameterValue(0); - assertEquals(Object.class, defaultClass); - } - } - - public void testNonGenericClassDefaultValue() { - new NonGenericClassTypeDefaultValueChecker().check(); - } - - private static class GenericTypeTokenDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(TypeToken> type, String s) { - calledWith(type, s); - } - - void check() { - runTester(); - TypeToken defaultType = (TypeToken) getDefaultParameterValue(0); - assertTrue(new TypeToken>() {}.isSupertypeOf(defaultType)); - } - } - - public void testGenericTypeTokenDefaultValue() { - new GenericTypeTokenDefaultValueChecker().check(); - } - - private static class NonGenericTypeTokenDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(@SuppressWarnings("rawtypes") TypeToken type, String s) { - calledWith(type, s); - } - - void check() { - runTester(); - TypeToken defaultType = (TypeToken) getDefaultParameterValue(0); - assertEquals(new TypeToken() {}, defaultType); - } - } - - public void testNonGenericTypeTokenDefaultValue() { - new NonGenericTypeTokenDefaultValueChecker().check(); - } - - private interface FromTo extends Function {} - - private static class GenericInterfaceDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(FromTo f, String s) { - calledWith(f, s); - } - - void check() { - runTester(); - FromTo defaultFunction = (FromTo) getDefaultParameterValue(0); - assertEquals(0, defaultFunction.apply(null)); - } - } - - public void testGenericInterfaceDefaultValue() { - new GenericInterfaceDefaultValueChecker().check(); - } - - private interface NullRejectingFromTo extends Function { - @Override - public abstract T apply(F from); - } - - private static class NullRejectingInterfaceDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(NullRejectingFromTo f, String s) { - calledWith(f, s); - } - - void check() { - runTester(); - NullRejectingFromTo defaultFunction = - (NullRejectingFromTo) getDefaultParameterValue(0); - assertNotNull(defaultFunction); - try { - defaultFunction.apply(null); - fail("Proxy Should have rejected null"); - } catch (NullPointerException expected) { - } - } - } - - public void testNullRejectingInterfaceDefaultValue() { - new NullRejectingInterfaceDefaultValueChecker().check(); - } - - private static class MultipleInterfacesDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public & Supplier> void checkArray(T f, String s) { - calledWith(f, s); - } - - void check() { - runTester(); - FromTo defaultFunction = (FromTo) getDefaultParameterValue(0); - assertEquals(0, defaultFunction.apply(null)); - Supplier defaultSupplier = (Supplier) defaultFunction; - assertEquals(Long.valueOf(0), defaultSupplier.get()); - } - } - - public void testMultipleInterfacesDefaultValue() { - new MultipleInterfacesDefaultValueChecker().check(); - } - - private static class GenericInterface2DefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(FromTo> f, String s) { - calledWith(f, s); - } - - void check() { - runTester(); - FromTo defaultFunction = (FromTo) getDefaultParameterValue(0); - FromTo returnValue = (FromTo) defaultFunction.apply(null); - assertEquals("", returnValue.apply(null)); - } - } - - public void testGenericInterfaceReturnedByGenericMethod() { - new GenericInterface2DefaultValueChecker().check(); - } - - private abstract static class AbstractGenericDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkGeneric(T value, String s) { - calledWith(value, s); - } - } - - private static class GenericDefaultValueResolvedToStringChecker - extends AbstractGenericDefaultValueChecker { - void check() { - runTester(); - assertEquals("", getDefaultParameterValue(0)); - } - } - - public void testGenericTypeResolvedForDefaultValue() { - new GenericDefaultValueResolvedToStringChecker().check(); - } - - private abstract static class AbstractGenericDefaultValueForPackagePrivateMethodChecker - extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - void checkGeneric(T value, String s) { - calledWith(value, s); - } - } - - private static class DefaultValueForPackagePrivateMethodResolvedToStringChecker - extends AbstractGenericDefaultValueForPackagePrivateMethodChecker { - void check() { - runTester(); - assertEquals("", getDefaultParameterValue(0)); - } - } - - public void testDefaultValueResolvedForPackagePrivateMethod() { - new DefaultValueForPackagePrivateMethodResolvedToStringChecker().check(); - } - - private static class ConverterDefaultValueChecker extends DefaultValueChecker { - - @SuppressWarnings("unused") // called by NullPointerTester - public void checkArray(Converter c, String s) { - calledWith(c, s); - } - - void check() { - runTester(); - @SuppressWarnings("unchecked") // We are checking it anyway - Converter defaultConverter = - (Converter) getDefaultParameterValue(0); - assertEquals(Integer.valueOf(0), defaultConverter.convert("anything")); - assertEquals("", defaultConverter.reverse().convert(123)); - assertNull(defaultConverter.convert(null)); - assertNull(defaultConverter.reverse().convert(null)); - } - } - - public void testConverterDefaultValue() { - new ConverterDefaultValueChecker().check(); - } - - private static class VisibilityMethods { - - @SuppressWarnings("unused") // Called by reflection - private void privateMethod() {} - - @SuppressWarnings("unused") // Called by reflection - void packagePrivateMethod() {} - - @SuppressWarnings("unused") // Called by reflection - protected void protectedMethod() {} - - @SuppressWarnings("unused") // Called by reflection - public void publicMethod() {} - } - - public void testVisibility_public() throws Exception { - assertFalse( - Visibility.PUBLIC.isVisible(VisibilityMethods.class.getDeclaredMethod("privateMethod"))); - assertFalse( - Visibility.PUBLIC.isVisible( - VisibilityMethods.class.getDeclaredMethod("packagePrivateMethod"))); - assertFalse( - Visibility.PUBLIC.isVisible(VisibilityMethods.class.getDeclaredMethod("protectedMethod"))); - assertTrue( - Visibility.PUBLIC.isVisible(VisibilityMethods.class.getDeclaredMethod("publicMethod"))); - } - - public void testVisibility_protected() throws Exception { - assertFalse( - Visibility.PROTECTED.isVisible(VisibilityMethods.class.getDeclaredMethod("privateMethod"))); - assertFalse( - Visibility.PROTECTED.isVisible( - VisibilityMethods.class.getDeclaredMethod("packagePrivateMethod"))); - assertTrue( - Visibility.PROTECTED.isVisible( - VisibilityMethods.class.getDeclaredMethod("protectedMethod"))); - assertTrue( - Visibility.PROTECTED.isVisible(VisibilityMethods.class.getDeclaredMethod("publicMethod"))); - } - - public void testVisibility_package() throws Exception { - assertFalse( - Visibility.PACKAGE.isVisible(VisibilityMethods.class.getDeclaredMethod("privateMethod"))); - assertTrue( - Visibility.PACKAGE.isVisible( - VisibilityMethods.class.getDeclaredMethod("packagePrivateMethod"))); - assertTrue( - Visibility.PACKAGE.isVisible(VisibilityMethods.class.getDeclaredMethod("protectedMethod"))); - assertTrue( - Visibility.PACKAGE.isVisible(VisibilityMethods.class.getDeclaredMethod("publicMethod"))); - } - - private class Inner { - public Inner(String s) { - checkNotNull(s); - } - } - - public void testNonStaticInnerClass() { - try { - new NullPointerTester().testAllPublicConstructors(Inner.class); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("inner class"); - } - } - - private static String rootLocaleFormat(String format, Object... args) { - return String.format(Locale.ROOT, format, args); - } - - static class OverridesEquals { - @SuppressWarnings("EqualsHashCode") - @Override - public boolean equals(Object o) { - return true; - } - } - - static class DoesNotOverrideEquals { - public boolean equals(Object a, Object b) { - return true; - } - } - - public void testEqualsMethod() { - shouldPass(new OverridesEquals()); - shouldFail(new DoesNotOverrideEquals()); - } - - private static final class FailOnOneOfTwoConstructors { - @SuppressWarnings("unused") // Called by reflection - public FailOnOneOfTwoConstructors(String s) {} - - @SuppressWarnings("unused") // Called by reflection - public FailOnOneOfTwoConstructors(Object o) { - checkNotNull(o); - } - } - - public void testConstructor_Ignored_ShouldPass() throws Exception { - new NullPointerTester() - .ignore(FailOnOneOfTwoConstructors.class.getDeclaredConstructor(String.class)) - .testAllPublicConstructors(FailOnOneOfTwoConstructors.class); - } - - public void testConstructor_ShouldFail() throws Exception { - try { - new NullPointerTester().testAllPublicConstructors(FailOnOneOfTwoConstructors.class); - } catch (AssertionFailedError expected) { - return; - } - fail("Should detect problem in " + FailOnOneOfTwoConstructors.class.getSimpleName()); - } -} diff --git a/android/guava-testlib/test/com/google/common/testing/PackageSanityTests.java b/android/guava-testlib/test/com/google/common/testing/PackageSanityTests.java index d5483d36f092..1770e3376685 100644 --- a/android/guava-testlib/test/com/google/common/testing/PackageSanityTests.java +++ b/android/guava-testlib/test/com/google/common/testing/PackageSanityTests.java @@ -16,7 +16,9 @@ package com.google.common.testing; +import org.jspecify.annotations.NullUnmarked; /** Test nulls for the entire package. */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests {} diff --git a/android/guava-testlib/test/com/google/common/testing/ReflectionFreeAssertThrows.java b/android/guava-testlib/test/com/google/common/testing/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..d08735297ad5 --- /dev/null +++ b/android/guava-testlib/test/com/google/common/testing/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.testing; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-testlib/test/com/google/common/testing/RelationshipTesterTest.java b/android/guava-testlib/test/com/google/common/testing/RelationshipTesterTest.java index 943c2951e3a0..e5c597892fb8 100644 --- a/android/guava-testlib/test/com/google/common/testing/RelationshipTesterTest.java +++ b/android/guava-testlib/test/com/google/common/testing/RelationshipTesterTest.java @@ -18,12 +18,14 @@ import com.google.common.testing.RelationshipTester.ItemReporter; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link RelationshipTester}. * * @author Ben Yu */ +@NullUnmarked public class RelationshipTesterTest extends TestCase { public void testNulls() { diff --git a/android/guava-testlib/test/com/google/common/testing/SerializableTesterTest.java b/android/guava-testlib/test/com/google/common/testing/SerializableTesterTest.java index 753c4ab63118..67f11de2e6ed 100644 --- a/android/guava-testlib/test/com/google/common/testing/SerializableTesterTest.java +++ b/android/guava-testlib/test/com/google/common/testing/SerializableTesterTest.java @@ -19,12 +19,15 @@ import java.io.Serializable; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link SerializableTester}. * * @author Nick Kralevich */ +@NullUnmarked public class SerializableTesterTest extends TestCase { public void testStringAssertions() { String original = "hello world"; @@ -82,7 +85,7 @@ private static class ClassWhichIsAlwaysEqualButHasDifferentHashcodes implements @SuppressWarnings("EqualsHashCode") @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return (other instanceof ClassWhichIsAlwaysEqualButHasDifferentHashcodes); } } @@ -91,7 +94,7 @@ private static class ObjectWhichIsEqualButChangesClass implements Serializable { private static final long serialVersionUID = 1L; @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return (other instanceof ObjectWhichIsEqualButChangesClass || other instanceof OtherForm); } @@ -106,7 +109,7 @@ private Object writeReplace() { private static class OtherForm implements Serializable { @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return (other instanceof ObjectWhichIsEqualButChangesClass || other instanceof OtherForm); } diff --git a/android/guava-testlib/test/com/google/common/testing/TearDownStackTest.java b/android/guava-testlib/test/com/google/common/testing/TearDownStackTest.java index 5a4f9ede4861..9a5a68dfb055 100644 --- a/android/guava-testlib/test/com/google/common/testing/TearDownStackTest.java +++ b/android/guava-testlib/test/com/google/common/testing/TearDownStackTest.java @@ -20,9 +20,14 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; -/** @author Luiz-Otavio "Z" Zorzella */ +/** + * @author Luiz-Otavio "Z" Zorzella + */ @GwtCompatible +@NullUnmarked public class TearDownStackTest extends TestCase { private TearDownStack tearDownStack = new TearDownStack(); @@ -116,7 +121,7 @@ private TearDownStack buildTearDownStack() { @Override public void tearDown() throws Exception { - synchronized (result.stack) { + synchronized (result.lock) { assertEquals( "The test should have cleared the stack (say, by virtue of running runTearDown)", 0, @@ -146,7 +151,7 @@ public void tearDown() throws Exception { private static final class SimpleTearDown implements TearDown { boolean ran = false; - Callback callback = null; + @Nullable Callback callback = null; public SimpleTearDown() {} diff --git a/android/guava-testlib/test/com/google/common/testing/TestLogHandlerTest.java b/android/guava-testlib/test/com/google/common/testing/TestLogHandlerTest.java index 212f6af9f0e9..4af6b6b3ed94 100644 --- a/android/guava-testlib/test/com/google/common/testing/TestLogHandlerTest.java +++ b/android/guava-testlib/test/com/google/common/testing/TestLogHandlerTest.java @@ -20,12 +20,14 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link TestLogHandler}. * * @author kevinb */ +@NullUnmarked public class TestLogHandlerTest extends TestCase { private TestLogHandler handler; diff --git a/android/guava-testlib/test/com/google/common/testing/anotherpackage/ForwardingWrapperTesterTest.java b/android/guava-testlib/test/com/google/common/testing/anotherpackage/ForwardingWrapperTesterTest.java index 6e3bf2397dee..e3d176311eba 100644 --- a/android/guava-testlib/test/com/google/common/testing/anotherpackage/ForwardingWrapperTesterTest.java +++ b/android/guava-testlib/test/com/google/common/testing/anotherpackage/ForwardingWrapperTesterTest.java @@ -17,6 +17,7 @@ package com.google.common.testing.anotherpackage; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.base.Equivalence; import com.google.common.base.Function; @@ -28,12 +29,14 @@ import com.google.common.primitives.UnsignedLong; import com.google.common.testing.ForwardingWrapperTester; import com.google.common.testing.NullPointerTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.InputStream; import java.nio.charset.Charset; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ForwardingWrapperTester}. Live in a different package to detect reflection @@ -119,7 +122,7 @@ public Runnable apply(final Runnable runnable) { @SuppressWarnings("EqualsHashCode") @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof ForwardingRunnable) { ForwardingRunnable that = (ForwardingRunnable) o; return runnable.equals(that.runnable); @@ -141,7 +144,7 @@ public void testEqualsAndHashCodeForwarded() { public Runnable apply(final Runnable runnable) { return new ForwardingRunnable(runnable) { @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof ForwardingRunnable) { ForwardingRunnable that = (ForwardingRunnable) o; return runnable.equals(that.runnable); @@ -255,7 +258,7 @@ public void testFailsToPropagateException() { new Function() { @Override public Adder apply(Adder adder) { - return new FailsToPropagageException(adder); + return new FailsToPropagateException(adder); } }, "add(", @@ -263,11 +266,11 @@ public Adder apply(Adder adder) { } public void testNotInterfaceType() { - try { - new ForwardingWrapperTester().testForwarding(String.class, Functions.identity()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new ForwardingWrapperTester() + .testForwarding(String.class, Functions.identity())); } public void testNulls() { @@ -284,7 +287,7 @@ private void assertFailure( tester.testForwarding(interfaceType, wrapperFunction); } catch (AssertionFailedError expected) { for (String message : expectedMessages) { - assertThat(expected.getMessage()).contains(message); + assertThat(expected).hasMessageThat().contains(message); } return; } @@ -373,18 +376,19 @@ public String toString() { } } - private static class FailsToPropagageException implements Adder { + private static class FailsToPropagateException implements Adder { private final Adder adder; - FailsToPropagageException(Adder adder) { + FailsToPropagateException(Adder adder) { this.adder = adder; } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public int add(int a, int b) { try { return adder.add(a, b); - } catch (Exception e) { + } catch (Exception e) { // sneaky checked exception // swallow! return 0; } @@ -530,7 +534,7 @@ public String toString() { private interface Equals { @Override - boolean equals(Object obj); + boolean equals(@Nullable Object obj); @Override int hashCode(); @@ -579,7 +583,9 @@ public void testExplicitEqualsAndHashCodeDelegatedWhenExplicitlyAsked() { /** An interface for the 2 ways that a chaining call might be defined. */ private interface ChainingCalls { // A method that is defined to 'return this' + @CanIgnoreReturnValue ChainingCalls chainingCall(); + // A method that just happens to return a ChainingCalls object ChainingCalls nonChainingCall(); } @@ -591,6 +597,7 @@ private static class ForwardingChainingCalls implements ChainingCalls { this.delegate = delegate; } + @CanIgnoreReturnValue @Override public ForwardingChainingCalls chainingCall() { delegate.chainingCall(); diff --git a/android/guava-testlib/test/com/google/common/util/concurrent/testing/TestingExecutorsTest.java b/android/guava-testlib/test/com/google/common/util/concurrent/testing/TestingExecutorsTest.java index 66ad78491c9c..1944dfda3e63 100644 --- a/android/guava-testlib/test/com/google/common/util/concurrent/testing/TestingExecutorsTest.java +++ b/android/guava-testlib/test/com/google/common/util/concurrent/testing/TestingExecutorsTest.java @@ -16,6 +16,9 @@ package com.google.common.util.concurrent.testing; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import java.util.List; @@ -24,7 +27,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import junit.framework.TestCase; /** @@ -45,7 +47,7 @@ public void run() { } }; ScheduledFuture future = - TestingExecutors.noOpScheduledExecutor().schedule(task, 10, TimeUnit.MILLISECONDS); + TestingExecutors.noOpScheduledExecutor().schedule(task, 10, MILLISECONDS); Thread.sleep(20); assertFalse(taskDone); assertFalse(future.isDone()); @@ -71,17 +73,11 @@ public Boolean call() { return taskDone; } }; - List> futureList = - executor.invokeAll(ImmutableList.of(task), 10, TimeUnit.MILLISECONDS); + List> futureList = executor.invokeAll(ImmutableList.of(task), 10, MILLISECONDS); Future future = futureList.get(0); assertFalse(taskDone); assertTrue(future.isDone()); - try { - future.get(); - fail(); - } catch (CancellationException e) { - // pass - } + assertThrows(CancellationException.class, () -> future.get()); } public void testSameThreadScheduledExecutor() throws ExecutionException, InterruptedException { @@ -95,7 +91,7 @@ public Integer call() { } }; Future future = - TestingExecutors.sameThreadScheduledExecutor().schedule(task, 10000, TimeUnit.MILLISECONDS); + TestingExecutors.sameThreadScheduledExecutor().schedule(task, 10000, MILLISECONDS); assertTrue("Should run callable immediately", taskDone); assertEquals(6, (int) future.get()); } @@ -110,11 +106,6 @@ public void run() { }; Future future = TestingExecutors.sameThreadScheduledExecutor().submit(runnable); - try { - future.get(); - fail("Should have thrown exception"); - } catch (ExecutionException e) { - // pass - } + assertThrows(ExecutionException.class, () -> future.get()); } } diff --git a/android/guava-tests/benchmark/com/google/common/base/AsciiBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/AsciiBenchmark.java index 6ed8006ee903..2727d3723fe6 100644 --- a/android/guava-tests/benchmark/com/google/common/base/AsciiBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/AsciiBenchmark.java @@ -25,12 +25,14 @@ import java.util.List; import java.util.Locale; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the ASCII class. * * @author Kevin Bourrillion */ +@NullUnmarked public class AsciiBenchmark { private static final String ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final String NONALPHA = "0123456789`~-_=+[]{}|;:',.<>/?!@#$%^&*()\"\\"; diff --git a/android/guava-tests/benchmark/com/google/common/base/CharMatcherBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/CharMatcherBenchmark.java index 9dee99b7ab6d..dcc5532ea7bd 100644 --- a/android/guava-tests/benchmark/com/google/common/base/CharMatcherBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/CharMatcherBenchmark.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.List; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmark for the {@link CharMatcher} class. @@ -33,6 +34,7 @@ * @author Kevin Bourrillion * @author David Richter */ +@NullUnmarked public class CharMatcherBenchmark { // Caliper injects params automatically @@ -78,7 +80,6 @@ void setUp() { if (size == Size.SMALL) { BitSet tmp = new BitSet(); matcher.setBits(tmp); - int matchedCharCount = tmp.cardinality(); this.matcher = SmallCharMatcher.from(tmp, ""); } this.string = checkString(length, percent, config.matchingChars, new Random(), forceSlow, web); @@ -130,9 +131,9 @@ private static String checkString( list.set(list.indexOf(0), list.get(0)); list.set(0, 0); } - // Get threshold in the range [0, length], rounding up to ensure that non - // zero percent values result in a non-zero threshold (so we always have at - // least one matching character). + // Get threshold in the range [0, length], rounding up to ensure that + // non-zero percent values result in a non-zero threshold (so we always + // have at least one matching character). int threshold = ((percent * length) + 99) / 100; StringBuilder builder = new StringBuilder(length); for (int n = 0; n < length; n++) { diff --git a/android/guava-tests/benchmark/com/google/common/base/EnumsBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/EnumsBenchmark.java index 73cda9af8629..79143409d672 100644 --- a/android/guava-tests/benchmark/com/google/common/base/EnumsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/EnumsBenchmark.java @@ -22,8 +22,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.NullUnmarked; @SuppressWarnings("unused") // Nested enums used reflectively in setUp. +@NullUnmarked public class EnumsBenchmark { @Param({"Small", "Medium", "Large"}) @@ -32,17 +34,20 @@ public class EnumsBenchmark { @Param({"0.2", "0.8"}) float hitRate; + // We could avoid the raw type here by initializing this with a ternary (? SmallEnum.class : ...). + // However, we end up needing a raw type in getIfPresent, as discussed there. + @SuppressWarnings("rawtypes") private Class enumType; + private String[] sampleData; @BeforeExperiment - @SuppressWarnings("unchecked") void setUp() throws ClassNotFoundException { Preconditions.checkArgument(hitRate >= 0 && hitRate <= 1, "hitRate must be in the range [0,1]"); enumType = - (Class) - Class.forName(EnumsBenchmark.class.getCanonicalName() + "$" + enumSize + "Enum"); + Class.forName(EnumsBenchmark.class.getCanonicalName() + "$" + enumSize + "Enum") + .asSubclass(Enum.class); Enum[] allConstants = enumType.getEnumConstants(); List hits = new ArrayList<>(); @@ -64,6 +69,8 @@ void setUp() throws ClassNotFoundException { sampleData = sampleDataList.toArray(new String[sampleDataList.size()]); } + // Since we can't pass a concrete SomeEnum.class directly, we need to use a raw type. + @SuppressWarnings("unchecked") @Benchmark boolean getIfPresent(int repetitions) { boolean retVal = false; diff --git a/android/guava-tests/benchmark/com/google/common/base/JoinerBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/JoinerBenchmark.java index 99728327e9bb..1d53f79f00af 100644 --- a/android/guava-tests/benchmark/com/google/common/base/JoinerBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/JoinerBenchmark.java @@ -21,12 +21,14 @@ import com.google.caliper.Param; import java.util.Arrays; import java.util.Iterator; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks {@link Joiner} against some common implementations of delimiter-based string joining. * * @author Adomas Paltanavicius */ +@NullUnmarked public class JoinerBenchmark { private static final String DELIMITER_STRING = ","; @@ -44,6 +46,7 @@ public class JoinerBenchmark { private Iterable components; @BeforeExperiment + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 void setUp() { String component = Strings.repeat("a", componentLength); String[] raw = new String[count]; diff --git a/android/guava-tests/benchmark/com/google/common/base/LazyStackTraceBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/LazyStackTraceBenchmark.java index 5f79d0c42366..f4843bddcaef 100644 --- a/android/guava-tests/benchmark/com/google/common/base/LazyStackTraceBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/LazyStackTraceBenchmark.java @@ -24,11 +24,13 @@ import com.google.caliper.Param; import com.google.caliper.api.SkipThisScenarioException; import java.util.List; +import org.jspecify.annotations.NullUnmarked; /** * Quick and dirty benchmark of {@link Throwables#lazyStackTrace(Throwable)}. We benchmark a "caller * finder" implementation that might be used in a logging framework. */ +@NullUnmarked public class LazyStackTraceBenchmark { @Param({"20", "200", "2000"}) int stackDepth; diff --git a/android/guava-tests/benchmark/com/google/common/base/ObjectsBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/ObjectsBenchmark.java index 561f76dca98b..82385daa3725 100644 --- a/android/guava-tests/benchmark/com/google/common/base/ObjectsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/ObjectsBenchmark.java @@ -17,12 +17,14 @@ package com.google.common.base; import com.google.caliper.Benchmark; +import org.jspecify.annotations.NullUnmarked; /** * Some microbenchmarks for the {@link com.google.common.base.Objects} class. * * @author Ben L. Titzer */ +@NullUnmarked public class ObjectsBenchmark { private static final Integer I0 = -45; diff --git a/android/guava-tests/benchmark/com/google/common/base/SplitterBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/SplitterBenchmark.java index 935951994eb4..f8a53e45e9b1 100644 --- a/android/guava-tests/benchmark/com/google/common/base/SplitterBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/SplitterBenchmark.java @@ -20,16 +20,19 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import com.google.common.collect.Iterables; +import org.jspecify.annotations.NullUnmarked; /** * Microbenchmark for {@link Splitter#on} with char vs String with length == 1. * * @author Paul Lindner */ +@NullUnmarked public class SplitterBenchmark { // overall size of string @Param({"1", "10", "100", "1000"}) int length; + // Number of matching strings @Param({"xxxx", "xxXx", "xXxX", "XXXX"}) String text; @@ -40,25 +43,30 @@ public class SplitterBenchmark { private static final Splitter STRING_SPLITTER = Splitter.on("X"); @BeforeExperiment + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 void setUp() { input = Strings.repeat(text, length); } @Benchmark - void charSplitter(int reps) { + int charSplitter(int reps) { int total = 0; for (int i = 0; i < reps; i++) { total += Iterables.size(CHAR_SPLITTER.split(input)); } + + return total; } @Benchmark - void stringSplitter(int reps) { + int stringSplitter(int reps) { int total = 0; for (int i = 0; i < reps; i++) { total += Iterables.size(STRING_SPLITTER.split(input)); } + + return total; } } diff --git a/android/guava-tests/benchmark/com/google/common/base/StopwatchBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/StopwatchBenchmark.java index 692ed365a230..21e71774391e 100644 --- a/android/guava-tests/benchmark/com/google/common/base/StopwatchBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/StopwatchBenchmark.java @@ -16,8 +16,10 @@ package com.google.common.base; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + import com.google.caliper.Benchmark; -import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.NullUnmarked; /** * Simple benchmark: create, start, read. This does not currently report the most useful result @@ -25,6 +27,7 @@ * * @author Kevin Bourrillion */ +@NullUnmarked public class StopwatchBenchmark { @Benchmark long stopwatch(int reps) { @@ -32,7 +35,7 @@ long stopwatch(int reps) { for (int i = 0; i < reps; i++) { Stopwatch s = Stopwatch.createStarted(); // here is where you would do something - total += s.elapsed(TimeUnit.NANOSECONDS); + total += s.elapsed(NANOSECONDS); } return total; } diff --git a/android/guava-tests/benchmark/com/google/common/base/StringsRepeatBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/StringsRepeatBenchmark.java index 16e00ef3d2e2..3750ca53f9c2 100644 --- a/android/guava-tests/benchmark/com/google/common/base/StringsRepeatBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/StringsRepeatBenchmark.java @@ -19,12 +19,14 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; +import org.jspecify.annotations.NullUnmarked; /** * Microbenchmark for {@link com.google.common.base.Strings#repeat} * * @author Mike Cripps */ +@NullUnmarked public class StringsRepeatBenchmark { @Param({"1", "5", "25", "125"}) int count; @@ -35,6 +37,7 @@ public class StringsRepeatBenchmark { private String originalString; @BeforeExperiment + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 void setUp() { originalString = Strings.repeat("x", length); } diff --git a/android/guava-tests/benchmark/com/google/common/base/ToStringHelperBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/ToStringHelperBenchmark.java index 47dd8f46a154..c0241db44f9b 100644 --- a/android/guava-tests/benchmark/com/google/common/base/ToStringHelperBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/ToStringHelperBenchmark.java @@ -20,12 +20,14 @@ import com.google.caliper.Param; import java.util.Arrays; import java.util.Collections; +import org.jspecify.annotations.NullUnmarked; /** * Some microbenchmarks for the {@link MoreObjects.ToStringHelper} class. * * @author Osvaldo Doederlein */ +@NullUnmarked public class ToStringHelperBenchmark { @Param({"0", "1", "5"}) @@ -36,6 +38,7 @@ public class ToStringHelperBenchmark { enum Dataset { SMALL { + @Override void addEntries(MoreObjects.ToStringHelper helper) { helper .add(SHORT_NAME, 10) @@ -47,6 +50,7 @@ void addEntries(MoreObjects.ToStringHelper helper) { } }, CONDITIONAL { + @Override void addEntries(MoreObjects.ToStringHelper helper) { helper .add(SHORT_NAME, "x") @@ -82,6 +86,7 @@ void addEntries(MoreObjects.ToStringHelper helper) { } }, UNCONDITIONAL { + @Override void addEntries(MoreObjects.ToStringHelper helper) { helper .add(SHORT_NAME, false) diff --git a/android/guava-tests/benchmark/com/google/common/base/WhitespaceMatcherBenchmark.java b/android/guava-tests/benchmark/com/google/common/base/WhitespaceMatcherBenchmark.java index fd3c9c4dc255..2793fc5e08d4 100644 --- a/android/guava-tests/benchmark/com/google/common/base/WhitespaceMatcherBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/base/WhitespaceMatcherBenchmark.java @@ -21,8 +21,10 @@ import com.google.caliper.Param; import java.util.BitSet; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** Benchmark for the {@link CharMatcher#whitespace} implementation. */ +@NullUnmarked public class WhitespaceMatcherBenchmark { private static final int STRING_LENGTH = 10000; diff --git a/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java index 43fc75cff318..01d47a6e47f6 100644 --- a/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/cache/ChainBenchmark.java @@ -20,12 +20,16 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import com.google.common.cache.LocalCache.Segment; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Benchmark for {@code LocalCache.Segment.removeEntryFromChain}. * * @author Charles Fry */ +@SuppressWarnings("CheckReturnValue") +@NullUnmarked public class ChainBenchmark { @Param({"1", "2", "3", "4", "5", "6"}) @@ -33,7 +37,7 @@ public class ChainBenchmark { private Segment segment; private ReferenceEntry head; - private ReferenceEntry chain; + private @Nullable ReferenceEntry chain; @SuppressWarnings("GuardedBy") @BeforeExperiment diff --git a/android/guava-tests/benchmark/com/google/common/cache/LoadingCacheSingleThreadBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/LoadingCacheSingleThreadBenchmark.java index b15de8f8892e..3383b407b448 100644 --- a/android/guava-tests/benchmark/com/google/common/cache/LoadingCacheSingleThreadBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/cache/LoadingCacheSingleThreadBenchmark.java @@ -23,12 +23,14 @@ import com.google.common.primitives.Ints; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; +import org.jspecify.annotations.NullUnmarked; /** * Single-threaded benchmark for {@link LoadingCache}. * * @author Charles Fry */ +@NullUnmarked public class LoadingCacheSingleThreadBenchmark { @Param({"1000", "2000"}) int maximumSize; diff --git a/android/guava-tests/benchmark/com/google/common/cache/MapMakerComparisonBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/MapMakerComparisonBenchmark.java index 5fa6cbc94d14..5b8fc509acfe 100644 --- a/android/guava-tests/benchmark/com/google/common/cache/MapMakerComparisonBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/cache/MapMakerComparisonBenchmark.java @@ -20,12 +20,14 @@ import com.google.caliper.Benchmark; import com.google.common.collect.MapMaker; import java.util.Map; +import org.jspecify.annotations.NullUnmarked; /** * Compare CacheBuilder and MapMaker performance, ensuring that they remain on par with each other. * * @author Nikita Sidorov */ +@NullUnmarked public class MapMakerComparisonBenchmark { private static final String TEST_KEY = "test key"; private static final String TEST_VALUE = "test value"; diff --git a/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java b/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java index a473d75e585d..e50da889645e 100644 --- a/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/cache/SegmentBenchmark.java @@ -23,12 +23,14 @@ import com.google.caliper.Param; import com.google.common.cache.LocalCache.Segment; import java.util.concurrent.atomic.AtomicReferenceArray; +import org.jspecify.annotations.NullUnmarked; /** * Benchmark for {@code LocalCache.Segment.expand()}. * * @author Charles Fry */ +@NullUnmarked public class SegmentBenchmark { @Param({"16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192"}) diff --git a/android/guava-tests/benchmark/com/google/common/collect/BinaryTreeTraverserBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/BinaryTreeTraverserBenchmark.java index 75fc1f90d2ef..76e04fddfc81 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/BinaryTreeTraverserBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/BinaryTreeTraverserBenchmark.java @@ -21,12 +21,14 @@ import com.google.common.primitives.Ints; import java.util.List; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the {@code TreeTraverser} operations on binary trees. * * @author Louis Wasserman */ +@NullUnmarked public class BinaryTreeTraverserBenchmark { private static class BinaryNode { final int x; diff --git a/android/guava-tests/benchmark/com/google/common/collect/ComparatorDelegationOverheadBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/ComparatorDelegationOverheadBenchmark.java index 856600013e91..9d618b5fb9d3 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/ComparatorDelegationOverheadBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/ComparatorDelegationOverheadBenchmark.java @@ -14,12 +14,15 @@ package com.google.common.collect; +import static java.util.Arrays.sort; + import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.util.Arrays; import java.util.Comparator; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * A benchmark to determine the overhead of sorting with {@link Ordering#from(Comparator)}, or with @@ -28,6 +31,7 @@ * * @author Louis Wasserman */ +@NullUnmarked public class ComparatorDelegationOverheadBenchmark { private final Integer[][] inputArrays = new Integer[0x100][]; @@ -51,7 +55,7 @@ int arraysSortNoComparator(int reps) { int tmp = 0; for (int i = 0; i < reps; i++) { Integer[] copy = inputArrays[i & 0xFF].clone(); - Arrays.sort(copy); + sort(copy); tmp += copy[0]; } return tmp; @@ -62,7 +66,7 @@ int arraysSortOrderingNatural(int reps) { int tmp = 0; for (int i = 0; i < reps; i++) { Integer[] copy = inputArrays[i & 0xFF].clone(); - Arrays.sort(copy, Ordering.natural()); + sort(copy, Ordering.natural()); tmp += copy[0]; } return tmp; @@ -81,7 +85,7 @@ int arraysSortOrderingFromNatural(int reps) { int tmp = 0; for (int i = 0; i < reps; i++) { Integer[] copy = inputArrays[i & 0xFF].clone(); - Arrays.sort(copy, Ordering.from(NATURAL_INTEGER)); + sort(copy, Ordering.from(NATURAL_INTEGER)); tmp += copy[0]; } return tmp; diff --git a/android/guava-tests/benchmark/com/google/common/collect/ConcurrentHashMultisetBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/ConcurrentHashMultisetBenchmark.java index 086a634932f3..1001350a0513 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/ConcurrentHashMultisetBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/ConcurrentHashMultisetBenchmark.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.Lists.newArrayListWithExpectedSize; import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; @@ -37,13 +38,15 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Benchmarks for {@link ConcurrentHashMultiset}. * * @author mike nonemacher */ +@NullUnmarked public class ConcurrentHashMultisetBenchmark { @Param({"1", "2", "4", "8"}) int threads; @@ -192,7 +195,7 @@ public static OldConcurrentHashMultiset create() { * @return the nonnegative number of occurrences of the element */ @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { try { return unbox(countMap.get(element)); } catch (NullPointerException | ClassCastException e) { @@ -235,7 +238,7 @@ public T[] toArray(T[] array) { * either of these would recurse back to us again! */ private List snapshot() { - List list = Lists.newArrayListWithExpectedSize(size()); + List list = newArrayListWithExpectedSize(size()); for (Multiset.Entry entry : entrySet()) { E element = entry.getElement(); for (int i = entry.getCount(); i > 0; i--) { @@ -295,7 +298,7 @@ public int add(E element, int occurrences) { * @throws IllegalArgumentException if {@code occurrences} is negative */ @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { if (occurrences == 0) { return count(element); } @@ -330,7 +333,7 @@ public int remove(@CheckForNull Object element, int occurrences) { * @param element the element whose occurrences should all be removed * @return the number of occurrences successfully removed, possibly zero */ - private int removeAllOccurrences(@CheckForNull Object element) { + private int removeAllOccurrences(@Nullable Object element) { try { return unbox(countMap.remove(element)); } catch (NullPointerException | ClassCastException e) { @@ -349,7 +352,7 @@ private int removeAllOccurrences(@CheckForNull Object element) { * @param occurrences the number of occurrences of {@code element} to remove * @return {@code true} if the removal was possible (including if {@code occurrences} is zero) */ - public boolean removeExactly(@CheckForNull Object element, int occurrences) { + public boolean removeExactly(@Nullable Object element, int occurrences) { if (occurrences == 0) { return true; } @@ -518,7 +521,7 @@ public T[] toArray(T[] array) { } private List> snapshot() { - List> list = Lists.newArrayListWithExpectedSize(size()); + List> list = newArrayListWithExpectedSize(size()); // not Iterables.addAll(list, this), because that'll forward back here Iterators.addAll(list, iterator()); return list; @@ -543,7 +546,7 @@ public int hashCode() { } /** We use a special form of unboxing that treats null as zero. */ - private static int unbox(@CheckForNull Integer i) { + private static int unbox(@Nullable Integer i) { return (i == null) ? 0 : i; } } diff --git a/android/guava-tests/benchmark/com/google/common/collect/HashMultisetAddPresentBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/HashMultisetAddPresentBenchmark.java index a109cc2ed324..f48d27cf5cc2 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/HashMultisetAddPresentBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/HashMultisetAddPresentBenchmark.java @@ -19,12 +19,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmark for HashMultiset.add for an already-present element. * * @author Louis Wasserman */ +@NullUnmarked public class HashMultisetAddPresentBenchmark { private static final int ARRAY_MASK = 0x0ffff; private static final int ARRAY_SIZE = 0x10000; diff --git a/android/guava-tests/benchmark/com/google/common/collect/ImmutableListCreationBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/ImmutableListCreationBenchmark.java index ce0e24abb616..a2c0f3976b95 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/ImmutableListCreationBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/ImmutableListCreationBenchmark.java @@ -19,12 +19,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.util.List; +import org.jspecify.annotations.NullUnmarked; /** * Benchmark for various ways to create an {@code ImmutableList}. * * @author Louis Wasserman */ +@NullUnmarked public class ImmutableListCreationBenchmark { @Param({"10", "1000", "1000000"}) diff --git a/android/guava-tests/benchmark/com/google/common/collect/InternersBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/InternersBenchmark.java index 2b7ef36c700a..b709e7def074 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/InternersBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/InternersBenchmark.java @@ -17,31 +17,37 @@ package com.google.common.collect; import com.google.caliper.Benchmark; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarking interners. * * @author Dimitris Andreou */ +@NullUnmarked public class InternersBenchmark { + @CanIgnoreReturnValue @Benchmark int weakInterner(int reps) { Interner interner = Interners.newWeakInterner(); for (int i = 0; i < reps; i++) { - interner.intern(Double.toHexString(Math.random())); + String unused = interner.intern(Double.toHexString(Math.random())); } return reps; } + @CanIgnoreReturnValue @Benchmark int strongInterner(int reps) { Interner interner = Interners.newStrongInterner(); for (int i = 0; i < reps; i++) { - interner.intern(Double.toHexString(Math.random())); + String unused = interner.intern(Double.toHexString(Math.random())); } return reps; } + @CanIgnoreReturnValue @Benchmark int stringIntern(int reps) { for (int i = 0; i < reps; i++) { diff --git a/android/guava-tests/benchmark/com/google/common/collect/IteratorBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/IteratorBenchmark.java index 20e832dd882e..931d86cc9ed8 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/IteratorBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/IteratorBenchmark.java @@ -21,12 +21,14 @@ import com.google.caliper.Param; import java.util.ArrayList; import java.util.LinkedList; +import org.jspecify.annotations.NullUnmarked; /** * Tests the speed of iteration of different iteration methods for collections. * * @author David Richter */ +@NullUnmarked public class IteratorBenchmark { @Param({"0", "1", "16", "256", "4096", "65536"}) int size; diff --git a/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java index f10b94ea6e10..4c71174b5073 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/MapBenchmark.java @@ -17,6 +17,8 @@ package com.google.common.collect; import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.sort; +import static java.util.Collections.unmodifiableMap; import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; @@ -28,6 +30,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; +import org.jspecify.annotations.NullUnmarked; /** * A microbenchmark that tests the performance of get() and iteration on various map @@ -35,6 +38,7 @@ * * @author Nicholaus Shupe */ +@NullUnmarked public class MapBenchmark { @Param({"Hash", "LinkedHM", "MapMaker1", "Immutable"}) private Impl impl; @@ -63,7 +67,7 @@ Map create(Collection keys) { UnmodHM { @Override Map create(Collection keys) { - return Collections.unmodifiableMap(Hash.create(keys)); + return unmodifiableMap(Hash.create(keys)); } }, SyncHM { @@ -139,7 +143,7 @@ Map create(Collection keys) { for (Element element : keys) { builder.put(element, element); } - return builder.build(); + return builder.buildOrThrow(); } }, ImmutableSorted { @@ -186,7 +190,7 @@ void setUp() { if (sortedData) { List valueList = newArrayList(sampleData.getValuesInSet()); - Collections.sort(valueList); + sort(valueList); values = valueList; } else { values = sampleData.getValuesInSet(); diff --git a/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java index b8348464d4e0..afb01a2ef654 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/MapsMemoryBenchmark.java @@ -18,6 +18,7 @@ import static com.google.common.base.Functions.toStringFunction; import static com.google.common.collect.Maps.uniqueIndex; +import static java.util.Arrays.asList; import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; @@ -28,17 +29,16 @@ import com.google.common.collect.BenchmarkHelpers.MapsImplEnum; import com.google.common.collect.BenchmarkHelpers.SortedMapImpl; import com.google.common.collect.CollectionBenchmarkSampleData.Element; -import java.util.Arrays; import java.util.Map; +import org.jspecify.annotations.NullUnmarked; /** Benchmarks for memory consumption of map implementations. */ +@NullUnmarked public class MapsMemoryBenchmark { static final Map mapEnums = uniqueIndex( Iterables.concat( - Arrays.asList(MapImpl.values()), - Arrays.asList(SortedMapImpl.values()), - Arrays.asList(BiMapImpl.values())), + asList(MapImpl.values()), asList(SortedMapImpl.values()), asList(BiMapImpl.values())), toStringFunction()); @Param({ diff --git a/android/guava-tests/benchmark/com/google/common/collect/MinMaxPriorityQueueBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MinMaxPriorityQueueBenchmark.java index 239a033f7e4d..04496c0bfa35 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/MinMaxPriorityQueueBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/MinMaxPriorityQueueBenchmark.java @@ -25,12 +25,15 @@ import java.util.PriorityQueue; import java.util.Queue; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Benchmarks to compare performance of MinMaxPriorityQueue and PriorityQueue. * * @author Sverre Sundsdal */ +@NullUnmarked public class MinMaxPriorityQueueBenchmark { @Param private ComparatorType comparator; @@ -90,7 +93,7 @@ protected Queue delegate() { } @Override - public T poll() { + public @Nullable T poll() { return mmHeap.pollLast(); } } diff --git a/android/guava-tests/benchmark/com/google/common/collect/MultipleSetContainsBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MultipleSetContainsBenchmark.java index b87d9641dcd7..e21eb4734698 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/MultipleSetContainsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/MultipleSetContainsBenchmark.java @@ -21,8 +21,10 @@ import com.google.caliper.Param; import com.google.caliper.api.SkipThisScenarioException; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** A benchmark that tries invoking {@code Set.contains} on many different sets. */ +@NullUnmarked public class MultipleSetContainsBenchmark { @Param({"0.0", "0.1", "0.7", "1.0"}) @@ -37,8 +39,7 @@ public class MultipleSetContainsBenchmark { static final Object PRESENT = new Object(); static final Object ABSENT = new Object(); - @SuppressWarnings("unchecked") - private final ImmutableSet[] sets = new ImmutableSet[0x1000]; + private final ImmutableSet[] sets = new ImmutableSet[0x1000]; private final Object[] queries = new Object[0x1000]; @@ -69,7 +70,7 @@ void setUp() { @Benchmark public boolean contains(int reps) { - ImmutableSet[] sets = this.sets; + ImmutableSet[] sets = this.sets; Object[] queries = this.queries; boolean result = false; for (int i = 0; i < reps; i++) { diff --git a/android/guava-tests/benchmark/com/google/common/collect/MultisetIteratorBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/MultisetIteratorBenchmark.java index f8700a4357f2..552743d69abe 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/MultisetIteratorBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/MultisetIteratorBenchmark.java @@ -16,17 +16,21 @@ package com.google.common.collect; +import static java.lang.Math.min; + import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; import com.google.common.base.Preconditions; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Tests the speed of iteration of different iteration methods for collections. * * @author David Richter */ +@NullUnmarked public class MultisetIteratorBenchmark { @Param({"0", "1", "16", "256", "4096", "65536"}) int size; @@ -48,10 +52,10 @@ void setUp() { int sizeRemaining = size; // TODO(kevinb): generate better test contents for multisets - for (int i = 0; sizeRemaining > 0; i++) { + while (sizeRemaining > 0) { // The JVM will return interned values for small ints. Integer value = random.nextInt(1000) + 128; - int count = Math.min(random.nextInt(10) + 1, sizeRemaining); + int count = min(random.nextInt(10) + 1, sizeRemaining); sizeRemaining -= count; hashMultiset.add(value, count); linkedHashMultiset.add(value, count); diff --git a/android/guava-tests/benchmark/com/google/common/collect/PowerSetBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/PowerSetBenchmark.java index e775b8b28acf..bf6cd36a5142 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/PowerSetBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/PowerSetBenchmark.java @@ -22,12 +22,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; /** * Very simple powerSet iteration benchmark. * * @author Kevin Bourrillion */ +@NullUnmarked public class PowerSetBenchmark { @Param({"2", "4", "8", "16"}) int elements; diff --git a/android/guava-tests/benchmark/com/google/common/collect/SetContainsBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/SetContainsBenchmark.java index c2a21b124460..88665d667521 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/SetContainsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/SetContainsBenchmark.java @@ -22,12 +22,14 @@ import com.google.common.collect.BenchmarkHelpers.SetImpl; import com.google.common.collect.CollectionBenchmarkSampleData.Element; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; /** * A microbenchmark that tests the performance of contains() on various Set implementations. * * @author Kevin Bourrillion */ +@NullUnmarked public class SetContainsBenchmark { // Start at 4.88 then multiply by 2*2^phi - The goal is be uniform // yet visit a variety of "values-relative-to-the-next-power-of-2" diff --git a/android/guava-tests/benchmark/com/google/common/collect/SetCreationBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/SetCreationBenchmark.java index be114e1539e3..00887ba70904 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/SetCreationBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/SetCreationBenchmark.java @@ -20,6 +20,7 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import com.google.common.collect.BenchmarkHelpers.SetImpl; +import org.jspecify.annotations.NullUnmarked; /** * This is meant to be used with {@code --measureMemory} to measure the memory usage of various @@ -27,6 +28,7 @@ * * @author Christopher Swenson */ +@NullUnmarked public class SetCreationBenchmark { @Param({ "3", "6", "11", "23", "45", "91", "181", "362", "724", "1448", "2896", "5793", "11585", "23170", diff --git a/android/guava-tests/benchmark/com/google/common/collect/SetIterationBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/SetIterationBenchmark.java index 796acefabfe6..958fc0c19817 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/SetIterationBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/SetIterationBenchmark.java @@ -22,12 +22,14 @@ import com.google.common.collect.BenchmarkHelpers.SetImpl; import com.google.common.collect.CollectionBenchmarkSampleData.Element; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; /** * Test iteration speed at various size for {@link Set} instances. * * @author Christopher Swenson */ +@NullUnmarked public class SetIterationBenchmark { @Param({ "3", "6", "11", "23", "45", "91", "181", "362", "724", "1448", "2896", "5793", "11585", "23170", diff --git a/android/guava-tests/benchmark/com/google/common/collect/SortedCopyBenchmark.java b/android/guava-tests/benchmark/com/google/common/collect/SortedCopyBenchmark.java index 891e4af393d0..3353ed96dc04 100644 --- a/android/guava-tests/benchmark/com/google/common/collect/SortedCopyBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/collect/SortedCopyBenchmark.java @@ -15,6 +15,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Collections.sort; import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; @@ -26,6 +27,7 @@ import java.util.Random; import java.util.Set; import java.util.TreeSet; +import org.jspecify.annotations.NullUnmarked; /** * Provides supporting data for performance notes in the documentation of {@link @@ -33,6 +35,7 @@ * suggestions. * */ +@NullUnmarked public class SortedCopyBenchmark { @Param({"1", "10", "1000", "1000000"}) int size; // logarithmic triangular @@ -45,13 +48,13 @@ enum InputOrder { SORTED { @Override void arrange(List list) { - Collections.sort(list); + sort(list); } }, ALMOST_SORTED { @Override void arrange(List list) { - Collections.sort(list); + sort(list); if (list.size() > 1) { int i = (list.size() - 1) / 2; Collections.swap(list, i, i + 1); @@ -89,13 +92,13 @@ int collections(int reps) { if (mutable) { for (int i = 0; i < reps; i++) { List copy = new ArrayList<>(input); - Collections.sort(copy); + sort(copy); dummy += copy.get(0); } } else { for (int i = 0; i < reps; i++) { List copy = new ArrayList<>(input); - Collections.sort(copy); + sort(copy); dummy += ImmutableList.copyOf(copy).get(0); } } diff --git a/android/guava-tests/benchmark/com/google/common/eventbus/EventBusBenchmark.java b/android/guava-tests/benchmark/com/google/common/eventbus/EventBusBenchmark.java index 53ddfb7cc0c7..0c4095efbb62 100644 --- a/android/guava-tests/benchmark/com/google/common/eventbus/EventBusBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/eventbus/EventBusBenchmark.java @@ -18,12 +18,14 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; +import org.jspecify.annotations.NullUnmarked; /** * Benchmark for {@link EventBus}. * * @author Eric Fellheimer */ +@NullUnmarked public class EventBusBenchmark { private EventBus eventBus; diff --git a/android/guava-tests/benchmark/com/google/common/hash/ChecksumBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/ChecksumBenchmark.java index 0fa3e2a88ebd..5fe37f971697 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/ChecksumBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/ChecksumBenchmark.java @@ -23,6 +23,7 @@ import java.util.zip.Adler32; import java.util.zip.CRC32; import java.util.zip.Checksum; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for comparing {@link Checksum}s and {@link HashFunction}s that wrap {@link Checksum}s. @@ -35,6 +36,7 @@ * * @author Colin Decker */ +@NullUnmarked public class ChecksumBenchmark { // Use a constant seed for all of the benchmarks to ensure apples to apples comparisons. @@ -63,12 +65,19 @@ byte crc32Checksum(int reps) throws Exception { byte result = 0x01; for (int i = 0; i < reps; i++) { CRC32 checksum = new CRC32(); - checksum.update(testBytes); + checksum.update(testBytes, 0, testBytes.length); result = (byte) (result ^ checksum.getValue()); } return result; } + // CRC32C + + @Benchmark + byte crc32cHashFunction(int reps) { + return runHashFunction(reps, Hashing.crc32c()); + } + // Adler32 @Benchmark @@ -81,12 +90,29 @@ byte adler32Checksum(int reps) throws Exception { byte result = 0x01; for (int i = 0; i < reps; i++) { Adler32 checksum = new Adler32(); - checksum.update(testBytes); + checksum.update(testBytes, 0, testBytes.length); result = (byte) (result ^ checksum.getValue()); } return result; } + // Fingerprint2011 + + @Benchmark + byte fingerprintHashFunction(int reps) { + return runHashFunction(reps, Hashing.fingerprint2011()); + } + + @Benchmark + byte fingerprintChecksum(int reps) throws Exception { + byte result = 0x01; + for (int i = 0; i < reps; i++) { + HashCode checksum = new Fingerprint2011().hashBytes(testBytes, 0, testBytes.length); + result = (byte) (result ^ checksum.asLong()); + } + return result; + } + // Helpers + main private byte runHashFunction(int reps, HashFunction hashFunction) { @@ -94,6 +120,7 @@ private byte runHashFunction(int reps, HashFunction hashFunction) { // Trick the JVM to prevent it from using the hash function non-polymorphically result ^= Hashing.crc32().hashInt(reps).asBytes()[0]; result ^= Hashing.adler32().hashInt(reps).asBytes()[0]; + result ^= Hashing.fingerprint2011().hashInt(reps).asBytes()[0]; for (int i = 0; i < reps; i++) { result ^= hashFunction.hashBytes(testBytes).asBytes()[0]; } diff --git a/android/guava-tests/benchmark/com/google/common/hash/HashCodeBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/HashCodeBenchmark.java index 3e3ec70fafa1..d7b6eb110454 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/HashCodeBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/HashCodeBenchmark.java @@ -22,6 +22,7 @@ import java.security.MessageDigest; import java.util.Arrays; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for comparing the various {@link HashCode#equals} methods. @@ -41,6 +42,7 @@ * * @author Kurt Alfred Kluever */ +@NullUnmarked public class HashCodeBenchmark { // Use a statically configured random instance for all of the benchmarks diff --git a/android/guava-tests/benchmark/com/google/common/hash/HashFunctionBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/HashFunctionBenchmark.java index 8a807f9b1ba1..69ece875f195 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/HashFunctionBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/HashFunctionBenchmark.java @@ -20,6 +20,7 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for comparing the various {@link HashFunction functions} that we provide. @@ -33,6 +34,7 @@ * * @author Kurt Alfred Kluever */ +@NullUnmarked public class HashFunctionBenchmark { // Use a statically configured random instance for all of the benchmarks diff --git a/android/guava-tests/benchmark/com/google/common/hash/HashStringBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/HashStringBenchmark.java index e7f9ffdc7b37..6dc7c0963569 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/HashStringBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/HashStringBenchmark.java @@ -16,13 +16,16 @@ package com.google.common.hash; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; -import java.nio.charset.StandardCharsets; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** Benchmarks for the hashing of UTF-8 strings. */ +@NullUnmarked public class HashStringBenchmark { static class MaxCodePoint { final int value; @@ -118,9 +121,7 @@ int hashUtf8(int reps) { for (int i = 0; i < reps; i++) { res += System.identityHashCode( - hashFunctionEnum - .getHashFunction() - .hashString(strings[i & SAMPLE_MASK], StandardCharsets.UTF_8)); + hashFunctionEnum.getHashFunction().hashString(strings[i & SAMPLE_MASK], UTF_8)); } return res; } @@ -134,7 +135,7 @@ int hashUtf8Hasher(int reps) { hashFunctionEnum .getHashFunction() .newHasher() - .putString(strings[i & SAMPLE_MASK], StandardCharsets.UTF_8) + .putString(strings[i & SAMPLE_MASK], UTF_8) .hash()); } return res; @@ -148,7 +149,7 @@ int hashUtf8GetBytes(int reps) { System.identityHashCode( hashFunctionEnum .getHashFunction() - .hashBytes(strings[i & SAMPLE_MASK].getBytes(StandardCharsets.UTF_8))); + .hashBytes(strings[i & SAMPLE_MASK].getBytes(UTF_8))); } return res; } @@ -162,7 +163,7 @@ int hashUtf8GetBytesHasher(int reps) { hashFunctionEnum .getHashFunction() .newHasher() - .putBytes(strings[i & SAMPLE_MASK].getBytes(StandardCharsets.UTF_8)) + .putBytes(strings[i & SAMPLE_MASK].getBytes(UTF_8)) .hash()); } return res; diff --git a/android/guava-tests/benchmark/com/google/common/hash/MessageDigestAlgorithmBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/MessageDigestAlgorithmBenchmark.java index 2e252d127b68..8eca5a99cced 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/MessageDigestAlgorithmBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/MessageDigestAlgorithmBenchmark.java @@ -22,6 +22,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for comparing {@link MessageDigest}s and {@link com.google.common.hash.HashFunction}s @@ -37,6 +38,7 @@ * * @author Kurt Alfred Kluever */ +@NullUnmarked public class MessageDigestAlgorithmBenchmark { @Param({"10", "1000", "100000", "1000000"}) int size; diff --git a/android/guava-tests/benchmark/com/google/common/hash/MessageDigestCreationBenchmark.java b/android/guava-tests/benchmark/com/google/common/hash/MessageDigestCreationBenchmark.java index bc2dc94102f2..3990b467633a 100644 --- a/android/guava-tests/benchmark/com/google/common/hash/MessageDigestCreationBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/hash/MessageDigestCreationBenchmark.java @@ -20,12 +20,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.security.MessageDigest; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for comparing instance creation of {@link MessageDigest}s. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class MessageDigestCreationBenchmark { @Param({"MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512"}) diff --git a/android/guava-tests/benchmark/com/google/common/io/BaseEncodingBenchmark.java b/android/guava-tests/benchmark/com/google/common/io/BaseEncodingBenchmark.java index a098542a7130..4cd211110c3d 100644 --- a/android/guava-tests/benchmark/com/google/common/io/BaseEncodingBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/io/BaseEncodingBenchmark.java @@ -23,8 +23,10 @@ import java.io.StringReader; import java.io.StringWriter; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** Benchmark for {@code BaseEncoding} performance. */ +@NullUnmarked public class BaseEncodingBenchmark { private static final int INPUTS_COUNT = 0x1000; private static final int INPUTS_MASK = 0xFFF; diff --git a/android/guava-tests/benchmark/com/google/common/io/ByteSourceAsCharSourceReadBenchmark.java b/android/guava-tests/benchmark/com/google/common/io/ByteSourceAsCharSourceReadBenchmark.java index ee81ea1259b2..cdc9ec4ce6b6 100644 --- a/android/guava-tests/benchmark/com/google/common/io/ByteSourceAsCharSourceReadBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/io/ByteSourceAsCharSourceReadBenchmark.java @@ -23,12 +23,14 @@ import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for various potential implementations of {@code ByteSource.asCharSource(...).read()}. */ // These benchmarks allocate a lot of data so use a large heap @VmOptions({"-Xms12g", "-Xmx12g", "-d64"}) +@NullUnmarked public class ByteSourceAsCharSourceReadBenchmark { enum ReadStrategy { TO_BYTE_ARRAY_NEW_STRING { @@ -78,7 +80,7 @@ String read(ByteSource byteSource, Charset cs) throws IOException { return new String(buffer, 0, bufIndex); } // otherwise we got the size wrong. This can happen if the size changes between when - // we called sizeIfKnown and when we started reading the file (or i guess if + // we called sizeIfKnown and when we started reading the file (or I guess if // maxCharsPerByte is wrong) // Fallback to an incremental approach StringBuilder builder = new StringBuilder(bufIndex + 32); diff --git a/android/guava-tests/benchmark/com/google/common/io/CharStreamsCopyBenchmark.java b/android/guava-tests/benchmark/com/google/common/io/CharStreamsCopyBenchmark.java index 52532bb61ac2..b6ff61f25d9f 100644 --- a/android/guava-tests/benchmark/com/google/common/io/CharStreamsCopyBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/io/CharStreamsCopyBenchmark.java @@ -21,8 +21,10 @@ import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.nio.Buffer; import java.nio.CharBuffer; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for {@link CharStreams#copy}. @@ -32,6 +34,7 @@ */ // These benchmarks allocate a lot of data so use a large heap @VmOptions({"-Xms12g", "-Xmx12g", "-d64"}) +@NullUnmarked public class CharStreamsCopyBenchmark { enum CopyStrategy { OLD { @@ -40,10 +43,10 @@ long copy(Readable from, Appendable to) throws IOException { CharBuffer buf = CharStreams.createBuffer(); long total = 0; while (from.read(buf) != -1) { - buf.flip(); + ((Buffer) buf).flip(); to.append(buf); total += buf.remaining(); - buf.clear(); + ((Buffer) buf).clear(); } return total; } diff --git a/android/guava-tests/benchmark/com/google/common/math/ApacheBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/ApacheBenchmark.java index afcf95c4285b..4105c1c49b46 100644 --- a/android/guava-tests/benchmark/com/google/common/math/ApacheBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/ApacheBenchmark.java @@ -25,6 +25,7 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks against the Apache Commons Math utilities. @@ -33,6 +34,7 @@ * * @author Louis Wasserman */ +@NullUnmarked public class ApacheBenchmark { private enum Impl { GUAVA { diff --git a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathBenchmark.java index 73118543e55d..d719e4adac75 100644 --- a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathBenchmark.java @@ -25,12 +25,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.math.BigInteger; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the non-rounding methods of {@code BigIntegerMath}. * * @author Louis Wasserman */ +@NullUnmarked public class BigIntegerMathBenchmark { private static final int[] factorials = new int[ARRAY_SIZE]; private static final int[] slowFactorials = new int[ARRAY_SIZE]; @@ -59,6 +61,7 @@ private static BigInteger oldSlowFactorial(int n) { } /** Returns the product of {@code n1} exclusive through {@code n2} inclusive. */ + @SuppressWarnings("UseCorrectAssertInTests") // TODO(b/345814817): Remove or convert assertion. private static BigInteger oldSlowFactorial(int n1, int n2) { assert n1 <= n2; if (IntMath.log2(n2, CEILING) * (n2 - n1) < Long.SIZE - 1) { diff --git a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java index be387f792459..e33aca162dd3 100644 --- a/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/BigIntegerMathRoundingBenchmark.java @@ -26,12 +26,14 @@ import com.google.caliper.Param; import java.math.BigInteger; import java.math.RoundingMode; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the rounding methods of {@code BigIntegerMath}. * * @author Louis Wasserman */ +@NullUnmarked public class BigIntegerMathRoundingBenchmark { private static final BigInteger[] nonzero1 = new BigInteger[ARRAY_SIZE]; private static final BigInteger[] nonzero2 = new BigInteger[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/DoubleMathBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/DoubleMathBenchmark.java index 937c94c98337..b012e729f40d 100644 --- a/android/guava-tests/benchmark/com/google/common/math/DoubleMathBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/DoubleMathBenchmark.java @@ -24,12 +24,14 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the non-rounding methods of {@code DoubleMath}. * * @author Louis Wasserman */ +@NullUnmarked public class DoubleMathBenchmark { private static final double[] positiveDoubles = new double[ARRAY_SIZE]; private static final int[] factorials = new int[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/DoubleMathRoundingBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/DoubleMathRoundingBenchmark.java index 7ab80b15cd6b..6ba25a1994ec 100644 --- a/android/guava-tests/benchmark/com/google/common/math/DoubleMathRoundingBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/DoubleMathRoundingBenchmark.java @@ -25,12 +25,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.math.RoundingMode; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the rounding methods of {@code DoubleMath}. * * @author Louis Wasserman */ +@NullUnmarked public class DoubleMathRoundingBenchmark { private static final double[] doubleInIntRange = new double[ARRAY_SIZE]; private static final double[] doubleInLongRange = new double[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/IntMathBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/IntMathBenchmark.java index 58b0bb64e0e9..730284d79cf9 100644 --- a/android/guava-tests/benchmark/com/google/common/math/IntMathBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/IntMathBenchmark.java @@ -25,12 +25,14 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the non-rounding methods of {@code IntMath}. * * @author Louis Wasserman */ +@NullUnmarked public class IntMathBenchmark { private static int[] exponent = new int[ARRAY_SIZE]; private static int[] factorial = new int[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/IntMathRoundingBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/IntMathRoundingBenchmark.java index cfa3d7393cd1..b1902b9f6a4f 100644 --- a/android/guava-tests/benchmark/com/google/common/math/IntMathRoundingBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/IntMathRoundingBenchmark.java @@ -26,12 +26,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.math.RoundingMode; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the rounding methods of {@code IntMath}. * * @author Louis Wasserman */ +@NullUnmarked public class IntMathRoundingBenchmark { private static final int[] positive = new int[ARRAY_SIZE]; private static final int[] nonzero = new int[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/LessThanBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/LessThanBenchmark.java index a63dd6b16b0a..de7a3e55c247 100644 --- a/android/guava-tests/benchmark/com/google/common/math/LessThanBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/LessThanBenchmark.java @@ -20,12 +20,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for various ways of writing the expression {@code foo + ((bar < baz) ? 1 : 0)}. * * @author Louis Wasserman */ +@NullUnmarked public class LessThanBenchmark { static final int SAMPLE_SIZE = 0x1000; static final int SAMPLE_MASK = 0x0FFF; diff --git a/android/guava-tests/benchmark/com/google/common/math/LongMathBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/LongMathBenchmark.java index 6b0407cd5787..7934a776c71f 100644 --- a/android/guava-tests/benchmark/com/google/common/math/LongMathBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/LongMathBenchmark.java @@ -25,12 +25,14 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the non-rounding methods of {@code LongMath}. * * @author Louis Wasserman */ +@NullUnmarked public class LongMathBenchmark { private static final int[] exponents = new int[ARRAY_SIZE]; private static final int[] factorialArguments = new int[ARRAY_SIZE]; diff --git a/android/guava-tests/benchmark/com/google/common/math/LongMathRoundingBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/LongMathRoundingBenchmark.java index 5f2ac40550c3..868f2d5d2d04 100644 --- a/android/guava-tests/benchmark/com/google/common/math/LongMathRoundingBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/LongMathRoundingBenchmark.java @@ -26,12 +26,14 @@ import com.google.caliper.Benchmark; import com.google.caliper.Param; import java.math.RoundingMode; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for the rounding methods of {@code LongMath}. * * @author Louis Wasserman */ +@NullUnmarked public class LongMathRoundingBenchmark { @Param({"DOWN", "UP", "FLOOR", "CEILING", "HALF_EVEN", "HALF_UP", "HALF_DOWN"}) RoundingMode mode; diff --git a/android/guava-tests/benchmark/com/google/common/math/QuantilesBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/QuantilesBenchmark.java index 6830482c2a7b..d55d7b1069d5 100644 --- a/android/guava-tests/benchmark/com/google/common/math/QuantilesBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/QuantilesBenchmark.java @@ -24,8 +24,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** Benchmarks some algorithms providing the same functionality as {@link Quantiles}. */ +@NullUnmarked public class QuantilesBenchmark { private static final ContiguousSet ALL_DECILE_INDEXES = @@ -50,7 +52,7 @@ void setUp() { } private double[] dataset(int i) { - // We must test on a fresh clone of the dataset each time. Doing sorts and quickselects on an + // We must test on a fresh clone of the dataset each time. Doing sorts and quickselects on a // dataset which is already sorted or partially sorted is cheating. return datasets[i & 0xFF].clone(); } diff --git a/android/guava-tests/benchmark/com/google/common/math/StatsBenchmark.java b/android/guava-tests/benchmark/com/google/common/math/StatsBenchmark.java index 7e50249162af..c37b1610e50b 100644 --- a/android/guava-tests/benchmark/com/google/common/math/StatsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/math/StatsBenchmark.java @@ -22,12 +22,14 @@ import com.google.caliper.api.SkipThisScenarioException; import com.google.common.primitives.Doubles; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for various algorithms for computing the mean and/or variance. * * @author Louis Wasserman */ +@NullUnmarked public class StatsBenchmark { enum MeanAlgorithm { diff --git a/android/guava-tests/benchmark/com/google/common/primitives/UnsignedBytesBenchmark.java b/android/guava-tests/benchmark/com/google/common/primitives/UnsignedBytesBenchmark.java index 71ea279bd057..3b4fda5a7f28 100644 --- a/android/guava-tests/benchmark/com/google/common/primitives/UnsignedBytesBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/primitives/UnsignedBytesBenchmark.java @@ -22,12 +22,14 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Microbenchmark for {@link UnsignedBytes}. * * @author Hiroshi Yamauchi */ +@NullUnmarked public class UnsignedBytesBenchmark { private byte[] ba1; diff --git a/android/guava-tests/benchmark/com/google/common/primitives/UnsignedLongsBenchmark.java b/android/guava-tests/benchmark/com/google/common/primitives/UnsignedLongsBenchmark.java index 288aa0c5c33c..d7531ee71875 100644 --- a/android/guava-tests/benchmark/com/google/common/primitives/UnsignedLongsBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/primitives/UnsignedLongsBenchmark.java @@ -19,16 +19,18 @@ import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for certain methods of {@code UnsignedLongs}. * * @author Eamonn McManus */ +@NullUnmarked public class UnsignedLongsBenchmark { private static final int ARRAY_SIZE = 0x10000; private static final int ARRAY_MASK = 0x0ffff; - private static final Random RANDOM_SOURCE = new Random(314159265358979L); + private static final Random randomSource = new Random(314159265358979L); private static final long[] longs = new long[ARRAY_SIZE]; private static final long[] divisors = new long[ARRAY_SIZE]; private static final String[] decimalStrings = new String[ARRAY_SIZE]; @@ -120,7 +122,7 @@ int toString(int reps) { } private static long random() { - return RANDOM_SOURCE.nextLong(); + return randomSource.nextLong(); } // A random value that cannot be 0 and that is unsigned-less-than or equal @@ -129,7 +131,7 @@ private static long random() { // Using remainder here does not give us a uniform distribution but it should // not have a big impact on the measurement. private static long randomDivisor(long dividend) { - long r = RANDOM_SOURCE.nextLong(); + long r = randomSource.nextLong(); if (dividend == -1) { return r; } else { diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/AbstractFutureFootprintBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/AbstractFutureFootprintBenchmark.java index 9a2d87140dff..b4dd6202da5f 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/AbstractFutureFootprintBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/AbstractFutureFootprintBenchmark.java @@ -27,8 +27,10 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.Executor; +import org.jspecify.annotations.NullUnmarked; /** Measures the size of AbstractFuture implementations. */ +@NullUnmarked public class AbstractFutureFootprintBenchmark { enum State { @@ -98,8 +100,6 @@ public void run() { case FAILED: f.setException(new Exception()); break; - default: - throw new AssertionError(); } return f; } diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/CycleDetectingLockFactoryBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/CycleDetectingLockFactoryBenchmark.java index ce674e14e8ce..ec28f78abab8 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/CycleDetectingLockFactoryBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/CycleDetectingLockFactoryBenchmark.java @@ -21,12 +21,14 @@ import com.google.caliper.Param; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for {@link CycleDetectingLockFactory}. * * @author Darick Tong */ +@NullUnmarked public class CycleDetectingLockFactoryBenchmark { @Param({"2", "3", "4", "5", "10"}) diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/ExecutionListBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/ExecutionListBenchmark.java index 22053c8d696a..9eb6cdd465e0 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/ExecutionListBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/ExecutionListBenchmark.java @@ -17,6 +17,7 @@ package com.google.common.util.concurrent; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.caliper.AfterExperiment; import com.google.caliper.BeforeExperiment; @@ -34,14 +35,15 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** Benchmarks for {@link ExecutionList}. */ @VmOptions({"-Xms8g", "-Xmx8g"}) +@NullUnmarked public class ExecutionListBenchmark { private static final int NUM_THREADS = 10; // make a param? @@ -50,6 +52,7 @@ interface ExecutionListWrapper { void add(Runnable runnable, Executor executor); void execute(); + /** Returns the underlying implementation, useful for the Footprint benchmark. */ Object getImpl(); } @@ -78,29 +81,6 @@ public Object getImpl() { }; } }, - NEW_WITH_CAS { - @Override - ExecutionListWrapper newExecutionList() { - return new ExecutionListWrapper() { - final ExecutionListCAS list = new ExecutionListCAS(); - - @Override - public void add(Runnable runnable, Executor executor) { - list.add(runnable, executor); - } - - @Override - public void execute() { - list.execute(); - } - - @Override - public Object getImpl() { - return list; - } - }; - } - }, NEW_WITH_QUEUE { @Override ExecutionListWrapper newExecutionList() { @@ -246,7 +226,7 @@ void setUp() throws Exception { NUM_THREADS, NUM_THREADS, Long.MAX_VALUE, - TimeUnit.SECONDS, + SECONDS, new ArrayBlockingQueue(1000)); executorService.prestartAllCoreThreads(); final AtomicInteger integer = new AtomicInteger(); @@ -440,7 +420,7 @@ private static final class NewExecutionListWithoutReverse { static final Logger log = Logger.getLogger(NewExecutionListWithoutReverse.class.getName()); @GuardedBy("this") - private RunnableExecutorPair runnables; + private @Nullable RunnableExecutorPair runnables; @GuardedBy("this") private boolean executed; @@ -488,7 +468,7 @@ private static void executeListener(Runnable runnable, Executor executor) { private static final class RunnableExecutorPair { final Runnable runnable; final Executor executor; - @CheckForNull RunnableExecutorPair next; + @Nullable RunnableExecutorPair next; RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) { this.runnable = runnable; @@ -504,10 +484,10 @@ private static final class NewExecutionListQueue { static final Logger log = Logger.getLogger(NewExecutionListQueue.class.getName()); @GuardedBy("this") - private RunnableExecutorPair head; + private @Nullable RunnableExecutorPair head; @GuardedBy("this") - private RunnableExecutorPair tail; + private @Nullable RunnableExecutorPair tail; @GuardedBy("this") private boolean executed; @@ -563,7 +543,7 @@ private static void executeListener(Runnable runnable, Executor executor) { private static final class RunnableExecutorPair { Runnable runnable; Executor executor; - @CheckForNull RunnableExecutorPair next; + @Nullable RunnableExecutorPair next; RunnableExecutorPair(Runnable runnable, Executor executor) { this.runnable = runnable; @@ -571,124 +551,4 @@ private static final class RunnableExecutorPair { } } } - - // A version of the list that uses compare and swap to manage the stack without locks. - private static final class ExecutionListCAS { - static final Logger log = Logger.getLogger(ExecutionListCAS.class.getName()); - - private static final sun.misc.Unsafe UNSAFE; - private static final long HEAD_OFFSET; - - /** - * A special instance of {@link RunnableExecutorPair} that is used as a sentinel value for the - * bottom of the stack. - */ - private static final RunnableExecutorPair NULL_PAIR = new RunnableExecutorPair(null, null); - - static { - try { - UNSAFE = getUnsafe(); - HEAD_OFFSET = UNSAFE.objectFieldOffset(ExecutionListCAS.class.getDeclaredField("head")); - } catch (Exception ex) { - throw new Error(ex); - } - } - - /** TODO(lukes): This was copied verbatim from Striped64.java... standardize this? */ - private static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) { - } - try { - return java.security.AccessController.doPrivileged( - new java.security.PrivilegedExceptionAction() { - @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) return k.cast(x); - } - throw new NoSuchFieldError("the Unsafe"); - } - }); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", e.getCause()); - } - } - - private volatile RunnableExecutorPair head = NULL_PAIR; - - public void add(Runnable runnable, Executor executor) { - Preconditions.checkNotNull(runnable, "Runnable was null."); - Preconditions.checkNotNull(executor, "Executor was null."); - - RunnableExecutorPair newHead = new RunnableExecutorPair(runnable, executor); - RunnableExecutorPair oldHead; - do { - oldHead = head; - if (oldHead == null) { - // If runnables == null then execute() has been called so we should just execute our - // listener immediately. - newHead.execute(); - return; - } - // Try to make newHead the new head of the stack at runnables. - newHead.next = oldHead; - } while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, oldHead, newHead)); - } - - public void execute() { - RunnableExecutorPair stack; - do { - stack = head; - if (stack == null) { - // If head == null then execute() has been called so we should just return - return; - } - // try to swap null into head. - } while (!UNSAFE.compareAndSwapObject(this, HEAD_OFFSET, stack, null)); - - RunnableExecutorPair reversedStack = null; - while (stack != NULL_PAIR) { - RunnableExecutorPair head = stack; - stack = stack.next; - head.next = reversedStack; - reversedStack = head; - } - stack = reversedStack; - while (stack != null) { - stack.execute(); - stack = stack.next; - } - } - - private static class RunnableExecutorPair { - final Runnable runnable; - final Executor executor; - // Volatile because this is written on one thread and read on another with no synchronization. - @CheckForNull volatile RunnableExecutorPair next; - - RunnableExecutorPair(Runnable runnable, Executor executor) { - this.runnable = runnable; - this.executor = executor; - } - - void execute() { - try { - executor.execute(runnable); - } catch (RuntimeException e) { - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " - + runnable - + " with executor " - + executor, - e); - } - } - } - } } diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/FuturesGetCheckedBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/FuturesGetCheckedBenchmark.java index bb764203fb8b..d1ce1a154a5e 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/FuturesGetCheckedBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/FuturesGetCheckedBenchmark.java @@ -33,7 +33,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.security.GeneralSecurityException; -import java.security.acl.NotOwnerException; +import java.security.KeyException; import java.util.List; import java.util.TooManyListenersException; import java.util.concurrent.BrokenBarrierException; @@ -44,8 +44,10 @@ import java.util.prefs.InvalidPreferencesFormatException; import java.util.zip.DataFormatException; import javax.security.auth.RefreshFailedException; +import org.jspecify.annotations.NullUnmarked; /** Microbenchmark for {@link Futures#getChecked}. */ +@NullUnmarked public class FuturesGetCheckedBenchmark { private enum Validator { NON_CACHING_WITH_CONSTRUCTOR_CHECK(nonCachingWithConstructorCheckValidator()), @@ -91,7 +93,7 @@ private enum ExceptionType { ExecutionException.class, GeneralSecurityException.class, InvalidPreferencesFormatException.class, - NotOwnerException.class, + KeyException.class, RefreshFailedException.class, TimeoutException.class, TooManyListenersException.class, @@ -100,6 +102,7 @@ private enum ExceptionType { @Param Validator validator; @Param Result result; @Param ExceptionType exceptionType; + /** * The number of other exception types in the cache of known-good exceptions and the number of * other {@code ClassValue} entries for the exception type to be tested. This lets us evaluate diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedArrayBlockingQueue.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedArrayBlockingQueue.java index 2233fb47d978..cc04a97201da 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedArrayBlockingQueue.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedArrayBlockingQueue.java @@ -25,7 +25,8 @@ import java.util.NoSuchElementException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * A bounded {@linkplain BlockingQueue blocking queue} backed by an array. This queue orders @@ -51,7 +52,8 @@ * @author Justin T. Sampson * @param the type of elements held in this collection */ -@CanIgnoreReturnValue +// TODO(kak): consider removing some of the @CanIgnoreReturnValue annotations as appropriate +@NullUnmarked public class MonitorBasedArrayBlockingQueue extends AbstractQueue implements BlockingQueue { @@ -60,10 +62,13 @@ public class MonitorBasedArrayBlockingQueue extends AbstractQueue /** The queued items */ final E[] items; + /** items index for next take, poll or remove */ int takeIndex; + /** items index for next put, offer, or add. */ int putIndex; + /** Number of items in the queue */ private int count; @@ -213,6 +218,7 @@ private static E[] newEArray(int capacity) { * @throws IllegalStateException if this queue is full * @throws NullPointerException if the specified element is null */ + @CanIgnoreReturnValue @Override public boolean add(E e) { return super.add(e); @@ -226,6 +232,7 @@ public boolean add(E e) { * * @throws NullPointerException if the specified element is null */ + @CanIgnoreReturnValue @Override public boolean offer(E e) { if (e == null) throw new NullPointerException(); @@ -249,6 +256,7 @@ public boolean offer(E e) { * @throws InterruptedException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ + @CanIgnoreReturnValue @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { @@ -285,8 +293,9 @@ public void put(E e) throws InterruptedException { } } + @CanIgnoreReturnValue @Override - public E poll() { + public @Nullable E poll() { final Monitor monitor = this.monitor; if (monitor.enterIf(notEmpty)) { try { @@ -299,8 +308,9 @@ public E poll() { } } + @CanIgnoreReturnValue @Override - public E poll(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(long timeout, TimeUnit unit) throws InterruptedException { final Monitor monitor = this.monitor; if (monitor.enterWhen(notEmpty, timeout, unit)) { try { @@ -313,6 +323,7 @@ public E poll(long timeout, TimeUnit unit) throws InterruptedException { } } + @CanIgnoreReturnValue @Override public E take() throws InterruptedException { final Monitor monitor = this.monitor; @@ -324,8 +335,9 @@ public E take() throws InterruptedException { } } + @CanIgnoreReturnValue @Override - public E peek() { + public @Nullable E peek() { final Monitor monitor = this.monitor; if (monitor.enterIf(notEmpty)) { try { @@ -345,6 +357,7 @@ public E peek() { * * @return the number of elements in this queue */ + @CanIgnoreReturnValue @Override public int size() { final Monitor monitor = this.monitor; @@ -367,6 +380,7 @@ public int size() { * inspecting {@code remainingCapacity} because it may be the case that another thread is about to * insert or remove an element. */ + @CanIgnoreReturnValue @Override public int remainingCapacity() { final Monitor monitor = this.monitor; @@ -387,8 +401,9 @@ public int remainingCapacity() { * @param o element to be removed from this queue, if present * @return {@code true} if this queue changed as a result of the call */ + @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o == null) return false; final E[] items = this.items; final Monitor monitor = this.monitor; @@ -417,8 +432,9 @@ public boolean remove(@CheckForNull Object o) { * @param o object to be checked for containment in this queue * @return {@code true} if this queue contains the specified element */ + @CanIgnoreReturnValue @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o == null) return false; final E[] items = this.items; final Monitor monitor = this.monitor; @@ -447,6 +463,7 @@ public boolean contains(@CheckForNull Object o) { * * @return an array containing all of the elements in this queue */ + @CanIgnoreReturnValue @Override public Object[] toArray() { final E[] items = this.items; @@ -495,6 +512,7 @@ public Object[] toArray() { * the runtime type of every element in this queue * @throws NullPointerException if the specified array is null */ + @CanIgnoreReturnValue @Override public T[] toArray(T[] a) { final E[] items = this.items; @@ -522,6 +540,7 @@ public T[] toArray(T[] a) { } } + @CanIgnoreReturnValue @Override public String toString() { final Monitor monitor = this.monitor; @@ -563,6 +582,7 @@ public void clear() { * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ + @CanIgnoreReturnValue @Override public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -597,6 +617,7 @@ public int drainTo(Collection c) { * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ + @CanIgnoreReturnValue @Override public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -634,6 +655,7 @@ public int drainTo(Collection c, int maxElements) { * * @return an iterator over the elements in this queue in proper sequence */ + @CanIgnoreReturnValue @Override public Iterator iterator() { final Monitor monitor = this.monitor; @@ -655,7 +677,7 @@ private class Itr implements Iterator { * we must return it in the following next() call even if it was in the process of being removed * when hasNext() was called. */ - private E nextItem; + private @Nullable E nextItem; /** * Index of element returned by most recent call to next. Reset to -1 if this element is deleted diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedPriorityBlockingQueue.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedPriorityBlockingQueue.java index f61885be2630..85923d826caf 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedPriorityBlockingQueue.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBasedPriorityBlockingQueue.java @@ -30,7 +30,8 @@ import java.util.SortedSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * An unbounded {@linkplain BlockingQueue blocking queue} that uses the same ordering rules as class @@ -76,7 +77,7 @@ * @author Justin T. Sampson * @param the type of elements held in this collection */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@NullUnmarked public class MonitorBasedPriorityBlockingQueue extends AbstractQueue implements BlockingQueue { @@ -124,7 +125,7 @@ public MonitorBasedPriorityBlockingQueue(int initialCapacity) { * @throws IllegalArgumentException if {@code initialCapacity} is less than 1 */ public MonitorBasedPriorityBlockingQueue( - int initialCapacity, @CheckForNull Comparator comparator) { + int initialCapacity, @Nullable Comparator comparator) { q = new PriorityQueue(initialCapacity, comparator); } @@ -152,6 +153,7 @@ public MonitorBasedPriorityBlockingQueue(Collection c) { * in the priority queue according to the priority queue's ordering * @throws NullPointerException if the specified element is null */ + @CanIgnoreReturnValue // pushed down from class to method @Override public boolean add(E e) { return offer(e); @@ -166,6 +168,7 @@ public boolean add(E e) { * in the priority queue according to the priority queue's ordering * @throws NullPointerException if the specified element is null */ + @CanIgnoreReturnValue // pushed down from class to method @Override public boolean offer(E e) { final Monitor monitor = this.monitor; @@ -193,6 +196,7 @@ public boolean offer(E e) { * in the priority queue according to the priority queue's ordering * @throws NullPointerException if the specified element is null */ + @CanIgnoreReturnValue // pushed down from class to method @Override public boolean offer(E e, long timeout, TimeUnit unit) { checkNotNull(unit); @@ -213,8 +217,9 @@ public void put(E e) { offer(e); // never need to block } + @CanIgnoreReturnValue // pushed down from class to method @Override - public E poll() { + public @Nullable E poll() { final Monitor monitor = this.monitor; monitor.enter(); try { @@ -224,8 +229,9 @@ public E poll() { } } + @CanIgnoreReturnValue // pushed down from class to method @Override - public E poll(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(long timeout, TimeUnit unit) throws InterruptedException { final Monitor monitor = this.monitor; if (monitor.enterWhen(notEmpty, timeout, unit)) { try { @@ -238,6 +244,7 @@ public E poll(long timeout, TimeUnit unit) throws InterruptedException { } } + @CanIgnoreReturnValue // pushed down from class to method @Override public E take() throws InterruptedException { final Monitor monitor = this.monitor; @@ -249,8 +256,9 @@ public E take() throws InterruptedException { } } + @CanIgnoreReturnValue // pushed down from class to method @Override - public E peek() { + public @Nullable E peek() { final Monitor monitor = this.monitor; monitor.enter(); try { @@ -267,10 +275,12 @@ public E peek() { * @return the comparator used to order the elements in this queue, or {@code null} if this queue * uses the natural ordering of its elements */ + @CanIgnoreReturnValue // pushed down from class to method public Comparator comparator() { return q.comparator(); } + @CanIgnoreReturnValue // pushed down from class to method @Override public int size() { final Monitor monitor = this.monitor; @@ -288,6 +298,7 @@ public int size() { * * @return {@code Integer.MAX_VALUE} */ + @CanIgnoreReturnValue // pushed down from class to method @Override public int remainingCapacity() { return Integer.MAX_VALUE; @@ -302,8 +313,9 @@ public int remainingCapacity() { * @param o element to be removed from this queue, if present * @return {@code true} if this queue changed as a result of the call */ + @CanIgnoreReturnValue // pushed down from class to method @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { final Monitor monitor = this.monitor; monitor.enter(); try { @@ -321,8 +333,9 @@ public boolean remove(@CheckForNull Object o) { * @param o object to be checked for containment in this queue * @return {@code true} if this queue contains the specified element */ + @CanIgnoreReturnValue // pushed down from class to method @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { final Monitor monitor = this.monitor; monitor.enter(); try { @@ -344,6 +357,7 @@ public boolean contains(@CheckForNull Object o) { * * @return an array containing all of the elements in this queue */ + @CanIgnoreReturnValue // pushed down from class to method @Override public Object[] toArray() { final Monitor monitor = this.monitor; @@ -384,6 +398,7 @@ public Object[] toArray() { * the runtime type of every element in this queue * @throws NullPointerException if the specified array is null */ + @CanIgnoreReturnValue // pushed down from class to method @Override public T[] toArray(T[] a) { final Monitor monitor = this.monitor; @@ -395,6 +410,7 @@ public T[] toArray(T[] a) { } } + @CanIgnoreReturnValue // pushed down from class to method @Override public String toString() { final Monitor monitor = this.monitor; @@ -412,6 +428,7 @@ public String toString() { * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ + @CanIgnoreReturnValue // pushed down from class to method @Override public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -437,6 +454,7 @@ public int drainTo(Collection c) { * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */ + @CanIgnoreReturnValue // pushed down from class to method @Override public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -481,6 +499,7 @@ public void clear() { * * @return an iterator over the elements in this queue */ + @CanIgnoreReturnValue // pushed down from class to method @Override public Iterator iterator() { return new Itr(toArray()); @@ -497,11 +516,13 @@ private class Itr implements Iterator { this.array = array; } + @CanIgnoreReturnValue // pushed down from class to method @Override public boolean hasNext() { return cursor < array.length; } + @CanIgnoreReturnValue // pushed down from class to method @Override public E next() { if (cursor >= array.length) throw new NoSuchElementException(); @@ -533,19 +554,4 @@ public void remove() { } } } - - /** - * Saves the state to a stream (that is, serializes it). This merely wraps default serialization - * within the monitor. The serialization strategy for items is left to underlying Queue. Note that - * locking is not needed on deserialization, so readObject is not defined, just relying on - * default. - */ - private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { - monitor.enter(); - try { - s.defaultWriteObject(); - } finally { - monitor.leave(); - } - } } diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBenchmark.java index 692017d786c2..2513d861776b 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/MonitorBenchmark.java @@ -21,12 +21,14 @@ import com.google.caliper.Param; import java.lang.reflect.Constructor; import java.util.concurrent.BlockingQueue; +import org.jspecify.annotations.NullUnmarked; /** * Benchmarks for {@link Monitor}. * * @author Justin T. Sampson */ +@NullUnmarked public class MonitorBenchmark { @Param({"10", "100", "1000"}) diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/MoreExecutorsDirectExecutorBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/MoreExecutorsDirectExecutorBenchmark.java index f64ae389eefe..b42305e2b3e1 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/MoreExecutorsDirectExecutorBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/MoreExecutorsDirectExecutorBenchmark.java @@ -29,12 +29,14 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; /** * A benchmark comparing the {@link MoreExecutors#newDirectExecutorService()} to {@link * MoreExecutors#directExecutor}. */ @VmOptions({"-Xms12g", "-Xmx12g", "-d64"}) +@NullUnmarked public class MoreExecutorsDirectExecutorBenchmark { enum Impl { EXECUTOR_SERVICE { diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/SingleThreadAbstractFutureBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/SingleThreadAbstractFutureBenchmark.java index a9b0ae5eac51..0d8cfd59a9b9 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/SingleThreadAbstractFutureBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/SingleThreadAbstractFutureBenchmark.java @@ -16,6 +16,9 @@ package com.google.common.util.concurrent; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + import com.google.caliper.BeforeExperiment; import com.google.caliper.Benchmark; import com.google.caliper.Param; @@ -26,11 +29,12 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.NullUnmarked; /** A benchmark that times how long it takes to add a given number of */ @VmOptions({"-Xms8g", "-Xmx8g"}) +@NullUnmarked public class SingleThreadAbstractFutureBenchmark { @Param Impl impl; @@ -105,7 +109,7 @@ public long timeGetWith0Timeout(long reps) throws Exception { long r = 0; for (int i = 0; i < reps; i++) { try { - f.get(0, TimeUnit.SECONDS); + f.get(0, SECONDS); r += 1; } catch (TimeoutException e) { r += 2; @@ -120,7 +124,7 @@ public long timeGetWithSmallTimeout(long reps) throws Exception { long r = 0; for (int i = 0; i < reps; i++) { try { - f.get(500, TimeUnit.NANOSECONDS); + f.get(500, NANOSECONDS); r += 1; } catch (TimeoutException e) { r += 2; diff --git a/android/guava-tests/benchmark/com/google/common/util/concurrent/StripedBenchmark.java b/android/guava-tests/benchmark/com/google/common/util/concurrent/StripedBenchmark.java index 03c90d31dd89..3c1401e901c8 100644 --- a/android/guava-tests/benchmark/com/google/common/util/concurrent/StripedBenchmark.java +++ b/android/guava-tests/benchmark/com/google/common/util/concurrent/StripedBenchmark.java @@ -33,9 +33,11 @@ import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.jspecify.annotations.NullUnmarked; /** A benchmark comparing the various striped implementations. */ @VmOptions({"-Xms12g", "-Xmx12g", "-d64"}) +@NullUnmarked public class StripedBenchmark { private static final Supplier LOCK_SUPPLIER = new Supplier() { diff --git a/android/guava-tests/pom.xml b/android/guava-tests/pom.xml index 229d0d069824..0a5bd64dfcd4 100644 --- a/android/guava-tests/pom.xml +++ b/android/guava-tests/pom.xml @@ -5,7 +5,7 @@ com.google.guava guava-parent - HEAD-android-SNAPSHOT + 999.0.0-HEAD-android-SNAPSHOT guava-tests Guava Unit Tests @@ -22,8 +22,8 @@ test - com.google.code.findbugs - jsr305 + org.jspecify + jspecify com.google.errorprone @@ -32,30 +32,43 @@ junit junit - - - org.easymock - easymock + 4.13.2 + test org.mockito mockito-core + 4.11.0 + test com.google.truth truth + ${truth.version} + test com.google.jimfs jimfs + 1.3.0 + test com.google.caliper caliper + 1.0-beta-3 + test + + org.mvnsearch + toolchains-maven-plugin + + + maven-toolchains-plugin + maven-compiler-plugin @@ -75,13 +88,6 @@ maven-jar-plugin - - default-jar - jar - - true - - create-test-jar test-jar diff --git a/android/guava-tests/test/com/google/common/base/AbstractIteratorTest.java b/android/guava-tests/test/com/google/common/base/AbstractIteratorTest.java index ddb7d709739a..cb7d4306dc8a 100644 --- a/android/guava-tests/test/com/google/common/base/AbstractIteratorTest.java +++ b/android/guava-tests/test/com/google/common/base/AbstractIteratorTest.java @@ -16,13 +16,21 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.base.SneakyThrows.sneakyThrow; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.TestExceptions.SomeCheckedException; +import com.google.common.base.TestExceptions.SomeUncheckedException; import com.google.common.testing.GcFinalization; import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code AbstractIterator}. @@ -30,6 +38,7 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked public class AbstractIteratorTest extends TestCase { public void testDefaultBehaviorOfNextAndHasNext() { @@ -41,7 +50,7 @@ public void testDefaultBehaviorOfNextAndHasNext() { private int rep; @Override - public Integer computeNext() { + public @Nullable Integer computeNext() { switch (rep++) { case 0: return 0; @@ -50,8 +59,7 @@ public Integer computeNext() { case 2: return endOfData(); default: - fail("Should not have been invoked again"); - return null; + throw new AssertionError("Should not have been invoked again"); } } }; @@ -70,11 +78,7 @@ public Integer computeNext() { // Make sure computeNext() doesn't get invoked again assertFalse(iter.hasNext()); - try { - iter.next(); - fail("no exception thrown"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, iter::next); } public void testSneakyThrow() throws Exception { @@ -85,31 +89,18 @@ public void testSneakyThrow() throws Exception { @Override public Integer computeNext() { if (haveBeenCalled) { - fail("Should not have been called again"); + throw new AssertionError("Should not have been called again"); } else { haveBeenCalled = true; - sneakyThrow(new SomeCheckedException()); + throw sneakyThrow(new SomeCheckedException()); } - return null; // never reached } }; // The first time, the sneakily-thrown exception comes out - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (Exception e) { - if (!(e instanceof SomeCheckedException)) { - throw e; - } - } - + assertThrows(SomeCheckedException.class, iter::hasNext); // But the second time, AbstractIterator itself throws an ISE - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iter::hasNext); } public void testException() { @@ -123,12 +114,8 @@ public Integer computeNext() { }; // It should pass through untouched - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (SomeUncheckedException e) { - assertSame(exception, e); - } + SomeUncheckedException e = assertThrows(SomeUncheckedException.class, iter::hasNext); + assertSame(exception, e); } public void testExceptionAfterEndOfData() { @@ -140,11 +127,7 @@ public Integer computeNext() { throw new SomeUncheckedException(); } }; - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (SomeUncheckedException expected) { - } + assertThrows(SomeUncheckedException.class, iter::hasNext); } public void testCantRemove() { @@ -164,15 +147,13 @@ public Integer computeNext() { assertEquals(0, (int) iter.next()); - try { - iter.remove(); - fail("No exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, iter::remove); } @GwtIncompatible // weak references + @J2ktIncompatible + @AndroidIncompatible // depends on details of GC public void testFreesNextReference() { Iterator itr = new AbstractIterator() { @@ -191,32 +172,13 @@ public void testReentrantHasNext() { @Override protected Integer computeNext() { boolean unused = hasNext(); - return null; + throw new AssertionError(); } }; - try { - iter.hasNext(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iter::hasNext); } // Technically we should test other reentrant scenarios (4 combinations of // hasNext/next), but we'll cop out for now, knowing that // next() both start by invoking hasNext() anyway. - - /** Throws an undeclared checked exception. */ - private static void sneakyThrow(Throwable t) { - class SneakyThrower { - @SuppressWarnings("unchecked") // intentionally unsafe for test - void throwIt(Throwable t) throws T { - throw (T) t; - } - } - new SneakyThrower().throwIt(t); - } - - private static class SomeCheckedException extends Exception {} - - private static class SomeUncheckedException extends RuntimeException {} } diff --git a/android/guava-tests/test/com/google/common/base/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/base/AndroidIncompatible.java index 5e190a3e1871..9ed987a26292 100644 --- a/android/guava-tests/test/com/google/common/base/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/base/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    Why use a custom annotation instead of {@code android.test.suitebuilder.annotation.Suppress}? * I'm not completely sure that this is the right choice, but it has various advantages: diff --git a/android/guava-tests/test/com/google/common/base/AsciiTest.java b/android/guava-tests/test/com/google/common/base/AsciiTest.java index 9e6b0e41ab86..6faf81046006 100644 --- a/android/guava-tests/test/com/google/common/base/AsciiTest.java +++ b/android/guava-tests/test/com/google/common/base/AsciiTest.java @@ -16,9 +16,12 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Ascii}. @@ -26,6 +29,7 @@ * @author Craig Berry */ @GwtCompatible +@NullUnmarked public class AsciiTest extends TestCase { /** @@ -98,29 +102,13 @@ public void testTruncate() { } public void testTruncateIllegalArguments() { - try { - Ascii.truncate("foobar", 2, "..."); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ascii.truncate("foobar", 2, "...")); - try { - Ascii.truncate("foobar", 8, "1234567890"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ascii.truncate("foobar", 8, "1234567890")); - try { - Ascii.truncate("foobar", -1, "..."); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ascii.truncate("foobar", -1, "...")); - try { - Ascii.truncate("foobar", -1, ""); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ascii.truncate("foobar", -1, "")); } public void testEqualsIgnoreCase() { diff --git a/android/guava-tests/test/com/google/common/base/BenchmarkHelpers.java b/android/guava-tests/test/com/google/common/base/BenchmarkHelpers.java index eda9074b5418..adba32ca9587 100644 --- a/android/guava-tests/test/com/google/common/base/BenchmarkHelpers.java +++ b/android/guava-tests/test/com/google/common/base/BenchmarkHelpers.java @@ -14,12 +14,15 @@ package com.google.common.base; +import org.jspecify.annotations.NullUnmarked; + /** * Common benchmarking utilities. * * @author Christopher Swenson * @author Louis Wasserman */ +@NullUnmarked class BenchmarkHelpers { private static final String WHITESPACE_CHARACTERS = "\u00a0\u180e\u202f\t\n\013\f\r \u0085" diff --git a/android/guava-tests/test/com/google/common/base/CaseFormatTest.java b/android/guava-tests/test/com/google/common/base/CaseFormatTest.java index f08d9f93692c..ac9efb1f7fea 100644 --- a/android/guava-tests/test/com/google/common/base/CaseFormatTest.java +++ b/android/guava-tests/test/com/google/common/base/CaseFormatTest.java @@ -24,9 +24,11 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link CaseFormat}. @@ -34,6 +36,7 @@ * @author Mike Bostock */ @GwtCompatible(emulated = true) +@NullUnmarked public class CaseFormatTest extends TestCase { public void testIdentity() { @@ -46,6 +49,7 @@ public void testIdentity() { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullArguments() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/base/CharMatcherTest.java b/android/guava-tests/test/com/google/common/base/CharMatcherTest.java index 49bdbbaffe25..0250fe94c4fe 100644 --- a/android/guava-tests/test/com/google/common/base/CharMatcherTest.java +++ b/android/guava-tests/test/com/google/common/base/CharMatcherTest.java @@ -27,6 +27,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; import java.util.Arrays; @@ -36,6 +37,7 @@ import java.util.Set; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link CharMatcher}. @@ -43,8 +45,10 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class CharMatcherTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStaticNullPointers() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -90,6 +94,7 @@ public void testWhitespaceBreakingWhitespaceSubset() throws Exception { // The next tests require ICU4J and have, at least for now, been sliced out // of the open-source view of the tests. + @J2ktIncompatible @GwtIncompatible // Character.isISOControl public void testJavaIsoControl() { for (int c = 0; c <= Character.MAX_VALUE; c++) { @@ -151,6 +156,7 @@ public void testEmpty() throws Exception { doTestEmpty(forPredicate(Predicates.equalTo('c'))); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNull() throws Exception { doTestNull(CharMatcher.any()); @@ -196,6 +202,7 @@ private void reallyTestEmpty(CharMatcher matcher) throws Exception { assertEquals(0, matcher.countIn("")); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester private static void doTestNull(CharMatcher matcher) throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -286,6 +293,7 @@ private void reallyTestNoMatches(CharMatcher matcher, CharSequence s) { assertEquals(0, matcher.countIn(s)); } + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 private void reallyTestAllMatches(CharMatcher matcher, CharSequence s) { assertTrue(matcher.matches(s.charAt(0))); assertEquals(0, matcher.indexIn(s)); @@ -303,6 +311,8 @@ private void reallyTestAllMatches(CharMatcher matcher, CharSequence s) { assertEquals(s.length(), matcher.countIn(s)); } + // Kotlin subSequence()/replace() always return new strings, violating expectations of this test + @J2ktIncompatible public void testGeneral() { doTestGeneral(is('a'), 'a', 'b'); doTestGeneral(isNot('a'), 'b', 'a'); @@ -352,7 +362,11 @@ private void doTestNoMatchThenMatch(CharMatcher matcher, String s) { reallyTestMatchThenNoMatch(matcher.precomputed().negate(), s); } - @SuppressWarnings("deprecation") // intentionally testing apply() method + // intentionally testing apply() method + @SuppressWarnings({ + "deprecation", + "InlineMeInliner", + }) private void reallyTestOneCharMatch(CharMatcher matcher, String s) { assertTrue(matcher.matches(s.charAt(0))); assertTrue(matcher.apply(s.charAt(0))); @@ -370,7 +384,11 @@ private void reallyTestOneCharMatch(CharMatcher matcher, String s) { assertEquals(1, matcher.countIn(s)); } - @SuppressWarnings("deprecation") // intentionally testing apply() method + // intentionally testing apply() method + @SuppressWarnings({ + "deprecation", + "InlineMeInliner", + }) private void reallyTestOneCharNoMatch(CharMatcher matcher, String s) { assertFalse(matcher.matches(s.charAt(0))); assertFalse(matcher.apply(s.charAt(0))); @@ -644,6 +662,14 @@ public void testReplaceFrom() { assertEquals("12 > 5", is('>').replaceFrom("12 > 5", ">")); } + public void testRetainFrom() { + assertEquals("aaa", is('a').retainFrom("bazaar")); + assertEquals("z", is('z').retainFrom("bazaar")); + assertEquals("!", is('!').retainFrom("!@#$%^&*()-=")); + assertEquals("", is('x').retainFrom("bazaar")); + assertEquals("", is('a').retainFrom("")); + } + public void testPrecomputedOptimizations() { // These are testing behavior that's never promised by the API. // Some matchers are so efficient that it is a waste of effort to @@ -719,7 +745,7 @@ static void checkExactMatches(CharMatcher m, char[] chars) { positive.add(c); } for (int c = 0; c <= Character.MAX_VALUE; c++) { - assertFalse(positive.contains(new Character((char) c)) ^ m.matches((char) c)); + assertFalse(positive.contains(Character.valueOf((char) c)) ^ m.matches((char) c)); } } diff --git a/android/guava-tests/test/com/google/common/base/CharsetsTest.java b/android/guava-tests/test/com/google/common/base/CharsetsTest.java index c968c8d39746..94dc1a115e2a 100644 --- a/android/guava-tests/test/com/google/common/base/CharsetsTest.java +++ b/android/guava-tests/test/com/google/common/base/CharsetsTest.java @@ -18,9 +18,11 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.nio.charset.Charset; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Charsets}. @@ -28,13 +30,16 @@ * @author Mike Bostock */ @GwtCompatible(emulated = true) +@NullUnmarked public class CharsetsTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testUsAscii() { assertEquals(Charset.forName("US-ASCII"), Charsets.US_ASCII); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testIso88591() { assertEquals(Charset.forName("ISO-8859-1"), Charsets.ISO_8859_1); @@ -44,21 +49,25 @@ public void testUtf8() { assertEquals(Charset.forName("UTF-8"), Charsets.UTF_8); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testUtf16be() { assertEquals(Charset.forName("UTF-16BE"), Charsets.UTF_16BE); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testUtf16le() { assertEquals(Charset.forName("UTF-16LE"), Charsets.UTF_16LE); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testUtf16() { assertEquals(Charset.forName("UTF-16"), Charsets.UTF_16); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testWhyUsAsciiIsDangerous() { byte[] b1 = "朝日新聞".getBytes(Charsets.US_ASCII); diff --git a/android/guava-tests/test/com/google/common/base/ConverterTest.java b/android/guava-tests/test/com/google/common/base/ConverterTest.java index c787ef004c40..e8817ab32e27 100644 --- a/android/guava-tests/test/com/google/common/base/ConverterTest.java +++ b/android/guava-tests/test/com/google/common/base/ConverterTest.java @@ -19,6 +19,8 @@ import static com.google.common.base.Functions.toStringFunction; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.primitives.Longs; @@ -27,9 +29,11 @@ import java.util.Iterator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link Converter}. */ -@GwtCompatible +@GwtCompatible(emulated = true) +@NullUnmarked public class ConverterTest extends TestCase { private static final Converter STR_TO_LONG = @@ -99,6 +103,8 @@ public void testReverseReverse() { assertEquals(converter, converter.reverse().reverse()); } + // We need to test that apply() does in fact behave like convert(). + @SuppressWarnings("InlineMeInliner") public void testApply() { assertEquals(LONG_VAL, STR_TO_LONG.apply(STR_VAL)); } @@ -111,6 +117,7 @@ public StringWrapper(String value) { } } + @GwtIncompatible // J2CL generics problem public void testAndThen() { Converter first = new Converter() { @@ -137,9 +144,12 @@ public String toString() { assertEquals("StringWrapper.andThen(string2long)", converter.toString()); - assertEquals(first.andThen(STR_TO_LONG), first.andThen(STR_TO_LONG)); + new EqualsTester() + .addEqualityGroup(first.andThen(STR_TO_LONG), first.andThen(STR_TO_LONG)) + .testEquals(); } + @GwtIncompatible // J2CL generics problem public void testIdentityConverter() { Converter stringIdentityConverter = Converter.identity(); @@ -173,6 +183,8 @@ public Integer apply(String input) { assertEquals("5", converter.reverse().convert(5)); } + // Null-passthrough violates our nullness annotations, so we don't support it under J2KT. + @J2ktIncompatible public void testNullIsPassedThrough() { Converter nullsArePassed = sillyConverter(false); assertEquals("forward", nullsArePassed.convert("foo")); @@ -213,6 +225,7 @@ public void testSerialization_reverse() { SerializableTester.reserializeAndAssert(reverseConverter); } + @GwtIncompatible // J2CL generics problem public void testSerialization_andThen() { Converter converterA = Longs.stringConverter(); Converter reverseConverter = Longs.stringConverter().reverse(); diff --git a/android/guava-tests/test/com/google/common/base/DefaultsTest.java b/android/guava-tests/test/com/google/common/base/DefaultsTest.java index 7b990ba5239a..3a95ab08f6a1 100644 --- a/android/guava-tests/test/com/google/common/base/DefaultsTest.java +++ b/android/guava-tests/test/com/google/common/base/DefaultsTest.java @@ -16,13 +16,19 @@ package com.google.common.base; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.annotations.GwtIncompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Defaults}. * * @author Jige Yu */ +@GwtIncompatible +@NullUnmarked public class DefaultsTest extends TestCase { public void testGetDefaultValue() { assertEquals(false, Defaults.defaultValue(boolean.class).booleanValue()); @@ -32,7 +38,7 @@ public void testGetDefaultValue() { assertEquals(0, Defaults.defaultValue(int.class).intValue()); assertEquals(0, Defaults.defaultValue(long.class).longValue()); assertEquals(0.0f, Defaults.defaultValue(float.class).floatValue()); - assertEquals(0.0d, Defaults.defaultValue(double.class).doubleValue()); + assertThat(Defaults.defaultValue(double.class).doubleValue()).isEqualTo(0.0d); assertNull(Defaults.defaultValue(void.class)); assertNull(Defaults.defaultValue(String.class)); } diff --git a/android/guava-tests/test/com/google/common/base/EnumsTest.java b/android/guava-tests/test/com/google/common/base/EnumsTest.java index d8b13af75423..394bbd3689b2 100644 --- a/android/guava-tests/test/com/google/common/base/EnumsTest.java +++ b/android/guava-tests/test/com/google/common/base/EnumsTest.java @@ -19,9 +19,10 @@ import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; -import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.testing.GcFinalization; @@ -38,13 +39,16 @@ import java.util.HashSet; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Enums}. * * @author Steve McKay */ -@GwtCompatible(emulated = true) +@GwtIncompatible +@J2ktIncompatible +@NullUnmarked public class EnumsTest extends TestCase { private enum TestEnum { @@ -53,8 +57,6 @@ private enum TestEnum { POODLE, } - private enum OtherEnum {} - public void testGetIfPresent() { assertThat(Enums.getIfPresent(TestEnum.class, "CHEETO")).hasValue(TestEnum.CHEETO); assertThat(Enums.getIfPresent(TestEnum.class, "HONDA")).hasValue(TestEnum.HONDA); @@ -80,7 +82,9 @@ public void testGetIfPresent_whenNoMatchingConstant() { } + @J2ktIncompatible @GwtIncompatible // weak references + @AndroidIncompatible // depends on details of GC and classloading public void testGetIfPresent_doesNotPreventClassUnloading() throws Exception { WeakReference shadowLoaderReference = doTestClassUnloading(); GcFinalization.awaitClear(shadowLoaderReference); @@ -91,6 +95,7 @@ public void testGetIfPresent_doesNotPreventClassUnloading() throws Exception { // new ClassLoader. If Enums.getIfPresent does caching that prevents the shadow TestEnum // (and therefore its ClassLoader) from being unloaded, then this WeakReference will never be // cleared. + @J2ktIncompatible @GwtIncompatible // weak references private WeakReference doTestClassUnloading() throws Exception { URLClassLoader shadowLoader = new URLClassLoader(getClassPathUrls(), null); @@ -112,6 +117,7 @@ private WeakReference doTestClassUnloading() throws Exception { return new WeakReference<>(shadowLoader); } + @GwtIncompatible // stringConverter public void testStringConverter_convert() { Converter converter = Enums.stringConverter(TestEnum.class); assertEquals(TestEnum.CHEETO, converter.convert("CHEETO")); @@ -121,15 +127,13 @@ public void testStringConverter_convert() { assertNull(converter.reverse().convert(null)); } + @GwtIncompatible // stringConverter public void testStringConverter_convertError() { Converter converter = Enums.stringConverter(TestEnum.class); - try { - converter.convert("xxx"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> converter.convert("xxx")); } + @GwtIncompatible // stringConverter public void testStringConverter_reverse() { Converter converter = Enums.stringConverter(TestEnum.class); assertEquals("CHEETO", converter.reverse().convert(TestEnum.CHEETO)); @@ -137,19 +141,22 @@ public void testStringConverter_reverse() { assertEquals("POODLE", converter.reverse().convert(TestEnum.POODLE)); } - @GwtIncompatible // NullPointerTester + @J2ktIncompatible + @GwtIncompatible // stringConverter public void testStringConverter_nullPointerTester() throws Exception { Converter converter = Enums.stringConverter(TestEnum.class); NullPointerTester tester = new NullPointerTester(); tester.testAllPublicInstanceMethods(converter); } + @GwtIncompatible // stringConverter public void testStringConverter_nullConversions() { Converter converter = Enums.stringConverter(TestEnum.class); assertNull(converter.convert(null)); assertNull(converter.reverse().convert(null)); } + @J2ktIncompatible @GwtIncompatible // Class.getName() public void testStringConverter_toString() { assertEquals( @@ -157,10 +164,12 @@ public void testStringConverter_toString() { Enums.stringConverter(TestEnum.class).toString()); } + @GwtIncompatible // stringConverter public void testStringConverter_serialization() { SerializableTester.reserializeAndAssert(Enums.stringConverter(TestEnum.class)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -176,6 +185,7 @@ private enum AnEnum { BAR } + @J2ktIncompatible @GwtIncompatible // reflection public void testGetField() { Field foo = Enums.getField(AnEnum.FOO); @@ -187,6 +197,7 @@ public void testGetField() { assertFalse(bar.isAnnotationPresent(ExampleAnnotation.class)); } + @J2ktIncompatible @GwtIncompatible // Class.getClassLoader() private URL[] getClassPathUrls() { ClassLoader classLoader = getClass().getClassLoader(); @@ -200,6 +211,7 @@ private URL[] getClassPathUrls() { * System#getProperty system property}. */ // TODO(b/65488446): Make this a public API. + @J2ktIncompatible @GwtIncompatible private static ImmutableList parseJavaClassPath() { ImmutableList.Builder urls = ImmutableList.builder(); @@ -211,9 +223,7 @@ private static ImmutableList parseJavaClassPath() { urls.add(new URL("file", null, new File(entry).getAbsolutePath())); } } catch (MalformedURLException e) { - AssertionError error = new AssertionError("malformed class path entry: " + entry); - error.initCause(e); - throw error; + throw new AssertionError("malformed class path entry: " + entry, e); } } return urls.build(); diff --git a/android/guava-tests/test/com/google/common/base/EquivalenceTest.java b/android/guava-tests/test/com/google/common/base/EquivalenceTest.java index 07c86eae8270..5de187b1b8be 100644 --- a/android/guava-tests/test/com/google/common/base/EquivalenceTest.java +++ b/android/guava-tests/test/com/google/common/base/EquivalenceTest.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; import com.google.common.testing.EqualsTester; @@ -25,15 +26,17 @@ import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Equivalence}. * * @author Jige Yu */ +@NullMarked @GwtCompatible(emulated = true) public class EquivalenceTest extends TestCase { - @SuppressWarnings("unchecked") // varargs public void testPairwiseEquivalent() { EquivalenceTester.of(Equivalence.equals().pairwise()) .addEquivalenceGroup(ImmutableList.of()) @@ -69,9 +72,11 @@ public void testWrap() { LENGTH_EQUIVALENCE.wrap("hello"), LENGTH_EQUIVALENCE.wrap("world")) .addEqualityGroup(LENGTH_EQUIVALENCE.wrap("hi"), LENGTH_EQUIVALENCE.wrap("yo")) - .addEqualityGroup(LENGTH_EQUIVALENCE.wrap(null), LENGTH_EQUIVALENCE.wrap(null)) + .addEqualityGroup( + LENGTH_EQUIVALENCE.<@Nullable String>wrap(null), + LENGTH_EQUIVALENCE.<@Nullable String>wrap(null)) .addEqualityGroup(Equivalence.equals().wrap("hello")) - .addEqualityGroup(Equivalence.equals().wrap(null)) + .addEqualityGroup(Equivalence.equals().<@Nullable Object>wrap(null)) .testEquals(); } @@ -81,6 +86,7 @@ public void testWrap_get() { assertSame(test, wrapper.get()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { SerializableTester.reserializeAndAssert(LENGTH_EQUIVALENCE.wrap("hello")); @@ -119,11 +125,11 @@ public void testOnResultOf_equals() { } public void testEquivalentTo() { - Predicate equalTo1 = Equivalence.equals().equivalentTo("1"); + Predicate<@Nullable Object> equalTo1 = Equivalence.equals().equivalentTo("1"); assertTrue(equalTo1.apply("1")); assertFalse(equalTo1.apply("2")); assertFalse(equalTo1.apply(null)); - Predicate isNull = Equivalence.equals().equivalentTo(null); + Predicate<@Nullable Object> isNull = Equivalence.equals().equivalentTo(null); assertFalse(isNull.apply("1")); assertFalse(isNull.apply("2")); assertTrue(isNull.apply(null)); @@ -135,17 +141,25 @@ public void testEquivalentTo() { .testEquals(); } + /* + * We use large numbers to avoid the integer cache. Normally, we'd accomplish that merely by using + * `new Integer` (as we do) instead of `Integer.valueOf`. However, under J2KT, `new Integer` + * gets translated back to `Integer.valueOf` because that is the only thing J2KT can support. And + * anyway, it's nice to avoid `Integer.valueOf` because the Android toolchain optimizes multiple + * `Integer.valueOf` calls into one! So we stick with the deprecated `Integer` constructor. + */ + public void testEqualsEquivalent() { EquivalenceTester.of(Equivalence.equals()) - .addEquivalenceGroup(new Integer(42), 42) + .addEquivalenceGroup(new Integer(42_000_000), 42_000_000) .addEquivalenceGroup("a") .test(); } public void testIdentityEquivalent() { EquivalenceTester.of(Equivalence.identity()) - .addEquivalenceGroup(new Integer(42)) - .addEquivalenceGroup(new Integer(42)) + .addEquivalenceGroup(new Integer(42_000_000)) + .addEquivalenceGroup(new Integer(42_000_000)) .addEquivalenceGroup("a") .test(); } @@ -157,10 +171,16 @@ public void testEquals() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester - public void testNulls() { - new NullPointerTester().testAllPublicStaticMethods(Equivalence.class); - new NullPointerTester().testAllPublicInstanceMethods(Equivalence.equals()); - new NullPointerTester().testAllPublicInstanceMethods(Equivalence.identity()); + public void testNulls() throws NoSuchMethodException { + NullPointerTester tester = new NullPointerTester(); + // Necessary until JDK15: + // https://bugs.openjdk.org/browse/JDK-8202469 + tester.ignore(Equivalence.class.getMethod("wrap", Object.class)); + + tester.testAllPublicStaticMethods(Equivalence.class); + tester.testAllPublicInstanceMethods(Equivalence.equals()); + tester.testAllPublicInstanceMethods(Equivalence.identity()); } } diff --git a/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueClassLoaderUnloadingTest.java b/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueClassLoaderUnloadingTest.java index ae35c16f6482..c4236becab70 100644 --- a/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueClassLoaderUnloadingTest.java +++ b/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueClassLoaderUnloadingTest.java @@ -17,12 +17,11 @@ package com.google.common.base; import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; -import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; +import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.testing.GcFinalization; -import java.io.Closeable; import java.io.File; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; @@ -30,14 +29,11 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.security.Permission; -import java.security.Policy; -import java.security.ProtectionDomain; -import java.util.concurrent.Callable; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests that the {@code ClassLoader} of {@link FinalizableReferenceQueue} can be unloaded. These @@ -46,9 +42,10 @@ * * @author Eamonn McManus */ - - -public class FinalizableReferenceQueueClassLoaderUnloadingTest extends TestCase { +@AndroidIncompatible +@RunWith(JUnit4.class) +@NullUnmarked +public class FinalizableReferenceQueueClassLoaderUnloadingTest { /* * The following tests check that the use of FinalizableReferenceQueue does not prevent the @@ -76,13 +73,6 @@ public MyFinalizableWeakReference(Object x, FinalizableReferenceQueue queue) { public void finalizeReferent() {} } - private static class PermissivePolicy extends Policy { - @Override - public boolean implies(ProtectionDomain pd, Permission perm) { - return true; - } - } - private WeakReference useFrqInSeparateLoader() throws Exception { final ClassLoader myLoader = getClass().getClassLoader(); URLClassLoader sepLoader = new URLClassLoader(getClassPathUrls(), myLoader.getParent()); @@ -94,7 +84,7 @@ private WeakReference useFrqInSeparateLoader() throws Exception { Class frqC = FinalizableReferenceQueue.class; Class sepFrqC = sepLoader.loadClass(frqC.getName()); - assertNotSame(frqC, sepFrqC); + assertThat(frqC).isNotSameInstanceAs(sepFrqC); // Check the assumptions above. // FRQ tries to load the Finalizer class (for the reference-collecting thread) in a few ways. @@ -117,13 +107,13 @@ private WeakReference useFrqInSeparateLoader() throws Exception { Constructor sepFwrCons = sepFwrC.getConstructor(Object.class, sepFrqC); // The object that we will wrap in FinalizableWeakReference is a Stopwatch. Class sepStopwatchC = sepLoader.loadClass(Stopwatch.class.getName()); - assertSame(sepLoader, sepStopwatchC.getClassLoader()); + assertThat(sepLoader).isSameInstanceAs(sepStopwatchC.getClassLoader()); AtomicReference sepStopwatchA = new AtomicReference(sepStopwatchC.getMethod("createUnstarted").invoke(null)); AtomicReference> sepStopwatchRef = new AtomicReference>( (WeakReference) sepFwrCons.newInstance(sepStopwatchA.get(), sepFrqA.get())); - assertNotNull(sepStopwatchA.get()); + assertThat(sepStopwatchA.get()).isNotNull(); // Clear all references to the Stopwatch and wait for it to be gc'd. sepStopwatchA.set(null); GcFinalization.awaitClear(sepStopwatchRef.get()); @@ -132,131 +122,14 @@ private WeakReference useFrqInSeparateLoader() throws Exception { return new WeakReference(sepLoader); } - private void doTestUnloadable() throws Exception { - WeakReference loaderRef = useFrqInSeparateLoader(); - GcFinalization.awaitClear(loaderRef); - } - /** * Tests that the use of a {@link FinalizableReferenceQueue} does not subsequently prevent the * loader of that class from being garbage-collected. */ - public void testUnloadableWithoutSecurityManager() throws Exception { - if (isJdk9OrHigher()) { - return; - } - SecurityManager oldSecurityManager = System.getSecurityManager(); - try { - System.setSecurityManager(null); - doTestUnloadable(); - } finally { - System.setSecurityManager(oldSecurityManager); - } - } - - /** - * Tests that the use of a {@link FinalizableReferenceQueue} does not subsequently prevent the - * loader of that class from being garbage-collected even if there is a {@link SecurityManager}. - * The {@link SecurityManager} environment makes such leaks more likely because when you create a - * {@link URLClassLoader} with a {@link SecurityManager}, the creating code's {@link - * java.security.AccessControlContext} is captured, and that references the creating code's {@link - * ClassLoader}. - */ - public void testUnloadableWithSecurityManager() throws Exception { - if (isJdk9OrHigher()) { - return; - } - Policy oldPolicy = Policy.getPolicy(); - SecurityManager oldSecurityManager = System.getSecurityManager(); - try { - Policy.setPolicy(new PermissivePolicy()); - System.setSecurityManager(new SecurityManager()); - doTestUnloadable(); - } finally { - System.setSecurityManager(oldSecurityManager); - Policy.setPolicy(oldPolicy); - } - } - - public static class FrqUser implements Callable> { - public static FinalizableReferenceQueue frq = new FinalizableReferenceQueue(); - public static final Semaphore finalized = new Semaphore(0); - - @Override - public WeakReference call() { - WeakReference wr = - new FinalizableWeakReference(new Integer(23), frq) { - @Override - public void finalizeReferent() { - finalized.release(); - } - }; - return wr; - } - } - - public void testUnloadableInStaticFieldIfClosed() throws Exception { - if (isJdk9OrHigher()) { - return; - } - Policy oldPolicy = Policy.getPolicy(); - SecurityManager oldSecurityManager = System.getSecurityManager(); - try { - Policy.setPolicy(new PermissivePolicy()); - System.setSecurityManager(new SecurityManager()); - WeakReference loaderRef = doTestUnloadableInStaticFieldIfClosed(); - GcFinalization.awaitClear(loaderRef); - } finally { - System.setSecurityManager(oldSecurityManager); - Policy.setPolicy(oldPolicy); - } - } - - // If you have a FinalizableReferenceQueue that is a static field of one of the classes of your - // app (like the FrqUser class above), then the app's ClassLoader will never be gc'd. The reason - // is that we attempt to run a thread in a separate ClassLoader that will detect when the FRQ - // is no longer referenced, meaning that the app's ClassLoader has been gc'd, and when that - // happens. But the thread's supposedly separate ClassLoader actually has a reference to the app's - // ClasLoader via its AccessControlContext. It does not seem to be possible to make a - // URLClassLoader without capturing this reference, and it probably would not be desirable for - // security reasons anyway. Therefore, the FRQ.close() method provides a way to stop the thread - // explicitly. This test checks that calling that method does allow an app's ClassLoader to be - // gc'd even if there is a still a FinalizableReferenceQueue in a static field. (Setting the field - // to null would also work, but only if there are no references to the FRQ anywhere else.) - private WeakReference doTestUnloadableInStaticFieldIfClosed() throws Exception { - final ClassLoader myLoader = getClass().getClassLoader(); - URLClassLoader sepLoader = new URLClassLoader(getClassPathUrls(), myLoader.getParent()); - - Class frqC = FinalizableReferenceQueue.class; - Class sepFrqC = sepLoader.loadClass(frqC.getName()); - assertNotSame(frqC, sepFrqC); - - Class sepFrqSystemLoaderC = - sepLoader.loadClass(FinalizableReferenceQueue.SystemLoader.class.getName()); - Field disabled = sepFrqSystemLoaderC.getDeclaredField("disabled"); - disabled.setAccessible(true); - disabled.set(null, true); - - Class frqUserC = FrqUser.class; - Class sepFrqUserC = sepLoader.loadClass(frqUserC.getName()); - assertNotSame(frqUserC, sepFrqUserC); - assertSame(sepLoader, sepFrqUserC.getClassLoader()); - - Callable sepFrqUser = (Callable) sepFrqUserC.getDeclaredConstructor().newInstance(); - WeakReference finalizableWeakReference = (WeakReference) sepFrqUser.call(); - - GcFinalization.awaitClear(finalizableWeakReference); - - Field sepFrqUserFinalizedF = sepFrqUserC.getField("finalized"); - Semaphore finalizeCount = (Semaphore) sepFrqUserFinalizedF.get(null); - boolean finalized = finalizeCount.tryAcquire(5, TimeUnit.SECONDS); - assertTrue(finalized); - - Field sepFrqUserFrqF = sepFrqUserC.getField("frq"); - Closeable frq = (Closeable) sepFrqUserFrqF.get(null); - frq.close(); - - return new WeakReference(sepLoader); + @Test + public void testUnloadable() throws Exception { + WeakReference loaderRef = useFrqInSeparateLoader(); + GcFinalization.awaitClear(loaderRef); } private URL[] getClassPathUrls() { @@ -281,21 +154,9 @@ private static ImmutableList parseJavaClassPath() { urls.add(new URL("file", null, new File(entry).getAbsolutePath())); } } catch (MalformedURLException e) { - AssertionError error = new AssertionError("malformed class path entry: " + entry); - error.initCause(e); - throw error; + throw new AssertionError("malformed class path entry: " + entry, e); } } return urls.build(); } - - /** - * These tests fail in JDK 9 and JDK 10 for an unknown reason. It might be the test; it might be - * the underlying functionality. Fixing this is not a high priority; if you need it to be fixed, - * please comment on issue 3086. - */ - private static boolean isJdk9OrHigher() { - return JAVA_SPECIFICATION_VERSION.value().startsWith("9") - || JAVA_SPECIFICATION_VERSION.value().startsWith("10"); - } } diff --git a/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueTest.java b/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueTest.java index 3e9912280b4b..66a07ffaf8fe 100644 --- a/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueTest.java +++ b/android/guava-tests/test/com/google/common/base/FinalizableReferenceQueueTest.java @@ -16,40 +16,58 @@ package com.google.common.base; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.annotations.GwtIncompatible; import com.google.common.base.internal.Finalizer; +import com.google.common.collect.Sets; import com.google.common.testing.GcFinalization; +import java.io.Closeable; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.ref.Cleaner; +import java.lang.ref.Cleaner.Cleanable; +import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; +import java.net.ServerSocket; import java.net.URL; import java.net.URLClassLoader; -import java.util.Arrays; -import java.util.Collections; -import junit.framework.TestCase; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Unit test for {@link FinalizableReferenceQueue}. * * @author Bob Lee */ -public class FinalizableReferenceQueueTest extends TestCase { +// - depends on details of GC and classloading +// - .class files aren't available +// - possibly no real concept of separate ClassLoaders? +@AndroidIncompatible +@GwtIncompatible +@RunWith(JUnit4.class) +@NullUnmarked +public class FinalizableReferenceQueueTest { - private FinalizableReferenceQueue frq; + private @Nullable FinalizableReferenceQueue frq; - @Override - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { frq = null; } - + @Test public void testFinalizeReferentCalled() { final MockReference reference = new MockReference(frq = new FinalizableReferenceQueue()); - GcFinalization.awaitDone( - new GcFinalization.FinalizationPredicate() { - public boolean isDone() { - return reference.finalizeReferentCalled; - } - }); + GcFinalization.awaitDone(() -> reference.finalizeReferentCalled); } static class MockReference extends FinalizableWeakReference { @@ -72,14 +90,14 @@ public void finalizeReferent() { */ private WeakReference> queueReference; - + @Test public void testThatFinalizerStops() { weaklyReferenceQueue(); GcFinalization.awaitClear(queueReference); } /** If we don't keep a strong reference to the reference object, it won't be enqueued. */ - FinalizableWeakReference reference; + @Nullable FinalizableWeakReference reference; /** Create the FRQ in a method that goes out of scope so that we're sure it will be reclaimed. */ private void weaklyReferenceQueue() { @@ -101,7 +119,7 @@ public void finalizeReferent() { }; } - @AndroidIncompatible // no concept of separate ClassLoaders + @Test public void testDecoupledLoader() { FinalizableReferenceQueue.DecoupledLoader decoupledLoader = new FinalizableReferenceQueue.DecoupledLoader() { @@ -113,10 +131,10 @@ URLClassLoader newLoader(URL base) { Class finalizerCopy = decoupledLoader.loadFinalizer(); - assertNotNull(finalizerCopy); - assertNotSame(Finalizer.class, finalizerCopy); + assertThat(finalizerCopy).isNotNull(); + assertThat(finalizerCopy).isNotSameInstanceAs(Finalizer.class); - assertNotNull(FinalizableReferenceQueue.getStartFinalizer(finalizerCopy)); + assertThat(FinalizableReferenceQueue.getStartFinalizer(finalizerCopy)).isNotNull(); } static class DecoupledClassLoader extends URLClassLoader { @@ -141,14 +159,120 @@ protected synchronized Class loadClass(String name, boolean resolve) } } - @AndroidIncompatible // TODO(cpovirk): How significant is this failure? + @Test public void testGetFinalizerUrl() { - assertNotNull(getClass().getResource("internal/Finalizer.class")); + assertThat(getClass().getResource("internal/Finalizer.class")).isNotNull(); } + @Test public void testFinalizeClassHasNoNestedClasses() throws Exception { // Ensure that the Finalizer class has no nested classes. - // See https://code.google.com/p/guava-libraries/issues/detail?id=1505 - assertEquals(Collections.emptyList(), Arrays.asList(Finalizer.class.getDeclaredClasses())); + // See https://github.com/google/guava/issues/1505 + assertThat(Finalizer.class.getDeclaredClasses()).isEmpty(); + } + + static class MyServerExampleWithFrq implements Closeable { + private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue(); + + private static final Set> references = Sets.newConcurrentHashSet(); + + private final ServerSocket serverSocket; + + private MyServerExampleWithFrq() throws IOException { + this.serverSocket = new ServerSocket(0); + } + + static MyServerExampleWithFrq create(AtomicBoolean finalizeReferentRan) throws IOException { + MyServerExampleWithFrq myServer = new MyServerExampleWithFrq(); + ServerSocket serverSocket = myServer.serverSocket; + Reference reference = + new FinalizablePhantomReference(myServer, frq) { + @Override + public void finalizeReferent() { + references.remove(this); + if (!serverSocket.isClosed()) { + try { + serverSocket.close(); + finalizeReferentRan.set(true); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + }; + references.add(reference); + return myServer; + } + + @Override + public void close() throws IOException { + serverSocket.close(); + } + } + + private ServerSocket makeMyServerExampleWithFrq(AtomicBoolean finalizeReferentRan) + throws IOException { + MyServerExampleWithFrq myServer = MyServerExampleWithFrq.create(finalizeReferentRan); + assertThat(myServer.serverSocket.isClosed()).isFalse(); + return myServer.serverSocket; + } + + @Test + public void testMyServerExampleWithFrq() throws Exception { + AtomicBoolean finalizeReferentRan = new AtomicBoolean(false); + ServerSocket serverSocket = makeMyServerExampleWithFrq(finalizeReferentRan); + GcFinalization.awaitDone(finalizeReferentRan::get); + assertThat(serverSocket.isClosed()).isTrue(); + } + + @SuppressWarnings("Java8ApiChecker") + static class MyServerExampleWithCleaner implements AutoCloseable { + private static final Cleaner cleaner = Cleaner.create(); + + private static Runnable closeServerSocketRunnable( + ServerSocket serverSocket, AtomicBoolean cleanerRan) { + return () -> { + try { + serverSocket.close(); + cleanerRan.set(true); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + + private final ServerSocket serverSocket; + private final Cleanable cleanable; + + MyServerExampleWithCleaner(AtomicBoolean cleanerRan) throws IOException { + this.serverSocket = new ServerSocket(0); + this.cleanable = cleaner.register(this, closeServerSocketRunnable(serverSocket, cleanerRan)); + } + + @Override + public void close() { + cleanable.clean(); + } + } + + @SuppressWarnings("Java8ApiChecker") + private ServerSocket makeMyServerExampleWithCleaner(AtomicBoolean cleanerRan) throws IOException { + MyServerExampleWithCleaner myServer = new MyServerExampleWithCleaner(cleanerRan); + assertThat(myServer.serverSocket.isClosed()).isFalse(); + return myServer.serverSocket; + } + + @SuppressWarnings("Java8ApiChecker") + @Test + public void testMyServerExampleWithCleaner() throws Exception { + try { + Class.forName("java.lang.ref.Cleaner"); + } catch (ClassNotFoundException beforeJava9) { + return; + } + AtomicBoolean cleanerRan = new AtomicBoolean(false); + ServerSocket serverSocket = makeMyServerExampleWithCleaner(cleanerRan); + GcFinalization.awaitDone(cleanerRan::get); + assertThat(serverSocket.isClosed()).isTrue(); } } diff --git a/android/guava-tests/test/com/google/common/base/FunctionsTest.java b/android/guava-tests/test/com/google/common/base/FunctionsTest.java index 1411c192b551..947f798ac56a 100644 --- a/android/guava-tests/test/com/google/common/base/FunctionsTest.java +++ b/android/guava-tests/test/com/google/common/base/FunctionsTest.java @@ -16,8 +16,11 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.testing.ClassSanityTester; @@ -27,6 +30,8 @@ import java.io.Serializable; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Functions}. @@ -35,10 +40,11 @@ * @author Vlad Patryshev */ @GwtCompatible(emulated = true) +@NullMarked public class FunctionsTest extends TestCase { public void testIdentity_same() { - Function identity = Functions.identity(); + Function<@Nullable String, @Nullable String> identity = Functions.identity(); assertNull(identity.apply(null)); assertSame("foo", identity.apply("foo")); } @@ -48,6 +54,7 @@ public void testIdentity_notSame() { assertNotSame(new Long(135135L), identity.apply(new Long(135135L))); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testIdentitySerializable() { checkCanReserializeSingleton(Functions.identity()); @@ -66,18 +73,16 @@ public String toString() { return "I'm a string"; } })); - try { - Functions.toStringFunction().apply(null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Functions.toStringFunction().apply(null)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testToStringFunctionSerializable() { checkCanReserializeSingleton(Functions.toStringFunction()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -85,21 +90,17 @@ public void testNullPointerExceptions() { } public void testForMapWithoutDefault() { - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("One", 1); map.put("Three", 3); map.put("Null", null); - Function function = Functions.forMap(map); + Function function = Functions.forMap(map); assertEquals(1, function.apply("One").intValue()); assertEquals(3, function.apply("Three").intValue()); assertNull(function.apply("Null")); - try { - function.apply("Two"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> function.apply("Two")); new EqualsTester() .addEqualityGroup(function, Functions.forMap(map)) @@ -107,17 +108,18 @@ public void testForMapWithoutDefault() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForMapWithoutDefaultSerializable() { checkCanReserialize(Functions.forMap(ImmutableMap.of(1, 2))); } public void testForMapWithDefault() { - Map map = Maps.newHashMap(); + Map map = Maps.newHashMap(); map.put("One", 1); map.put("Three", 3); map.put("Null", null); - Function function = Functions.forMap(map, 42); + Function function = Functions.forMap(map, 42); assertEquals(1, function.apply("One").intValue()); assertEquals(42, function.apply("Two").intValue()); @@ -132,6 +134,7 @@ public void testForMapWithDefault() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForMapWithDefault_includeSerializable() { Map map = Maps.newHashMap(); @@ -152,6 +155,7 @@ public void testForMapWithDefault_includeSerializable() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForMapWithDefaultSerializable() { checkCanReserialize(Functions.forMap(ImmutableMap.of(1, 2), 3)); @@ -159,7 +163,7 @@ public void testForMapWithDefaultSerializable() { public void testForMapWithDefault_null() { ImmutableMap map = ImmutableMap.of("One", 1); - Function function = Functions.forMap(map, null); + Function function = Functions.forMap(map, null); assertEquals((Integer) 1, function.apply("One")); assertNull(function.apply("Two")); @@ -171,6 +175,7 @@ public void testForMapWithDefault_null() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForMapWithDefault_null_compareWithSerializable() { ImmutableMap map = ImmutableMap.of("One", 1); @@ -215,17 +220,9 @@ public void testComposition() { Functions.compose(integerToSpanish, japaneseToInteger); assertEquals("Uno", japaneseToSpanish.apply("Ichi")); - try { - japaneseToSpanish.apply("Ni"); - fail(); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> japaneseToSpanish.apply("Ni")); assertEquals("Tres", japaneseToSpanish.apply("San")); - try { - japaneseToSpanish.apply("Shi"); - fail(); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> japaneseToSpanish.apply("Shi")); new EqualsTester() .addEqualityGroup(japaneseToSpanish, Functions.compose(integerToSpanish, japaneseToInteger)) @@ -235,6 +232,7 @@ public void testComposition() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testComposition_includeReserializabled() { Map mJapaneseToInteger = Maps.newHashMap(); @@ -269,13 +267,13 @@ public void testCompositionWildcard() { Function numberToSpanish = Functions.constant("Yo no se"); - Function japaneseToSpanish = + Function unusedJapaneseToSpanish = Functions.compose(numberToSpanish, japaneseToInteger); } - private static class HashCodeFunction implements Function { + private static class HashCodeFunction implements Function<@Nullable Object, Integer> { @Override - public Integer apply(Object o) { + public Integer apply(@Nullable Object o) { return (o == null) ? 0 : o.hashCode(); } } @@ -332,17 +330,18 @@ public void testForPredicate() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForPredicateSerializable() { checkCanReserialize(Functions.forPredicate(Predicates.equalTo(5))); } public void testConstant() { - Function f = Functions.constant("correct"); + Function<@Nullable Object, Object> f = Functions.constant("correct"); assertEquals("correct", f.apply(new Object())); assertEquals("correct", f.apply(null)); - Function g = Functions.constant(null); + Function<@Nullable Object, @Nullable String> g = Functions.constant(null); assertEquals(null, g.apply(2)); assertEquals(null, g.apply(null)); @@ -354,13 +353,14 @@ public void testConstant() { .testEquals(); new EqualsTester() - .addEqualityGroup(g, Functions.constant(null)) + .addEqualityGroup(g, Functions.<@Nullable Object>constant(null)) .addEqualityGroup(Functions.constant("incorrect")) .addEqualityGroup(Functions.toStringFunction()) .addEqualityGroup(f) .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testConstantSerializable() { checkCanReserialize(Functions.constant(5)); @@ -368,7 +368,7 @@ public void testConstantSerializable() { private static class CountingSupplier implements Supplier, Serializable { - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; private int value; @@ -378,7 +378,7 @@ public Integer get() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof CountingSupplier) { return this.value == ((CountingSupplier) obj).value; } @@ -393,7 +393,7 @@ public int hashCode() { public void testForSupplier() { Supplier supplier = new CountingSupplier(); - Function function = Functions.forSupplier(supplier); + Function<@Nullable Object, Integer> function = Functions.forSupplier(supplier); assertEquals(1, (int) function.apply(null)); assertEquals(2, (int) function.apply("foo")); @@ -406,16 +406,19 @@ public void testForSupplier() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForSupplierSerializable() { checkCanReserialize(Functions.forSupplier(new CountingSupplier())); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { new ClassSanityTester().forAllPublicStaticMethods(Functions.class).testNulls(); } + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException: com.google.common.base.Function // (I suspect that this and the other similar failures happen with ArbitraryInstances proxies.) @@ -423,6 +426,7 @@ public void testEqualsAndSerializable() throws Exception { new ClassSanityTester().forAllPublicStaticMethods(Functions.class).testEqualsAndSerializable(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester private static void checkCanReserialize(Function f) { Function g = SerializableTester.reserializeAndAssert(f); @@ -443,6 +447,7 @@ private static void checkCanReserialize(Function f) { } } + @J2ktIncompatible @GwtIncompatible // SerializableTester private static void checkCanReserializeSingleton(Function f) { Function g = SerializableTester.reserializeAndAssert(f); diff --git a/android/guava-tests/test/com/google/common/base/JoinerTest.java b/android/guava-tests/test/com/google/common/base/JoinerTest.java index d9ed3472184c..cdb4206dea25 100644 --- a/android/guava-tests/test/com/google/common/base/JoinerTest.java +++ b/android/guava-tests/test/com/google/common/base/JoinerTest.java @@ -16,23 +16,28 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Joiner.MapJoiner; +import com.google.common.collect.ForwardingList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.testing.NullPointerTester; import java.io.IOException; import java.util.Arrays; -import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Joiner}. @@ -40,92 +45,129 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class JoinerTest extends TestCase { private static final Joiner J = Joiner.on("-"); // needed to prevent warning :( - private static final Iterable ITERABLE_ = Arrays.asList(); - private static final Iterable ITERABLE_1 = Arrays.asList(1); - private static final Iterable ITERABLE_12 = Arrays.asList(1, 2); - private static final Iterable ITERABLE_123 = Arrays.asList(1, 2, 3); - private static final Iterable ITERABLE_NULL = Arrays.asList((Integer) null); - private static final Iterable ITERABLE_NULL_NULL = Arrays.asList((Integer) null, null); - private static final Iterable ITERABLE_NULL_1 = Arrays.asList(null, 1); - private static final Iterable ITERABLE_1_NULL = Arrays.asList(1, null); - private static final Iterable ITERABLE_1_NULL_2 = Arrays.asList(1, null, 2); - private static final Iterable ITERABLE_FOUR_NULLS = + private static final Iterable iterable = Arrays.asList(); + private static final Iterable iterable1 = Arrays.asList(1); + private static final Iterable iterable12 = Arrays.asList(1, 2); + private static final Iterable iterable123 = Arrays.asList(1, 2, 3); + private static final Iterable<@Nullable Integer> iterableNull = Arrays.asList((Integer) null); + private static final Iterable<@Nullable Integer> iterableNullNull = + Arrays.asList((Integer) null, null); + private static final Iterable<@Nullable Integer> iterableNull1 = Arrays.asList(null, 1); + private static final Iterable<@Nullable Integer> iterable1Null = Arrays.asList(1, null); + private static final Iterable<@Nullable Integer> iterable1Null2 = Arrays.asList(1, null, 2); + private static final Iterable<@Nullable Integer> iterableFourNulls = Arrays.asList((Integer) null, null, null, null); - public void testNoSpecialNullBehavior() { - checkNoOutput(J, ITERABLE_); - checkResult(J, ITERABLE_1, "1"); - checkResult(J, ITERABLE_12, "1-2"); - checkResult(J, ITERABLE_123, "1-2-3"); + /* + * Both of these fields *are* immutable/constant. They don't use the type ImmutableList because + * they need to behave slightly differently. + */ + @SuppressWarnings("ConstantCaseForConstants") + private static final List UNDERREPORTING_SIZE_LIST; - try { - J.join(ITERABLE_NULL); - fail(); - } catch (NullPointerException expected) { - } - try { - J.join(ITERABLE_1_NULL_2); - fail(); - } catch (NullPointerException expected) { + @SuppressWarnings("ConstantCaseForConstants") + private static final List OVERREPORTING_SIZE_LIST; + + static { + List collection123 = Arrays.asList(1, 2, 3); + UNDERREPORTING_SIZE_LIST = unmodifiableList(new MisleadingSizeList<>(collection123, -1)); + OVERREPORTING_SIZE_LIST = unmodifiableList(new MisleadingSizeList<>(collection123, 1)); + } + + /* + * c.g.c.collect.testing.Helpers.misleadingSizeList has a broken Iterator, so we can't use it. (I + * mean, ideally we'd fix it....) Also, we specifically need a List so that we trigger the fast + * path in join(Iterable). + */ + private static final class MisleadingSizeList + extends ForwardingList { + final List delegate; + final int delta; + + MisleadingSizeList(List delegate, int delta) { + this.delegate = delegate; + this.delta = delta; } - try { - J.join(ITERABLE_NULL.iterator()); - fail(); - } catch (NullPointerException expected) { + @Override + protected List delegate() { + return delegate; } - try { - J.join(ITERABLE_1_NULL_2.iterator()); - fail(); - } catch (NullPointerException expected) { + + @Override + public int size() { + return delegate.size() + delta; } } + @SuppressWarnings("JoinIterableIterator") // explicitly testing iterator overload, too + public void testNoSpecialNullBehavior() { + checkNoOutput(J, iterable); + checkResult(J, iterable1, "1"); + checkResult(J, iterable12, "1-2"); + checkResult(J, iterable123, "1-2-3"); + checkResult(J, UNDERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(J, OVERREPORTING_SIZE_LIST, "1-2-3"); + + assertThrows(NullPointerException.class, () -> J.join(iterableNull)); + assertThrows(NullPointerException.class, () -> J.join(iterable1Null2)); + + assertThrows(NullPointerException.class, () -> J.join(iterableNull.iterator())); + assertThrows(NullPointerException.class, () -> J.join(iterable1Null2.iterator())); + } + public void testOnCharOverride() { Joiner onChar = Joiner.on('-'); - checkNoOutput(onChar, ITERABLE_); - checkResult(onChar, ITERABLE_1, "1"); - checkResult(onChar, ITERABLE_12, "1-2"); - checkResult(onChar, ITERABLE_123, "1-2-3"); + checkNoOutput(onChar, iterable); + checkResult(onChar, iterable1, "1"); + checkResult(onChar, iterable12, "1-2"); + checkResult(onChar, iterable123, "1-2-3"); + checkResult(J, UNDERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(J, OVERREPORTING_SIZE_LIST, "1-2-3"); } public void testSkipNulls() { Joiner skipNulls = J.skipNulls(); - checkNoOutput(skipNulls, ITERABLE_); - checkNoOutput(skipNulls, ITERABLE_NULL); - checkNoOutput(skipNulls, ITERABLE_NULL_NULL); - checkNoOutput(skipNulls, ITERABLE_FOUR_NULLS); - checkResult(skipNulls, ITERABLE_1, "1"); - checkResult(skipNulls, ITERABLE_12, "1-2"); - checkResult(skipNulls, ITERABLE_123, "1-2-3"); - checkResult(skipNulls, ITERABLE_NULL_1, "1"); - checkResult(skipNulls, ITERABLE_1_NULL, "1"); - checkResult(skipNulls, ITERABLE_1_NULL_2, "1-2"); + checkNoOutput(skipNulls, iterable); + checkNoOutput(skipNulls, iterableNull); + checkNoOutput(skipNulls, iterableNullNull); + checkNoOutput(skipNulls, iterableFourNulls); + checkResult(skipNulls, iterable1, "1"); + checkResult(skipNulls, iterable12, "1-2"); + checkResult(skipNulls, iterable123, "1-2-3"); + checkResult(J, UNDERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(J, OVERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(skipNulls, iterableNull1, "1"); + checkResult(skipNulls, iterable1Null, "1"); + checkResult(skipNulls, iterable1Null2, "1-2"); } public void testUseForNull() { Joiner zeroForNull = J.useForNull("0"); - checkNoOutput(zeroForNull, ITERABLE_); - checkResult(zeroForNull, ITERABLE_1, "1"); - checkResult(zeroForNull, ITERABLE_12, "1-2"); - checkResult(zeroForNull, ITERABLE_123, "1-2-3"); - checkResult(zeroForNull, ITERABLE_NULL, "0"); - checkResult(zeroForNull, ITERABLE_NULL_NULL, "0-0"); - checkResult(zeroForNull, ITERABLE_NULL_1, "0-1"); - checkResult(zeroForNull, ITERABLE_1_NULL, "1-0"); - checkResult(zeroForNull, ITERABLE_1_NULL_2, "1-0-2"); - checkResult(zeroForNull, ITERABLE_FOUR_NULLS, "0-0-0-0"); + checkNoOutput(zeroForNull, iterable); + checkResult(zeroForNull, iterable1, "1"); + checkResult(zeroForNull, iterable12, "1-2"); + checkResult(zeroForNull, iterable123, "1-2-3"); + checkResult(J, UNDERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(J, OVERREPORTING_SIZE_LIST, "1-2-3"); + checkResult(zeroForNull, iterableNull, "0"); + checkResult(zeroForNull, iterableNullNull, "0-0"); + checkResult(zeroForNull, iterableNull1, "0-1"); + checkResult(zeroForNull, iterable1Null, "1-0"); + checkResult(zeroForNull, iterable1Null2, "1-0-2"); + checkResult(zeroForNull, iterableFourNulls, "0-0-0-0"); } private static void checkNoOutput(Joiner joiner, Iterable set) { assertEquals("", joiner.join(set)); assertEquals("", joiner.join(set.iterator())); - Object[] array = Lists.newArrayList(set).toArray(new Integer[0]); + Object[] array = newArrayList(set).toArray(new Integer[0]); assertEquals("", joiner.join(array)); StringBuilder sb1FromIterable = new StringBuilder(); @@ -162,12 +204,13 @@ private static void checkNoOutput(Joiner joiner, Iterable set) { private static final Appendable NASTY_APPENDABLE = new Appendable() { @Override - public Appendable append(CharSequence csq) throws IOException { + public Appendable append(@Nullable CharSequence csq) throws IOException { throw new IOException(); } @Override - public Appendable append(CharSequence csq, int start, int end) throws IOException { + public Appendable append(@Nullable CharSequence csq, int start, int end) + throws IOException { throw new IOException(); } @@ -189,7 +232,8 @@ private static void checkResult(Joiner joiner, Iterable parts, String e joiner.appendTo(sb1FromIterator, parts.iterator()); assertEquals("x" + expected, sb1FromIterator.toString()); - Integer[] partsArray = Lists.newArrayList(parts).toArray(new Integer[0]); + // The use of iterator() works around J2KT b/381065164. + Integer[] partsArray = newArrayList(parts.iterator()).toArray(new Integer[0]); assertEquals(expected, joiner.join(partsArray)); StringBuilder sb2 = new StringBuilder().append('x'); @@ -213,29 +257,17 @@ private static void checkResult(Joiner joiner, Iterable parts, String e public void test_useForNull_skipNulls() { Joiner j = Joiner.on("x").useForNull("y"); - try { - j = j.skipNulls(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, j::skipNulls); } public void test_skipNulls_useForNull() { Joiner j = Joiner.on("x").skipNulls(); - try { - j = j.useForNull("y"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> j.useForNull("y")); } public void test_useForNull_twice() { Joiner j = Joiner.on("x").useForNull("y"); - try { - j = j.useForNull("y"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> j.useForNull("y")); } public void testMap() { @@ -243,15 +275,11 @@ public void testMap() { assertEquals("", j.join(ImmutableMap.of())); assertEquals(":", j.join(ImmutableMap.of("", ""))); - Map mapWithNulls = Maps.newLinkedHashMap(); + Map<@Nullable String, @Nullable String> mapWithNulls = Maps.newLinkedHashMap(); mapWithNulls.put("a", null); mapWithNulls.put(null, "b"); - try { - j.join(mapWithNulls); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> j.join(mapWithNulls)); assertEquals("a:00;00:b", j.useForNull("00").join(mapWithNulls)); @@ -269,22 +297,14 @@ public void testEntries() { assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries())); assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries().iterator())); - Map mapWithNulls = Maps.newLinkedHashMap(); + Map<@Nullable String, @Nullable String> mapWithNulls = Maps.newLinkedHashMap(); mapWithNulls.put("a", null); mapWithNulls.put(null, "b"); Set> entriesWithNulls = mapWithNulls.entrySet(); - try { - j.join(entriesWithNulls); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> j.join(entriesWithNulls)); - try { - j.join(entriesWithNulls.iterator()); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> j.join(entriesWithNulls.iterator())); assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls)); assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls.iterator())); @@ -300,73 +320,10 @@ public void testEntries() { public void test_skipNulls_onMap() { Joiner j = Joiner.on(",").skipNulls(); - try { - j.withKeyValueSeparator("/"); - fail(); - } catch (UnsupportedOperationException expected) { - } - } - - private static class DontStringMeBro implements CharSequence { - @Override - public int length() { - return 3; - } - - @Override - public char charAt(int index) { - return "foo".charAt(index); - } - - @Override - public CharSequence subSequence(int start, int end) { - return "foo".subSequence(start, end); - } - - @Override - public String toString() { - throw new AssertionFailedError("shouldn't be invoked"); - } - } - - // Don't do this. - private static class IterableIterator implements Iterable, Iterator { - private static final ImmutableSet INTEGERS = ImmutableSet.of(1, 2, 3, 4); - private final Iterator iterator; - - public IterableIterator() { - this.iterator = iterator(); - } - - @Override - public Iterator iterator() { - return INTEGERS.iterator(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public Integer next() { - return iterator.next(); - } - - @Override - public void remove() { - iterator.remove(); - } - } - - @GwtIncompatible // StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version. - public void testDontConvertCharSequenceToString() { - assertEquals("foo,foo", Joiner.on(",").join(new DontStringMeBro(), new DontStringMeBro())); - assertEquals( - "foo,bar,foo", - Joiner.on(",").useForNull("bar").join(new DontStringMeBro(), null, new DontStringMeBro())); + assertThrows(UnsupportedOperationException.class, () -> j.withKeyValueSeparator("/")); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/base/MoreObjectsTest.java b/android/guava-tests/test/com/google/common/base/MoreObjectsTest.java new file mode 100644 index 000000000000..3b7c8068953b --- /dev/null +++ b/android/guava-tests/test/com/google/common/base/MoreObjectsTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.testing.NullPointerTester; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** Tests for {@link MoreObjects}. */ +@GwtCompatible(emulated = true) +@NullUnmarked +public class MoreObjectsTest extends TestCase { + public void testFirstNonNull_withNonNull() { + String s1 = "foo"; + String s2 = MoreObjects.firstNonNull(s1, "bar"); + assertSame(s1, s2); + + Long n1 = 42L; + Long n2 = MoreObjects.firstNonNull(null, n1); + assertSame(n1, n2); + + Boolean b1 = true; + Boolean b2 = MoreObjects.firstNonNull(b1, null); + assertSame(b1, b2); + } + + public void testFirstNonNull_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> MoreObjects.firstNonNull(null, null)); + } + + // ToStringHelper's tests are in ToStringHelperTest + + @J2ktIncompatible + @GwtIncompatible("NullPointerTester") + public void testNulls() throws Exception { + NullPointerTester tester = new NullPointerTester(); + tester.ignore(MoreObjects.class.getMethod("firstNonNull", Object.class, Object.class)); + tester.testAllPublicStaticMethods(MoreObjects.class); + tester.testAllPublicInstanceMethods(MoreObjects.toStringHelper(new TestClass())); + } + + /** Test class for testing formatting of inner classes. */ + private static class TestClass {} +} diff --git a/android/guava-tests/test/com/google/common/base/ObjectsTest.java b/android/guava-tests/test/com/google/common/base/ObjectsTest.java index 03881402a335..97344d979750 100644 --- a/android/guava-tests/test/com/google/common/base/ObjectsTest.java +++ b/android/guava-tests/test/com/google/common/base/ObjectsTest.java @@ -18,8 +18,10 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Objects}. @@ -27,8 +29,10 @@ * @author Laurence Gonsalves */ @GwtCompatible(emulated = true) +@NullUnmarked public class ObjectsTest extends TestCase { + @SuppressWarnings("YodaCondition") // test of reversed call public void testEqual() throws Exception { assertTrue(Objects.equal(1, 1)); assertTrue(Objects.equal(null, null)); @@ -46,7 +50,7 @@ public void testEqual() throws Exception { public void testHashCode() throws Exception { int h1 = Objects.hashCode(1, "two", 3.0); - int h2 = Objects.hashCode(new Integer(1), new String("two"), new Double(3.0)); + int h2 = Objects.hashCode(Integer.valueOf(1), new String("two"), Double.valueOf(3.0)); // repeatable assertEquals(h1, h2); @@ -58,6 +62,7 @@ public void testHashCode() throws Exception { assertTrue(Objects.hashCode(1, 2, 3) != Objects.hashCode(2, 3, 1)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/base/OptionalTest.java b/android/guava-tests/test/com/google/common/base/OptionalTest.java index ba6ace7e94a7..8961f1f228f7 100644 --- a/android/guava-tests/test/com/google/common/base/OptionalTest.java +++ b/android/guava-tests/test/com/google/common/base/OptionalTest.java @@ -16,11 +16,13 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.testing.EqualsTester; @@ -29,14 +31,36 @@ import java.util.List; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Optional}. * * @author Kurt Alfred Kluever */ +@NullMarked @GwtCompatible(emulated = true) public final class OptionalTest extends TestCase { + @SuppressWarnings("NullOptional") + public void testToJavaUtil_static() { + assertNull(Optional.toJavaUtil(null)); + assertEquals(java.util.Optional.empty(), Optional.toJavaUtil(Optional.absent())); + assertEquals(java.util.Optional.of("abc"), Optional.toJavaUtil(Optional.of("abc"))); + } + + public void testToJavaUtil_instance() { + assertEquals(java.util.Optional.empty(), Optional.absent().toJavaUtil()); + assertEquals(java.util.Optional.of("abc"), Optional.of("abc").toJavaUtil()); + } + + @SuppressWarnings("NullOptional") + public void testFromJavaUtil() { + assertNull(Optional.fromJavaUtil(null)); + assertEquals(Optional.absent(), Optional.fromJavaUtil(java.util.Optional.empty())); + assertEquals(Optional.of("abc"), Optional.fromJavaUtil(java.util.Optional.of("abc"))); + } + public void testAbsent() { Optional optionalName = Optional.absent(); assertFalse(optionalName.isPresent()); @@ -47,11 +71,7 @@ public void testOf() { } public void testOf_null() { - try { - Optional.of(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Optional.of(null)); } public void testFromNullable() { @@ -75,11 +95,7 @@ public void testIsPresent_yes() { public void testGet_absent() { Optional optional = Optional.absent(); - try { - optional.get(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, optional::get); } public void testGet_present() { @@ -87,11 +103,11 @@ public void testGet_present() { } @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional - public void testOr_T_present() { + public void testOr_t_present() { assertEquals("a", Optional.of("a").or("default")); } - public void testOr_T_absent() { + public void testOr_t_absent() { assertEquals("default", Optional.absent().or("default")); } @@ -105,27 +121,23 @@ public void testOr_supplier_absent() { } public void testOr_nullSupplier_absent() { - Supplier nullSupplier = Suppliers.ofInstance(null); + Supplier nullSupplier = (Supplier) Suppliers.<@Nullable Object>ofInstance(null); Optional absentOptional = Optional.absent(); - try { - absentOptional.or(nullSupplier); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> absentOptional.or(nullSupplier)); } @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional public void testOr_nullSupplier_present() { - Supplier nullSupplier = Suppliers.ofInstance(null); + Supplier nullSupplier = (Supplier) Suppliers.<@Nullable String>ofInstance(null); assertEquals("a", Optional.of("a").or(nullSupplier)); } @SuppressWarnings("OptionalOfRedundantMethod") // Unit tests for Optional - public void testOr_Optional_present() { + public void testOr_optional_present() { assertEquals(Optional.of("a"), Optional.of("a").or(Optional.of("fallback"))); } - public void testOr_Optional_absent() { + public void testOr_optional_absent() { assertEquals(Optional.of("fallback"), Optional.absent().or(Optional.of("fallback"))); } @@ -149,20 +161,12 @@ public void testAsSet_absent() { public void testAsSet_presentIsImmutable() { Set presentAsSet = Optional.of("a").asSet(); - try { - presentAsSet.add("b"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> presentAsSet.add("b")); } public void testAsSet_absentIsImmutable() { Set absentAsSet = Optional.absent().asSet(); - try { - absentAsSet.add("foo"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> absentAsSet.add("foo")); } public void testTransform_absent() { @@ -179,39 +183,18 @@ public void testTransform_presentToString() { } public void testTransform_present_functionReturnsNull() { - try { - Optional unused = - Optional.of("a") - .transform( - new Function() { - @Override - public String apply(String input) { - return null; - } - }); - fail("Should throw if Function returns null."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Optional.of("a").transform(input -> null)); } public void testTransform_absent_functionReturnsNull() { - assertEquals( - Optional.absent(), - Optional.absent() - .transform( - new Function() { - @Override - public Object apply(Object input) { - return null; - } - })); + assertEquals(Optional.absent(), Optional.absent().transform(input -> null)); } public void testEqualsAndHashCode() { new EqualsTester() .addEqualityGroup(Optional.absent(), reserialize(Optional.absent())) - .addEqualityGroup(Optional.of(new Long(5)), reserialize(Optional.of(new Long(5)))) - .addEqualityGroup(Optional.of(new Long(42)), reserialize(Optional.of(new Long(42)))) + .addEqualityGroup(Optional.of(Long.valueOf(5)), reserialize(Optional.of(Long.valueOf(5)))) + .addEqualityGroup(Optional.of(Long.valueOf(42)), reserialize(Optional.of(Long.valueOf(42)))) .testEquals(); } @@ -298,6 +281,7 @@ public void testSampleCodeFine2() { Number value = first.or(0.5); // fine } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester npTester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/base/PackageSanityTests.java b/android/guava-tests/test/com/google/common/base/PackageSanityTests.java index f524fbb6ccae..7b77c80c7f63 100644 --- a/android/guava-tests/test/com/google/common/base/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/base/PackageSanityTests.java @@ -16,10 +16,14 @@ package com.google.common.base; +import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** Basic sanity tests for classes in {@code common.base}. */ +@GwtIncompatible +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { // package private classes like FunctionalEquivalence are tested through the public API. diff --git a/android/guava-tests/test/com/google/common/base/PreconditionsTest.java b/android/guava-tests/test/com/google/common/base/PreconditionsTest.java index 1add44d32386..7e8d8bb55dbd 100644 --- a/android/guava-tests/test/com/google/common/base/PreconditionsTest.java +++ b/android/guava-tests/test/com/google/common/base/PreconditionsTest.java @@ -16,10 +16,18 @@ package com.google.common.base; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndex; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; @@ -31,6 +39,8 @@ import java.util.List; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Preconditions}. @@ -38,352 +48,257 @@ * @author Kevin Bourrillion * @author Jared Levy */ +@NullMarked +@SuppressWarnings("LenientFormatStringValidation") // Intentional for testing @GwtCompatible(emulated = true) public class PreconditionsTest extends TestCase { public void testCheckArgument_simple_success() { - Preconditions.checkArgument(true); + checkArgument(true); } public void testCheckArgument_simple_failure() { - try { - Preconditions.checkArgument(false); - fail("no exception thrown"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> checkArgument(false)); } public void testCheckArgument_simpleMessage_success() { - Preconditions.checkArgument(true, IGNORE_ME); + checkArgument(true, IGNORE_ME); } public void testCheckArgument_simpleMessage_failure() { - try { - Preconditions.checkArgument(false, new Message()); - fail("no exception thrown"); - } catch (IllegalArgumentException expected) { - verifySimpleMessage(expected); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> checkArgument(false, new Message())); + verifySimpleMessage(expected); } public void testCheckArgument_nullMessage_failure() { - try { - Preconditions.checkArgument(false, null); - fail("no exception thrown"); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("null"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> checkArgument(false, null)); + assertThat(expected).hasMessageThat().isEqualTo("null"); } public void testCheckArgument_nullMessageWithArgs_failure() { - try { - Preconditions.checkArgument(false, null, "b", "d"); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("null [b, d]"); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> checkArgument(false, null, "b", "d")); + assertThat(e).hasMessageThat().isEqualTo("null [b, d]"); } public void testCheckArgument_nullArgs_failure() { - try { - Preconditions.checkArgument(false, "A %s C %s E", null, null); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("A null C null E"); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> checkArgument(false, "A %s C %s E", null, null)); + assertThat(e).hasMessageThat().isEqualTo("A null C null E"); } public void testCheckArgument_notEnoughArgs_failure() { - try { - Preconditions.checkArgument(false, "A %s C %s E", "b"); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("A b C %s E"); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> checkArgument(false, "A %s C %s E", "b")); + assertThat(e).hasMessageThat().isEqualTo("A b C %s E"); } public void testCheckArgument_tooManyArgs_failure() { - try { - Preconditions.checkArgument(false, "A %s C %s E", "b", "d", "f"); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("A b C d E [f]"); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> checkArgument(false, "A %s C %s E", "b", "d", "f")); + assertThat(e).hasMessageThat().isEqualTo("A b C d E [f]"); } public void testCheckArgument_singleNullArg_failure() { - try { - Preconditions.checkArgument(false, "A %s C", (Object) null); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("A null C"); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> checkArgument(false, "A %s C", (Object) null)); + assertThat(e).hasMessageThat().isEqualTo("A null C"); } + @J2ktIncompatible // TODO(b/319404022): Allow passing null array as varargs public void testCheckArgument_singleNullArray_failure() { - try { - Preconditions.checkArgument(false, "A %s C", (Object[]) null); - fail("no exception thrown"); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("A (Object[])null C"); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> checkArgument(false, "A %s C", (Object[]) null)); + assertThat(e).hasMessageThat().isEqualTo("A (Object[])null C"); } public void testCheckArgument_complexMessage_success() { - Preconditions.checkArgument(true, "%s", IGNORE_ME); + checkArgument(true, "%s", IGNORE_ME); } public void testCheckArgument_complexMessage_failure() { - try { - Preconditions.checkArgument(false, FORMAT, 5); - fail("no exception thrown"); - } catch (IllegalArgumentException expected) { - verifyComplexMessage(expected); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> checkArgument(false, FORMAT, 5)); + verifyComplexMessage(expected); } public void testCheckState_simple_success() { - Preconditions.checkState(true); + checkState(true); } public void testCheckState_simple_failure() { - try { - Preconditions.checkState(false); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> checkState(false)); } public void testCheckState_simpleMessage_success() { - Preconditions.checkState(true, IGNORE_ME); + checkState(true, IGNORE_ME); } public void testCheckState_simpleMessage_failure() { - try { - Preconditions.checkState(false, new Message()); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - verifySimpleMessage(expected); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> checkState(false, new Message())); + verifySimpleMessage(expected); } public void testCheckState_nullMessage_failure() { - try { - Preconditions.checkState(false, null); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - assertThat(expected).hasMessageThat().isEqualTo("null"); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> checkState(false, null)); + assertThat(expected).hasMessageThat().isEqualTo("null"); } public void testCheckState_complexMessage_success() { - Preconditions.checkState(true, "%s", IGNORE_ME); + checkState(true, "%s", IGNORE_ME); } public void testCheckState_complexMessage_failure() { - try { - Preconditions.checkState(false, FORMAT, 5); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - verifyComplexMessage(expected); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> checkState(false, FORMAT, 5)); + verifyComplexMessage(expected); } private static final String NON_NULL_STRING = "foo"; public void testCheckNotNull_simple_success() { - String result = Preconditions.checkNotNull(NON_NULL_STRING); + String result = checkNotNull(NON_NULL_STRING); assertSame(NON_NULL_STRING, result); } public void testCheckNotNull_simple_failure() { - try { - Preconditions.checkNotNull(null); - fail("no exception thrown"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> checkNotNull(null)); } public void testCheckNotNull_simpleMessage_success() { - String result = Preconditions.checkNotNull(NON_NULL_STRING, IGNORE_ME); + String result = checkNotNull(NON_NULL_STRING, IGNORE_ME); assertSame(NON_NULL_STRING, result); } public void testCheckNotNull_simpleMessage_failure() { - try { - Preconditions.checkNotNull(null, new Message()); - fail("no exception thrown"); - } catch (NullPointerException expected) { - verifySimpleMessage(expected); - } + NullPointerException expected = + assertThrows(NullPointerException.class, () -> checkNotNull(null, new Message())); + verifySimpleMessage(expected); } public void testCheckNotNull_complexMessage_success() { - String result = Preconditions.checkNotNull(NON_NULL_STRING, "%s", IGNORE_ME); + String result = checkNotNull(NON_NULL_STRING, "%s", IGNORE_ME); assertSame(NON_NULL_STRING, result); } public void testCheckNotNull_complexMessage_failure() { - try { - Preconditions.checkNotNull(null, FORMAT, 5); - fail("no exception thrown"); - } catch (NullPointerException expected) { - verifyComplexMessage(expected); - } + NullPointerException expected = + assertThrows(NullPointerException.class, () -> checkNotNull(null, FORMAT, 5)); + verifyComplexMessage(expected); } public void testCheckElementIndex_ok() { - assertEquals(0, Preconditions.checkElementIndex(0, 1)); - assertEquals(0, Preconditions.checkElementIndex(0, 2)); - assertEquals(1, Preconditions.checkElementIndex(1, 2)); + assertEquals(0, checkElementIndex(0, 1)); + assertEquals(0, checkElementIndex(0, 2)); + assertEquals(1, checkElementIndex(1, 2)); } public void testCheckElementIndex_badSize() { - try { - Preconditions.checkElementIndex(1, -1); - fail(); - } catch (IllegalArgumentException expected) { - // don't care what the message text is, as this is an invalid usage of - // the Preconditions class, unlike all the other exceptions it throws - } + assertThrows(IllegalArgumentException.class, () -> checkElementIndex(1, -1)); } public void testCheckElementIndex_negative() { - try { - Preconditions.checkElementIndex(-1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(-1, 1)); + assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); } public void testCheckElementIndex_tooHigh() { - try { - Preconditions.checkElementIndex(1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("index (1) must be less than size (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(1, 1)); + assertThat(expected).hasMessageThat().isEqualTo("index (1) must be less than size (1)"); } public void testCheckElementIndex_withDesc_negative() { - try { - Preconditions.checkElementIndex(-1, 1, "foo"); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(-1, 1, "foo")); + assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); } public void testCheckElementIndex_withDesc_tooHigh() { - try { - Preconditions.checkElementIndex(1, 1, "foo"); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("foo (1) must be less than size (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkElementIndex(1, 1, "foo")); + assertThat(expected).hasMessageThat().isEqualTo("foo (1) must be less than size (1)"); } public void testCheckPositionIndex_ok() { - assertEquals(0, Preconditions.checkPositionIndex(0, 0)); - assertEquals(0, Preconditions.checkPositionIndex(0, 1)); - assertEquals(1, Preconditions.checkPositionIndex(1, 1)); + assertEquals(0, checkPositionIndex(0, 0)); + assertEquals(0, checkPositionIndex(0, 1)); + assertEquals(1, checkPositionIndex(1, 1)); } public void testCheckPositionIndex_badSize() { - try { - Preconditions.checkPositionIndex(1, -1); - fail(); - } catch (IllegalArgumentException expected) { - // don't care what the message text is, as this is an invalid usage of - // the Preconditions class, unlike all the other exceptions it throws - } + assertThrows(IllegalArgumentException.class, () -> checkPositionIndex(1, -1)); } public void testCheckPositionIndex_negative() { - try { - Preconditions.checkPositionIndex(-1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(-1, 1)); + assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative"); } public void testCheckPositionIndex_tooHigh() { - try { - Preconditions.checkPositionIndex(2, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("index (2) must not be greater than size (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(2, 1)); + assertThat(expected).hasMessageThat().isEqualTo("index (2) must not be greater than size (1)"); } public void testCheckPositionIndex_withDesc_negative() { - try { - Preconditions.checkPositionIndex(-1, 1, "foo"); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(-1, 1, "foo")); + assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative"); } public void testCheckPositionIndex_withDesc_tooHigh() { - try { - Preconditions.checkPositionIndex(2, 1, "foo"); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("foo (2) must not be greater than size (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndex(2, 1, "foo")); + assertThat(expected).hasMessageThat().isEqualTo("foo (2) must not be greater than size (1)"); } public void testCheckPositionIndexes_ok() { - Preconditions.checkPositionIndexes(0, 0, 0); - Preconditions.checkPositionIndexes(0, 0, 1); - Preconditions.checkPositionIndexes(0, 1, 1); - Preconditions.checkPositionIndexes(1, 1, 1); + checkPositionIndexes(0, 0, 0); + checkPositionIndexes(0, 0, 1); + checkPositionIndexes(0, 1, 1); + checkPositionIndexes(1, 1, 1); } public void testCheckPositionIndexes_badSize() { - try { - Preconditions.checkPositionIndexes(1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> checkPositionIndexes(1, 1, -1)); } public void testCheckPositionIndex_startNegative() { - try { - Preconditions.checkPositionIndexes(-1, 1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected).hasMessageThat().isEqualTo("start index (-1) must not be negative"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(-1, 1, 1)); + assertThat(expected).hasMessageThat().isEqualTo("start index (-1) must not be negative"); } public void testCheckPositionIndexes_endTooHigh() { - try { - Preconditions.checkPositionIndexes(0, 2, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("end index (2) must not be greater than size (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(0, 2, 1)); + assertThat(expected) + .hasMessageThat() + .isEqualTo("end index (2) must not be greater than size (1)"); } public void testCheckPositionIndexes_reversed() { - try { - Preconditions.checkPositionIndexes(1, 0, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("end index (0) must not be less than start index (1)"); - } + IndexOutOfBoundsException expected = + assertThrows(IndexOutOfBoundsException.class, () -> checkPositionIndexes(1, 0, 1)); + assertThat(expected) + .hasMessageThat() + .isEqualTo("end index (0) must not be less than start index (1)"); } @GwtIncompatible("Reflection") + @J2ktIncompatible public void testAllOverloads_checkArgument() throws Exception { for (ImmutableList> sig : allSignatures(boolean.class)) { Method checkArgumentMethod = @@ -391,16 +306,16 @@ public void testAllOverloads_checkArgument() throws Exception { checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig)); Object[] failingParams = getParametersForSignature(false, sig); - try { - checkArgumentMethod.invoke(null /* static method */, failingParams); - fail(); - } catch (InvocationTargetException ite) { - assertFailureCause(ite.getCause(), IllegalArgumentException.class, failingParams); - } + InvocationTargetException ite = + assertThrows( + InvocationTargetException.class, + () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); + assertFailureCause(ite.getCause(), IllegalArgumentException.class, failingParams); } } @GwtIncompatible("Reflection") + @J2ktIncompatible public void testAllOverloads_checkState() throws Exception { for (ImmutableList> sig : allSignatures(boolean.class)) { Method checkArgumentMethod = @@ -408,16 +323,16 @@ public void testAllOverloads_checkState() throws Exception { checkArgumentMethod.invoke(null /* static method */, getParametersForSignature(true, sig)); Object[] failingParams = getParametersForSignature(false, sig); - try { - checkArgumentMethod.invoke(null /* static method */, failingParams); - fail(); - } catch (InvocationTargetException ite) { - assertFailureCause(ite.getCause(), IllegalStateException.class, failingParams); - } + InvocationTargetException ite = + assertThrows( + InvocationTargetException.class, + () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); + assertFailureCause(ite.getCause(), IllegalStateException.class, failingParams); } } @GwtIncompatible("Reflection") + @J2ktIncompatible public void testAllOverloads_checkNotNull() throws Exception { for (ImmutableList> sig : allSignatures(Object.class)) { Method checkArgumentMethod = @@ -426,12 +341,11 @@ public void testAllOverloads_checkNotNull() throws Exception { null /* static method */, getParametersForSignature(new Object(), sig)); Object[] failingParams = getParametersForSignature(null, sig); - try { - checkArgumentMethod.invoke(null /* static method */, failingParams); - fail(); - } catch (InvocationTargetException ite) { - assertFailureCause(ite.getCause(), NullPointerException.class, failingParams); - } + InvocationTargetException ite = + assertThrows( + InvocationTargetException.class, + () -> checkArgumentMethod.invoke(null /* static method */, failingParams)); + assertFailureCause(ite.getCause(), NullPointerException.class, failingParams); } } @@ -461,7 +375,9 @@ private void assertFailureCause( * @param sig The method signature */ @GwtIncompatible("ArbitraryInstances") - private Object[] getParametersForSignature(Object firstParam, ImmutableList> sig) { + @J2ktIncompatible + private Object[] getParametersForSignature( + @Nullable Object firstParam, ImmutableList> sig) { Object[] params = new Object[sig.size()]; params[0] = firstParam; if (params.length > 1) { @@ -476,7 +392,7 @@ private Object[] getParametersForSignature(Object firstParam, ImmutableList> possibleParamTypes = + private static final ImmutableList> POSSIBLE_PARAM_TYPES = ImmutableList.of(char.class, int.class, long.class, Object.class); /** @@ -494,7 +410,7 @@ private static ImmutableList>> allSignatures(Class pre List>> typesLists = new ArrayList<>(); for (int i = 0; i < 2; i++) { - typesLists.add(possibleParamTypes); + typesLists.add(POSSIBLE_PARAM_TYPES); for (List> curr : Lists.cartesianProduct(typesLists)) { allOverloads.add( ImmutableList.>builder() @@ -520,22 +436,23 @@ public void overloadSelection() { int anInt = 1; // With a boxed predicate, no overloads can be selected in phase 1 // ambiguous without the call to .booleanValue to unbox the Boolean - Preconditions.checkState(boxedBoolean.booleanValue(), "", 1); + checkState(boxedBoolean.booleanValue(), "", 1); // ambiguous without the cast to Object because the boxed predicate prevents any overload from // being selected in phase 1 - Preconditions.checkState(boxedBoolean, "", (Object) boxedLong); + checkState(boxedBoolean, "", (Object) boxedLong); // ternaries introduce their own problems. because of the ternary (which requires a boxing // operation) no overload can be selected in phase 1. and in phase 2 it is ambiguous since it // matches with the second parameter being boxed and without it being boxed. The cast to Object // avoids this. - Preconditions.checkState(aBoolean, "", aBoolean ? "" : anInt, (Object) anInt); + checkState(aBoolean, "", aBoolean ? "" : anInt, (Object) anInt); // ambiguous without the .booleanValue() call since the boxing forces us into phase 2 resolution short s = 2; - Preconditions.checkState(boxedBoolean.booleanValue(), "", s); + checkState(boxedBoolean.booleanValue(), "", s); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { /* diff --git a/android/guava-tests/test/com/google/common/base/PredicatesTest.java b/android/guava-tests/test/com/google/common/base/PredicatesTest.java index 8f8647f4d190..1b53326958f5 100644 --- a/android/guava-tests/test/com/google/common/base/PredicatesTest.java +++ b/android/guava-tests/test/com/google/common/base/PredicatesTest.java @@ -18,9 +18,11 @@ import static com.google.common.base.CharMatcher.whitespace; import static com.google.common.collect.Lists.newArrayList; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSet; import com.google.common.testing.ClassSanityTester; import com.google.common.testing.EqualsTester; @@ -36,30 +38,33 @@ import java.util.regex.Pattern; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Predicates}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) public class PredicatesTest extends TestCase { - private static final Predicate TRUE = Predicates.alwaysTrue(); - private static final Predicate FALSE = Predicates.alwaysFalse(); - private static final Predicate NEVER_REACHED = - new Predicate() { + private static final Predicate<@Nullable Integer> TRUE = Predicates.alwaysTrue(); + private static final Predicate<@Nullable Integer> FALSE = Predicates.alwaysFalse(); + private static final Predicate<@Nullable Integer> NEVER_REACHED = + new Predicate<@Nullable Integer>() { @Override - public boolean apply(Integer i) { + public boolean apply(@Nullable Integer i) { throw new AssertionFailedError("This predicate should never have been evaluated"); } }; /** Instantiable predicate with reasonable hashCode() and equals() methods. */ - static class IsOdd implements Predicate, Serializable { - private static final long serialVersionUID = 0x150ddL; + static class IsOdd implements Predicate<@Nullable Integer>, Serializable { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0x150ddL; @Override - public boolean apply(Integer i) { + public boolean apply(@Nullable Integer i) { return (i.intValue() & 1) == 1; } @@ -69,7 +74,7 @@ public int hashCode() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof IsOdd; } @@ -105,6 +110,7 @@ public void testAlwaysTrue_equality() throws Exception { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testAlwaysTrue_serialization() { checkSerialization(Predicates.alwaysTrue()); @@ -126,6 +132,7 @@ public void testAlwaysFalse_equality() throws Exception { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testAlwaysFalse_serialization() { checkSerialization(Predicates.alwaysFalse()); @@ -175,6 +182,7 @@ public void testNot_equalityForNotOfKnownValues() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNot_serialization() { checkSerialization(Predicates.not(isOdd())); @@ -184,12 +192,10 @@ public void testNot_serialization() { * Tests for all the different flavors of Predicates.and(). */ - @SuppressWarnings("unchecked") // varargs public void testAnd_applyNoArgs() { assertEvalsToTrue(Predicates.and()); } - @SuppressWarnings("unchecked") // varargs public void testAnd_equalityNoArgs() { new EqualsTester() .addEqualityGroup(Predicates.and(), Predicates.and()) @@ -198,18 +204,16 @@ public void testAnd_equalityNoArgs() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testAnd_serializationNoArgs() { checkSerialization(Predicates.and()); } - @SuppressWarnings("unchecked") // varargs public void testAnd_applyOneArg() { assertEvalsLikeOdd(Predicates.and(isOdd())); } - @SuppressWarnings("unchecked") // varargs public void testAnd_equalityOneArg() { Object[] notEqualObjects = {Predicates.and(NEVER_REACHED, FALSE)}; new EqualsTester() @@ -221,8 +225,8 @@ public void testAnd_equalityOneArg() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testAnd_serializationOneArg() { checkSerialization(Predicates.and(isOdd())); } @@ -233,7 +237,6 @@ public void testAnd_applyBinary() { assertEvalsToFalse(Predicates.and(FALSE, NEVER_REACHED)); } - @SuppressWarnings("unchecked") // varargs public void testAnd_equalityBinary() { new EqualsTester() .addEqualityGroup(Predicates.and(TRUE, NEVER_REACHED), Predicates.and(TRUE, NEVER_REACHED)) @@ -243,12 +246,12 @@ public void testAnd_equalityBinary() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testAnd_serializationBinary() { checkSerialization(Predicates.and(TRUE, isOdd())); } - @SuppressWarnings("unchecked") // varargs public void testAnd_applyTernary() { assertEvalsLikeOdd(Predicates.and(isOdd(), TRUE, TRUE)); assertEvalsLikeOdd(Predicates.and(TRUE, isOdd(), TRUE)); @@ -256,7 +259,6 @@ public void testAnd_applyTernary() { assertEvalsToFalse(Predicates.and(TRUE, FALSE, NEVER_REACHED)); } - @SuppressWarnings("unchecked") // varargs public void testAnd_equalityTernary() { new EqualsTester() .addEqualityGroup( @@ -268,22 +270,20 @@ public void testAnd_equalityTernary() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testAnd_serializationTernary() { checkSerialization(Predicates.and(TRUE, isOdd(), FALSE)); } - @SuppressWarnings("unchecked") // varargs public void testAnd_applyIterable() { - Collection> empty = Arrays.asList(); + Collection> empty = Arrays.asList(); assertEvalsToTrue(Predicates.and(empty)); assertEvalsLikeOdd(Predicates.and(Arrays.asList(isOdd()))); assertEvalsLikeOdd(Predicates.and(Arrays.asList(TRUE, isOdd()))); assertEvalsToFalse(Predicates.and(Arrays.asList(FALSE, NEVER_REACHED))); } - @SuppressWarnings("unchecked") // varargs public void testAnd_equalityIterable() { new EqualsTester() .addEqualityGroup( @@ -295,15 +295,15 @@ public void testAnd_equalityIterable() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testAnd_serializationIterable() { checkSerialization(Predicates.and(Arrays.asList(TRUE, FALSE))); } - @SuppressWarnings("unchecked") // varargs public void testAnd_arrayDefensivelyCopied() { - Predicate[] array = {Predicates.alwaysFalse()}; + @SuppressWarnings("unchecked") // generic arrays + Predicate[] array = (Predicate[]) new Predicate[] {Predicates.alwaysFalse()}; Predicate predicate = Predicates.and(array); assertFalse(predicate.apply(1)); array[0] = Predicates.alwaysTrue(); @@ -337,12 +337,10 @@ public Iterator> iterator() { * Tests for all the different flavors of Predicates.or(). */ - @SuppressWarnings("unchecked") // varargs public void testOr_applyNoArgs() { assertEvalsToFalse(Predicates.or()); } - @SuppressWarnings("unchecked") // varargs public void testOr_equalityNoArgs() { new EqualsTester() .addEqualityGroup(Predicates.or(), Predicates.or()) @@ -351,19 +349,17 @@ public void testOr_equalityNoArgs() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testOr_serializationNoArgs() { checkSerialization(Predicates.or()); } - @SuppressWarnings("unchecked") // varargs public void testOr_applyOneArg() { assertEvalsToTrue(Predicates.or(TRUE)); assertEvalsToFalse(Predicates.or(FALSE)); } - @SuppressWarnings("unchecked") // varargs public void testOr_equalityOneArg() { new EqualsTester() .addEqualityGroup(Predicates.or(NEVER_REACHED), Predicates.or(NEVER_REACHED)) @@ -374,23 +370,22 @@ public void testOr_equalityOneArg() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testOr_serializationOneArg() { checkSerialization(Predicates.or(isOdd())); } public void testOr_applyBinary() { - Predicate falseOrFalse = Predicates.or(FALSE, FALSE); - Predicate falseOrTrue = Predicates.or(FALSE, TRUE); - Predicate trueOrAnything = Predicates.or(TRUE, NEVER_REACHED); + Predicate<@Nullable Integer> falseOrFalse = Predicates.or(FALSE, FALSE); + Predicate<@Nullable Integer> falseOrTrue = Predicates.or(FALSE, TRUE); + Predicate<@Nullable Integer> trueOrAnything = Predicates.or(TRUE, NEVER_REACHED); assertEvalsToFalse(falseOrFalse); assertEvalsToTrue(falseOrTrue); assertEvalsToTrue(trueOrAnything); } - @SuppressWarnings("unchecked") // varargs public void testOr_equalityBinary() { new EqualsTester() .addEqualityGroup(Predicates.or(FALSE, NEVER_REACHED), Predicates.or(FALSE, NEVER_REACHED)) @@ -400,12 +395,12 @@ public void testOr_equalityBinary() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testOr_serializationBinary() { checkSerialization(Predicates.or(isOdd(), TRUE)); } - @SuppressWarnings("unchecked") // varargs public void testOr_applyTernary() { assertEvalsLikeOdd(Predicates.or(isOdd(), FALSE, FALSE)); assertEvalsLikeOdd(Predicates.or(FALSE, isOdd(), FALSE)); @@ -413,7 +408,6 @@ public void testOr_applyTernary() { assertEvalsToTrue(Predicates.or(FALSE, TRUE, NEVER_REACHED)); } - @SuppressWarnings("unchecked") // varargs public void testOr_equalityTernary() { new EqualsTester() .addEqualityGroup( @@ -424,28 +418,27 @@ public void testOr_equalityTernary() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testOr_serializationTernary() { checkSerialization(Predicates.or(FALSE, isOdd(), TRUE)); } - @SuppressWarnings("unchecked") // varargs public void testOr_applyIterable() { - Predicate vacuouslyFalse = Predicates.or(Collections.>emptyList()); - Predicate troo = Predicates.or(Collections.singletonList(TRUE)); + Predicate<@Nullable Integer> vacuouslyFalse = + Predicates.or(Collections.>emptyList()); + Predicate<@Nullable Integer> troo = Predicates.or(Collections.singletonList(TRUE)); /* * newLinkedList() takes varargs. TRUE and FALSE are both instances of * Predicate, so the call is safe. */ - Predicate trueAndFalse = Predicates.or(Arrays.asList(TRUE, FALSE)); + Predicate<@Nullable Integer> trueAndFalse = Predicates.or(Arrays.asList(TRUE, FALSE)); assertEvalsToFalse(vacuouslyFalse); assertEvalsToTrue(troo); assertEvalsToTrue(trueAndFalse); } - @SuppressWarnings("unchecked") // varargs public void testOr_equalityIterable() { new EqualsTester() .addEqualityGroup( @@ -457,17 +450,17 @@ public void testOr_equalityIterable() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester - @SuppressWarnings("unchecked") // varargs public void testOr_serializationIterable() { Predicate pre = Predicates.or(Arrays.asList(TRUE, FALSE)); Predicate post = SerializableTester.reserializeAndAssert(pre); assertEquals(pre.apply(0), post.apply(0)); } - @SuppressWarnings("unchecked") // varargs public void testOr_arrayDefensivelyCopied() { - Predicate[] array = {Predicates.alwaysFalse()}; + @SuppressWarnings("unchecked") // generic arrays + Predicate[] array = (Predicate[]) new Predicate[] {Predicates.alwaysFalse()}; Predicate predicate = Predicates.or(array); assertFalse(predicate.apply(1)); array[0] = Predicates.alwaysTrue(); @@ -502,7 +495,7 @@ public Iterator> iterator() { */ public void testIsEqualTo_apply() { - Predicate isOne = Predicates.equalTo(1); + Predicate<@Nullable Integer> isOne = Predicates.equalTo(1); assertTrue(isOne.apply(1)); assertFalse(isOne.apply(2)); @@ -513,29 +506,33 @@ public void testIsEqualTo_equality() { new EqualsTester() .addEqualityGroup(Predicates.equalTo(1), Predicates.equalTo(1)) .addEqualityGroup(Predicates.equalTo(2)) - .addEqualityGroup(Predicates.equalTo(null)) + .addEqualityGroup(Predicates.<@Nullable Integer>equalTo(null)) .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testIsEqualTo_serialization() { checkSerialization(Predicates.equalTo(1)); } public void testIsEqualToNull_apply() { - Predicate isNull = Predicates.equalTo(null); + Predicate<@Nullable Integer> isNull = Predicates.equalTo(null); assertTrue(isNull.apply(null)); assertFalse(isNull.apply(1)); } public void testIsEqualToNull_equality() { new EqualsTester() - .addEqualityGroup(Predicates.equalTo(null), Predicates.equalTo(null)) + .addEqualityGroup( + Predicates.<@Nullable Integer>equalTo(null), + Predicates.<@Nullable Integer>equalTo(null)) .addEqualityGroup(Predicates.equalTo(1)) .addEqualityGroup(Predicates.equalTo("null")) .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testIsEqualToNull_serialization() { checkSerialization(Predicates.equalTo(null)); @@ -548,7 +545,7 @@ public void testIsEqualToNull_serialization() { */ @GwtIncompatible // Predicates.instanceOf public void testIsInstanceOf_apply() { - Predicate isInteger = Predicates.instanceOf(Integer.class); + Predicate<@Nullable Object> isInteger = Predicates.instanceOf(Integer.class); assertTrue(isInteger.apply(1)); assertFalse(isInteger.apply(2.0f)); @@ -558,7 +555,7 @@ public void testIsInstanceOf_apply() { @GwtIncompatible // Predicates.instanceOf public void testIsInstanceOf_subclass() { - Predicate isNumber = Predicates.instanceOf(Number.class); + Predicate<@Nullable Object> isNumber = Predicates.instanceOf(Number.class); assertTrue(isNumber.apply(1)); assertTrue(isNumber.apply(2.0f)); @@ -568,7 +565,7 @@ public void testIsInstanceOf_subclass() { @GwtIncompatible // Predicates.instanceOf public void testIsInstanceOf_interface() { - Predicate isComparable = Predicates.instanceOf(Comparable.class); + Predicate<@Nullable Object> isComparable = Predicates.instanceOf(Comparable.class); assertTrue(isComparable.apply(1)); assertTrue(isComparable.apply(2.0f)); @@ -586,11 +583,13 @@ public void testIsInstanceOf_equality() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // Predicates.instanceOf, SerializableTester public void testIsInstanceOf_serialization() { checkSerialization(Predicates.instanceOf(Integer.class)); } + @J2ktIncompatible @GwtIncompatible // Predicates.subtypeOf public void testSubtypeOf_apply() { Predicate> isInteger = Predicates.subtypeOf(Integer.class); @@ -598,13 +597,10 @@ public void testSubtypeOf_apply() { assertTrue(isInteger.apply(Integer.class)); assertFalse(isInteger.apply(Float.class)); - try { - isInteger.apply(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> isInteger.apply(null)); } + @J2ktIncompatible @GwtIncompatible // Predicates.subtypeOf public void testSubtypeOf_subclass() { Predicate> isNumber = Predicates.subtypeOf(Number.class); @@ -613,6 +609,7 @@ public void testSubtypeOf_subclass() { assertTrue(isNumber.apply(Float.class)); } + @J2ktIncompatible @GwtIncompatible // Predicates.subtypeOf public void testSubtypeOf_interface() { Predicate> isComparable = Predicates.subtypeOf(Comparable.class); @@ -621,6 +618,7 @@ public void testSubtypeOf_interface() { assertTrue(isComparable.apply(Float.class)); } + @J2ktIncompatible @GwtIncompatible // Predicates.subtypeOf public void testSubtypeOf_equality() { new EqualsTester() @@ -630,6 +628,7 @@ public void testSubtypeOf_equality() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // Predicates.subtypeOf, SerializableTester public void testSubtypeOf_serialization() { Predicate> predicate = Predicates.subtypeOf(Integer.class); @@ -645,7 +644,7 @@ public void testSubtypeOf_serialization() { */ public void testIsNull_apply() { - Predicate isNull = Predicates.isNull(); + Predicate<@Nullable Integer> isNull = Predicates.isNull(); assertTrue(isNull.apply(null)); assertFalse(isNull.apply(1)); } @@ -657,6 +656,7 @@ public void testIsNull_equality() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testIsNull_serialization() { Predicate pre = Predicates.isNull(); @@ -666,7 +666,7 @@ public void testIsNull_serialization() { } public void testNotNull_apply() { - Predicate notNull = Predicates.notNull(); + Predicate<@Nullable Integer> notNull = Predicates.notNull(); assertFalse(notNull.apply(null)); assertTrue(notNull.apply(1)); } @@ -678,6 +678,7 @@ public void testNotNull_equality() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNotNull_serialization() { checkSerialization(Predicates.notNull()); @@ -685,7 +686,7 @@ public void testNotNull_serialization() { public void testIn_apply() { Collection nums = Arrays.asList(1, 5); - Predicate isOneOrFive = Predicates.in(nums); + Predicate<@Nullable Integer> isOneOrFive = Predicates.in(nums); assertTrue(isOneOrFive.apply(1)); assertTrue(isOneOrFive.apply(5)); @@ -709,36 +710,37 @@ public void testIn_equality() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testIn_serialization() { checkSerialization(Predicates.in(Arrays.asList(1, 2, 3, null))); } public void testIn_handlesNullPointerException() { - class CollectionThatThrowsNPE extends ArrayList { - private static final long serialVersionUID = 1L; + class CollectionThatThrowsNullPointerException extends ArrayList { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1L; @Override - public boolean contains(Object element) { + public boolean contains(@Nullable Object element) { Preconditions.checkNotNull(element); return super.contains(element); } } - Collection nums = new CollectionThatThrowsNPE<>(); - Predicate isFalse = Predicates.in(nums); + Collection nums = new CollectionThatThrowsNullPointerException<>(); + Predicate<@Nullable Integer> isFalse = Predicates.in(nums); assertFalse(isFalse.apply(null)); } public void testIn_handlesClassCastException() { - class CollectionThatThrowsCCE extends ArrayList { - private static final long serialVersionUID = 1L; + class CollectionThatThrowsClassCastException extends ArrayList { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1L; @Override - public boolean contains(Object element) { + public boolean contains(@Nullable Object element) { throw new ClassCastException(""); } } - Collection nums = new CollectionThatThrowsCCE<>(); + Collection nums = new CollectionThatThrowsClassCastException<>(); nums.add(3); Predicate isThree = Predicates.in(nums); assertFalse(isThree.apply(3)); @@ -757,14 +759,15 @@ public void testIn_compilesWithExplicitSupertype() { // Predicate p4 = Predicates.in(nums); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(Predicates.class); } - @SuppressWarnings("unchecked") // varargs - @GwtIncompatible // SerializbleTester + @J2ktIncompatible + @GwtIncompatible // SerializableTester public void testCascadingSerialization() throws Exception { // Eclipse says Predicate; javac says Predicate. Predicate nasty = @@ -815,6 +818,7 @@ public void testCompose() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testComposeSerialization() { Function trim = TrimStringFunction.INSTANCE; @@ -843,6 +847,7 @@ public void testContains_apply() { assertFalse(isFoobar.apply("Foobarx")); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testContainsPattern_nulls() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -851,6 +856,7 @@ public void testContainsPattern_nulls() throws Exception { tester.testAllPublicInstanceMethods(isWooString); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testContains_nulls() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -859,6 +865,7 @@ public void testContains_nulls() throws Exception { tester.testAllPublicInstanceMethods(isWooPattern); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testContainsPattern_serialization() { Predicate pre = Predicates.containsPattern("foo"); @@ -877,13 +884,13 @@ public void testContains_equals() { } public void assertEqualHashCode( - Predicate expected, Predicate actual) { + Predicate expected, Predicate actual) { assertEquals(actual + " should hash like " + expected, expected.hashCode(), actual.hashCode()); } public void testHashCodeForBooleanOperations() { - Predicate p1 = Predicates.isNull(); - Predicate p2 = isOdd(); + Predicate<@Nullable Integer> p1 = Predicates.isNull(); + Predicate<@Nullable Integer> p2 = isOdd(); // Make sure that hash codes are not computed per-instance. assertEqualHashCode(Predicates.not(p1), Predicates.not(p1)); @@ -897,41 +904,43 @@ public void testHashCodeForBooleanOperations() { assertTrue(Predicates.and(p1, p2).hashCode() != Predicates.or(p1, p2).hashCode()); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { new ClassSanityTester().forAllPublicStaticMethods(Predicates.class).testNulls(); } + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException: com.google.common.base.Function public void testEqualsAndSerializable() throws Exception { new ClassSanityTester().forAllPublicStaticMethods(Predicates.class).testEqualsAndSerializable(); } - private static void assertEvalsToTrue(Predicate predicate) { + private static void assertEvalsToTrue(Predicate predicate) { assertTrue(predicate.apply(0)); assertTrue(predicate.apply(1)); assertTrue(predicate.apply(null)); } - private static void assertEvalsToFalse(Predicate predicate) { + private static void assertEvalsToFalse(Predicate predicate) { assertFalse(predicate.apply(0)); assertFalse(predicate.apply(1)); assertFalse(predicate.apply(null)); } - private static void assertEvalsLikeOdd(Predicate predicate) { + private static void assertEvalsLikeOdd(Predicate predicate) { assertEvalsLike(isOdd(), predicate); } private static void assertEvalsLike( - Predicate expected, Predicate actual) { + Predicate expected, Predicate actual) { assertEvalsLike(expected, actual, 0); assertEvalsLike(expected, actual, 1); - assertEvalsLike(expected, actual, null); + PredicatesTest.<@Nullable Integer>assertEvalsLike(expected, actual, null); } - private static void assertEvalsLike( + private static void assertEvalsLike( Predicate expected, Predicate actual, T input) { Boolean expectedResult = null; RuntimeException expectedRuntimeException = null; @@ -956,9 +965,11 @@ private static void assertEvalsLike( } } + @J2ktIncompatible @GwtIncompatible // SerializableTester - private static void checkSerialization(Predicate predicate) { - Predicate reserialized = SerializableTester.reserializeAndAssert(predicate); + private static void checkSerialization(Predicate predicate) { + Predicate reserialized = + SerializableTester.reserializeAndAssert(predicate); assertEvalsLike(predicate, reserialized); } } diff --git a/android/guava-tests/test/com/google/common/base/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/base/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..f583b4bf043a --- /dev/null +++ b/android/guava-tests/test/com/google/common/base/ReflectionFreeAssertThrows.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.TestExceptions.SomeCheckedException; +import com.google.common.base.TestExceptions.SomeError; +import com.google.common.base.TestExceptions.SomeOtherCheckedException; +import com.google.common.base.TestExceptions.SomeUncheckedException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(SomeCheckedException.class, e -> e instanceof SomeCheckedException) + .put(SomeError.class, e -> e instanceof SomeError) + .put(SomeOtherCheckedException.class, e -> e instanceof SomeOtherCheckedException) + .put(SomeUncheckedException.class, e -> e instanceof SomeUncheckedException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/base/SplitterTest.java b/android/guava-tests/test/com/google/common/base/SplitterTest.java index 46928aa36a3b..d5b5a9aeb373 100644 --- a/android/guava-tests/test/com/google/common/base/SplitterTest.java +++ b/android/guava-tests/test/com/google/common/base/SplitterTest.java @@ -16,10 +16,13 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Splitter.MapSplitter; import com.google.common.collect.ImmutableMap; import com.google.common.testing.NullPointerTester; @@ -28,19 +31,19 @@ import java.util.Map; import java.util.regex.Pattern; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; -/** @author Julien Silland */ +/** + * @author Julien Silland + */ +@NullMarked @GwtCompatible(emulated = true) public class SplitterTest extends TestCase { private static final Splitter COMMA_SPLITTER = Splitter.on(','); public void testSplitNullString() { - try { - COMMA_SPLITTER.split(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> COMMA_SPLITTER.split(null)); } public void testCharacterSimpleSplit() { @@ -62,6 +65,12 @@ public void testCharacterSimpleSplitToList() { assertThat(letters).containsExactly("a", "b", "c").inOrder(); } + public void testCharacterSimpleSplitToStream() { + String simple = "a,b,c"; + List letters = COMMA_SPLITTER.splitToStream(simple).collect(toImmutableList()); + assertThat(letters).containsExactly("a", "b", "c").inOrder(); + } + public void testToString() { assertEquals("[]", COMMA_SPLITTER.split("").toString()); assertEquals("[a, b, c]", COMMA_SPLITTER.split("a,b,c").toString()); @@ -249,11 +258,7 @@ public void testStringSplitWithDelimiterSubstringInValue() { } public void testStringSplitWithEmptyString() { - try { - Splitter.on(""); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Splitter.on("")); } public void testStringSplitOnEmptyString() { @@ -352,6 +357,7 @@ public void testPatternSplitWithDoubleDelimiterOmitEmptyStrings() { assertThat(letters).containsExactly("a", "b", "c").inOrder(); } + @J2ktIncompatible // Kotlin Native's regex is based on Apache Harmony, like old Android @GwtIncompatible // java.util.regex.Pattern @AndroidIncompatible // Bug in older versions of Android we test against, since fixed. public void testPatternSplitLookBehind() { @@ -365,6 +371,7 @@ public void testPatternSplitLookBehind() { // splits into chunks ending in : } + @J2ktIncompatible // Kotlin Native's regex is based on Apache Harmony, like old Android @GwtIncompatible // java.util.regex.Pattern @AndroidIncompatible // Bug in older versions of Android we test against, since fixed. public void testPatternSplitWordBoundary() { @@ -381,6 +388,7 @@ public void testPatternSplitWordBoundary_singleCharInput() { } @AndroidIncompatible // Apparently Gingerbread's regex API is buggy. + @J2ktIncompatible // Kotlin Native's regex is based on Apache Harmony, like old Android @GwtIncompatible // java.util.regex.Pattern public void testPatternSplitWordBoundary_singleWordInput() { String string = "foo"; @@ -439,11 +447,7 @@ public void testPatternSplitWithLongTrailingDelimiter() { @GwtIncompatible // java.util.regex.Pattern public void testPatternSplitInvalidPattern() { - try { - Splitter.on(Pattern.compile("a*")); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Splitter.on(Pattern.compile("a*"))); } @GwtIncompatible // java.util.regex.Pattern @@ -489,6 +493,7 @@ public void testSplitterIterableIsLazy_string() { assertSplitterIterableIsLazy(Splitter.on(",")); } + @J2ktIncompatible @GwtIncompatible // java.util.regex.Pattern @AndroidIncompatible // not clear that j.u.r.Matcher promises to handle mutations during use public void testSplitterIterableIsLazy_pattern() { @@ -556,19 +561,11 @@ public void testFixedLengthSplitIntoChars() { } public void testFixedLengthSplitZeroChunkLen() { - try { - Splitter.fixedLength(0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLength(0)); } public void testFixedLengthSplitNegativeChunkLen() { - try { - Splitter.fixedLength(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLength(-1)); } public void testLimitLarge() { @@ -656,13 +653,10 @@ public void testLimitExtraSeparatorsTrim1EmptyOmit() { } public void testInvalidZeroLimit() { - try { - COMMA_SPLITTER.limit(0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> COMMA_SPLITTER.limit(0)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -718,7 +712,7 @@ public void testMapSplitter_notTrimmed() { assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder(); } - public void testMapSplitter_CharacterSeparator() { + public void testMapSplitter_characterSeparator() { // try different delimiters. Map m = Splitter.on(",").withKeyValueSeparator(':').split("boy:tom,girl:tina,cat:kitty,dog:tommy"); @@ -743,19 +737,13 @@ public void testMapSplitter_multiCharacterSeparator() { } public void testMapSplitter_emptySeparator() { - try { - COMMA_SPLITTER.withKeyValueSeparator(""); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> COMMA_SPLITTER.withKeyValueSeparator("")); } public void testMapSplitter_malformedEntry() { - try { - COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,b,c=2"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,b,c=2")); } /** @@ -763,11 +751,9 @@ public void testMapSplitter_malformedEntry() { * be changed? */ public void testMapSplitter_extraValueDelimiter() { - try { - COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,c=2="); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,c=2=")); } public void testMapSplitter_orderedResults() { @@ -787,11 +773,9 @@ public void testMapSplitter_orderedResults() { } public void testMapSplitter_duplicateKeys() { - try { - COMMA_SPLITTER.withKeyValueSeparator(":").split("a:1,b:2,a:3"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> COMMA_SPLITTER.withKeyValueSeparator(":").split("a:1,b:2,a:3")); } public void testMapSplitter_varyingTrimLevels() { diff --git a/android/guava-tests/test/com/google/common/base/StandardSystemPropertyTest.java b/android/guava-tests/test/com/google/common/base/StandardSystemPropertyTest.java index 3a88366c0cc0..6c898bf3dc52 100644 --- a/android/guava-tests/test/com/google/common/base/StandardSystemPropertyTest.java +++ b/android/guava-tests/test/com/google/common/base/StandardSystemPropertyTest.java @@ -20,13 +20,17 @@ import static com.google.common.base.StandardSystemProperty.JAVA_EXT_DIRS; import static com.google.common.truth.Truth.assertWithMessage; +import com.google.common.annotations.GwtIncompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link StandardSystemProperty}. * * @author Kurt Alfred Kluever */ +@GwtIncompatible +@NullUnmarked public class StandardSystemPropertyTest extends TestCase { public void testGetKeyMatchesString() { diff --git a/android/guava-tests/test/com/google/common/base/StopwatchJavaTimeTest.java b/android/guava-tests/test/com/google/common/base/StopwatchJavaTimeTest.java new file mode 100644 index 000000000000..71bcfc740779 --- /dev/null +++ b/android/guava-tests/test/com/google/common/base/StopwatchJavaTimeTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.testing.FakeTicker; +import java.time.Duration; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** Unit test for the {@code java.time} support in {@link Stopwatch}. */ +@J2ktIncompatible +@GwtIncompatible +@NullUnmarked +public class StopwatchJavaTimeTest extends TestCase { + private final FakeTicker ticker = new FakeTicker(); + private final Stopwatch stopwatch = new Stopwatch(ticker); + + public void testElapsed_duration() { + stopwatch.start(); + ticker.advance(999999); + assertEquals(Duration.ofNanos(999999), stopwatch.elapsed()); + ticker.advance(1); + assertEquals(Duration.ofMillis(1), stopwatch.elapsed()); + } +} diff --git a/android/guava-tests/test/com/google/common/base/StopwatchTest.java b/android/guava-tests/test/com/google/common/base/StopwatchTest.java index b85ebb7b0e94..53d583b1b17b 100644 --- a/android/guava-tests/test/com/google/common/base/StopwatchTest.java +++ b/android/guava-tests/test/com/google/common/base/StopwatchTest.java @@ -16,13 +16,16 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.FakeTicker; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Stopwatch}. @@ -30,6 +33,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullUnmarked public class StopwatchTest extends TestCase { private final FakeTicker ticker = new FakeTicker(); @@ -58,11 +62,7 @@ public void testStart() { public void testStart_whileRunning() { stopwatch.start(); - try { - stopwatch.start(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, stopwatch::start); assertTrue(stopwatch.isRunning()); } @@ -73,22 +73,14 @@ public void testStop() { } public void testStop_new() { - try { - stopwatch.stop(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, stopwatch::stop); assertFalse(stopwatch.isRunning()); } public void testStop_alreadyStopped() { stopwatch.start(); stopwatch.stop(); - try { - stopwatch.stop(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, stopwatch::stop); assertFalse(stopwatch.isRunning()); } @@ -166,6 +158,7 @@ public void testElapsed_millis() { assertEquals(1, stopwatch.elapsed(MILLISECONDS)); } + @J2ktIncompatible // TODO(b/259213718): Switch J2kt to String.format("%.4g") once that's supported public void testToString() { stopwatch.start(); assertEquals("0.000 ns", stopwatch.toString()); diff --git a/android/guava-tests/test/com/google/common/base/StringsTest.java b/android/guava-tests/test/com/google/common/base/StringsTest.java index ac3ad20a52dc..79fd2651cd24 100644 --- a/android/guava-tests/test/com/google/common/base/StringsTest.java +++ b/android/guava-tests/test/com/google/common/base/StringsTest.java @@ -16,18 +16,22 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link Strings}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) public class StringsTest extends TestCase { public void testNullToEmpty() { @@ -70,11 +74,7 @@ public void testPadStart_negativeMinLength() { // TODO: could remove if we got NPT working in GWT somehow public void testPadStart_null() { - try { - Strings.padStart(null, 5, '0'); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Strings.padStart(null, 5, '0')); } public void testPadEnd_noPadding() { @@ -97,15 +97,11 @@ public void testPadEnd_negativeMinLength() { assertSame("x", Strings.padEnd("x", -1, '-')); } - // TODO: could remove if we got NPT working in GWT somehow public void testPadEnd_null() { - try { - Strings.padEnd(null, 5, '0'); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Strings.padEnd(null, 5, '0')); } + @SuppressWarnings("InlineMeInliner") // test of method that doesn't just delegate public void testRepeat() { String input = "20"; assertEquals("", Strings.repeat(input, 0)); @@ -119,28 +115,17 @@ public void testRepeat() { assertEquals(2 * i, Strings.repeat(input, i).length()); } - try { - Strings.repeat("x", -1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // Massive string - Strings.repeat("12345678", (1 << 30) + 3); - fail(); - } catch (ArrayIndexOutOfBoundsException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Strings.repeat("x", -1)); + assertThrows( + ArrayIndexOutOfBoundsException.class, () -> Strings.repeat("12345678", (1 << 30) + 3)); } - // TODO: could remove if we got NPT working in GWT somehow + @SuppressWarnings("InlineMeInliner") // test of method that doesn't just delegate public void testRepeat_null() { - try { - Strings.repeat(null, 5); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Strings.repeat(null, 5)); } + @SuppressWarnings("UnnecessaryStringBuilder") // We want to test a non-String CharSequence public void testCommonPrefix() { assertEquals("", Strings.commonPrefix("", "")); assertEquals("", Strings.commonPrefix("abc", "")); @@ -150,7 +135,7 @@ public void testCommonPrefix() { assertEquals("", Strings.commonPrefix("xyz", "abcxyz")); assertEquals("a", Strings.commonPrefix("abc", "aaaaa")); assertEquals("aa", Strings.commonPrefix("aa", "aaaaa")); - assertEquals("abc", Strings.commonPrefix(new StringBuffer("abcdef"), "abcxyz")); + assertEquals("abc", Strings.commonPrefix(new StringBuilder("abcdef"), "abcxyz")); // Identical valid surrogate pairs. assertEquals( @@ -170,6 +155,7 @@ public void testCommonPrefix() { assertEquals("\uD8AB", Strings.commonPrefix("\uD8AB", "\uD8AB")); } + @SuppressWarnings("UnnecessaryStringBuilder") // We want to test a non-String CharSequence public void testCommonSuffix() { assertEquals("", Strings.commonSuffix("", "")); assertEquals("", Strings.commonSuffix("abc", "")); @@ -179,7 +165,7 @@ public void testCommonSuffix() { assertEquals("", Strings.commonSuffix("xyz", "xyzabc")); assertEquals("c", Strings.commonSuffix("abc", "ccccc")); assertEquals("aa", Strings.commonSuffix("aa", "aaaaa")); - assertEquals("abc", Strings.commonSuffix(new StringBuffer("xyzabc"), "xxxabc")); + assertEquals("abc", Strings.commonSuffix(new StringBuilder("xyzabc"), "xxxabc")); // Identical valid surrogate pairs. assertEquals( @@ -213,6 +199,7 @@ public void testValidSurrogatePairAt() { assertFalse(Strings.validSurrogatePairAt("\uD8ABx", 0)); } + @SuppressWarnings("LenientFormatStringValidation") // Intentional for testing. public void testLenientFormat() { assertEquals("%s", Strings.lenientFormat("%s")); assertEquals("5", Strings.lenientFormat("%s", 5)); @@ -229,6 +216,10 @@ public void testLenientFormat() { assertEquals("null [null, null]", Strings.lenientFormat("%s", null, null, null)); assertEquals("null [5, 6]", Strings.lenientFormat(null, 5, 6)); assertEquals("null", Strings.lenientFormat("%s", (Object) null)); + } + + @J2ktIncompatible // TODO(b/319404022): Allow passing null array as varargs + public void testLenientFormat_nullArrayVarargs() { assertEquals("(Object[])null", Strings.lenientFormat("%s", (Object[]) null)); } @@ -236,7 +227,8 @@ public void testLenientFormat() { public void testLenientFormat_badArgumentToString() { assertThat(Strings.lenientFormat("boiler %s plate", new ThrowsOnToString())) .matches( - "boiler plate"); } @@ -252,6 +244,7 @@ public String toString() { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/base/SuppliersTest.java b/android/guava-tests/test/com/google/common/base/SuppliersTest.java index a97fe656c91b..4a6c68bee726 100644 --- a/android/guava-tests/test/com/google/common/base/SuppliersTest.java +++ b/android/guava-tests/test/com/google/common/base/SuppliersTest.java @@ -16,22 +16,30 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Lists; import com.google.common.testing.ClassSanityTester; import com.google.common.testing.EqualsTester; +import java.io.NotSerializableException; import java.io.Serializable; +import java.time.Duration; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests com.google.common.base.Suppliers. @@ -39,6 +47,7 @@ * @author Laurence Gonsalves * @author Harry Heymann */ +@NullMarked @GwtCompatible(emulated = true) public class SuppliersTest extends TestCase { @@ -65,11 +74,11 @@ public Integer get() { } static class SerializableCountingSupplier extends CountingSupplier implements Serializable { - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } static class SerializableThrowingSupplier extends ThrowingSupplier implements Serializable { - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } static void checkMemoize(CountingSupplier countingSupplier, Supplier memoizedSupplier) { @@ -98,11 +107,11 @@ private void memoizeTest(CountingSupplier countingSupplier) { } public void testMemoize_redudantly() { - memoize_redudantlyTest(new CountingSupplier()); - memoize_redudantlyTest(new SerializableCountingSupplier()); + memoizeRedudantlyTest(new CountingSupplier()); + memoizeRedudantlyTest(new SerializableCountingSupplier()); } - private void memoize_redudantlyTest(CountingSupplier countingSupplier) { + private void memoizeRedudantlyTest(CountingSupplier countingSupplier) { Supplier memoizedSupplier = Suppliers.memoize(countingSupplier); assertSame(memoizedSupplier, Suppliers.memoize(memoizedSupplier)); } @@ -126,6 +135,7 @@ private void memoizeExceptionThrownTest(ThrowingSupplier throwingSupplier) { } } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testMemoizeNonSerializable() throws Exception { CountingSupplier countingSupplier = new CountingSupplier(); @@ -133,19 +143,16 @@ public void testMemoizeNonSerializable() throws Exception { assertThat(memoizedSupplier.toString()).isEqualTo("Suppliers.memoize(CountingSupplier)"); checkMemoize(countingSupplier, memoizedSupplier); // Calls to the original memoized supplier shouldn't affect its copy. - memoizedSupplier.get(); + Object unused = memoizedSupplier.get(); assertThat(memoizedSupplier.toString()) .isEqualTo("Suppliers.memoize()"); // Should get an exception when we try to serialize. - try { - reserialize(memoizedSupplier); - fail(); - } catch (RuntimeException ex) { - assertThat(ex).hasCauseThat().isInstanceOf(java.io.NotSerializableException.class); - } + RuntimeException ex = assertThrows(RuntimeException.class, () -> reserialize(memoizedSupplier)); + assertThat(ex).hasCauseThat().isInstanceOf(NotSerializableException.class); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testMemoizeSerializable() throws Exception { SerializableCountingSupplier countingSupplier = new SerializableCountingSupplier(); @@ -153,12 +160,12 @@ public void testMemoizeSerializable() throws Exception { assertThat(memoizedSupplier.toString()).isEqualTo("Suppliers.memoize(CountingSupplier)"); checkMemoize(countingSupplier, memoizedSupplier); // Calls to the original memoized supplier shouldn't affect its copy. - memoizedSupplier.get(); + Object unused = memoizedSupplier.get(); assertThat(memoizedSupplier.toString()) .isEqualTo("Suppliers.memoize()"); Supplier copy = reserialize(memoizedSupplier); - memoizedSupplier.get(); + Object unused2 = memoizedSupplier.get(); CountingSupplier countingCopy = (CountingSupplier) ((Suppliers.MemoizingSupplier) copy).delegate; @@ -213,33 +220,72 @@ public List apply(List list) { assertEquals(Integer.valueOf(1), result.get(1)); } + @J2ktIncompatible + @GwtIncompatible // Thread.sleep + @SuppressWarnings("DoNotCall") + public void testMemoizeWithExpiration_longTimeUnit() throws InterruptedException { + CountingSupplier countingSupplier = new CountingSupplier(); + + Supplier memoizedSupplier = + Suppliers.memoizeWithExpiration(countingSupplier, 75, MILLISECONDS); + + checkExpiration(countingSupplier, memoizedSupplier); + } + + @J2ktIncompatible @GwtIncompatible // Thread.sleep - public void testMemoizeWithExpiration() throws InterruptedException { + public void testMemoizeWithExpiration_duration() throws InterruptedException { CountingSupplier countingSupplier = new CountingSupplier(); Supplier memoizedSupplier = - Suppliers.memoizeWithExpiration(countingSupplier, 75, TimeUnit.MILLISECONDS); + Suppliers.memoizeWithExpiration(countingSupplier, Duration.ofMillis(75)); checkExpiration(countingSupplier, memoizedSupplier); } + @SuppressWarnings("DoNotCall") + public void testMemoizeWithExpiration_longTimeUnitNegative() throws InterruptedException { + assertThrows( + IllegalArgumentException.class, + () -> Suppliers.memoizeWithExpiration(() -> "", 0, MILLISECONDS)); + + assertThrows( + IllegalArgumentException.class, + () -> Suppliers.memoizeWithExpiration(() -> "", -1, MILLISECONDS)); + } + + @J2ktIncompatible // Duration + @GwtIncompatible // Duration + public void testMemoizeWithExpiration_durationNegative() throws InterruptedException { + assertThrows( + IllegalArgumentException.class, + () -> Suppliers.memoizeWithExpiration(() -> "", Duration.ZERO)); + + assertThrows( + IllegalArgumentException.class, + () -> Suppliers.memoizeWithExpiration(() -> "", Duration.ofMillis(-1))); + } + + @J2ktIncompatible @GwtIncompatible // Thread.sleep, SerializationTester + @SuppressWarnings("DoNotCall") public void testMemoizeWithExpirationSerialized() throws InterruptedException { SerializableCountingSupplier countingSupplier = new SerializableCountingSupplier(); Supplier memoizedSupplier = - Suppliers.memoizeWithExpiration(countingSupplier, 75, TimeUnit.MILLISECONDS); + Suppliers.memoizeWithExpiration(countingSupplier, 75, MILLISECONDS); // Calls to the original memoized supplier shouldn't affect its copy. - memoizedSupplier.get(); + Object unused = memoizedSupplier.get(); Supplier copy = reserialize(memoizedSupplier); - memoizedSupplier.get(); + Object unused2 = memoizedSupplier.get(); CountingSupplier countingCopy = (CountingSupplier) ((Suppliers.ExpiringMemoizingSupplier) copy).delegate; checkExpiration(countingCopy, copy); } + @J2ktIncompatible @GwtIncompatible // Thread.sleep private void checkExpiration( CountingSupplier countingSupplier, Supplier memoizedSupplier) @@ -274,25 +320,26 @@ public void testOfInstanceSuppliesSameInstance() { } public void testOfInstanceSuppliesNull() { - Supplier nullSupplier = Suppliers.ofInstance(null); + Supplier<@Nullable Integer> nullSupplier = Suppliers.ofInstance(null); assertNull(nullSupplier.get()); } + @J2ktIncompatible @GwtIncompatible // Thread - + @SuppressWarnings("DoNotCall") public void testExpiringMemoizedSupplierThreadSafe() throws Throwable { Function, Supplier> memoizer = new Function, Supplier>() { @Override public Supplier apply(Supplier supplier) { - return Suppliers.memoizeWithExpiration(supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS); + return Suppliers.memoizeWithExpiration(supplier, Long.MAX_VALUE, NANOSECONDS); } }; testSupplierThreadSafe(memoizer); } + @J2ktIncompatible @GwtIncompatible // Thread - public void testMemoizedSupplierThreadSafe() throws Throwable { Function, Supplier> memoizer = new Function, Supplier>() { @@ -304,14 +351,15 @@ public Supplier apply(Supplier supplier) { testSupplierThreadSafe(memoizer); } + @J2ktIncompatible @GwtIncompatible // Thread - public void testSupplierThreadSafe(Function, Supplier> memoizer) + private void testSupplierThreadSafe(Function, Supplier> memoizer) throws Throwable { final AtomicInteger count = new AtomicInteger(0); final AtomicReference thrown = new AtomicReference<>(null); final int numThreads = 3; final Thread[] threads = new Thread[numThreads]; - final long timeout = TimeUnit.SECONDS.toNanos(60); + final long timeout = SECONDS.toNanos(60); final Supplier supplier = new Supplier() { @@ -337,6 +385,7 @@ int waitingThreads() { } @Override + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public Boolean get() { // Check that this method is called exactly once, by the first // thread to synchronize. @@ -380,8 +429,9 @@ public void run() { assertEquals(1, count.get()); } + @J2ktIncompatible @GwtIncompatible // Thread - + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public void testSynchronizedSupplierThreadSafe() throws InterruptedException { final Supplier nonThreadSafe = new Supplier() { @@ -405,7 +455,7 @@ public Integer get() { @Override public void run() { for (int j = 0; j < iterations; j++) { - Suppliers.synchronizedSupplier(nonThreadSafe).get(); + Object unused = Suppliers.synchronizedSupplier(nonThreadSafe).get(); } } }; @@ -427,7 +477,9 @@ public void testSupplierFunction() { assertEquals(14, (int) supplierFunction.apply(supplier)); } + @J2ktIncompatible @GwtIncompatible // SerializationTester + @SuppressWarnings("DoNotCall") public void testSerialization() { assertEquals(Integer.valueOf(5), reserialize(Suppliers.ofInstance(5)).get()); assertEquals( @@ -436,22 +488,29 @@ public void testSerialization() { assertEquals(Integer.valueOf(5), reserialize(Suppliers.memoize(Suppliers.ofInstance(5))).get()); assertEquals( Integer.valueOf(5), - reserialize(Suppliers.memoizeWithExpiration(Suppliers.ofInstance(5), 30, TimeUnit.SECONDS)) - .get()); + reserialize(Suppliers.memoizeWithExpiration(Suppliers.ofInstance(5), 30, SECONDS)).get()); assertEquals( Integer.valueOf(5), reserialize(Suppliers.synchronizedSupplier(Suppliers.ofInstance(5))).get()); } + @J2ktIncompatible @GwtIncompatible // reflection public void testSuppliersNullChecks() throws Exception { - new ClassSanityTester().forAllPublicStaticMethods(Suppliers.class).testNulls(); + new ClassSanityTester() + .setDefault(Duration.class, Duration.ofSeconds(1)) + .forAllPublicStaticMethods(Suppliers.class) + .testNulls(); } + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // TODO(cpovirk): ClassNotFoundException: com.google.common.base.Function public void testSuppliersSerializable() throws Exception { - new ClassSanityTester().forAllPublicStaticMethods(Suppliers.class).testSerializable(); + new ClassSanityTester() + .setDefault(Duration.class, Duration.ofSeconds(1)) + .forAllPublicStaticMethods(Suppliers.class) + .testSerializable(); } public void testOfInstance_equals() { diff --git a/android/guava-tests/test/com/google/common/base/TestExceptions.java b/android/guava-tests/test/com/google/common/base/TestExceptions.java new file mode 100644 index 000000000000..3eadceb43b43 --- /dev/null +++ b/android/guava-tests/test/com/google/common/base/TestExceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullUnmarked; + +/** Exception classes for use in tests. */ +@GwtCompatible +@NullUnmarked +final class TestExceptions { + static class SomeError extends Error {} + + static class SomeCheckedException extends Exception {} + + static class SomeOtherCheckedException extends Exception {} + + static class YetAnotherCheckedException extends Exception {} + + static class SomeUncheckedException extends RuntimeException {} + + static class SomeChainingException extends RuntimeException { + public SomeChainingException(Throwable cause) { + super(cause); + } + } + + private TestExceptions() {} +} diff --git a/android/guava-tests/test/com/google/common/base/ThrowablesTest.java b/android/guava-tests/test/com/google/common/base/ThrowablesTest.java index e4c64aa8df1c..4ae5ad110da3 100644 --- a/android/guava-tests/test/com/google/common/base/ThrowablesTest.java +++ b/android/guava-tests/test/com/google/common/base/ThrowablesTest.java @@ -16,23 +16,37 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.base.Throwables.getCausalChain; +import static com.google.common.base.Throwables.getCauseAs; +import static com.google.common.base.Throwables.getRootCause; import static com.google.common.base.Throwables.getStackTraceAsString; import static com.google.common.base.Throwables.lazyStackTrace; import static com.google.common.base.Throwables.lazyStackTraceIsLazy; +import static com.google.common.base.Throwables.propagate; +import static com.google.common.base.Throwables.propagateIfInstanceOf; +import static com.google.common.base.Throwables.propagateIfPossible; import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.truth.Truth.assertThat; -import static java.util.Arrays.asList; import static java.util.regex.Pattern.quote; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.TestExceptions.SomeChainingException; +import com.google.common.base.TestExceptions.SomeCheckedException; +import com.google.common.base.TestExceptions.SomeError; +import com.google.common.base.TestExceptions.SomeOtherCheckedException; +import com.google.common.base.TestExceptions.SomeUncheckedException; +import com.google.common.base.TestExceptions.YetAnotherCheckedException; import com.google.common.collect.Iterables; import com.google.common.primitives.Ints; import com.google.common.testing.NullPointerTester; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Throwables}. @@ -40,591 +54,241 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@SuppressWarnings("deprecation") // tests of numerous deprecated methods +@NullUnmarked public class ThrowablesTest extends TestCase { - public void testThrowIfUnchecked_Unchecked() { - try { - throwIfUnchecked(new SomeUncheckedException()); - fail(); - } catch (SomeUncheckedException expected) { - } + // We're testing that the method is in fact equivalent to throwing the exception directly. + @SuppressWarnings("ThrowIfUncheckedKnownUnchecked") + public void testThrowIfUnchecked_unchecked() { + assertThrows( + SomeUncheckedException.class, () -> throwIfUnchecked(new SomeUncheckedException())); } - public void testThrowIfUnchecked_Error() { - try { - throwIfUnchecked(new SomeError()); - fail(); - } catch (SomeError expected) { - } + // We're testing that the method is in fact equivalent to throwing the exception directly. + @SuppressWarnings("ThrowIfUncheckedKnownUnchecked") + public void testThrowIfUnchecked_error() { + assertThrows(SomeError.class, () -> throwIfUnchecked(new SomeError())); } @SuppressWarnings("ThrowIfUncheckedKnownChecked") - public void testThrowIfUnchecked_Checked() { + public void testThrowIfUnchecked_checked() { throwIfUnchecked(new SomeCheckedException()); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible - public void testPropagateIfPossible_NoneDeclared_NoneThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatDoesntThrowAnything(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t); - throw new SomeChainingException(t); - } - } - }; - - // Expect no exception to be thrown - sample.noneDeclared(); - } - - @GwtIncompatible // propagateIfPossible - public void testPropagateIfPossible_NoneDeclared_UncheckedThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatThrowsUnchecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t); - throw new SomeChainingException(t); - } - } - }; - - // Expect the unchecked exception to propagate as-is - try { - sample.noneDeclared(); - fail(); - } catch (SomeUncheckedException expected) { - } + // We're testing that the method is in fact equivalent to throwing the exception directly. + @SuppressWarnings("ThrowIfUncheckedKnownUnchecked") + public void testPropagateIfPossible_noneDeclared_unchecked() { + assertThrows( + SomeUncheckedException.class, () -> propagateIfPossible(new SomeUncheckedException())); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible - public void testPropagateIfPossible_NoneDeclared_UndeclaredThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatThrowsUndeclaredChecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t); - throw new SomeChainingException(t); - } - } - }; - - // Expect the undeclared exception to have been chained inside another - try { - sample.noneDeclared(); - fail(); - } catch (SomeChainingException expected) { - } - } - - @GwtIncompatible // propagateIfPossible(Throwable, Class) - public void testPropagateIfPossible_OneDeclared_NoneThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatDoesntThrowAnything(); - } catch (Throwable t) { - // yes, this block is never reached, but for purposes of illustration - // we're keeping it the same in each test - Throwables.propagateIfPossible(t, SomeCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect no exception to be thrown - sample.oneDeclared(); + @SuppressWarnings("ThrowIfUncheckedKnownChecked") + public void testPropagateIfPossible_noneDeclared_checked() { + propagateIfPossible(new SomeCheckedException()); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class) - public void testPropagateIfPossible_OneDeclared_UncheckedThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsUnchecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t, SomeCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the unchecked exception to propagate as-is - try { - sample.oneDeclared(); - fail(); - } catch (SomeUncheckedException expected) { - } + public void testPropagateIfPossible_oneDeclared_unchecked() { + assertThrows( + SomeUncheckedException.class, + () -> propagateIfPossible(new SomeUncheckedException(), SomeCheckedException.class)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class) - public void testPropagateIfPossible_OneDeclared_CheckedThrown() { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsChecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t, SomeCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the checked exception to propagate as-is - try { - sample.oneDeclared(); - fail(); - } catch (SomeCheckedException expected) { - } + public void testPropagateIfPossible_oneDeclared_checkedSame() { + assertThrows( + SomeCheckedException.class, + () -> propagateIfPossible(new SomeCheckedException(), SomeCheckedException.class)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class) - public void testPropagateIfPossible_OneDeclared_UndeclaredThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsUndeclaredChecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible(t, SomeCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the undeclared exception to have been chained inside another - try { - sample.oneDeclared(); - fail(); - } catch (SomeChainingException expected) { - } + public void testPropagateIfPossible_oneDeclared_checkedDifferent() throws SomeCheckedException { + propagateIfPossible(new SomeOtherCheckedException(), SomeCheckedException.class); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class, Class) - public void testPropagateIfPossible_TwoDeclared_NoneThrown() - throws SomeCheckedException, SomeOtherCheckedException { - Sample sample = - new Sample() { - @Override - public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { - try { - methodThatDoesntThrowAnything(); - } catch (Throwable t) { - Throwables.propagateIfPossible( - t, SomeCheckedException.class, SomeOtherCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect no exception to be thrown - sample.twoDeclared(); + public void testPropagateIfPossible_twoDeclared_unchecked() { + assertThrows( + SomeUncheckedException.class, + () -> + propagateIfPossible( + new SomeUncheckedException(), + SomeCheckedException.class, + SomeOtherCheckedException.class)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class, Class) - public void testPropagateIfPossible_TwoDeclared_UncheckedThrown() - throws SomeCheckedException, SomeOtherCheckedException { - Sample sample = - new Sample() { - @Override - public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { - try { - methodThatThrowsUnchecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible( - t, SomeCheckedException.class, SomeOtherCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the unchecked exception to propagate as-is - try { - sample.twoDeclared(); - fail(); - } catch (SomeUncheckedException expected) { - } + public void testPropagateIfPossible_twoDeclared_firstSame() { + assertThrows( + SomeCheckedException.class, + () -> + propagateIfPossible( + new SomeCheckedException(), + SomeCheckedException.class, + SomeOtherCheckedException.class)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class, Class) - public void testPropagateIfPossible_TwoDeclared_CheckedThrown() throws SomeOtherCheckedException { - Sample sample = - new Sample() { - @Override - public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { - try { - methodThatThrowsChecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible( - t, SomeCheckedException.class, SomeOtherCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the checked exception to propagate as-is - try { - sample.twoDeclared(); - fail(); - } catch (SomeCheckedException expected) { - } + public void testPropagateIfPossible_twoDeclared_secondSame() { + assertThrows( + SomeOtherCheckedException.class, + () -> + propagateIfPossible( + new SomeOtherCheckedException(), + SomeCheckedException.class, + SomeOtherCheckedException.class)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class, Class) - public void testPropagateIfPossible_TwoDeclared_OtherCheckedThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException { - try { - methodThatThrowsOtherChecked(); - } catch (Throwable t) { - Throwables.propagateIfPossible( - t, SomeCheckedException.class, SomeOtherCheckedException.class); - throw new SomeChainingException(t); - } - } - }; - - // Expect the checked exception to propagate as-is - try { - sample.twoDeclared(); - fail(); - } catch (SomeOtherCheckedException expected) { - } + public void testPropagateIfPossible_twoDeclared_neitherSame() + throws SomeCheckedException, SomeOtherCheckedException { + propagateIfPossible( + new YetAnotherCheckedException(), + SomeCheckedException.class, + SomeOtherCheckedException.class); } - public void testThrowIfUnchecked_null() throws SomeCheckedException { - try { - throwIfUnchecked(null); - fail(); - } catch (NullPointerException expected) { - } + // I guess it's technically a bug that ThrowIfUncheckedKnownUnchecked fires here. + @SuppressWarnings("ThrowIfUncheckedKnownUnchecked") + public void testThrowIfUnchecked_null() { + assertThrows(NullPointerException.class, () -> throwIfUnchecked(null)); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible - public void testPropageIfPossible_null() throws SomeCheckedException { - Throwables.propagateIfPossible(null); + // I guess it's technically a bug that ThrowIfUncheckedKnownUnchecked fires here. + @SuppressWarnings("ThrowIfUncheckedKnownUnchecked") + public void testPropageIfPossible_null() { + propagateIfPossible(null); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class) - public void testPropageIfPossible_OneDeclared_null() throws SomeCheckedException { - Throwables.propagateIfPossible(null, SomeCheckedException.class); + public void testPropageIfPossible_oneDeclared_null() throws SomeCheckedException { + propagateIfPossible(null, SomeCheckedException.class); } + @J2ktIncompatible @GwtIncompatible // propagateIfPossible(Throwable, Class, Class) - public void testPropageIfPossible_TwoDeclared_null() throws SomeCheckedException { - Throwables.propagateIfPossible(null, SomeCheckedException.class, SomeUncheckedException.class); - } - - @GwtIncompatible // propagate - public void testPropagate_NoneDeclared_NoneThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatDoesntThrowAnything(); - } catch (Throwable t) { - throw Throwables.propagate(t); - } - } - }; - - // Expect no exception to be thrown - sample.noneDeclared(); + public void testPropageIfPossible_twoDeclared_null() + throws SomeCheckedException, SomeOtherCheckedException { + propagateIfPossible(null, SomeCheckedException.class, SomeOtherCheckedException.class); } + @J2ktIncompatible @GwtIncompatible // propagate - public void testPropagate_NoneDeclared_UncheckedThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatThrowsUnchecked(); - } catch (Throwable t) { - throw Throwables.propagate(t); - } - } - }; - - // Expect the unchecked exception to propagate as-is - try { - sample.noneDeclared(); - fail(); - } catch (SomeUncheckedException expected) { - } + public void testPropagate_noneDeclared_unchecked() { + assertThrows(SomeUncheckedException.class, () -> propagate(new SomeUncheckedException())); } + @J2ktIncompatible @GwtIncompatible // propagate - public void testPropagate_NoneDeclared_ErrorThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatThrowsError(); - } catch (Throwable t) { - throw Throwables.propagate(t); - } - } - }; - - // Expect the error to propagate as-is - try { - sample.noneDeclared(); - fail(); - } catch (SomeError expected) { - } + public void testPropagate_noneDeclared_error() { + assertThrows(SomeError.class, () -> propagate(new SomeError())); } + @J2ktIncompatible @GwtIncompatible // propagate - public void testPropagate_NoneDeclared_CheckedThrown() { - Sample sample = - new Sample() { - @Override - public void noneDeclared() { - try { - methodThatThrowsChecked(); - } catch (Throwable t) { - throw Throwables.propagate(t); - } - } - }; - - // Expect the undeclared exception to have been chained inside another - try { - sample.noneDeclared(); - fail(); - } catch (RuntimeException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(SomeCheckedException.class); - } + public void testPropagate_noneDeclared_checked() { + RuntimeException expected = + assertThrows(RuntimeException.class, () -> propagate(new SomeCheckedException())); + assertThat(expected).hasCauseThat().isInstanceOf(SomeCheckedException.class); } @GwtIncompatible // throwIfInstanceOf - public void testThrowIfInstanceOf_Unchecked() throws SomeCheckedException { + public void testThrowIfInstanceOf_unchecked() throws SomeCheckedException { throwIfInstanceOf(new SomeUncheckedException(), SomeCheckedException.class); } @GwtIncompatible // throwIfInstanceOf - public void testThrowIfInstanceOf_CheckedDifferent() throws SomeCheckedException { + public void testThrowIfInstanceOf_checkedDifferent() throws SomeCheckedException { throwIfInstanceOf(new SomeOtherCheckedException(), SomeCheckedException.class); } @GwtIncompatible // throwIfInstanceOf - public void testThrowIfInstanceOf_CheckedSame() { - try { - throwIfInstanceOf(new SomeCheckedException(), SomeCheckedException.class); - fail(); - } catch (SomeCheckedException expected) { - } - } - - @GwtIncompatible // throwIfInstanceOf - public void testThrowIfInstanceOf_CheckedSubclass() { - try { - throwIfInstanceOf(new SomeCheckedException() {}, SomeCheckedException.class); - fail(); - } catch (SomeCheckedException expected) { - } + public void testThrowIfInstanceOf_checkedSame() { + assertThrows( + SomeCheckedException.class, + () -> throwIfInstanceOf(new SomeCheckedException(), SomeCheckedException.class)); } @GwtIncompatible // throwIfInstanceOf - public void testPropagateIfInstanceOf_NoneThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatDoesntThrowAnything(); - } catch (Throwable t) { - Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); - throw Throwables.propagate(t); - } - } - }; - - // Expect no exception to be thrown - sample.oneDeclared(); + public void testThrowIfInstanceOf_checkedSubclass() { + assertThrows( + SomeCheckedException.class, + () -> throwIfInstanceOf(new SomeCheckedException() {}, SomeCheckedException.class)); } - @GwtIncompatible // throwIfInstanceOf - public void testPropagateIfInstanceOf_DeclaredThrown() { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsChecked(); - } catch (Throwable t) { - Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); - throw Throwables.propagate(t); - } - } - }; - - // Expect declared exception to be thrown as-is - try { - sample.oneDeclared(); - fail(); - } catch (SomeCheckedException expected) { - } + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf + public void testPropagateIfInstanceOf_checkedSame() { + assertThrows( + SomeCheckedException.class, + () -> propagateIfInstanceOf(new SomeCheckedException(), SomeCheckedException.class)); } - @GwtIncompatible // throwIfInstanceOf - public void testPropagateIfInstanceOf_UncheckedThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsUnchecked(); - } catch (Throwable t) { - Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); - throw Throwables.propagate(t); - } - } - }; - - // Expect unchecked exception to be thrown as-is - try { - sample.oneDeclared(); - fail(); - } catch (SomeUncheckedException expected) { - } + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf + public void testPropagateIfInstanceOf_unchecked() throws SomeCheckedException { + propagateIfInstanceOf(new SomeUncheckedException(), SomeCheckedException.class); } - @GwtIncompatible // throwIfInstanceOf - public void testPropagateIfInstanceOf_UndeclaredThrown() throws SomeCheckedException { - Sample sample = - new Sample() { - @Override - public void oneDeclared() throws SomeCheckedException { - try { - methodThatThrowsOtherChecked(); - } catch (Throwable t) { - Throwables.propagateIfInstanceOf(t, SomeCheckedException.class); - throw Throwables.propagate(t); - } - } - }; - - // Expect undeclared exception wrapped by RuntimeException to be thrown - try { - sample.oneDeclared(); - fail(); - } catch (RuntimeException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(SomeOtherCheckedException.class); - } + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf + public void testPropagateIfInstanceOf_checkedDifferent() throws SomeCheckedException { + propagateIfInstanceOf(new SomeOtherCheckedException(), SomeCheckedException.class); } @GwtIncompatible // throwIfInstanceOf - public void testThrowIfInstanceOf_null() throws SomeCheckedException { - try { - throwIfInstanceOf(null, SomeCheckedException.class); - fail(); - } catch (NullPointerException expected) { - } + public void testThrowIfInstanceOf_null() { + assertThrows( + NullPointerException.class, () -> throwIfInstanceOf(null, SomeCheckedException.class)); } - @GwtIncompatible // throwIfInstanceOf + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf public void testPropageIfInstanceOf_null() throws SomeCheckedException { - Throwables.propagateIfInstanceOf(null, SomeCheckedException.class); + propagateIfInstanceOf(null, SomeCheckedException.class); } - public void testGetRootCause_NoCause() { + public void testGetRootCause_noCause() { SomeCheckedException exception = new SomeCheckedException(); - assertSame(exception, Throwables.getRootCause(exception)); + assertSame(exception, getRootCause(exception)); } - public void testGetRootCause_SingleWrapped() { + public void testGetRootCause_singleWrapped() { SomeCheckedException cause = new SomeCheckedException(); SomeChainingException exception = new SomeChainingException(cause); - assertSame(cause, Throwables.getRootCause(exception)); + assertSame(cause, getRootCause(exception)); } - public void testGetRootCause_DoubleWrapped() { + public void testGetRootCause_doubleWrapped() { SomeCheckedException cause = new SomeCheckedException(); SomeChainingException exception = new SomeChainingException(new SomeChainingException(cause)); - assertSame(cause, Throwables.getRootCause(exception)); + assertSame(cause, getRootCause(exception)); } - public void testGetRootCause_Loop() { + public void testGetRootCause_loop() { Exception cause = new Exception(); Exception exception = new Exception(cause); cause.initCause(exception); - try { - Throwables.getRootCause(cause); - fail("Should have throw IAE"); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(cause); - } - } - - private static class SomeError extends Error {} - - private static class SomeCheckedException extends Exception {} - - private static class SomeOtherCheckedException extends Exception {} - - private static class SomeUncheckedException extends RuntimeException {} - - private static class SomeUndeclaredCheckedException extends Exception {} - - private static class SomeChainingException extends RuntimeException { - public SomeChainingException(Throwable cause) { - super(cause); - } - } - - static class Sample { - void noneDeclared() {} - - void oneDeclared() throws SomeCheckedException {} - - void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {} - } - - static void methodThatDoesntThrowAnything() {} - - static void methodThatThrowsError() { - throw new SomeError(); - } - - static void methodThatThrowsUnchecked() { - throw new SomeUncheckedException(); - } - - static void methodThatThrowsChecked() throws SomeCheckedException { - throw new SomeCheckedException(); - } - - static void methodThatThrowsOtherChecked() throws SomeOtherCheckedException { - throw new SomeOtherCheckedException(); - } - - static void methodThatThrowsUndeclaredChecked() throws SomeUndeclaredCheckedException { - throw new SomeUndeclaredCheckedException(); + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getRootCause(cause)); + assertThat(expected).hasCauseThat().isSameInstanceAs(cause); } + @J2ktIncompatible // Format does not match @GwtIncompatible // getStackTraceAsString(Throwable) public void testGetStackTraceAsString() { class StackTraceException extends Exception { @@ -637,8 +301,9 @@ class StackTraceException extends Exception { String firstLine = quote(e.getClass().getName() + ": " + e.getMessage()); String secondLine = "\\s*at " + ThrowablesTest.class.getName() + "\\..*"; - String moreLines = "(?:.*\n?)*"; - String expected = firstLine + "\n" + secondLine + "\n" + moreLines; + String moreLines = "(?:.*" + System.lineSeparator() + "?)*"; + String expected = + firstLine + System.lineSeparator() + secondLine + System.lineSeparator() + moreLines; assertThat(getStackTraceAsString(e)).matches(expected); } @@ -648,55 +313,43 @@ public void testGetCausalChain() { RuntimeException re = new RuntimeException(iae); IllegalStateException ex = new IllegalStateException(re); - assertEquals(asList(ex, re, iae, sue), Throwables.getCausalChain(ex)); - assertSame(sue, Iterables.getOnlyElement(Throwables.getCausalChain(sue))); + assertThat(getCausalChain(ex)).containsExactly(ex, re, iae, sue).inOrder(); + assertSame(sue, Iterables.getOnlyElement(getCausalChain(sue))); - List causes = Throwables.getCausalChain(ex); - try { - causes.add(new RuntimeException()); - fail("List should be unmodifiable"); - } catch (UnsupportedOperationException expected) { - } + List causes = getCausalChain(ex); + assertThrows(UnsupportedOperationException.class, () -> causes.add(new RuntimeException())); } public void testGetCasualChainNull() { - try { - Throwables.getCausalChain(null); - fail("Should have throw NPE"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> getCausalChain(null)); } public void testGetCasualChainLoop() { Exception cause = new Exception(); Exception exception = new Exception(cause); cause.initCause(exception); - try { - Throwables.getCausalChain(cause); - fail("Should have throw IAE"); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(cause); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getCausalChain(cause)); + assertThat(expected).hasCauseThat().isSameInstanceAs(cause); } - @GwtIncompatible // Throwables.getCauseAs(Throwable, Class) + @GwtIncompatible // getCauseAs(Throwable, Class) public void testGetCauseAs() { SomeCheckedException cause = new SomeCheckedException(); SomeChainingException thrown = new SomeChainingException(cause); assertThat(thrown).hasCauseThat().isSameInstanceAs(cause); - assertThat(Throwables.getCauseAs(thrown, SomeCheckedException.class)).isSameInstanceAs(cause); - assertThat(Throwables.getCauseAs(thrown, Exception.class)).isSameInstanceAs(cause); - - try { - Throwables.getCauseAs(thrown, IllegalStateException.class); - fail("Should have thrown CCE"); - } catch (ClassCastException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(thrown); - } + assertThat(getCauseAs(thrown, SomeCheckedException.class)).isSameInstanceAs(cause); + assertThat(getCauseAs(thrown, Exception.class)).isSameInstanceAs(cause); + + ClassCastException expected = + assertThrows( + ClassCastException.class, () -> getCauseAs(thrown, IllegalStateException.class)); + assertThat(expected).hasCauseThat().isSameInstanceAs(thrown); } @AndroidIncompatible // No getJavaLangAccess in Android (at least not in the version we use). + @J2ktIncompatible @GwtIncompatible // lazyStackTraceIsLazy() public void testLazyStackTraceWorksInProd() { // TODO(b/64442212): Remove this guard once lazyStackTrace() works in Java 9+. @@ -708,6 +361,7 @@ public void testLazyStackTraceWorksInProd() { assertTrue(lazyStackTraceIsLazy()); } + @J2ktIncompatible @GwtIncompatible // lazyStackTrace(Throwable) public void testLazyStackTrace() { Exception e = new Exception(); @@ -715,11 +369,7 @@ public void testLazyStackTrace() { assertThat(lazyStackTrace(e)).containsExactly((Object[]) originalStackTrace).inOrder(); - try { - lazyStackTrace(e).set(0, null); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> lazyStackTrace(e).set(0, null)); // Now we test a property that holds only for the lazy implementation. @@ -731,24 +381,7 @@ public void testLazyStackTrace() { assertThat(lazyStackTrace(e)).containsExactly((Object[]) originalStackTrace).inOrder(); } - @GwtIncompatible // lazyStackTrace - private void doTestLazyStackTraceFallback() { - assertFalse(lazyStackTraceIsLazy()); - - Exception e = new Exception(); - - assertThat(lazyStackTrace(e)).containsExactly((Object[]) e.getStackTrace()).inOrder(); - - try { - lazyStackTrace(e).set(0, null); - fail(); - } catch (UnsupportedOperationException expected) { - } - - e.setStackTrace(new StackTraceElement[0]); - assertThat(lazyStackTrace(e)).isEmpty(); - } - + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { new NullPointerTester().testAllPublicStaticMethods(Throwables.class); diff --git a/android/guava-tests/test/com/google/common/base/ToStringHelperTest.java b/android/guava-tests/test/com/google/common/base/ToStringHelperTest.java index db15f2ef811c..7129a7011d56 100644 --- a/android/guava-tests/test/com/google/common/base/ToStringHelperTest.java +++ b/android/guava-tests/test/com/google/common/base/ToStringHelperTest.java @@ -16,12 +16,20 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap; +import java.nio.CharBuffer; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link MoreObjects#toStringHelper(Object)}. @@ -29,6 +37,7 @@ * @author Jason Lee */ @GwtCompatible +@NullUnmarked public class ToStringHelperTest extends TestCase { @GwtIncompatible // Class names are obfuscated in GWT @@ -156,7 +165,7 @@ public void testToString_oneField() { @GwtIncompatible // Class names are obfuscated in GWT public void testToString_oneIntegerField() { String toTest = - MoreObjects.toStringHelper(new TestClass()).add("field1", new Integer(42)).toString(); + MoreObjects.toStringHelper(new TestClass()).add("field1", Integer.valueOf(42)).toString(); assertEquals("TestClass{field1=42}", toTest); } @@ -174,7 +183,7 @@ public void testToStringLenient_oneField() { public void testToStringLenient_oneIntegerField() { String toTest = - MoreObjects.toStringHelper(new TestClass()).add("field1", new Integer(42)).toString(); + MoreObjects.toStringHelper(new TestClass()).add("field1", Integer.valueOf(42)).toString(); assertTrue(toTest, toTest.matches(".*\\{field1\\=42\\}")); } @@ -186,7 +195,6 @@ public void testToStringLenient_nullInteger() { @GwtIncompatible // Class names are obfuscated in GWT public void testToString_complexFields() { - Map map = ImmutableMap.builder().put("abc", 1).put("def", 2).put("ghi", 3).build(); String toTest = @@ -203,7 +211,6 @@ public void testToString_complexFields() { } public void testToStringLenient_complexFields() { - Map map = ImmutableMap.builder().put("abc", 1).put("def", 2).put("ghi", 3).build(); String toTest = @@ -223,11 +230,7 @@ public void testToStringLenient_complexFields() { public void testToString_addWithNullName() { MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(new TestClass()); - try { - helper.add(null, "Hello"); - fail("No exception was thrown."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> helper.add(null, "Hello")); } @GwtIncompatible // Class names are obfuscated in GWT @@ -243,7 +246,7 @@ public void testToStringLenient_addWithNullValue() { } @GwtIncompatible // Class names are obfuscated in GWT - public void testToString_ToStringTwice() { + public void testToString_toStringTwice() { MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(new TestClass()) .add("field1", 1) @@ -320,6 +323,13 @@ public void testToStringOmitNullValues_oneField() { assertEquals("TestClass{}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_oneField() { + String toTest = + MoreObjects.toStringHelper(new TestClass()).omitEmptyValues().add("field1", "").toString(); + assertEquals("TestClass{}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_manyFieldsFirstNull() { String toTest = @@ -332,6 +342,18 @@ public void testToStringOmitNullValues_manyFieldsFirstNull() { assertEquals("TestClass{field2=Googley, field3=World}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_manyFieldsFirstEmpty() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .add("field1", "") + .add("field2", "Googley") + .add("field3", "World") + .toString(); + assertEquals("TestClass{field2=Googley, field3=World}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_manyFieldsOmitAfterNull() { String toTest = @@ -344,6 +366,18 @@ public void testToStringOmitNullValues_manyFieldsOmitAfterNull() { assertEquals("TestClass{field2=Googley, field3=World}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_manyFieldsOmitAfterEmpty() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .add("field1", "") + .add("field2", "Googley") + .add("field3", "World") + .omitEmptyValues() + .toString(); + assertEquals("TestClass{field2=Googley, field3=World}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_manyFieldsLastNull() { String toTest = @@ -356,6 +390,25 @@ public void testToStringOmitNullValues_manyFieldsLastNull() { assertEquals("TestClass{field1=Hello, field2=Googley}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_manyFieldsLastEmpty() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .add("field1", "Hello") + .add("field2", "Googley") + .add("field3", "") + .toString(); + assertEquals("TestClass{field1=Hello, field2=Googley}", toTest); + } + + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitNullValues_oneValue() { + String toTest = + MoreObjects.toStringHelper(new TestClass()).omitEmptyValues().addValue("").toString(); + assertEquals("TestClass{}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitEmptyValues_oneValue() { String toTest = @@ -375,6 +428,18 @@ public void testToStringOmitNullValues_manyValuesFirstNull() { assertEquals("TestClass{Googley, World}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_manyValuesFirstEmpty() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .addValue("") + .addValue("Googley") + .addValue("World") + .toString(); + assertEquals("TestClass{Googley, World}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_manyValuesLastNull() { String toTest = @@ -387,6 +452,18 @@ public void testToStringOmitNullValues_manyValuesLastNull() { assertEquals("TestClass{Hello, Googley}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_manyValuesLastEmpty() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .addValue("Hello") + .addValue("Googley") + .addValue("") + .toString(); + assertEquals("TestClass{Hello, Googley}", toTest); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_differentOrder() { String expected = "TestClass{field1=Hello, field2=Googley, field3=World}"; @@ -408,6 +485,27 @@ public void testToStringOmitNullValues_differentOrder() { assertEquals(expected, toTest2); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_differentOrder() { + String expected = "TestClass{field1=Hello, field2=Googley, field3=World}"; + String toTest1 = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .add("field1", "Hello") + .add("field2", "Googley") + .add("field3", "World") + .toString(); + String toTest2 = + MoreObjects.toStringHelper(new TestClass()) + .add("field1", "Hello") + .add("field2", "Googley") + .omitEmptyValues() + .add("field3", "World") + .toString(); + assertEquals(expected, toTest1); + assertEquals(expected, toTest2); + } + @GwtIncompatible // Class names are obfuscated in GWT public void testToStringOmitNullValues_canBeCalledManyTimes() { String toTest = @@ -423,6 +521,83 @@ public void testToStringOmitNullValues_canBeCalledManyTimes() { assertEquals("TestClass{field1=Hello, field2=Googley, field3=World}", toTest); } + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_canBeCalledManyTimes() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .omitEmptyValues() + .add("field1", "Hello") + .omitEmptyValues() + .add("field2", "Googley") + .omitEmptyValues() + .add("field3", "World") + .toString(); + assertEquals("TestClass{field1=Hello, field2=Googley, field3=World}", toTest); + } + + @GwtIncompatible // Class names are obfuscated in GWT + public void testToStringOmitEmptyValues_allEmptyTypes() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + // CharSequences + .add("field1", "") + .add("field2", new StringBuilder()) + // nio CharBuffer (implements CharSequence) is tested separately below + // Collections and Maps + .add("field11", Arrays.asList("Hello")) + .add("field12", new ArrayList<>()) + .add("field13", new HashMap<>()) + // Optionals + .add("field26", Optional.of("World")) + .add("field27", Optional.absent()) + // Arrays + .add("field31", new Object[] {"!!!"}) + .add("field32", new boolean[0]) + .add("field33", new byte[0]) + .add("field34", new char[0]) + .add("field35", new short[0]) + .add("field36", new int[0]) + .add("field37", new long[0]) + .add("field38", new float[0]) + .add("field39", new double[0]) + .add("field40", new Object[0]) + .toString(); + assertEquals("TestClass{field11=[Hello], field26=Optional.of(World), field31=[!!!]}", toTest); + } + + @J2ktIncompatible // J2kt CharBuffer does not implement CharSequence so not recognized as empty + @GwtIncompatible // CharBuffer not available + public void testToStringOmitEmptyValues_charBuffer() { + String toTest = + MoreObjects.toStringHelper(new TestClass()) + .omitEmptyValues() + .add("field1", "Hello") + .add("field2", CharBuffer.allocate(0)) + .toString(); + assertEquals("TestClass{field1=Hello}", toTest); + } + + public void testToStringHelperWithArrays() { + String[] strings = {"hello", "world"}; + int[] ints = {2, 42}; + Object[] objects = {"obj"}; + @Nullable String[] arrayWithNull = new @Nullable String[] {null}; + Object[] empty = {}; + String toTest = + MoreObjects.toStringHelper("TSH") + .add("strings", strings) + .add("ints", ints) + .add("objects", objects) + .add("arrayWithNull", arrayWithNull) + .add("empty", empty) + .toString(); + assertEquals( + "TSH{strings=[hello, world], ints=[2, 42], objects=[obj], arrayWithNull=[null], empty=[]}", + toTest); + } + /** Test class for testing formatting of inner classes. */ private static class TestClass {} } diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java b/android/guava-tests/test/com/google/common/base/UnannotatedJavaClass.java similarity index 62% rename from guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java rename to android/guava-tests/test/com/google/common/base/UnannotatedJavaClass.java index 5b89401540ea..0d7cc9cae8e8 100644 --- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java +++ b/android/guava-tests/test/com/google/common/base/UnannotatedJavaClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Guava Authors + * Copyright (C) 2023 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,16 @@ * limitations under the License. */ -package com.google.common.cache; +package com.google.common.base; -/** - * GWT emulated version of LongAdder. - * - * @author Charles Fry - */ -class LongAdder implements LongAddable { - - private long value; +import org.jspecify.annotations.NullUnmarked; - public void increment() { - value++; +/** Class containing an unannotated Java method for use from {@code OptionalExtensionsTest}. */ +@NullUnmarked +final class UnannotatedJavaClass { + static Object getNull() { + return null; } - public void add(long x) { - value += x; - } - - public long sum() { - return value; - } + private UnannotatedJavaClass() {} } diff --git a/android/guava-tests/test/com/google/common/base/Utf8Test.java b/android/guava-tests/test/com/google/common/base/Utf8Test.java index 049e8d2abf5a..72da351b5e24 100644 --- a/android/guava-tests/test/com/google/common/base/Utf8Test.java +++ b/android/guava-tests/test/com/google/common/base/Utf8Test.java @@ -23,6 +23,7 @@ import static java.lang.Character.MIN_HIGH_SURROGATE; import static java.lang.Character.MIN_LOW_SURROGATE; import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -31,6 +32,7 @@ import java.util.HashMap; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Utf8}. @@ -40,6 +42,7 @@ * @author Clément Roux */ @GwtCompatible(emulated = true) +@NullUnmarked public class Utf8Test extends TestCase { private static final ImmutableList ILL_FORMED_STRINGS; @@ -59,6 +62,8 @@ public class Utf8Test extends TestCase { ILL_FORMED_STRINGS = builder.build(); } + // We can't use Character.isSurrogate(c) because of GWT. + public void testEncodedLength_validStrings() { assertEquals(0, Utf8.encodedLength("")); assertEquals(11, Utf8.encodedLength("Hello world")); @@ -332,8 +337,8 @@ private static void testBytes(int numBytes, long expectedCount, long start, long } boolean isRoundTrippable = Utf8.isWellFormed(bytes); assertEquals(isRoundTrippable, Utf8.isWellFormed(bytes, 0, numBytes)); - String s = new String(bytes, Charsets.UTF_8); - byte[] bytesReencoded = s.getBytes(Charsets.UTF_8); + String s = new String(bytes, UTF_8); + byte[] bytesReencoded = s.getBytes(UTF_8); boolean bytesEqual = Arrays.equals(bytes, bytesReencoded); if (bytesEqual != isRoundTrippable) { diff --git a/android/guava-tests/test/com/google/common/base/VerifyTest.java b/android/guava-tests/test/com/google/common/base/VerifyTest.java index 37c6efc1a990..6a1e8a0a0785 100644 --- a/android/guava-tests/test/com/google/common/base/VerifyTest.java +++ b/android/guava-tests/test/com/google/common/base/VerifyTest.java @@ -14,28 +14,28 @@ package com.google.common.base; +import static com.google.common.base.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.base.Verify.verify; import static com.google.common.base.Verify.verifyNotNull; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit test for {@link com.google.common.base.Verify}. */ @GwtCompatible(emulated = true) +@NullUnmarked public class VerifyTest extends TestCase { public void testVerify_simple_success() { verify(true); } public void testVerify_simple_failure() { - try { - verify(false); - fail(); - } catch (VerifyException expected) { - } + assertThrows(VerifyException.class, () -> verify(false)); } public void testVerify_simpleMessage_success() { @@ -43,12 +43,8 @@ public void testVerify_simpleMessage_success() { } public void testVerify_simpleMessage_failure() { - try { - verify(false, "message"); - fail(); - } catch (VerifyException expected) { - assertThat(expected).hasMessageThat().isEqualTo("message"); - } + VerifyException expected = assertThrows(VerifyException.class, () -> verify(false, "message")); + assertThat(expected).hasMessageThat().isEqualTo("message"); } public void testVerify_complexMessage_success() { @@ -56,12 +52,8 @@ public void testVerify_complexMessage_success() { } public void testVerify_complexMessage_failure() { - try { - verify(false, FORMAT, 5); - fail(); - } catch (VerifyException expected) { - checkMessage(expected); - } + VerifyException expected = assertThrows(VerifyException.class, () -> verify(false, FORMAT, 5)); + checkMessage(expected); } private static final String NON_NULL_STRING = "foo"; @@ -72,11 +64,7 @@ public void testVerifyNotNull_simple_success() { } public void testVerifyNotNull_simple_failure() { - try { - verifyNotNull(null); - fail(); - } catch (VerifyException expected) { - } + assertThrows(VerifyException.class, () -> verifyNotNull(null)); } public void testVerifyNotNull_complexMessage_success() { @@ -85,14 +73,12 @@ public void testVerifyNotNull_complexMessage_success() { } public void testVerifyNotNull_simpleMessage_failure() { - try { - verifyNotNull(null, FORMAT, 5); - fail(); - } catch (VerifyException expected) { - checkMessage(expected); - } + VerifyException expected = + assertThrows(VerifyException.class, () -> verifyNotNull(null, FORMAT, 5)); + checkMessage(expected); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { // Don't bother testing: Verify is like Preconditions. See the discussion on that class. diff --git a/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java b/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java index 13ef33db280d..ed42350b30a7 100644 --- a/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/AbstractCacheTest.java @@ -16,6 +16,8 @@ package com.google.common.cache; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.cache.AbstractCache.SimpleStatsCounter; import com.google.common.cache.AbstractCache.StatsCounter; import com.google.common.collect.ImmutableList; @@ -24,12 +26,15 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link AbstractCache}. * * @author Charles Fry */ +@NullUnmarked public class AbstractCacheTest extends TestCase { public void testGetIfPresent() { @@ -37,7 +42,7 @@ public void testGetIfPresent() { Cache cache = new AbstractCache() { @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return valueRef.get(); } }; @@ -53,7 +58,7 @@ public void testGetAllPresent_empty() { Cache cache = new AbstractCache() { @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return null; } }; @@ -67,7 +72,7 @@ public void testGetAllPresent_cached() { Cache cache = new AbstractCache() { @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return cachedKey.equals(key) ? cachedValue : null; } }; @@ -102,14 +107,14 @@ public void testEmptySimpleStats() { CacheStats stats = counter.snapshot(); assertEquals(0, stats.requestCount()); assertEquals(0, stats.hitCount()); - assertEquals(1.0, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(1.0); assertEquals(0, stats.missCount()); - assertEquals(0.0, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(0.0); assertEquals(0, stats.loadSuccessCount()); assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.loadCount()); assertEquals(0, stats.totalLoadTime()); - assertEquals(0.0, stats.averageLoadPenalty()); + assertThat(stats.averageLoadPenalty()).isEqualTo(0.0); assertEquals(0, stats.evictionCount()); } @@ -134,15 +139,15 @@ public void testSingleSimpleStats() { int requestCount = 11 + 23; assertEquals(requestCount, stats.requestCount()); assertEquals(11, stats.hitCount()); - assertEquals(11.0 / requestCount, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(11.0 / requestCount); int missCount = 23; assertEquals(missCount, stats.missCount()); - assertEquals(((double) missCount) / requestCount, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(((double) missCount) / requestCount); assertEquals(13, stats.loadSuccessCount()); assertEquals(17, stats.loadExceptionCount()); assertEquals(13 + 17, stats.loadCount()); assertEquals(214, stats.totalLoadTime()); - assertEquals(214.0 / (13 + 17), stats.averageLoadPenalty()); + assertThat(stats.averageLoadPenalty()).isEqualTo(214.0 / (13 + 17)); assertEquals(27, stats.evictionCount()); } diff --git a/android/guava-tests/test/com/google/common/cache/AbstractLoadingCacheTest.java b/android/guava-tests/test/com/google/common/cache/AbstractLoadingCacheTest.java index c2ddef7b1522..8d1c8a5ed19f 100644 --- a/android/guava-tests/test/com/google/common/cache/AbstractLoadingCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/AbstractLoadingCacheTest.java @@ -17,18 +17,22 @@ package com.google.common.cache; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.util.concurrent.ExecutionError; import com.google.common.util.concurrent.UncheckedExecutionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link AbstractLoadingCache}. * * @author Charles Fry */ +@NullUnmarked public class AbstractLoadingCacheTest extends TestCase { public void testGetUnchecked_checked() { @@ -46,17 +50,14 @@ public Object get(Object key) throws ExecutionException { } @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return valueRef.get(); } }; - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(cause); - } + UncheckedExecutionException expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isEqualTo(cause); Object newValue = new Object(); valueRef.set(newValue); @@ -78,17 +79,14 @@ public Object get(Object key) throws ExecutionException { } @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return valueRef.get(); } }; - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(cause); - } + UncheckedExecutionException expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isEqualTo(cause); Object newValue = new Object(); valueRef.set(newValue); @@ -110,17 +108,14 @@ public Object get(Object key) throws ExecutionException { } @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return valueRef.get(); } }; - try { - cache.getUnchecked(new Object()); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isEqualTo(cause); - } + ExecutionError expected = + assertThrows(ExecutionError.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isEqualTo(cause); Object newValue = new Object(); valueRef.set(newValue); @@ -142,17 +137,14 @@ public Object get(Object key) throws ExecutionException { } @Override - public Object getIfPresent(Object key) { + public @Nullable Object getIfPresent(Object key) { return valueRef.get(); } }; - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(cause); - } + UncheckedExecutionException expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isEqualTo(cause); Object newValue = new Object(); valueRef.set(newValue); diff --git a/android/guava-tests/test/com/google/common/cache/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/cache/AndroidIncompatible.java index 135f524f6306..2c520cb6e610 100644 --- a/android/guava-tests/test/com/google/common/cache/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/cache/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/cache/CacheBuilderFactory.java b/android/guava-tests/test/com/google/common/cache/CacheBuilderFactory.java index 948b4b560467..de7f37998aec 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheBuilderFactory.java +++ b/android/guava-tests/test/com/google/common/cache/CacheBuilderFactory.java @@ -23,10 +23,12 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Helper class for creating {@link CacheBuilder} instances with all combinations of several sets of @@ -34,6 +36,7 @@ * * @author mike nonemacher */ +@NullUnmarked class CacheBuilderFactory { // Default values contain only 'null', which means don't call the CacheBuilder method (just give // the CacheBuilder default). @@ -46,49 +49,56 @@ class CacheBuilderFactory { private Set keyStrengths = Sets.newHashSet((Strength) null); private Set valueStrengths = Sets.newHashSet((Strength) null); + @CanIgnoreReturnValue CacheBuilderFactory withConcurrencyLevels(Set concurrencyLevels) { this.concurrencyLevels = Sets.newLinkedHashSet(concurrencyLevels); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withInitialCapacities(Set initialCapacities) { this.initialCapacities = Sets.newLinkedHashSet(initialCapacities); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withMaximumSizes(Set maximumSizes) { this.maximumSizes = Sets.newLinkedHashSet(maximumSizes); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withExpireAfterWrites(Set durations) { this.expireAfterWrites = Sets.newLinkedHashSet(durations); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withExpireAfterAccesses(Set durations) { this.expireAfterAccesses = Sets.newLinkedHashSet(durations); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withRefreshes(Set durations) { this.refreshes = Sets.newLinkedHashSet(durations); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withKeyStrengths(Set keyStrengths) { this.keyStrengths = Sets.newLinkedHashSet(keyStrengths); Preconditions.checkArgument(!this.keyStrengths.contains(Strength.SOFT)); return this; } + @CanIgnoreReturnValue CacheBuilderFactory withValueStrengths(Set valueStrengths) { this.valueStrengths = Sets.newLinkedHashSet(valueStrengths); return this; } Iterable> buildAllPermutations() { - @SuppressWarnings("unchecked") Iterable> combinations = buildCartesianProduct( concurrencyLevels, @@ -120,15 +130,15 @@ public CacheBuilder apply(List combination) { private static final Function> NULLABLE_TO_OPTIONAL = new Function>() { @Override - public Optional apply(@CheckForNull Object obj) { + public Optional apply(@Nullable Object obj) { return Optional.fromNullable(obj); } }; - private static final Function, Object> OPTIONAL_TO_NULLABLE = - new Function, Object>() { + private static final Function, @Nullable Object> OPTIONAL_TO_NULLABLE = + new Function, @Nullable Object>() { @Override - public Object apply(Optional optional) { + public @Nullable Object apply(Optional optional) { return optional.orNull(); } }; @@ -158,14 +168,14 @@ public List apply(List> objs) { } private CacheBuilder createCacheBuilder( - Integer concurrencyLevel, - Integer initialCapacity, - Integer maximumSize, - DurationSpec expireAfterWrite, - DurationSpec expireAfterAccess, - DurationSpec refresh, - Strength keyStrength, - Strength valueStrength) { + @Nullable Integer concurrencyLevel, + @Nullable Integer initialCapacity, + @Nullable Integer maximumSize, + @Nullable DurationSpec expireAfterWrite, + @Nullable DurationSpec expireAfterAccess, + @Nullable DurationSpec refresh, + @Nullable Strength keyStrength, + @Nullable Strength valueStrength) { CacheBuilder builder = CacheBuilder.newBuilder(); if (concurrencyLevel != null) { @@ -214,7 +224,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof DurationSpec) { DurationSpec that = (DurationSpec) o; return unit.toNanos(duration) == that.unit.toNanos(that.duration); diff --git a/android/guava-tests/test/com/google/common/cache/CacheBuilderGwtTest.java b/android/guava-tests/test/com/google/common/cache/CacheBuilderGwtTest.java index 2ae81443b03e..087cbb48980c 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheBuilderGwtTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheBuilderGwtTest.java @@ -16,6 +16,8 @@ package com.google.common.cache; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -29,8 +31,8 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test suite for {@link CacheBuilder}. TODO(cpovirk): merge into CacheBuilderTest? @@ -38,6 +40,7 @@ * @author Jon Donovan */ @GwtCompatible +@NullUnmarked public class CacheBuilderGwtTest extends TestCase { private FakeTicker fakeTicker; @@ -130,39 +133,33 @@ public Integer load(Integer key) throws Exception { public void testExpireAfterAccess() { final Cache cache = - CacheBuilder.newBuilder() - .expireAfterAccess(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterAccess(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(0, 10); cache.put(2, 30); - fakeTicker.advance(999, TimeUnit.MILLISECONDS); + fakeTicker.advance(999, MILLISECONDS); assertEquals(Integer.valueOf(30), cache.getIfPresent(2)); - fakeTicker.advance(1, TimeUnit.MILLISECONDS); + fakeTicker.advance(1, MILLISECONDS); assertEquals(Integer.valueOf(30), cache.getIfPresent(2)); - fakeTicker.advance(1000, TimeUnit.MILLISECONDS); + fakeTicker.advance(1000, MILLISECONDS); assertEquals(null, cache.getIfPresent(0)); } public void testExpireAfterWrite() { final Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 100); cache.put(20, 200); cache.put(4, 2); - fakeTicker.advance(999, TimeUnit.MILLISECONDS); + fakeTicker.advance(999, MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); assertEquals(Integer.valueOf(2), cache.getIfPresent(4)); - fakeTicker.advance(2, TimeUnit.MILLISECONDS); + fakeTicker.advance(2, MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); assertEquals(null, cache.getIfPresent(20)); assertEquals(null, cache.getIfPresent(4)); @@ -170,15 +167,15 @@ public void testExpireAfterWrite() { cache.put(10, 20); assertEquals(Integer.valueOf(20), cache.getIfPresent(10)); - fakeTicker.advance(1000, TimeUnit.MILLISECONDS); + fakeTicker.advance(1000, MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); } public void testExpireAfterWriteAndAccess() { final Cache cache = CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .expireAfterAccess(500, TimeUnit.MILLISECONDS) + .expireAfterWrite(1000, MILLISECONDS) + .expireAfterAccess(500, MILLISECONDS) .ticker(fakeTicker) .build(); @@ -186,23 +183,23 @@ public void testExpireAfterWriteAndAccess() { cache.put(20, 200); cache.put(4, 2); - fakeTicker.advance(499, TimeUnit.MILLISECONDS); + fakeTicker.advance(499, MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); - fakeTicker.advance(2, TimeUnit.MILLISECONDS); + fakeTicker.advance(2, MILLISECONDS); assertEquals(Integer.valueOf(100), cache.getIfPresent(10)); assertEquals(Integer.valueOf(200), cache.getIfPresent(20)); assertEquals(null, cache.getIfPresent(4)); - fakeTicker.advance(499, TimeUnit.MILLISECONDS); + fakeTicker.advance(499, MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); assertEquals(null, cache.getIfPresent(20)); cache.put(10, 20); assertEquals(Integer.valueOf(20), cache.getIfPresent(10)); - fakeTicker.advance(500, TimeUnit.MILLISECONDS); + fakeTicker.advance(500, MILLISECONDS); assertEquals(null, cache.getIfPresent(10)); } @@ -276,7 +273,7 @@ public void onRemoval(RemovalNotification notification) { Cache cache = CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) + .expireAfterWrite(1000, MILLISECONDS) .removalListener(countingListener) .ticker(fakeTicker) .maximumSize(2) @@ -296,7 +293,7 @@ public void onRemoval(RemovalNotification notification) { cache.put(56, 4); // Expire the two present elements. - fakeTicker.advance(1001, TimeUnit.MILLISECONDS); + fakeTicker.advance(1001, MILLISECONDS); cache.getIfPresent(23); cache.getIfPresent(56); @@ -371,17 +368,14 @@ public void testInvalidateAll() { public void testAsMap_containsValue() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(20000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(20000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(654, 2675); - fakeTicker.advance(10000, TimeUnit.MILLISECONDS); + fakeTicker.advance(10000, MILLISECONDS); cache.put(2456, 56); cache.put(2, 15); - fakeTicker.advance(10001, TimeUnit.MILLISECONDS); + fakeTicker.advance(10001, MILLISECONDS); assertTrue(cache.asMap().containsValue(15)); assertTrue(cache.asMap().containsValue(56)); @@ -390,17 +384,14 @@ public void testAsMap_containsValue() { public void testAsMap_containsKey() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(20000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(20000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(654, 2675); - fakeTicker.advance(10000, TimeUnit.MILLISECONDS); + fakeTicker.advance(10000, MILLISECONDS); cache.put(2456, 56); cache.put(2, 15); - fakeTicker.advance(10001, TimeUnit.MILLISECONDS); + fakeTicker.advance(10001, MILLISECONDS); assertTrue(cache.asMap().containsKey(2)); assertTrue(cache.asMap().containsKey(2456)); @@ -409,17 +400,14 @@ public void testAsMap_containsKey() { public void testAsMapValues_contains() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 20); - fakeTicker.advance(500, TimeUnit.MILLISECONDS); + fakeTicker.advance(500, MILLISECONDS); cache.put(20, 22); cache.put(5, 10); - fakeTicker.advance(501, TimeUnit.MILLISECONDS); + fakeTicker.advance(501, MILLISECONDS); assertTrue(cache.asMap().values().contains(22)); assertTrue(cache.asMap().values().contains(10)); @@ -428,17 +416,14 @@ public void testAsMapValues_contains() { public void testAsMapKeySet() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 20); - fakeTicker.advance(500, TimeUnit.MILLISECONDS); + fakeTicker.advance(500, MILLISECONDS); cache.put(20, 22); cache.put(5, 10); - fakeTicker.advance(501, TimeUnit.MILLISECONDS); + fakeTicker.advance(501, MILLISECONDS); Set foundKeys = new HashSet<>(cache.asMap().keySet()); @@ -447,17 +432,14 @@ public void testAsMapKeySet() { public void testAsMapKeySet_contains() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 20); - fakeTicker.advance(500, TimeUnit.MILLISECONDS); + fakeTicker.advance(500, MILLISECONDS); cache.put(20, 22); cache.put(5, 10); - fakeTicker.advance(501, TimeUnit.MILLISECONDS); + fakeTicker.advance(501, MILLISECONDS); assertTrue(cache.asMap().keySet().contains(20)); assertTrue(cache.asMap().keySet().contains(5)); @@ -466,17 +448,14 @@ public void testAsMapKeySet_contains() { public void testAsMapEntrySet() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 20); - fakeTicker.advance(500, TimeUnit.MILLISECONDS); + fakeTicker.advance(500, MILLISECONDS); cache.put(20, 22); cache.put(5, 10); - fakeTicker.advance(501, TimeUnit.MILLISECONDS); + fakeTicker.advance(501, MILLISECONDS); int sum = 0; for (Entry current : cache.asMap().entrySet()) { @@ -487,10 +466,7 @@ public void testAsMapEntrySet() { public void testAsMapValues_iteratorRemove() { Cache cache = - CacheBuilder.newBuilder() - .expireAfterWrite(1000, TimeUnit.MILLISECONDS) - .ticker(fakeTicker) - .build(); + CacheBuilder.newBuilder().expireAfterWrite(1000, MILLISECONDS).ticker(fakeTicker).build(); cache.put(10, 20); Iterator iterator = cache.asMap().values().iterator(); diff --git a/android/guava-tests/test/com/google/common/cache/CacheBuilderSpecTest.java b/android/guava-tests/test/com/google/common/cache/CacheBuilderSpecTest.java index 09f2eb6c2844..670275b08252 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheBuilderSpecTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheBuilderSpecTest.java @@ -18,12 +18,17 @@ import static com.google.common.cache.CacheBuilderSpec.parse; import static com.google.common.cache.TestingWeighers.constantWeigher; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Suppliers; import com.google.common.cache.LocalCache.Strength; import com.google.common.testing.EqualsTester; -import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests CacheBuilderSpec. TODO(user): tests of a few invalid input conditions, boundary @@ -31,6 +36,7 @@ * * @author Adam Winer */ +@NullUnmarked public class CacheBuilderSpecTest extends TestCase { public void testParse_empty() { CacheBuilderSpec spec = parse(""); @@ -60,11 +66,8 @@ public void testParse_initialCapacity() { } public void testParse_initialCapacityRepeated() { - try { - parse("initialCapacity=10, initialCapacity=20"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> parse("initialCapacity=10, initialCapacity=20")); } public void testParse_maximumSize() { @@ -81,11 +84,7 @@ public void testParse_maximumSize() { } public void testParse_maximumSizeRepeated() { - try { - parse("maximumSize=10, maximumSize=20"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("maximumSize=10, maximumSize=20")); } public void testParse_maximumWeight() { @@ -102,19 +101,11 @@ public void testParse_maximumWeight() { } public void testParse_maximumWeightRepeated() { - try { - parse("maximumWeight=10, maximumWeight=20"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("maximumWeight=10, maximumWeight=20")); } public void testParse_maximumSizeAndMaximumWeight() { - try { - parse("maximumSize=10, maximumWeight=20"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("maximumSize=10, maximumWeight=20")); } public void testParse_concurrencyLevel() { @@ -132,11 +123,8 @@ public void testParse_concurrencyLevel() { } public void testParse_concurrencyLevelRepeated() { - try { - parse("concurrencyLevel=10, concurrencyLevel=20"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> parse("concurrencyLevel=10, concurrencyLevel=20")); } public void testParse_weakKeys() { @@ -153,19 +141,11 @@ public void testParse_weakKeys() { } public void testParse_weakKeysCannotHaveValue() { - try { - parse("weakKeys=true"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("weakKeys=true")); } public void testParse_repeatedKeyStrength() { - try { - parse("weakKeys, weakKeys"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("weakKeys, weakKeys")); } public void testParse_softValues() { @@ -182,11 +162,7 @@ public void testParse_softValues() { } public void testParse_softValuesCannotHaveValue() { - try { - parse("softValues=true"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("softValues=true")); } public void testParse_weakValues() { @@ -203,37 +179,17 @@ public void testParse_weakValues() { } public void testParse_weakValuesCannotHaveValue() { - try { - parse("weakValues=true"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("weakValues=true")); } public void testParse_repeatedValueStrength() { - try { - parse("softValues, softValues"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } - - try { - parse("softValues, weakValues"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } - - try { - parse("weakValues, softValues"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } - - try { - parse("weakValues, weakValues"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("softValues, softValues")); + + assertThrows(IllegalArgumentException.class, () -> parse("softValues, weakValues")); + + assertThrows(IllegalArgumentException.class, () -> parse("weakValues, softValues")); + + assertThrows(IllegalArgumentException.class, () -> parse("weakValues, weakValues")); } public void testParse_writeExpirationDays() { @@ -244,43 +200,40 @@ public void testParse_writeExpirationDays() { assertNull(spec.concurrencyLevel); assertNull(spec.keyStrength); assertNull(spec.valueStrength); - assertEquals(TimeUnit.DAYS, spec.writeExpirationTimeUnit); + assertEquals(DAYS, spec.writeExpirationTimeUnit); assertEquals(10L, spec.writeExpirationDuration); assertNull(spec.accessExpirationTimeUnit); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterWrite(10L, TimeUnit.DAYS), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterWrite(10L, DAYS), CacheBuilder.from(spec)); } public void testParse_writeExpirationHours() { CacheBuilderSpec spec = parse("expireAfterWrite=150h"); - assertEquals(TimeUnit.HOURS, spec.writeExpirationTimeUnit); + assertEquals(HOURS, spec.writeExpirationTimeUnit); assertEquals(150L, spec.writeExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterWrite(150L, TimeUnit.HOURS), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterWrite(150L, HOURS), CacheBuilder.from(spec)); } public void testParse_writeExpirationMinutes() { CacheBuilderSpec spec = parse("expireAfterWrite=10m"); - assertEquals(TimeUnit.MINUTES, spec.writeExpirationTimeUnit); + assertEquals(MINUTES, spec.writeExpirationTimeUnit); assertEquals(10L, spec.writeExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterWrite(10L, TimeUnit.MINUTES), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterWrite(10L, MINUTES), CacheBuilder.from(spec)); } public void testParse_writeExpirationSeconds() { CacheBuilderSpec spec = parse("expireAfterWrite=10s"); - assertEquals(TimeUnit.SECONDS, spec.writeExpirationTimeUnit); + assertEquals(SECONDS, spec.writeExpirationTimeUnit); assertEquals(10L, spec.writeExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterWrite(10L, TimeUnit.SECONDS), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterWrite(10L, SECONDS), CacheBuilder.from(spec)); } public void testParse_writeExpirationRepeated() { - try { - parse("expireAfterWrite=10s,expireAfterWrite=10m"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> parse("expireAfterWrite=10s,expireAfterWrite=10m")); } public void testParse_accessExpirationDays() { @@ -292,44 +245,39 @@ public void testParse_accessExpirationDays() { assertNull(spec.keyStrength); assertNull(spec.valueStrength); assertNull(spec.writeExpirationTimeUnit); - assertEquals(TimeUnit.DAYS, spec.accessExpirationTimeUnit); + assertEquals(DAYS, spec.accessExpirationTimeUnit); assertEquals(10L, spec.accessExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterAccess(10L, TimeUnit.DAYS), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterAccess(10L, DAYS), CacheBuilder.from(spec)); } public void testParse_accessExpirationHours() { CacheBuilderSpec spec = parse("expireAfterAccess=150h"); - assertEquals(TimeUnit.HOURS, spec.accessExpirationTimeUnit); + assertEquals(HOURS, spec.accessExpirationTimeUnit); assertEquals(150L, spec.accessExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterAccess(150L, TimeUnit.HOURS), CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterAccess(150L, HOURS), CacheBuilder.from(spec)); } public void testParse_accessExpirationMinutes() { CacheBuilderSpec spec = parse("expireAfterAccess=10m"); - assertEquals(TimeUnit.MINUTES, spec.accessExpirationTimeUnit); + assertEquals(MINUTES, spec.accessExpirationTimeUnit); assertEquals(10L, spec.accessExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterAccess(10L, TimeUnit.MINUTES), - CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterAccess(10L, MINUTES), CacheBuilder.from(spec)); } public void testParse_accessExpirationSeconds() { CacheBuilderSpec spec = parse("expireAfterAccess=10s"); - assertEquals(TimeUnit.SECONDS, spec.accessExpirationTimeUnit); + assertEquals(SECONDS, spec.accessExpirationTimeUnit); assertEquals(10L, spec.accessExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder().expireAfterAccess(10L, TimeUnit.SECONDS), - CacheBuilder.from(spec)); + CacheBuilder.newBuilder().expireAfterAccess(10L, SECONDS), CacheBuilder.from(spec)); } public void testParse_accessExpirationRepeated() { - try { - parse("expireAfterAccess=10s,expireAfterAccess=10m"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> parse("expireAfterAccess=10s,expireAfterAccess=10m")); } public void testParse_recordStats() { @@ -339,31 +287,21 @@ public void testParse_recordStats() { } public void testParse_recordStatsValueSpecified() { - try { - parse("recordStats=True"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("recordStats=True")); } public void testParse_recordStatsRepeated() { - try { - parse("recordStats,recordStats"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("recordStats,recordStats")); } public void testParse_accessExpirationAndWriteExpiration() { CacheBuilderSpec spec = parse("expireAfterAccess=10s,expireAfterWrite=9m"); - assertEquals(TimeUnit.MINUTES, spec.writeExpirationTimeUnit); + assertEquals(MINUTES, spec.writeExpirationTimeUnit); assertEquals(9L, spec.writeExpirationDuration); - assertEquals(TimeUnit.SECONDS, spec.accessExpirationTimeUnit); + assertEquals(SECONDS, spec.accessExpirationTimeUnit); assertEquals(10L, spec.accessExpirationDuration); assertCacheBuilderEquivalence( - CacheBuilder.newBuilder() - .expireAfterAccess(10L, TimeUnit.SECONDS) - .expireAfterWrite(9L, TimeUnit.MINUTES), + CacheBuilder.newBuilder().expireAfterAccess(10L, SECONDS).expireAfterWrite(9L, MINUTES), CacheBuilder.from(spec)); } @@ -378,8 +316,8 @@ public void testParse_multipleKeys() { assertEquals(30, spec.concurrencyLevel.intValue()); assertEquals(Strength.WEAK, spec.keyStrength); assertEquals(Strength.WEAK, spec.valueStrength); - assertEquals(TimeUnit.HOURS, spec.writeExpirationTimeUnit); - assertEquals(TimeUnit.MINUTES, spec.accessExpirationTimeUnit); + assertEquals(HOURS, spec.writeExpirationTimeUnit); + assertEquals(MINUTES, spec.accessExpirationTimeUnit); assertEquals(1L, spec.writeExpirationDuration); assertEquals(10L, spec.accessExpirationDuration); CacheBuilder expected = @@ -389,8 +327,8 @@ public void testParse_multipleKeys() { .concurrencyLevel(30) .weakKeys() .weakValues() - .expireAfterAccess(10L, TimeUnit.MINUTES) - .expireAfterWrite(1L, TimeUnit.HOURS); + .expireAfterAccess(10L, MINUTES) + .expireAfterWrite(1L, HOURS); assertCacheBuilderEquivalence(expected, CacheBuilder.from(spec)); } @@ -405,7 +343,7 @@ public void testParse_whitespaceAllowed() { assertNull(spec.concurrencyLevel); assertEquals(Strength.WEAK, spec.keyStrength); assertEquals(Strength.SOFT, spec.valueStrength); - assertEquals(TimeUnit.SECONDS, spec.writeExpirationTimeUnit); + assertEquals(SECONDS, spec.writeExpirationTimeUnit); assertEquals(15L, spec.writeExpirationDuration); assertNull(spec.accessExpirationTimeUnit); CacheBuilder expected = @@ -414,36 +352,20 @@ public void testParse_whitespaceAllowed() { .maximumSize(20) .weakKeys() .softValues() - .expireAfterWrite(15L, TimeUnit.SECONDS); + .expireAfterWrite(15L, SECONDS); assertCacheBuilderEquivalence(expected, CacheBuilder.from(spec)); } public void testParse_unknownKey() { - try { - parse("foo=17"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("foo=17")); } public void testParse_extraCommaIsInvalid() { - try { - parse("weakKeys,"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("weakKeys,")); - try { - parse(",weakKeys"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse(",weakKeys")); - try { - parse("weakKeys,,softValues"); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> parse("weakKeys,,softValues")); } public void testEqualsAndHashCode() { @@ -477,11 +399,9 @@ public void testMaximumWeight_withWeigher() { @SuppressWarnings("ReturnValueIgnored") public void testMaximumWeight_withoutWeigher() { CacheBuilder builder = CacheBuilder.from(parse("maximumWeight=9000")); - try { - builder.build(CacheLoader.from(Suppliers.ofInstance(null))); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, + () -> builder.build(CacheLoader.from(Suppliers.ofInstance(null)))); } @SuppressWarnings("ReturnValueIgnored") @@ -521,7 +441,7 @@ public void testCacheBuilderFrom_string() { .concurrencyLevel(30) .weakKeys() .weakValues() - .expireAfterAccess(10L, TimeUnit.MINUTES); + .expireAfterAccess(10L, MINUTES); assertCacheBuilderEquivalence(expected, fromString); } diff --git a/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java b/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java index 1a9faeeaed2f..d7dfb3af6311 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheBuilderTest.java @@ -16,6 +16,7 @@ package com.google.common.cache; +import static com.google.common.cache.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.cache.TestingCacheLoaders.constantLoader; import static com.google.common.cache.TestingCacheLoaders.identityLoader; import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener; @@ -35,6 +36,8 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.Map; import java.util.Random; import java.util.Set; @@ -45,9 +48,13 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for CacheBuilder. */ @GwtCompatible(emulated = true) +// We are intentionally testing the TimeUnit overloads, too. +@SuppressWarnings("LongTimeUnit_ExpireAfterWrite_Seconds") +@NullUnmarked public class CacheBuilderTest extends TestCase { public void testNewBuilder() { @@ -62,21 +69,12 @@ public void testNewBuilder() { public void testInitialCapacity_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.initialCapacity(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.initialCapacity(-1)); } public void testInitialCapacity_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().initialCapacity(16); - try { - // even to the same value is not allowed - builder.initialCapacity(16); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.initialCapacity(16)); } @GwtIncompatible // CacheTesting @@ -112,21 +110,12 @@ public void testInitialCapacity_large() { public void testConcurrencyLevel_zero() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.concurrencyLevel(0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.concurrencyLevel(0)); } public void testConcurrencyLevel_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().concurrencyLevel(16); - try { - // even to the same value is not allowed - builder.concurrencyLevel(16); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.concurrencyLevel(16)); } @GwtIncompatible // CacheTesting @@ -144,31 +133,18 @@ public void testConcurrencyLevel_large() { public void testMaximumSize_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.maximumSize(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.maximumSize(-1)); } public void testMaximumSize_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().maximumSize(16); - try { - // even to the same value is not allowed - builder.maximumSize(16); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.maximumSize(16)); } @GwtIncompatible // maximumWeight public void testMaximumSize_andWeight() { CacheBuilder builder = CacheBuilder.newBuilder().maximumSize(16); - try { - builder.maximumWeight(16); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.maximumWeight(16)); } @GwtIncompatible // digs into internals of the non-GWT implementation @@ -182,107 +158,76 @@ public void testMaximumSize_largerThanInt() { @GwtIncompatible // maximumWeight public void testMaximumWeight_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.maximumWeight(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.maximumWeight(-1)); } @GwtIncompatible // maximumWeight public void testMaximumWeight_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().maximumWeight(16); - try { - // even to the same value is not allowed - builder.maximumWeight(16); - fail(); - } catch (IllegalStateException expected) { - } - try { - builder.maximumSize(16); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.maximumWeight(16)); + assertThrows(IllegalStateException.class, () -> builder.maximumSize(16)); } @GwtIncompatible // maximumWeight public void testMaximumWeight_withoutWeigher() { CacheBuilder builder = CacheBuilder.newBuilder().maximumWeight(1); - try { - builder.build(identityLoader()); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.build(identityLoader())); } @GwtIncompatible // weigher public void testWeigher_withoutMaximumWeight() { CacheBuilder builder = CacheBuilder.newBuilder().weigher(constantWeigher(42)); - try { - builder.build(identityLoader()); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.build(identityLoader())); } @GwtIncompatible // weigher public void testWeigher_withMaximumSize() { - try { - CacheBuilder.newBuilder().weigher(constantWeigher(42)).maximumSize(1); - fail(); - } catch (IllegalStateException expected) { - } - try { - CacheBuilder.newBuilder().maximumSize(1).weigher(constantWeigher(42)); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, + () -> CacheBuilder.newBuilder().weigher(constantWeigher(42)).maximumSize(1)); + assertThrows( + IllegalStateException.class, + () -> CacheBuilder.newBuilder().maximumSize(1).weigher(constantWeigher(42))); } @GwtIncompatible // weakKeys public void testKeyStrengthSetTwice() { CacheBuilder builder1 = CacheBuilder.newBuilder().weakKeys(); - try { - builder1.weakKeys(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder1.weakKeys()); } @GwtIncompatible // weakValues public void testValueStrengthSetTwice() { CacheBuilder builder1 = CacheBuilder.newBuilder().weakValues(); - try { - builder1.weakValues(); - fail(); - } catch (IllegalStateException expected) { - } - try { - builder1.softValues(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder1.weakValues()); + assertThrows(IllegalStateException.class, () -> builder1.softValues()); CacheBuilder builder2 = CacheBuilder.newBuilder().softValues(); - try { - builder2.softValues(); - fail(); - } catch (IllegalStateException expected) { - } - try { - builder2.weakValues(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder2.softValues()); + assertThrows(IllegalStateException.class, () -> builder2.weakValues()); + } + + @GwtIncompatible // Duration + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + public void testLargeDurationsAreOk() { + Duration threeHundredYears = Duration.ofDays(365 * 300); + CacheBuilder unused = + CacheBuilder.newBuilder() + .expireAfterWrite(threeHundredYears) + .expireAfterAccess(threeHundredYears) + .refreshAfterWrite(threeHundredYears); } public void testTimeToLive_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.expireAfterWrite(-1, SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.expireAfterWrite(-1, SECONDS)); + } + + @GwtIncompatible // Duration + public void testTimeToLive_negative_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows( + IllegalArgumentException.class, () -> builder.expireAfterWrite(Duration.ofSeconds(-1))); } @SuppressWarnings("ReturnValueIgnored") @@ -294,21 +239,26 @@ public void testTimeToLive_small() { public void testTimeToLive_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().expireAfterWrite(3600, SECONDS); - try { - // even to the same value is not allowed - builder.expireAfterWrite(3600, SECONDS); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.expireAfterWrite(3600, SECONDS)); + } + + @GwtIncompatible // Duration + public void testTimeToLive_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().expireAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterWrite(Duration.ofHours(1))); } public void testTimeToIdle_negative() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.expireAfterAccess(-1, SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.expireAfterAccess(-1, SECONDS)); + } + + @GwtIncompatible // Duration + public void testTimeToIdle_negative_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows( + IllegalArgumentException.class, () -> builder.expireAfterAccess(Duration.ofSeconds(-1))); } @SuppressWarnings("ReturnValueIgnored") @@ -320,65 +270,61 @@ public void testTimeToIdle_small() { public void testTimeToIdle_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().expireAfterAccess(3600, SECONDS); - try { - // even to the same value is not allowed - builder.expireAfterAccess(3600, SECONDS); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.expireAfterAccess(3600, SECONDS)); + } + + @GwtIncompatible // Duration + public void testTimeToIdle_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().expireAfterAccess(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.expireAfterAccess(Duration.ofHours(1))); } - @SuppressWarnings("ReturnValueIgnored") public void testTimeToIdleAndToLive() { - CacheBuilder.newBuilder() - .expireAfterWrite(1, NANOSECONDS) - .expireAfterAccess(1, NANOSECONDS) - .build(identityLoader()); + LoadingCache unused = + CacheBuilder.newBuilder() + .expireAfterWrite(1, NANOSECONDS) + .expireAfterAccess(1, NANOSECONDS) + .build(identityLoader()); // well, it didn't blow up. } @GwtIncompatible // refreshAfterWrite public void testRefresh_zero() { CacheBuilder builder = CacheBuilder.newBuilder(); - try { - builder.refreshAfterWrite(0, SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(0, SECONDS)); + } + + @GwtIncompatible // Duration + public void testRefresh_zero_duration() { + CacheBuilder builder = CacheBuilder.newBuilder(); + assertThrows(IllegalArgumentException.class, () -> builder.refreshAfterWrite(Duration.ZERO)); } @GwtIncompatible // refreshAfterWrite public void testRefresh_setTwice() { CacheBuilder builder = CacheBuilder.newBuilder().refreshAfterWrite(3600, SECONDS); - try { - // even to the same value is not allowed - builder.refreshAfterWrite(3600, SECONDS); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(3600, SECONDS)); + } + + @GwtIncompatible // Duration + public void testRefresh_setTwice_duration() { + CacheBuilder builder = + CacheBuilder.newBuilder().refreshAfterWrite(Duration.ofHours(1)); + assertThrows(IllegalStateException.class, () -> builder.refreshAfterWrite(Duration.ofHours(1))); } public void testTicker_setTwice() { Ticker testTicker = Ticker.systemTicker(); CacheBuilder builder = CacheBuilder.newBuilder().ticker(testTicker); - try { - // even to the same instance is not allowed - builder.ticker(testTicker); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.ticker(testTicker)); } public void testRemovalListener_setTwice() { RemovalListener testListener = nullRemovalListener(); CacheBuilder builder = CacheBuilder.newBuilder().removalListener(testListener); - try { - // even to the same instance is not allowed - builder = builder.removalListener(testListener); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> builder.removalListener(testListener)); } public void testValuesIsNotASet() { @@ -399,7 +345,6 @@ public void testNullCache() { } @GwtIncompatible // QueuingRemovalListener - public void testRemovalNotification_clear() throws InterruptedException { // If a clear() happens while a computation is pending, we should not get a removal // notification. @@ -471,6 +416,7 @@ public void run() { */ @GwtIncompatible // QueuingRemovalListener + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. public void testRemovalNotification_clear_basher() throws InterruptedException { // If a clear() happens close to the end of computation, one of two things should happen: // - computation ends first: the removal listener is called, and the cache does not contain the @@ -547,6 +493,8 @@ public void run() { // notification. assertEquals(expectedKeys, Sets.union(cache.asMap().keySet(), removalNotifications.keySet())); assertTrue(Sets.intersection(cache.asMap().keySet(), removalNotifications.keySet()).isEmpty()); + threadPool.shutdown(); + threadPool.awaitTermination(300, SECONDS); } /** @@ -566,6 +514,7 @@ public void testRemovalNotification_get_basher() throws InterruptedException { final AtomicInteger computeCount = new AtomicInteger(); final AtomicInteger exceptionCount = new AtomicInteger(); final AtomicInteger computeNullCount = new AtomicInteger(); + @SuppressWarnings("CacheLoaderNull") // test of handling of erroneous implementation CacheLoader countingIdentityLoader = new CacheLoader() { @Override @@ -658,6 +607,7 @@ static final class DelayingIdentityLoader extends CacheLoader { this.delayLatch = delayLatch; } + @CanIgnoreReturnValue // Sure, why not? @Override public T load(T key) throws InterruptedException { if (shouldWait.get()) { diff --git a/android/guava-tests/test/com/google/common/cache/CacheEvictionTest.java b/android/guava-tests/test/com/google/common/cache/CacheEvictionTest.java index ad5f844fe4e4..1f0d7b41f650 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheEvictionTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheEvictionTest.java @@ -20,6 +20,7 @@ import static com.google.common.cache.TestingWeighers.intKeyWeigher; import static com.google.common.cache.TestingWeighers.intValueWeigher; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.min; import static java.util.Arrays.asList; import com.google.common.cache.CacheTesting.Receiver; @@ -28,6 +29,7 @@ import java.util.List; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests relating to cache eviction: what does and doesn't count toward maximumSize, what happens @@ -35,6 +37,7 @@ * * @author mike nonemacher */ +@NullUnmarked public class CacheEvictionTest extends TestCase { static final int MAX_SIZE = 100; @@ -61,7 +64,7 @@ public void testEviction_maxSizeOneSegment() { CacheBuilder.newBuilder().concurrencyLevel(1).maximumSize(MAX_SIZE).build(loader); for (int i = 0; i < 2 * MAX_SIZE; i++) { cache.getUnchecked(i); - assertEquals(Math.min(i + 1, MAX_SIZE), cache.size()); + assertEquals(min(i + 1, MAX_SIZE), cache.size()); } assertEquals(MAX_SIZE, cache.size()); @@ -78,7 +81,7 @@ public void testEviction_maxWeightOneSegment() { .build(loader); for (int i = 0; i < 2 * MAX_SIZE; i++) { cache.getUnchecked(i); - assertEquals(Math.min(i + 1, MAX_SIZE), cache.size()); + assertEquals(min(i + 1, MAX_SIZE), cache.size()); } assertEquals(MAX_SIZE, cache.size()); diff --git a/android/guava-tests/test/com/google/common/cache/CacheExpirationTest.java b/android/guava-tests/test/com/google/common/cache/CacheExpirationTest.java index c1416bd7d815..86d3e4af648c 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheExpirationTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheExpirationTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import com.google.common.cache.TestingCacheLoaders.IdentityLoader; import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener; @@ -29,9 +30,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests relating to cache expiration: make sure entries expire at the right times, make sure @@ -40,6 +41,7 @@ * @author mike nonemacher */ @SuppressWarnings("deprecation") // tests of deprecated method +@NullUnmarked public class CacheExpirationTest extends TestCase { private static final long EXPIRING_TIME = 1000; @@ -339,7 +341,7 @@ public void testExpirationOrder_write() throws ExecutionException { assertThat(keySet).containsExactly(2, 3, 4, 5, 6, 7, 8, 9, 0); // get(K, Callable) doesn't stop 2 from expiring - cache.get(2, Callables.returning(-2)); + Integer unused = cache.get(2, Callables.returning(-2)); CacheTesting.drainRecencyQueues(cache); ticker.advance(1, MILLISECONDS); assertThat(keySet).containsExactly(3, 4, 5, 6, 7, 8, 9, 0); @@ -403,7 +405,7 @@ public void testExpirationOrder_writeAccess() throws ExecutionException { // get(K, Callable) fails to save 8, replace saves 6 cache.asMap().replace(6, -6); - cache.get(8, Callables.returning(-8)); + Integer unused = cache.get(8, Callables.returning(-8)); CacheTesting.drainRecencyQueues(cache); ticker.advance(1, MILLISECONDS); assertThat(keySet).containsExactly(3, 6); @@ -415,12 +417,12 @@ public void testExpiration_invalidateAll() { TestingRemovalListeners.queuingRemovalListener(); Cache cache = CacheBuilder.newBuilder() - .expireAfterAccess(1, TimeUnit.MINUTES) + .expireAfterAccess(1, MINUTES) .removalListener(listener) .ticker(ticker) .build(); cache.put(1, 1); - ticker.advance(10, TimeUnit.MINUTES); + ticker.advance(10, MINUTES); cache.invalidateAll(); assertThat(listener.poll().getCause()).isEqualTo(RemovalCause.EXPIRED); diff --git a/android/guava-tests/test/com/google/common/cache/CacheLoaderTest.java b/android/guava-tests/test/com/google/common/cache/CacheLoaderTest.java index e078dddd7736..9c8cd2819658 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheLoaderTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheLoaderTest.java @@ -16,10 +16,11 @@ package com.google.common.cache; +import static com.google.common.util.concurrent.Futures.immediateFuture; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Queues; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import java.util.Deque; import java.util.Map; @@ -27,12 +28,14 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link CacheLoader}. * * @author Charles Fry */ +@NullUnmarked public class CacheLoaderTest extends TestCase { private static class QueuingExecutor implements Executor { @@ -64,7 +67,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { reloadCount.incrementAndGet(); - return Futures.immediateFuture(new Object()); + return immediateFuture(new Object()); } @Override @@ -78,10 +81,10 @@ public Map loadAll(Iterable keys) { assertEquals(0, reloadCount.get()); assertEquals(0, loadAllCount.get()); - baseLoader.load(new Object()); + Object unused1 = baseLoader.load(new Object()); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = baseLoader.reload(new Object(), new Object()); - baseLoader.loadAll(ImmutableList.of(new Object())); + Map unused2 = baseLoader.loadAll(ImmutableList.of(new Object())); assertEquals(1, loadCount.get()); assertEquals(1, reloadCount.get()); assertEquals(1, loadAllCount.get()); @@ -89,10 +92,10 @@ public Map loadAll(Iterable keys) { QueuingExecutor executor = new QueuingExecutor(); CacheLoader asyncReloader = CacheLoader.asyncReloading(baseLoader, executor); - asyncReloader.load(new Object()); + Object unused3 = asyncReloader.load(new Object()); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError1 = asyncReloader.reload(new Object(), new Object()); - asyncReloader.loadAll(ImmutableList.of(new Object())); + Map unused4 = asyncReloader.loadAll(ImmutableList.of(new Object())); assertEquals(2, loadCount.get()); assertEquals(1, reloadCount.get()); assertEquals(2, loadAllCount.get()); diff --git a/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java b/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java index f5ea54603a1e..cd37da76bfba 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheLoadingTest.java @@ -21,9 +21,12 @@ import static com.google.common.cache.TestingCacheLoaders.identityLoader; import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener; import static com.google.common.truth.Truth.assertThat; -import static java.lang.Thread.currentThread; +import static com.google.common.util.concurrent.Futures.immediateFailedFuture; +import static com.google.common.util.concurrent.Futures.immediateFuture; import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.TestingCacheLoaders.CountingLoader; @@ -37,7 +40,6 @@ import com.google.common.testing.TestLogHandler; import com.google.common.util.concurrent.Callables; import com.google.common.util.concurrent.ExecutionError; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.UncheckedExecutionException; import java.io.IOException; @@ -49,17 +51,18 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.logging.LogRecord; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests relating to cache loading: concurrent loading, exceptions during loading, etc. * * @author mike nonemacher */ +@NullUnmarked public class CacheLoadingTest extends TestCase { TestLogHandler logHandler; @@ -74,7 +77,7 @@ public void setUp() throws Exception { public void tearDown() throws Exception { super.tearDown(); // TODO(cpovirk): run tests in other thread instead of messing with main thread interrupt status - currentThread().interrupted(); + Thread.interrupted(); LocalCache.logger.removeHandler(logHandler); } @@ -169,7 +172,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFuture(two); + return immediateFuture(two); } }; @@ -217,7 +220,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFuture(two); + return immediateFuture(two); } }; @@ -279,7 +282,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFuture(two); + return immediateFuture(two); } }; @@ -521,11 +524,7 @@ public Map loadAll(Iterable keys) throws Exception { assertSame(extraKey, cache.asMap().get(extraKey)); Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; - try { - cache.getAll(asList(lookupKeys)); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); for (Object key : lookupKeys) { assertTrue(cache.asMap().containsKey(key)); @@ -561,11 +560,7 @@ public Map loadAll(Iterable keys) throws Exception { assertSame(extraKey, cache.asMap().get(extraKey)); Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; - try { - cache.getAll(asList(lookupKeys)); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); for (Object key : lookupKeys) { assertTrue(cache.asMap().containsKey(key)); @@ -595,11 +590,7 @@ public Map loadAll(Iterable keys) throws Exception { LoadingCache cache = CacheBuilder.newBuilder().build(loader); Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; - try { - cache.getAll(asList(lookupKeys)); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); assertSame(extraValue, cache.asMap().get(extraKey)); } @@ -612,22 +603,14 @@ public void testLoadNull() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.get(new Object()); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.get(new Object())); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(1, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getUnchecked(new Object())); stats = cache.stats(); assertEquals(2, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -642,22 +625,15 @@ public void testLoadNull() throws ExecutionException { assertEquals(3, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.get(new Object(), Callables.returning(null)); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows( + InvalidCacheLoadException.class, () -> cache.get(new Object(), Callables.returning(null))); stats = cache.stats(); assertEquals(3, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(4, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); stats = cache.stats(); assertEquals(4, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -722,7 +698,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFuture(null); + return immediateFuture(null); } }; @@ -769,7 +745,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFuture(null); + return immediateFuture(null); } }; @@ -828,11 +804,7 @@ public void testBulkLoadNull() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -863,11 +835,7 @@ public Map loadAll(Iterable keys) { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (InvalidCacheLoadException expected) { - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -885,24 +853,16 @@ public void testLoadError() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.get(new Object()); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + ExecutionError expected = assertThrows(ExecutionError.class, () -> cache.get(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(1, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = assertThrows(ExecutionError.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(2, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -918,31 +878,27 @@ public void testLoadError() throws ExecutionException { assertEquals(0, stats.hitCount()); final Error callableError = new Error(); - try { - cache.get( - new Object(), - new Callable() { - @Override - public Object call() { - throw callableError; - } - }); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(callableError); - } + expected = + assertThrows( + ExecutionError.class, + () -> + cache.get( + new Object(), + new Callable() { + @Override + public Object call() { + throw callableError; + } + })); + assertThat(expected).hasCauseThat().isSameInstanceAs(callableError); stats = cache.stats(); assertEquals(3, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(4, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(4, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1009,7 +965,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1057,7 +1013,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1118,12 +1074,9 @@ public void testBulkLoadError() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + ExecutionError expected = + assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1141,24 +1094,17 @@ public void testLoadCheckedException() { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.get(new Object()); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(1, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(2, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1174,24 +1120,18 @@ public void testLoadCheckedException() { assertEquals(0, stats.hitCount()); Exception callableException = new Exception(); - try { - cache.get(new Object(), throwing(callableException)); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); - } + expected = + assertThrows( + ExecutionException.class, () -> cache.get(new Object(), throwing(callableException))); + assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); stats = cache.stats(); assertEquals(3, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(4, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(4, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1210,28 +1150,21 @@ public void testLoadInterruptedException() { assertEquals(0, stats.hitCount()); // Sanity check: - assertFalse(currentThread().interrupted()); + assertFalse(Thread.interrupted()); - try { - cache.get(new Object()); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } - assertTrue(currentThread().interrupted()); + Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); + assertTrue(Thread.interrupted()); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(1, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } - assertTrue(currentThread().interrupted()); + expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); + assertTrue(Thread.interrupted()); stats = cache.stats(); assertEquals(2, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1239,7 +1172,7 @@ public void testLoadInterruptedException() { assertEquals(0, stats.hitCount()); cache.refresh(new Object()); - assertTrue(currentThread().interrupted()); + assertTrue(Thread.interrupted()); checkLoggedCause(e); stats = cache.stats(); assertEquals(2, stats.missCount()); @@ -1248,26 +1181,20 @@ public void testLoadInterruptedException() { assertEquals(0, stats.hitCount()); Exception callableException = new InterruptedException(); - try { - cache.get(new Object(), throwing(callableException)); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); - } - assertTrue(currentThread().interrupted()); + expected = + assertThrows( + ExecutionException.class, () -> cache.get(new Object(), throwing(callableException))); + assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); + assertTrue(Thread.interrupted()); stats = cache.stats(); assertEquals(3, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(4, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } - assertTrue(currentThread().interrupted()); + expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); + assertTrue(Thread.interrupted()); stats = cache.stats(); assertEquals(4, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1334,7 +1261,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1382,7 +1309,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1443,12 +1370,9 @@ public void testBulkLoadCheckedException() { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1467,13 +1391,10 @@ public void testBulkLoadInterruptedException() { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } - assertTrue(currentThread().interrupted()); + ExecutionException expected = + assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); + assertTrue(Thread.interrupted()); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1491,24 +1412,18 @@ public void testLoadUncheckedException() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.get(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + UncheckedExecutionException expected = + assertThrows(UncheckedExecutionException.class, () -> cache.get(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(1, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(2, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1524,24 +1439,20 @@ public void testLoadUncheckedException() throws ExecutionException { assertEquals(0, stats.hitCount()); Exception callableException = new RuntimeException(); - try { - cache.get(new Object(), throwing(callableException)); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); - } + expected = + assertThrows( + UncheckedExecutionException.class, + () -> cache.get(new Object(), throwing(callableException))); + assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); stats = cache.stats(); assertEquals(3, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); assertEquals(4, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(4, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1608,7 +1519,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1656,7 +1567,7 @@ public Object load(Object key) { @Override public ListenableFuture reload(Object key, Object oldValue) { - return Futures.immediateFailedFuture(e); + return immediateFailedFuture(e); } }; @@ -1717,12 +1628,9 @@ public void testBulkLoadUncheckedException() throws ExecutionException { assertEquals(0, stats.loadExceptionCount()); assertEquals(0, stats.hitCount()); - try { - cache.getAll(asList(new Object())); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(e); - } + UncheckedExecutionException expected = + assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object()))); + assertThat(expected).hasCauseThat().isSameInstanceAs(e); stats = cache.stats(); assertEquals(1, stats.missCount()); assertEquals(0, stats.loadSuccessCount()); @@ -1748,12 +1656,9 @@ public String load(Integer key) throws Exception { LoadingCache cache = CacheBuilder.newBuilder().removalListener(removalListener).build(failOnceFunction); - try { - cache.getUnchecked(1); - fail(); - } catch (UncheckedExecutionException ue) { - assertThat(ue).hasCauseThat().isSameInstanceAs(e); - } + UncheckedExecutionException ue = + assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(1)); + assertThat(ue).hasCauseThat().isSameInstanceAs(e); assertEquals("1", cache.getUnchecked(1)); assertEquals(0, removalListener.getCount()); @@ -1767,6 +1672,7 @@ public String load(Integer key) throws Exception { } + @AndroidIncompatible // Depends on GC behavior public void testReloadAfterValueReclamation() throws InterruptedException, ExecutionException { CountingLoader countingLoader = new CountingLoader(); LoadingCache cache = @@ -1861,57 +1767,38 @@ public void testLoadingExceptionWithCause() { LoadingCache cacheChecked = CacheBuilder.newBuilder().build(exceptionLoader(ee)); - try { - cacheUnchecked.get(new Object()); - fail(); - } catch (ExecutionException e) { - fail(); - } catch (UncheckedExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); - } + UncheckedExecutionException caughtUee = + assertThrows(UncheckedExecutionException.class, () -> cacheUnchecked.get(new Object())); + assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); - try { - cacheUnchecked.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException caughtUee) { - assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); - } + caughtUee = + assertThrows( + UncheckedExecutionException.class, () -> cacheUnchecked.getUnchecked(new Object())); + assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); cacheUnchecked.refresh(new Object()); checkLoggedCause(uee); - try { - cacheUnchecked.getAll(asList(new Object())); - fail(); - } catch (ExecutionException e) { - fail(); - } catch (UncheckedExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); - } + caughtUee = + assertThrows( + UncheckedExecutionException.class, () -> cacheUnchecked.getAll(asList(new Object()))); + assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); - try { - cacheChecked.get(new Object()); - fail(); - } catch (ExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); - } + ExecutionException caughtEe = + assertThrows(ExecutionException.class, () -> cacheChecked.get(new Object())); + assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); - try { - cacheChecked.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException caughtUee) { - assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee); - } + caughtUee = + assertThrows( + UncheckedExecutionException.class, () -> cacheChecked.getUnchecked(new Object())); + assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee); cacheChecked.refresh(new Object()); checkLoggedCause(ee); - try { - cacheChecked.getAll(asList(new Object())); - fail(); - } catch (ExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); - } + caughtEe = + assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object()))); + assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); } public void testBulkLoadingExceptionWithCause() { @@ -1924,24 +1811,17 @@ public void testBulkLoadingExceptionWithCause() { LoadingCache cacheChecked = CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(ee))); - try { - cacheUnchecked.getAll(asList(new Object())); - fail(); - } catch (ExecutionException e) { - fail(); - } catch (UncheckedExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); - } + UncheckedExecutionException caughtUee = + assertThrows( + UncheckedExecutionException.class, () -> cacheUnchecked.getAll(asList(new Object()))); + assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); - try { - cacheChecked.getAll(asList(new Object())); - fail(); - } catch (ExecutionException caughtEe) { - assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); - } + ExecutionException caughtEe = + assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object()))); + assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); } - + @AndroidIncompatible // Bug? expected:<1> but was:<2> public void testConcurrentLoading() throws InterruptedException { testConcurrentLoading(CacheBuilder.newBuilder()); } @@ -1954,9 +1834,9 @@ private static void testConcurrentLoading(CacheBuilder builder) testConcurrentLoadingCheckedException(builder); } - + @AndroidIncompatible // Bug? expected:<1> but was:<2> public void testConcurrentExpirationLoading() throws InterruptedException { - testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS)); + testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, SECONDS)); } /** @@ -2006,6 +1886,7 @@ private static void testConcurrentLoadingNull(CacheBuilder build builder.build( new CacheLoader() { @Override + @SuppressWarnings("CacheLoaderNull") // test of broken user implementation public String load(String key) throws InterruptedException { callCount.incrementAndGet(); startSignal.await(); @@ -2132,6 +2013,7 @@ public String load(String key) throws IOException, InterruptedException { * {@code getUnchecked}, and threads with an odd index will call {@code get}. If the cache throws * exceptions, this difference may be visible in the returned List. */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. private static List doConcurrentGet( final LoadingCache cache, final K key, @@ -2184,7 +2066,6 @@ public void run() { return resultList; } - public void testAsMapDuringLoading() throws InterruptedException, ExecutionException { final CountDownLatch getStartedSignal = new CountDownLatch(2); final CountDownLatch letGetFinishSignal = new CountDownLatch(1); @@ -2243,7 +2124,6 @@ public void run() { assertEquals(refreshKey + suffix, map.get(refreshKey)); } - public void testInvalidateDuringLoading() throws InterruptedException, ExecutionException { // computation starts; invalidate() is called on the key being computed, computation finishes final CountDownLatch computationStarted = new CountDownLatch(2); @@ -2300,7 +2180,6 @@ public void run() { assertEquals(2, cache.size()); } - public void testInvalidateAndReloadDuringLoading() throws InterruptedException, ExecutionException { // computation starts; clear() is called, computation finishes @@ -2373,7 +2252,7 @@ public void run() { assertEquals(refreshKey + suffix, map.get(refreshKey)); } - + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public void testExpandDuringLoading() throws InterruptedException { final int count = 3; final AtomicInteger callCount = new AtomicInteger(); @@ -2463,7 +2342,7 @@ public void run() { } // Test ignored because it is extremely flaky in CI builds - + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public void ignoreTestExpandDuringRefresh() throws InterruptedException, ExecutionException { diff --git a/android/guava-tests/test/com/google/common/cache/CacheManualTest.java b/android/guava-tests/test/com/google/common/cache/CacheManualTest.java index abe0b15eb6cc..a2cf24dcef28 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheManualTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheManualTest.java @@ -19,8 +19,12 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author Charles Fry */ +/** + * @author Charles Fry + */ +@NullUnmarked public class CacheManualTest extends TestCase { public void testGetIfPresent() { diff --git a/android/guava-tests/test/com/google/common/cache/CacheReferencesTest.java b/android/guava-tests/test/com/google/common/cache/CacheReferencesTest.java index 1fe26361a106..a11d14a090fd 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheReferencesTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheReferencesTest.java @@ -17,6 +17,7 @@ import static com.google.common.cache.LocalCache.Strength.STRONG; import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; import com.google.common.base.Function; import com.google.common.cache.LocalCache.Strength; @@ -25,6 +26,7 @@ import com.google.common.collect.Iterables; import java.lang.ref.WeakReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests of basic {@link LoadingCache} operations with all possible combinations of key & value @@ -32,6 +34,7 @@ * * @author mike nonemacher */ +@NullUnmarked public class CacheReferencesTest extends TestCase { private static final CacheLoader KEY_TO_STRING_LOADER = @@ -120,7 +123,7 @@ public void testInvalidate() { } } - // fails in Maven with 64-bit JDK: http://code.google.com/p/guava-libraries/issues/detail?id=1568 + // fails in Maven with 64-bit JDK: https://github.com/google/guava/issues/1568 private void assertCleanup( LoadingCache cache, @@ -147,7 +150,7 @@ private void assertCleanup( } try { // Fill up heap so soft references get cleared. - filler = new byte[Math.max(filler.length, filler.length * 2)]; + filler = new byte[max(filler.length, filler.length * 2)]; } catch (OutOfMemoryError e) { } } diff --git a/android/guava-tests/test/com/google/common/cache/CacheRefreshTest.java b/android/guava-tests/test/com/google/common/cache/CacheRefreshTest.java index 823ad360f8b9..6a7948c470dc 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheRefreshTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheRefreshTest.java @@ -20,12 +20,14 @@ import com.google.common.cache.TestingCacheLoaders.IncrementingLoader; import com.google.common.testing.FakeTicker; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests relating to automatic cache refreshing. * * @author Charles Fry */ +@NullUnmarked public class CacheRefreshTest extends TestCase { public void testAutoRefresh() { FakeTicker ticker = new FakeTicker(); diff --git a/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java b/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java index 3e715f1fefdb..cfd174aea242 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java +++ b/android/guava-tests/test/com/google/common/cache/CacheStatsTest.java @@ -16,28 +16,32 @@ package com.google.common.cache; +import static com.google.common.truth.Truth.assertThat; + import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link CacheStats}. * * @author Charles Fry */ +@NullUnmarked public class CacheStatsTest extends TestCase { public void testEmpty() { CacheStats stats = new CacheStats(0, 0, 0, 0, 0, 0); assertEquals(0, stats.requestCount()); assertEquals(0, stats.hitCount()); - assertEquals(1.0, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(1.0); assertEquals(0, stats.missCount()); - assertEquals(0.0, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(0.0); assertEquals(0, stats.loadSuccessCount()); assertEquals(0, stats.loadExceptionCount()); - assertEquals(0.0, stats.loadExceptionRate()); + assertThat(stats.loadExceptionRate()).isEqualTo(0.0); assertEquals(0, stats.loadCount()); assertEquals(0, stats.totalLoadTime()); - assertEquals(0.0, stats.averageLoadPenalty()); + assertThat(stats.averageLoadPenalty()).isEqualTo(0.0); assertEquals(0, stats.evictionCount()); } @@ -45,15 +49,15 @@ public void testSingle() { CacheStats stats = new CacheStats(11, 13, 17, 19, 23, 27); assertEquals(24, stats.requestCount()); assertEquals(11, stats.hitCount()); - assertEquals(11.0 / 24, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(11.0 / 24); assertEquals(13, stats.missCount()); - assertEquals(13.0 / 24, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(13.0 / 24); assertEquals(17, stats.loadSuccessCount()); assertEquals(19, stats.loadExceptionCount()); - assertEquals(19.0 / 36, stats.loadExceptionRate()); + assertThat(stats.loadExceptionRate()).isEqualTo(19.0 / 36); assertEquals(17 + 19, stats.loadCount()); assertEquals(23, stats.totalLoadTime()); - assertEquals(23.0 / (17 + 19), stats.averageLoadPenalty()); + assertThat(stats.averageLoadPenalty()).isEqualTo(23.0 / (17 + 19)); assertEquals(27, stats.evictionCount()); } @@ -64,15 +68,15 @@ public void testMinus() { CacheStats diff = two.minus(one); assertEquals(76, diff.requestCount()); assertEquals(42, diff.hitCount()); - assertEquals(42.0 / 76, diff.hitRate()); + assertThat(diff.hitRate()).isEqualTo(42.0 / 76); assertEquals(34, diff.missCount()); - assertEquals(34.0 / 76, diff.missRate()); + assertThat(diff.missRate()).isEqualTo(34.0 / 76); assertEquals(26, diff.loadSuccessCount()); assertEquals(22, diff.loadExceptionCount()); - assertEquals(22.0 / 48, diff.loadExceptionRate()); + assertThat(diff.loadExceptionRate()).isEqualTo(22.0 / 48); assertEquals(26 + 22, diff.loadCount()); assertEquals(14, diff.totalLoadTime()); - assertEquals(14.0 / (26 + 22), diff.averageLoadPenalty()); + assertThat(diff.averageLoadPenalty()).isEqualTo(14.0 / (26 + 22)); assertEquals(4, diff.evictionCount()); assertEquals(new CacheStats(0, 0, 0, 0, 0, 0), one.minus(two)); @@ -85,15 +89,15 @@ public void testPlus() { CacheStats sum = two.plus(one); assertEquals(124, sum.requestCount()); assertEquals(64, sum.hitCount()); - assertEquals(64.0 / 124, sum.hitRate()); + assertThat(sum.hitRate()).isEqualTo(64.0 / 124); assertEquals(60, sum.missCount()); - assertEquals(60.0 / 124, sum.missRate()); + assertThat(sum.missRate()).isEqualTo(60.0 / 124); assertEquals(56, sum.loadSuccessCount()); assertEquals(52, sum.loadExceptionCount()); - assertEquals(52.0 / 108, sum.loadExceptionRate()); + assertThat(sum.loadExceptionRate()).isEqualTo(52.0 / 108); assertEquals(56 + 52, sum.loadCount()); assertEquals(48, sum.totalLoadTime()); - assertEquals(48.0 / (56 + 52), sum.averageLoadPenalty()); + assertThat(sum.averageLoadPenalty()).isEqualTo(48.0 / (56 + 52)); assertEquals(44, sum.evictionCount()); assertEquals(sum, one.plus(two)); @@ -113,15 +117,15 @@ public void testPlusLarge() { CacheStats sum = smallCacheStats.plus(maxCacheStats); assertEquals(Long.MAX_VALUE, sum.requestCount()); assertEquals(Long.MAX_VALUE, sum.hitCount()); - assertEquals(1.0, sum.hitRate()); + assertThat(sum.hitRate()).isEqualTo(1.0); assertEquals(Long.MAX_VALUE, sum.missCount()); - assertEquals(1.0, sum.missRate()); + assertThat(sum.missRate()).isEqualTo(1.0); assertEquals(Long.MAX_VALUE, sum.loadSuccessCount()); assertEquals(Long.MAX_VALUE, sum.loadExceptionCount()); - assertEquals(1.0, sum.loadExceptionRate()); + assertThat(sum.loadExceptionRate()).isEqualTo(1.0); assertEquals(Long.MAX_VALUE, sum.loadCount()); assertEquals(Long.MAX_VALUE, sum.totalLoadTime()); - assertEquals(1.0, sum.averageLoadPenalty()); + assertThat(sum.averageLoadPenalty()).isEqualTo(1.0); assertEquals(Long.MAX_VALUE, sum.evictionCount()); assertEquals(sum, maxCacheStats.plus(smallCacheStats)); diff --git a/android/guava-tests/test/com/google/common/cache/CacheTesting.java b/android/guava-tests/test/com/google/common/cache/CacheTesting.java index 4c58ed50272c..3156e409ca29 100644 --- a/android/guava-tests/test/com/google/common/cache/CacheTesting.java +++ b/android/guava-tests/test/com/google/common/cache/CacheTesting.java @@ -16,6 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -42,9 +44,9 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReferenceArray; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * A collection of utilities for {@link Cache} testing. @@ -52,6 +54,7 @@ * @author mike nonemacher */ @SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. +@NullUnmarked class CacheTesting { /** @@ -79,7 +82,6 @@ static void simulateValueReclamation(Cache cache, K key) { * that the given entry is a weak or soft reference, and throws an IllegalStateException if that * assumption does not hold. */ - @SuppressWarnings("unchecked") // the instanceof check and the cast generate this warning static void simulateKeyReclamation(Cache cache, K key) { ReferenceEntry entry = getReferenceEntry(cache, key); @@ -98,7 +100,7 @@ static ReferenceEntry getReferenceEntry(Cache cache, K key) { } /** - * Forces the segment containing the given {@code key} to expand (see {@link Segment#expand()}. + * Forces the segment containing the given {@code key} to expand (see {@link Segment#expand()}). */ static void forceExpandSegment(Cache cache, K key) { checkNotNull(cache); @@ -356,7 +358,7 @@ static int accessQueueSize(Segment segment) { } static int expirationQueueSize(Cache cache) { - return Math.max(accessQueueSize(cache), writeQueueSize(cache)); + return max(accessQueueSize(cache), writeQueueSize(cache)); } static void processPendingNotifications(Cache cache) { @@ -367,7 +369,7 @@ static void processPendingNotifications(Cache cache) { } interface Receiver { - void accept(@CheckForNull T object); + void accept(@Nullable T object); } /** @@ -421,7 +423,7 @@ static void expireEntries(LocalCache cchm, long expiringTime, FakeTicker t drainRecencyQueue(segment); } - ticker.advance(2 * expiringTime, TimeUnit.MILLISECONDS); + ticker.advance(2 * expiringTime, MILLISECONDS); long now = ticker.read(); for (Segment segment : cchm.segments) { diff --git a/android/guava-tests/test/com/google/common/cache/EmptyCachesTest.java b/android/guava-tests/test/com/google/common/cache/EmptyCachesTest.java index a5e3a59a01ff..f93568bc29a5 100644 --- a/android/guava-tests/test/com/google/common/cache/EmptyCachesTest.java +++ b/android/guava-tests/test/com/google/common/cache/EmptyCachesTest.java @@ -19,6 +19,7 @@ import static java.util.Arrays.asList; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Function; import com.google.common.cache.CacheBuilderFactory.DurationSpec; @@ -32,6 +33,7 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * {@link LoadingCache} tests that deal with empty caches. @@ -39,6 +41,7 @@ * @author mike nonemacher */ +@NullUnmarked public class EmptyCachesTest extends TestCase { public void testEmpty() { @@ -89,22 +92,14 @@ public void testEqualsAndHashCode_different() { public void testGet_null() throws ExecutionException { for (LoadingCache cache : caches()) { - try { - cache.get(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> cache.get(null)); checkEmpty(cache); } } public void testGetUnchecked_null() { for (LoadingCache cache : caches()) { - try { - cache.getUnchecked(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> cache.getUnchecked(null)); checkEmpty(cache); } } @@ -114,28 +109,17 @@ public void testGetUnchecked_null() { public void testKeySet_nullToArray() { for (LoadingCache cache : caches()) { Set keys = cache.asMap().keySet(); - try { - keys.toArray((Object[]) null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> keys.toArray((Object[]) null)); checkEmpty(cache); } } public void testKeySet_addNotSupported() { for (LoadingCache cache : caches()) { - try { - cache.asMap().keySet().add(1); - fail(); - } catch (UnsupportedOperationException expected) { - } - - try { - cache.asMap().keySet().addAll(asList(1, 2)); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> cache.asMap().keySet().add(1)); + + assertThrows( + UnsupportedOperationException.class, () -> cache.asMap().keySet().addAll(asList(1, 2))); } } @@ -189,28 +173,17 @@ public void testKeySet_remove() { public void testValues_nullToArray() { for (LoadingCache cache : caches()) { Collection values = cache.asMap().values(); - try { - values.toArray((Object[]) null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> values.toArray((Object[]) null)); checkEmpty(cache); } } public void testValues_addNotSupported() { for (LoadingCache cache : caches()) { - try { - cache.asMap().values().add(1); - fail(); - } catch (UnsupportedOperationException expected) { - } - - try { - cache.asMap().values().addAll(asList(1, 2)); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> cache.asMap().values().add(1)); + + assertThrows( + UnsupportedOperationException.class, () -> cache.asMap().values().addAll(asList(1, 2))); } } @@ -264,28 +237,20 @@ public void testValues_remove() { public void testEntrySet_nullToArray() { for (LoadingCache cache : caches()) { Set> entries = cache.asMap().entrySet(); - try { - entries.toArray((Entry[]) null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> entries.toArray((Entry[]) null)); checkEmpty(cache); } } public void testEntrySet_addNotSupported() { for (LoadingCache cache : caches()) { - try { - cache.asMap().entrySet().add(entryOf(1, 1)); - fail(); - } catch (UnsupportedOperationException expected) { - } - - try { - cache.asMap().values().addAll(asList(entryOf(1, 1), entryOf(2, 2))); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> cache.asMap().entrySet().add(entryOf(1, 1))); + + assertThrows( + UnsupportedOperationException.class, + () -> cache.asMap().values().addAll(asList(entryOf(1, 1), entryOf(2, 2)))); } } diff --git a/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java b/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java index 412ff7802f1a..76de76c3707c 100644 --- a/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/ForwardingCacheTest.java @@ -24,12 +24,14 @@ import com.google.common.collect.ImmutableMap; import java.util.concurrent.ExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingCache}. * * @author Charles Fry */ +@NullUnmarked public class ForwardingCacheTest extends TestCase { private Cache forward; private Cache mock; @@ -104,7 +106,7 @@ public void testCleanUp() { private static class OnlyGet extends ForwardingCache { @Override protected Cache delegate() { - return null; + throw new AssertionError(); } } } diff --git a/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java b/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java index d78db2d375cc..cdef91afe699 100644 --- a/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/ForwardingLoadingCacheTest.java @@ -24,12 +24,14 @@ import com.google.common.collect.ImmutableMap; import java.util.concurrent.ExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingLoadingCache}. * * @author Charles Fry */ +@NullUnmarked public class ForwardingLoadingCacheTest extends TestCase { private LoadingCache forward; private LoadingCache mock; @@ -112,7 +114,7 @@ public void testCleanUp() { private static class OnlyGet extends ForwardingLoadingCache { @Override protected LoadingCache delegate() { - return null; + throw new AssertionError(); } } } diff --git a/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java b/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java index b8ec7dfe6882..5a9c0961ceb1 100644 --- a/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/LocalCacheTest.java @@ -25,9 +25,13 @@ import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener; import static com.google.common.cache.TestingRemovalListeners.queuingRemovalListener; import static com.google.common.cache.TestingWeighers.constantWeigher; -import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; +import static java.lang.Math.max; +import static java.lang.Thread.State.WAITING; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; @@ -47,6 +51,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.testing.ConcurrentMapTestSuiteBuilder; @@ -58,9 +63,14 @@ import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import com.google.common.testing.TestLogHandler; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.SettableFuture; +import com.google.common.util.concurrent.UncheckedExecutionException; import java.io.Serializable; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -76,10 +86,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; -/** @author Charles Fry */ +/** + * @author Charles Fry + */ @SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. +@NullUnmarked public class LocalCacheTest extends TestCase { + @AndroidIncompatible private static class TestStringCacheGenerator extends TestStringMapGenerator { private final CacheBuilder builder; @@ -97,7 +113,7 @@ protected Map create(Entry[] entries) { } } - + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(LocalCacheTest.class); @@ -246,11 +262,24 @@ private void checkLogged(Throwable t) { assertSame(t, popLoggedThrowable()); } + /* + * TODO(cpovirk): Can we replace makeLocalCache with a call to builder.build()? Some tests may + * need access to LocalCache APIs, but maybe we can at least make makeLocalCache use + * builder.build() and then cast? + */ + private static LocalCache makeLocalCache( CacheBuilder builder) { return new LocalCache<>(builder, null); } + private static LocalCache makeLocalCache( + CacheBuilder builder, CacheLoader loader) { + return new LocalCache<>(builder, loader); + } + + // TODO(cpovirk): Inline createCacheBuilder()? + private static CacheBuilder createCacheBuilder() { return CacheBuilder.newBuilder(); } @@ -330,7 +359,7 @@ protected int doHash(Object t) { } public void testSetConcurrencyLevel() { - // round up to nearest power of two + // round up to the nearest power of two checkConcurrencyLevel(1, 1); checkConcurrencyLevel(2, 2); @@ -349,7 +378,7 @@ private static void checkConcurrencyLevel(int concurrencyLevel, int segmentCount } public void testSetInitialCapacity() { - // share capacity over each segment, then round up to nearest power of two + // share capacity over each segment, then round up to the nearest power of two checkInitialCapacity(1, 0, 1); checkInitialCapacity(1, 1, 1); @@ -429,7 +458,7 @@ private static void checkMaximumSize(int concurrencyLevel, int initialCapacity, long totalCapacity = 0; assertTrue( "segments=" + map.segments.length + ", maxSize=" + maxSize, - map.segments.length <= Math.max(1, maxSize / 10)); + map.segments.length <= max(1, maxSize / 10)); for (int i = 0; i < map.segments.length; i++) { totalCapacity += map.segments[i].maxSegmentWeight; } @@ -444,7 +473,7 @@ private static void checkMaximumSize(int concurrencyLevel, int initialCapacity, .weigher(constantWeigher(1))); assertTrue( "segments=" + map.segments.length + ", maxSize=" + maxSize, - map.segments.length <= Math.max(1, maxSize / 10)); + map.segments.length <= max(1, maxSize / 10)); totalCapacity = 0; for (int i = 0; i < map.segments.length; i++) { totalCapacity += map.segments[i].maxSegmentWeight; @@ -493,7 +522,7 @@ private static void checkStrength( public void testSetExpireAfterWrite() { long duration = 42; - TimeUnit unit = TimeUnit.SECONDS; + TimeUnit unit = SECONDS; LocalCache map = makeLocalCache(createCacheBuilder().expireAfterWrite(duration, unit)); assertEquals(unit.toNanos(duration), map.expireAfterWriteNanos); @@ -501,7 +530,7 @@ public void testSetExpireAfterWrite() { public void testSetExpireAfterAccess() { long duration = 42; - TimeUnit unit = TimeUnit.SECONDS; + TimeUnit unit = SECONDS; LocalCache map = makeLocalCache(createCacheBuilder().expireAfterAccess(duration, unit)); assertEquals(unit.toNanos(duration), map.expireAfterAccessNanos); @@ -509,12 +538,64 @@ public void testSetExpireAfterAccess() { public void testSetRefresh() { long duration = 42; - TimeUnit unit = TimeUnit.SECONDS; + TimeUnit unit = SECONDS; LocalCache map = makeLocalCache(createCacheBuilder().refreshAfterWrite(duration, unit)); assertEquals(unit.toNanos(duration), map.refreshNanos); } + public void testLongAsyncRefresh() throws Exception { + FakeTicker ticker = new FakeTicker(); + CountDownLatch reloadStarted = new CountDownLatch(1); + SettableFuture threadAboutToBlockForRefresh = SettableFuture.create(); + + ListeningExecutorService refreshExecutor = listeningDecorator(newSingleThreadExecutor()); + try { + CacheBuilder builder = + createCacheBuilder() + .expireAfterWrite(100, MILLISECONDS) + .refreshAfterWrite(5, MILLISECONDS) + .ticker(ticker); + + CacheLoader loader = + new CacheLoader() { + @Override + public String load(String key) { + return key + "Load"; + } + + @Override + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. + public ListenableFuture reload(String key, String oldValue) { + return refreshExecutor.submit( + () -> { + reloadStarted.countDown(); + + Thread blockingForRefresh = threadAboutToBlockForRefresh.get(); + while (blockingForRefresh.isAlive() + && blockingForRefresh.getState() != WAITING) { + Thread.yield(); + } + + return key + "Reload"; + }); + } + }; + LocalCache cache = makeLocalCache(builder, loader); + + assertThat(cache.getOrLoad("test")).isEqualTo("testLoad"); + + ticker.advance(10, MILLISECONDS); // so that the next call will trigger refresh + assertThat(cache.getOrLoad("test")).isEqualTo("testLoad"); + reloadStarted.await(); + ticker.advance(500, MILLISECONDS); // so that the entry expires during the reload + threadAboutToBlockForRefresh.set(Thread.currentThread()); + assertThat(cache.getOrLoad("test")).isEqualTo("testReload"); + } finally { + refreshExecutor.shutdown(); + } + } + public void testSetRemovalListener() { RemovalListener testListener = TestingRemovalListeners.nullRemovalListener(); LocalCache map = @@ -583,7 +664,7 @@ public void testRecordReadOnCompute() throws ExecutionException { // access some of the elements Random random = new Random(); - List> reads = Lists.newArrayList(); + List> reads = new ArrayList<>(); Iterator> i = readOrder.iterator(); while (i.hasNext()) { ReferenceEntry entry = i.next(); @@ -676,8 +757,7 @@ public void testComputePartiallyCollectedValue() throws ExecutionException { @AndroidIncompatible // Perhaps emulator clock does not update between the two get() calls? public void testComputeExpiredEntry() throws ExecutionException { - CacheBuilder builder = - createCacheBuilder().expireAfterWrite(1, TimeUnit.NANOSECONDS); + CacheBuilder builder = createCacheBuilder().expireAfterWrite(1, NANOSECONDS); CountingLoader loader = new CountingLoader(); LocalCache map = makeLocalCache(builder); assertEquals(0, loader.getCount()); @@ -701,7 +781,6 @@ public void testValues() { assertEquals(1, map.size()); } - public void testCopyEntry_computing() { final CountDownLatch startSignal = new CountDownLatch(1); final CountDownLatch computingSignal = new CountDownLatch(1); @@ -803,7 +882,6 @@ public void onRemoval(RemovalNotification notification) { checkLogged(e); } - public void testRemovalListener_replaced_computing() { final CountDownLatch startSignal = new CountDownLatch(1); final CountDownLatch computingSignal = new CountDownLatch(1); @@ -980,7 +1058,7 @@ public void testRemovalListener_expired() { makeLocalCache( createCacheBuilder() .concurrencyLevel(1) - .expireAfterWrite(3, TimeUnit.NANOSECONDS) + .expireAfterWrite(3, NANOSECONDS) .ticker(ticker) .removalListener(listener)); assertTrue(listener.isEmpty()); @@ -1123,7 +1201,7 @@ public void testSegmentGetAndContains() { createCacheBuilder() .concurrencyLevel(1) .ticker(ticker) - .expireAfterAccess(1, TimeUnit.NANOSECONDS)); + .expireAfterAccess(1, NANOSECONDS)); Segment segment = map.segments[0]; // TODO(fry): check recency ordering @@ -1888,7 +1966,7 @@ public void testRemoveEntry() { table.set(0, entry); segment.count = 1; assertTrue(segment.removeEntry(entry, hash, RemovalCause.COLLECTED)); - assertNotificationEnqueued(map, key, value, hash); + assertNotificationEnqueued(map, key, value); assertTrue(map.removalNotificationQueue.isEmpty()); assertFalse(segment.accessQueue.contains(entry)); assertFalse(segment.writeQueue.contains(entry)); @@ -1997,8 +2075,7 @@ public void testRemoveComputingValue() { assertTrue(segment.removeLoadingValue(key, hash, valueRef)); } - private static void assertNotificationEnqueued( - LocalCache map, K key, V value, int hash) { + private static void assertNotificationEnqueued(LocalCache map, K key, V value) { RemovalNotification notification = map.removalNotificationQueue.poll(); assertSame(key, notification.getKey()); assertSame(value, notification.getValue()); @@ -2098,7 +2175,7 @@ public void testRecordRead() { // access some of the elements Random random = new Random(); - List> reads = Lists.newArrayList(); + List> reads = new ArrayList<>(); Iterator> i = readOrder.iterator(); while (i.hasNext()) { ReferenceEntry entry = i.next(); @@ -2139,7 +2216,7 @@ public void testRecordReadOnGet() { // access some of the elements Random random = new Random(); - List> reads = Lists.newArrayList(); + List> reads = new ArrayList<>(); Iterator> i = readOrder.iterator(); while (i.hasNext()) { ReferenceEntry entry = i.next(); @@ -2180,7 +2257,7 @@ public void testRecordWrite() { // access some of the elements Random random = new Random(); - List> writes = Lists.newArrayList(); + List> writes = new ArrayList<>(); Iterator> i = writeOrder.iterator(); while (i.hasNext()) { ReferenceEntry entry = i.next(); @@ -2269,7 +2346,7 @@ public void testExpireAfterWrite() { createCacheBuilder() .concurrencyLevel(1) .ticker(ticker) - .expireAfterWrite(2, TimeUnit.NANOSECONDS)); + .expireAfterWrite(2, NANOSECONDS)); Segment segment = map.segments[0]; Object key = new Object(); @@ -2310,7 +2387,7 @@ public void testExpireAfterAccess() { createCacheBuilder() .concurrencyLevel(1) .ticker(ticker) - .expireAfterAccess(2, TimeUnit.NANOSECONDS)); + .expireAfterAccess(2, NANOSECONDS)); Segment segment = map.segments[0]; Object key = new Object(); @@ -2524,7 +2601,7 @@ public void testNullParameters() throws Exception { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicInstanceMethods(makeLocalCache(createCacheBuilder())); CacheLoader loader = identityLoader(); - tester.testAllPublicInstanceMethods(makeLocalCache(createCacheBuilder())); + tester.testAllPublicInstanceMethods(makeLocalCache(createCacheBuilder(), loader)); } public void testSerializationProxyLoading() { @@ -2641,15 +2718,93 @@ public void testSerializationProxyManual() { assertEquals(localCacheTwo.ticker, localCacheThree.ticker); } + public void testLoadDifferentKeyInLoader() throws ExecutionException, InterruptedException { + LocalCache cache = makeLocalCache(createCacheBuilder()); + String key1 = "key1"; + String key2 = "key2"; + + assertEquals( + key2, + cache.get( + key1, + new CacheLoader() { + @Override + public String load(String key) throws Exception { + return cache.get(key2, identityLoader()); // loads a different key, should work + } + })); + } + + public void testRecursiveLoad() throws InterruptedException { + LocalCache cache = makeLocalCache(createCacheBuilder()); + String key = "key"; + CacheLoader loader = + new CacheLoader() { + @Override + public String load(String key) throws Exception { + return cache.get(key, identityLoader()); // recursive load, this should fail + } + }; + testLoadThrows(key, cache, loader); + } + + public void testRecursiveLoadWithProxy() throws InterruptedException { + String key = "key"; + String otherKey = "otherKey"; + LocalCache cache = makeLocalCache(createCacheBuilder()); + CacheLoader loader = + new CacheLoader() { + @Override + public String load(String key) throws Exception { + return cache.get( + key, + identityLoader()); // recursive load (same as the initial one), this should fail + } + }; + CacheLoader proxyLoader = + new CacheLoader() { + @Override + public String load(String key) throws Exception { + return cache.get(otherKey, loader); // loads another key, is ok + } + }; + testLoadThrows(key, cache, proxyLoader); + } + // utility methods + private void testLoadThrows( + String key, LocalCache cache, CacheLoader loader) + throws InterruptedException { + CountDownLatch doneSignal = new CountDownLatch(1); + Thread thread = + new Thread( + () -> { + try { + cache.get(key, loader); + } catch (UncheckedExecutionException | ExecutionException e) { + doneSignal.countDown(); + } + }); + thread.start(); + + boolean done = doneSignal.await(1, SECONDS); + if (!done) { + StringBuilder builder = new StringBuilder(); + for (StackTraceElement trace : thread.getStackTrace()) { + builder.append("\tat ").append(trace).append('\n'); + } + fail(builder.toString()); + } + } + /** * Returns an iterable containing all combinations of maximumSize, expireAfterAccess/Write, * weakKeys and weak/softValues. */ - @SuppressWarnings("unchecked") // varargs private static Iterable> allEntryTypeMakers() { - List> result = newArrayList(allKeyValueStrengthMakers()); + List> result = new ArrayList<>(); + Iterables.addAll(result, allKeyValueStrengthMakers()); for (CacheBuilder builder : allKeyValueStrengthMakers()) { result.add(builder.maximumSize(SMALL_MAX_SIZE)); } @@ -2669,22 +2824,16 @@ private static Iterable> allEntryTypeMakers() { } /** Returns an iterable containing all combinations of maximumSize and expireAfterAccess/Write. */ - @SuppressWarnings("unchecked") // varargs static Iterable> allEvictingMakers() { return ImmutableList.of( createCacheBuilder().maximumSize(SMALL_MAX_SIZE), createCacheBuilder().expireAfterAccess(99999, SECONDS), createCacheBuilder().expireAfterWrite(99999, SECONDS), - createCacheBuilder() - .maximumSize(SMALL_MAX_SIZE) - .expireAfterAccess(SMALL_MAX_SIZE, TimeUnit.SECONDS), - createCacheBuilder() - .maximumSize(SMALL_MAX_SIZE) - .expireAfterWrite(SMALL_MAX_SIZE, TimeUnit.SECONDS)); + createCacheBuilder().maximumSize(SMALL_MAX_SIZE).expireAfterAccess(SMALL_MAX_SIZE, SECONDS), + createCacheBuilder().maximumSize(SMALL_MAX_SIZE).expireAfterWrite(SMALL_MAX_SIZE, SECONDS)); } /** Returns an iterable containing all combinations weakKeys and weak/softValues. */ - @SuppressWarnings("unchecked") // varargs private static Iterable> allKeyValueStrengthMakers() { return ImmutableList.of( createCacheBuilder(), @@ -2698,7 +2847,7 @@ private static Iterable> allKeyValueStrengthMakers( // entries and values private static DummyEntry createDummyEntry( - K key, int hash, V value, ReferenceEntry next) { + K key, int hash, V value, @Nullable ReferenceEntry next) { DummyEntry entry = DummyEntry.create(key, hash, next); DummyValueReference valueRef = DummyValueReference.create(value); entry.setValueReference(valueRef); @@ -2706,7 +2855,7 @@ private static DummyEntry createDummyEntry( } static class DummyEntry implements ReferenceEntry { - private K key; + private @Nullable K key; private final int hash; private final ReferenceEntry next; @@ -2716,7 +2865,8 @@ public DummyEntry(K key, int hash, ReferenceEntry next) { this.next = next; } - public static DummyEntry create(K key, int hash, ReferenceEntry next) { + public static DummyEntry create( + K key, int hash, @Nullable ReferenceEntry next) { return new DummyEntry<>(key, hash, next); } @@ -2825,7 +2975,7 @@ public void setPreviousInWriteQueue(ReferenceEntry previous) { } static class DummyValueReference implements ValueReference { - private V value; + private @Nullable V value; boolean loading = false; public DummyValueReference() { @@ -2855,7 +3005,7 @@ public int getWeight() { } @Override - public ReferenceEntry getEntry() { + public @Nullable ReferenceEntry getEntry() { return null; } @@ -2905,7 +3055,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof SerializableCacheLoader); } } @@ -2921,7 +3071,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof SerializableRemovalListener); } } @@ -2938,7 +3088,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof SerializableTicker); } } @@ -2955,7 +3105,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof SerializableWeigher); } } diff --git a/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java b/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java index 8ba9dbc97e9a..8111b2e6ad00 100644 --- a/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/LocalLoadingCacheTest.java @@ -20,6 +20,7 @@ import static com.google.common.cache.LocalCacheTest.SMALL_MAX_SIZE; import static com.google.common.cache.TestingCacheLoaders.identityLoader; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.cache.LocalCache.LocalLoadingCache; import com.google.common.cache.LocalCache.Segment; @@ -31,11 +32,14 @@ import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author Charles Fry */ +/** + * @author Charles Fry + */ +@NullUnmarked public class LocalLoadingCacheTest extends TestCase { private static LocalLoadingCache makeCache( @@ -81,9 +85,9 @@ public void testStats() { CacheStats stats = cache.stats(); assertEquals(1, stats.requestCount()); assertEquals(0, stats.hitCount()); - assertEquals(0.0, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(0.0); assertEquals(1, stats.missCount()); - assertEquals(1.0, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(1.0); assertEquals(1, stats.loadCount()); long totalLoadTime = stats.totalLoadTime(); assertTrue(totalLoadTime >= 0); @@ -94,9 +98,9 @@ public void testStats() { stats = cache.stats(); assertEquals(2, stats.requestCount()); assertEquals(1, stats.hitCount()); - assertEquals(1.0 / 2, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(1.0 / 2); assertEquals(1, stats.missCount()); - assertEquals(1.0 / 2, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(1.0 / 2); assertEquals(1, stats.loadCount()); assertEquals(0, stats.evictionCount()); @@ -105,9 +109,9 @@ public void testStats() { stats = cache.stats(); assertEquals(3, stats.requestCount()); assertEquals(1, stats.hitCount()); - assertEquals(1.0 / 3, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(1.0 / 3); assertEquals(2, stats.missCount()); - assertEquals(2.0 / 3, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(2.0 / 3); assertEquals(2, stats.loadCount()); assertTrue(stats.totalLoadTime() >= totalLoadTime); totalLoadTime = stats.totalLoadTime(); @@ -119,12 +123,11 @@ public void testStats() { stats = cache.stats(); assertEquals(4, stats.requestCount()); assertEquals(1, stats.hitCount()); - assertEquals(1.0 / 4, stats.hitRate()); + assertThat(stats.hitRate()).isEqualTo(1.0 / 4); assertEquals(3, stats.missCount()); - assertEquals(3.0 / 4, stats.missRate()); + assertThat(stats.missRate()).isEqualTo(3.0 / 4); assertEquals(3, stats.loadCount()); assertTrue(stats.totalLoadTime() >= totalLoadTime); - totalLoadTime = stats.totalLoadTime(); assertTrue(stats.averageLoadPenalty() >= 0.0); assertEquals(1, stats.evictionCount()); } @@ -157,7 +160,7 @@ public void testStatsNoops() { assertThat(map).containsEntry(three, one); assertThat(map).containsEntry(one, two); - // TODO(user): Confirm with fry@ that this is a reasonable substitute. + // TODO(cgruber): Confirm with fry@ that this is a reasonable substitute. // Set> entries = map.entrySet(); // assertThat(entries).containsExactly( // Maps.immutableEntry(three, one), Maps.immutableEntry(one, two)); @@ -293,7 +296,6 @@ public void testAsMapRecency() { assertFalse(segment.recencyQueue.isEmpty()); } - public void testRecursiveComputation() throws InterruptedException { final AtomicReference> cacheRef = new AtomicReference<>(); CacheLoader recursiveLoader = @@ -324,7 +326,7 @@ public String load(Integer key) { recursiveCache = CacheBuilder.newBuilder().weakKeys().weakValues().build(recursiveLoader); cacheRef.set(recursiveCache); - // tells the test when the compution has completed + // tells the test when the computation has completed final CountDownLatch doneSignal = new CountDownLatch(1); Thread thread = @@ -345,7 +347,7 @@ public void uncaughtException(Thread t, Throwable e) {} }); thread.start(); - boolean done = doneSignal.await(1, TimeUnit.SECONDS); + boolean done = doneSignal.await(1, SECONDS); if (!done) { StringBuilder builder = new StringBuilder(); for (StackTraceElement trace : thread.getStackTrace()) { diff --git a/android/guava-tests/test/com/google/common/cache/LongAdderTest.java b/android/guava-tests/test/com/google/common/cache/LongAdderTest.java index 876f90748880..8a5bed0bb235 100644 --- a/android/guava-tests/test/com/google/common/cache/LongAdderTest.java +++ b/android/guava-tests/test/com/google/common/cache/LongAdderTest.java @@ -17,8 +17,10 @@ import static com.google.common.truth.Truth.assertThat; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link LongAdder}. */ +@NullUnmarked public class LongAdderTest extends TestCase { /** diff --git a/android/guava-tests/test/com/google/common/cache/NullCacheTest.java b/android/guava-tests/test/com/google/common/cache/NullCacheTest.java index 89dc3fb146df..23ccbe9ff0eb 100644 --- a/android/guava-tests/test/com/google/common/cache/NullCacheTest.java +++ b/android/guava-tests/test/com/google/common/cache/NullCacheTest.java @@ -20,17 +20,20 @@ import static com.google.common.cache.TestingRemovalListeners.queuingRemovalListener; import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.TestingRemovalListeners.QueuingRemovalListener; import com.google.common.util.concurrent.UncheckedExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * {@link LoadingCache} tests for caches with a maximum size of zero. * * @author mike nonemacher */ +@NullUnmarked public class NullCacheTest extends TestCase { QueuingRemovalListener listener; @@ -100,12 +103,7 @@ public void testGet_computeNull() { .removalListener(listener) .build(constantLoader(null)); - try { - cache.getUnchecked(new Object()); - fail(); - } catch (InvalidCacheLoadException e) { - /* expected */ - } + assertThrows(InvalidCacheLoadException.class, () -> cache.getUnchecked(new Object())); assertTrue(listener.isEmpty()); checkEmpty(cache); @@ -119,12 +117,9 @@ public void testGet_runtimeException() { .removalListener(listener) .build(exceptionLoader(e)); - try { - map.getUnchecked(new Object()); - fail(); - } catch (UncheckedExecutionException uee) { - assertThat(uee).hasCauseThat().isSameInstanceAs(e); - } + UncheckedExecutionException uee = + assertThrows(UncheckedExecutionException.class, () -> map.getUnchecked(new Object())); + assertThat(uee).hasCauseThat().isSameInstanceAs(e); assertTrue(listener.isEmpty()); checkEmpty(map); } diff --git a/android/guava-tests/test/com/google/common/cache/PackageSanityTests.java b/android/guava-tests/test/com/google/common/cache/PackageSanityTests.java index 64cdc13375f2..e4909906e5d8 100644 --- a/android/guava-tests/test/com/google/common/cache/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/cache/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.cache; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -24,6 +25,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { setDefault( diff --git a/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java b/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java index b02b8ac61a48..81eea6c63ff3 100644 --- a/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java +++ b/android/guava-tests/test/com/google/common/cache/PopulatedCachesTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Function; import com.google.common.cache.CacheBuilderFactory.DurationSpec; @@ -37,6 +38,7 @@ import java.util.Map.Entry; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * {@link LoadingCache} tests that deal with caches that actually contain some key-value mappings. @@ -44,6 +46,7 @@ * @author mike nonemacher */ +@NullUnmarked public class PopulatedCachesTest extends TestCase { // we use integers as keys; make sure the range covers some values that ARE cached by // Integer.valueOf(int), and some that are not cached. (127 is the highest cached value.) @@ -54,7 +57,7 @@ public class PopulatedCachesTest extends TestCase { public void testSize_populated() { for (LoadingCache cache : caches()) { // don't let the entries get GCed - List> warmed = warmUp(cache); + List> unused = warmUp(cache); assertEquals(WARMUP_SIZE, cache.size()); assertMapSize(cache.asMap(), WARMUP_SIZE); checkValidState(cache); @@ -124,7 +127,7 @@ public void testPutIfAbsent_populated() { public void testPutAll_populated() { for (LoadingCache cache : caches()) { // don't let the entries get GCed - List> warmed = warmUp(cache); + List> unused = warmUp(cache); Object newKey = new Object(); Object newValue = new Object(); cache.asMap().putAll(ImmutableMap.of(newKey, newValue)); @@ -279,11 +282,7 @@ public void testWriteThroughEntry() { assertEquals(3, cache.getIfPresent(1)); checkValidState(cache); - try { - entry.setValue(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> entry.setValue(null)); checkValidState(cache); } } diff --git a/android/guava-tests/test/com/google/common/cache/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/cache/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..c29f754b140b --- /dev/null +++ b/android/guava-tests/test/com/google/common/cache/ReflectionFreeAssertThrows.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.cache; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.ExecutionError; +import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionError.class, e -> e instanceof ExecutionError) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UncheckedExecutionException.class, e -> e instanceof UncheckedExecutionException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/cache/RemovalNotificationTest.java b/android/guava-tests/test/com/google/common/cache/RemovalNotificationTest.java index 9cd587db7c82..12d6e0e0cfcd 100644 --- a/android/guava-tests/test/com/google/common/cache/RemovalNotificationTest.java +++ b/android/guava-tests/test/com/google/common/cache/RemovalNotificationTest.java @@ -18,12 +18,14 @@ import com.google.common.testing.EqualsTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests of {@link RemovalNotification}. * * @author Ben Yu */ +@NullUnmarked public class RemovalNotificationTest extends TestCase { public void testEquals() { diff --git a/android/guava-tests/test/com/google/common/cache/TestingCacheLoaders.java b/android/guava-tests/test/com/google/common/cache/TestingCacheLoaders.java index 6cc02602126e..83f576e75704 100644 --- a/android/guava-tests/test/com/google/common/cache/TestingCacheLoaders.java +++ b/android/guava-tests/test/com/google/common/cache/TestingCacheLoaders.java @@ -15,15 +15,17 @@ package com.google.common.cache; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateFuture; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.Maps; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Utility {@link CacheLoader} implementations intended for use in testing. @@ -31,6 +33,7 @@ * @author mike nonemacher */ @GwtCompatible(emulated = true) +@NullUnmarked class TestingCacheLoaders { /** @@ -57,7 +60,7 @@ public Map loadAll(Iterable keys) throws Exception { } /** Returns a {@link CacheLoader} that returns the given {@code constant} for every request. */ - static ConstantLoader constantLoader(@CheckForNull V constant) { + static ConstantLoader constantLoader(@Nullable V constant) { return new ConstantLoader<>(constant); } @@ -134,6 +137,7 @@ static class IncrementingLoader extends CacheLoader { private final AtomicInteger countLoad = new AtomicInteger(); private final AtomicInteger countReload = new AtomicInteger(); + @CanIgnoreReturnValue // Sure, why not? @Override public Integer load(Integer key) { countLoad.incrementAndGet(); @@ -144,7 +148,7 @@ public Integer load(Integer key) { @Override public ListenableFuture reload(Integer key, Integer oldValue) { countReload.incrementAndGet(); - return Futures.immediateFuture(oldValue + 1); + return immediateFuture(oldValue + 1); } public int getLoadCount() { diff --git a/android/guava-tests/test/com/google/common/cache/TestingRemovalListeners.java b/android/guava-tests/test/com/google/common/cache/TestingRemovalListeners.java index 698467fceb07..4aab99e1aaec 100644 --- a/android/guava-tests/test/com/google/common/cache/TestingRemovalListeners.java +++ b/android/guava-tests/test/com/google/common/cache/TestingRemovalListeners.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtIncompatible; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; +import org.jspecify.annotations.NullUnmarked; /** * Utility {@link RemovalListener} implementations intended for use in testing. @@ -25,6 +26,7 @@ * @author mike nonemacher */ @GwtCompatible(emulated = true) +@NullUnmarked class TestingRemovalListeners { /** Returns a new no-op {@code RemovalListener}. */ diff --git a/android/guava-tests/test/com/google/common/cache/TestingWeighers.java b/android/guava-tests/test/com/google/common/cache/TestingWeighers.java index b09fdf03e78a..db508aae5749 100644 --- a/android/guava-tests/test/com/google/common/cache/TestingWeighers.java +++ b/android/guava-tests/test/com/google/common/cache/TestingWeighers.java @@ -14,11 +14,14 @@ package com.google.common.cache; +import org.jspecify.annotations.NullUnmarked; + /** * Utility {@link Weigher} implementations intended for use in testing. * * @author Charles Fry */ +@NullUnmarked public class TestingWeighers { /** Returns a {@link Weigher} that returns the given {@code constant} for every request. */ diff --git a/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java b/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java index e430f0e523b9..b0e4055ab9cb 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractBiMapTest.java @@ -18,12 +18,14 @@ import java.util.Iterator; import java.util.Map.Entry; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code AbstractBiMap}. * * @author Mike Bostock */ +@NullUnmarked public class AbstractBiMapTest extends TestCase { // The next two tests verify that map entries are not accessed after they're diff --git a/android/guava-tests/test/com/google/common/collect/AbstractFilteredMapTest.java b/android/guava-tests/test/com/google/common/collect/AbstractFilteredMapTest.java new file mode 100644 index 000000000000..ab39d750352d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/AbstractFilteredMapTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import java.util.Map; +import java.util.Map.Entry; +import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@GwtCompatible +@NullMarked +abstract class AbstractFilteredMapTest extends TestCase { + private static final Predicate<@Nullable String> NOT_LENGTH_3 = + input -> input == null || input.length() != 3; + private static final Predicate<@Nullable Integer> EVEN = input -> input == null || input % 2 == 0; + static final Predicate> CORRECT_LENGTH = + input -> input.getKey().length() == input.getValue(); + + abstract Map createUnfiltered(); + + public void testFilteredKeysIllegalPut() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); + filtered.put("a", 1); + filtered.put("b", 2); + assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); + + assertThrows(IllegalArgumentException.class, () -> filtered.put("yyy", 3)); + } + + public void testFilteredKeysIllegalPutAll() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); + filtered.put("a", 1); + filtered.put("b", 2); + assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); + + assertThrows( + IllegalArgumentException.class, + () -> filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5))); + + assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); + } + + public void testFilteredKeysFilteredReflectsBackingChanges() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); + unfiltered.put("two", 2); + unfiltered.put("three", 3); + unfiltered.put("four", 4); + assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered); + assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered); + + unfiltered.remove("three"); + assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered); + assertEquals(ImmutableMap.of("four", 4), filtered); + + unfiltered.clear(); + assertEquals(ImmutableMap.of(), unfiltered); + assertEquals(ImmutableMap.of(), filtered); + } + + public void testFilteredValuesIllegalPut() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterValues(unfiltered, EVEN); + filtered.put("a", 2); + unfiltered.put("b", 4); + unfiltered.put("c", 5); + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + + assertThrows(IllegalArgumentException.class, () -> filtered.put("yyy", 3)); + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + } + + public void testFilteredValuesIllegalPutAll() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterValues(unfiltered, EVEN); + filtered.put("a", 2); + unfiltered.put("b", 4); + unfiltered.put("c", 5); + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + + assertThrows( + IllegalArgumentException.class, + () -> filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6))); + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + } + + public void testFilteredValuesIllegalSetValue() { + Map unfiltered = createUnfiltered(); + Map filtered = Maps.filterValues(unfiltered, EVEN); + filtered.put("a", 2); + filtered.put("b", 4); + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + + Entry entry = filtered.entrySet().iterator().next(); + assertThrows(IllegalArgumentException.class, () -> entry.setValue(5)); + + assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); + } + + public void testFilteredValuesClear() { + Map unfiltered = createUnfiltered(); + unfiltered.put("one", 1); + unfiltered.put("two", 2); + unfiltered.put("three", 3); + unfiltered.put("four", 4); + Map filtered = Maps.filterValues(unfiltered, EVEN); + assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), unfiltered); + assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered); + + filtered.clear(); + assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered); + assertTrue(filtered.isEmpty()); + } + + public void testFilteredEntriesIllegalPut() { + Map unfiltered = createUnfiltered(); + unfiltered.put("cat", 3); + unfiltered.put("dog", 2); + unfiltered.put("horse", 5); + Map filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered); + + filtered.put("chicken", 7); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); + + assertThrows(IllegalArgumentException.class, () -> filtered.put("cow", 7)); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); + } + + public void testFilteredEntriesIllegalPutAll() { + Map unfiltered = createUnfiltered(); + unfiltered.put("cat", 3); + unfiltered.put("dog", 2); + unfiltered.put("horse", 5); + Map filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered); + + filtered.put("chicken", 7); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); + + assertThrows( + IllegalArgumentException.class, + () -> filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7))); + assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); + } + + public void testFilteredEntriesObjectPredicate() { + Map unfiltered = createUnfiltered(); + unfiltered.put("cat", 3); + unfiltered.put("dog", 2); + unfiltered.put("horse", 5); + Predicate predicate = Predicates.alwaysFalse(); + Map filtered = Maps.filterEntries(unfiltered, predicate); + assertTrue(filtered.isEmpty()); + } + + public void testFilteredEntriesWildCardEntryPredicate() { + Map unfiltered = createUnfiltered(); + unfiltered.put("cat", 3); + unfiltered.put("dog", 2); + unfiltered.put("horse", 5); + Predicate> predicate = e -> e.getKey().equals("cat") || e.getValue().equals(2); + Map filtered = Maps.filterEntries(unfiltered, predicate); + assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/AbstractImmutableBiMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/AbstractImmutableBiMapMapInterfaceTest.java new file mode 100644 index 000000000000..5a4934ebcc84 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/AbstractImmutableBiMapMapInterfaceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Sets.newHashSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Joiner; +import com.google.common.collect.testing.MapInterfaceTest; +import java.util.Map; +import java.util.Map.Entry; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +abstract class AbstractImmutableBiMapMapInterfaceTest extends MapInterfaceTest { + AbstractImmutableBiMapMapInterfaceTest() { + super(false, false, false, false, false); + } + + @Override + protected Map makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + private static final Joiner JOINER = Joiner.on(", "); + + @Override + protected final void assertMoreInvariants(Map map) { + BiMap bimap = (BiMap) map; + + for (Entry entry : map.entrySet()) { + assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); + assertEquals(entry.getKey(), bimap.inverse().get(entry.getValue())); + } + + assertEquals("{" + JOINER.join(map.entrySet()) + "}", map.toString()); + assertEquals("[" + JOINER.join(map.entrySet()) + "]", map.entrySet().toString()); + assertEquals("[" + JOINER.join(map.keySet()) + "]", map.keySet().toString()); + assertEquals("[" + JOINER.join(map.values()) + "]", map.values().toString()); + + assertEquals(newHashSet(map.entrySet()), map.entrySet()); + assertEquals(newHashSet(map.keySet()), map.keySet()); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/AbstractImmutableMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/AbstractImmutableMapMapInterfaceTest.java new file mode 100644 index 000000000000..29c57ec3cc25 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/AbstractImmutableMapMapInterfaceTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Sets.newHashSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Joiner; +import com.google.common.collect.testing.MapInterfaceTest; +import com.google.common.collect.testing.MinimalSet; +import java.util.Map; +import java.util.Map.Entry; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +abstract class AbstractImmutableMapMapInterfaceTest extends MapInterfaceTest { + AbstractImmutableMapMapInterfaceTest() { + super(false, false, false, false, false); + } + + @Override + protected Map makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + private static final Joiner JOINER = Joiner.on(", "); + + @Override + protected final void assertMoreInvariants(Map map) { + // TODO: can these be moved to MapInterfaceTest? + for (Entry entry : map.entrySet()) { + assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); + } + + assertEquals("{" + JOINER.join(map.entrySet()) + "}", map.toString()); + assertEquals("[" + JOINER.join(map.entrySet()) + "]", map.entrySet().toString()); + assertEquals("[" + JOINER.join(map.keySet()) + "]", map.keySet().toString()); + assertEquals("[" + JOINER.join(map.values()) + "]", map.values().toString()); + + assertEquals(MinimalSet.from(map.entrySet()), map.entrySet()); + assertEquals(newHashSet(map.keySet()), map.keySet()); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/AbstractImmutableSetTest.java b/android/guava-tests/test/com/google/common/collect/AbstractImmutableSetTest.java index d7a8d16cadf1..1ded6f1ee474 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractImmutableSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractImmutableSetTest.java @@ -16,13 +16,20 @@ package com.google.common.collect; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Iterators.singletonIterator; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static java.util.Arrays.asList; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.base.Strings; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.MinimalIterable; @@ -33,6 +40,8 @@ import java.util.List; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Base class for {@link ImmutableSet} and {@link ImmutableSortedSet} tests. @@ -41,6 +50,7 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public abstract class AbstractImmutableSetTest extends TestCase { protected abstract > Set of(); @@ -55,7 +65,6 @@ public abstract class AbstractImmutableSetTest extends TestCase { protected abstract > Set of(E e1, E e2, E e3, E e4, E e5); - @SuppressWarnings("unchecked") protected abstract > Set of( E e1, E e2, E e3, E e4, E e5, E e6, E... rest); @@ -73,97 +82,88 @@ protected abstract > Set copyOf( public void testCreation_noArgs() { Set set = of(); assertEquals(Collections.emptySet(), set); - assertSame(of(), set); + assertSame(this.of(), set); } public void testCreation_oneElement() { Set set = of("a"); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCreation_twoElements() { Set set = of("a", "b"); - assertEquals(Sets.newHashSet("a", "b"), set); + assertEquals(newHashSet("a", "b"), set); } public void testCreation_threeElements() { Set set = of("a", "b", "c"); - assertEquals(Sets.newHashSet("a", "b", "c"), set); + assertEquals(newHashSet("a", "b", "c"), set); } public void testCreation_fourElements() { Set set = of("a", "b", "c", "d"); - assertEquals(Sets.newHashSet("a", "b", "c", "d"), set); + assertEquals(newHashSet("a", "b", "c", "d"), set); } public void testCreation_fiveElements() { Set set = of("a", "b", "c", "d", "e"); - assertEquals(Sets.newHashSet("a", "b", "c", "d", "e"), set); + assertEquals(newHashSet("a", "b", "c", "d", "e"), set); } public void testCreation_sixElements() { Set set = of("a", "b", "c", "d", "e", "f"); - assertEquals(Sets.newHashSet("a", "b", "c", "d", "e", "f"), set); + assertEquals(newHashSet("a", "b", "c", "d", "e", "f"), set); } public void testCreation_sevenElements() { Set set = of("a", "b", "c", "d", "e", "f", "g"); - assertEquals(Sets.newHashSet("a", "b", "c", "d", "e", "f", "g"), set); + assertEquals(newHashSet("a", "b", "c", "d", "e", "f", "g"), set); } public void testCreation_eightElements() { Set set = of("a", "b", "c", "d", "e", "f", "g", "h"); - assertEquals(Sets.newHashSet("a", "b", "c", "d", "e", "f", "g", "h"), set); + assertEquals(newHashSet("a", "b", "c", "d", "e", "f", "g", "h"), set); } public void testCopyOf_emptyArray() { String[] array = new String[0]; Set set = copyOf(array); assertEquals(Collections.emptySet(), set); - assertSame(of(), set); + assertSame(this.of(), set); } public void testCopyOf_arrayOfOneElement() { String[] array = new String[] {"a"}; Set set = copyOf(array); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCopyOf_nullArray() { - try { - copyOf((String[]) null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> copyOf((String[]) null)); } public void testCopyOf_arrayContainingOnlyNull() { - String[] array = new String[] {null}; - try { - copyOf(array); - fail(); - } catch (NullPointerException expected) { - } + @Nullable String[] array = new @Nullable String[] {null}; + assertThrows(NullPointerException.class, () -> copyOf((String[]) array)); } public void testCopyOf_collection_empty() { - // "" is required to work around a javac 1.5 bug. - Collection c = MinimalCollection.of(); + Collection c = MinimalCollection.of(); Set set = copyOf(c); assertEquals(Collections.emptySet(), set); - assertSame(of(), set); + assertSame(this.of(), set); } public void testCopyOf_collection_oneElement() { Collection c = MinimalCollection.of("a"); Set set = copyOf(c); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCopyOf_collection_oneElementRepeated() { Collection c = MinimalCollection.of("a", "a", "a"); Set set = copyOf(c); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCopyOf_collection_general() { @@ -175,12 +175,8 @@ public void testCopyOf_collection_general() { } public void testCopyOf_collectionContainingNull() { - Collection c = MinimalCollection.of("a", null, "b"); - try { - copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + Collection<@Nullable String> c = MinimalCollection.of("a", null, "b"); + assertThrows(NullPointerException.class, () -> copyOf((Collection) c)); } enum TestEnum { @@ -198,22 +194,22 @@ public void testCopyOf_collection_enumSet() { } public void testCopyOf_iterator_empty() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); Set set = copyOf(iterator); assertEquals(Collections.emptySet(), set); - assertSame(of(), set); + assertSame(this.of(), set); } public void testCopyOf_iterator_oneElement() { - Iterator iterator = Iterators.singletonIterator("a"); + Iterator iterator = singletonIterator("a"); Set set = copyOf(iterator); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCopyOf_iterator_oneElementRepeated() { Iterator iterator = Iterators.forArray("a", "a", "a"); Set set = copyOf(iterator); - assertEquals(Collections.singleton("a"), set); + assertEquals(singleton("a"), set); } public void testCopyOf_iterator_general() { @@ -225,12 +221,8 @@ public void testCopyOf_iterator_general() { } public void testCopyOf_iteratorContainingNull() { - Iterator c = Iterators.forArray("a", null, "b"); - try { - copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + Iterator<@Nullable String> c = Iterators.forArray("a", null, "b"); + assertThrows(NullPointerException.class, () -> copyOf((Iterator) c)); } private static class CountingIterable implements Iterable { @@ -265,7 +257,7 @@ public void testCopyOf_shortcut_empty() { public void testCopyOf_shortcut_singleton() { Collection c = of("a"); - assertEquals(Collections.singleton("a"), copyOf(c)); + assertEquals(singleton("a"), copyOf(c)); assertSame(c, copyOf(c)); } @@ -282,7 +274,7 @@ public void testToString() { @GwtIncompatible // slow (~40s) public void testIterator_oneElement() { new IteratorTester( - 5, UNMODIFIABLE, Collections.singleton("a"), IteratorTester.KnownOrder.KNOWN_ORDER) { + 5, UNMODIFIABLE, singleton("a"), IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { return of("a").iterator(); @@ -396,99 +388,147 @@ public void testComplexBuilder() { abstract int getComplexBuilderSetLastElement(); public void testBuilderAddHandlesNullsCorrectly() { - ImmutableSet.Builder builder = this.builder(); - try { - builder.add((String) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add((String) null)); } - builder = this.builder(); - try { - builder.add((String[]) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add((String[]) null)); } - builder = this.builder(); - try { - builder.add("a", (String) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add("a", (String) null)); } - builder = this.builder(); - try { - builder.add("a", "b", (String) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add("a", "b", (String) null)); } - builder = this.builder(); - try { - builder.add("a", "b", "c", null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add("a", "b", "c", null)); } - builder = this.builder(); - try { - builder.add("a", "b", null, "c"); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.add("a", "b", null, "c")); } } public void testBuilderAddAllHandlesNullsCorrectly() { - ImmutableSet.Builder builder = this.builder(); - try { - builder.addAll((Iterable) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Iterable) null)); } - try { - builder.addAll((Iterator) null); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Iterator) null)); } - builder = this.builder(); - List listWithNulls = asList("a", null, "b"); - try { - builder.addAll(listWithNulls); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + List<@Nullable String> listWithNulls = asList("a", null, "b"); + assertThrows(NullPointerException.class, () -> builder.addAll((List) listWithNulls)); } - Iterable iterableWithNulls = MinimalIterable.of("a", null, "b"); - try { - builder.addAll(iterableWithNulls); - fail("expected NullPointerException"); // COV_NF_LINE - } catch (NullPointerException expected) { + { + ImmutableSet.Builder builder = this.builder(); + Iterable<@Nullable String> iterableWithNulls = MinimalIterable.of("a", null, "b"); + assertThrows( + NullPointerException.class, () -> builder.addAll((Iterable) iterableWithNulls)); } } /** * Verify thread safety by using a collection whose size() may be inconsistent with the actual - * number of elements. Tests using this method might fail in GWT because the GWT emulations might - * count on size() during copy. It is safe to do so in GWT because javascript is single-threaded. + * number of elements and whose elements may change over time. + * + *

    This test might fail in GWT because the GWT emulations might count on the input collection + * not to change during the copy. It is safe to do so in GWT because javascript is + * single-threaded. */ - // TODO(benyu): turn this into a test once all copyOf(Collection) are - // thread-safe @GwtIncompatible // GWT is single threaded - void verifyThreadSafe() { - List sample = Lists.newArrayList("a", "b", "c"); - for (int delta : new int[] {-1, 0, 1}) { - for (int i = 0; i < sample.size(); i++) { - Collection misleading = Helpers.misleadingSizeCollection(delta); - List expected = sample.subList(0, i); - misleading.addAll(expected); - assertEquals( - "delta: " + delta + " sample size: " + i, - Sets.newHashSet(expected), - copyOf(misleading)); + public void testCopyOf_threadSafe() { + /* + * The actual collections that we pass as inputs will be wrappers around these, so + * ImmutableSet.copyOf won't short-circuit because it won't see an ImmutableSet input. + */ + ImmutableList> distinctCandidatesByAscendingSize = + ImmutableList.of( + ImmutableSet.of(), + ImmutableSet.of("a"), + ImmutableSet.of("b", "a"), + ImmutableSet.of("c", "b", "a"), + ImmutableSet.of("d", "c", "b", "a")); + for (boolean byAscendingSize : new boolean[] {true, false}) { + Iterable> infiniteSets = + Iterables.cycle( + byAscendingSize + ? distinctCandidatesByAscendingSize + : Lists.reverse(distinctCandidatesByAscendingSize)); + for (int startIndex = 0; + startIndex < distinctCandidatesByAscendingSize.size(); + startIndex++) { + Iterable> infiniteSetsFromStartIndex = + Iterables.skip(infiniteSets, startIndex); + for (boolean inputIsSet : new boolean[] {true, false}) { + Collection input = + inputIsSet + ? new MutatedOnQuerySet<>(infiniteSetsFromStartIndex) + : new MutatedOnQueryList<>( + transform(infiniteSetsFromStartIndex, ImmutableList::copyOf)); + Set immutableCopy; + try { + immutableCopy = copyOf(input); + } catch (RuntimeException e) { + throw new RuntimeException( + Strings.lenientFormat( + "byAscendingSize %s, startIndex %s, inputIsSet %s", + byAscendingSize, startIndex, inputIsSet), + e); + } + /* + * TODO(cpovirk): Check that the values match one of candidates that + * MutatedOnQuery*.delegate() actually returned during this test? + */ + assertWithMessage( + "byAscendingSize %s, startIndex %s, inputIsSet %s", + byAscendingSize, startIndex, inputIsSet) + .that(immutableCopy) + .isIn(distinctCandidatesByAscendingSize); + } } } } + + private static final class MutatedOnQuerySet extends ForwardingSet { + final Iterator> infiniteCandidates; + + MutatedOnQuerySet(Iterable> infiniteCandidates) { + this.infiniteCandidates = infiniteCandidates.iterator(); + } + + @Override + protected Set delegate() { + return infiniteCandidates.next(); + } + } + + private static final class MutatedOnQueryList extends ForwardingList { + final Iterator> infiniteCandidates; + + MutatedOnQueryList(Iterable> infiniteCandidates) { + this.infiniteCandidates = infiniteCandidates.iterator(); + } + + @Override + protected List delegate() { + return infiniteCandidates.next(); + } + } } diff --git a/android/guava-tests/test/com/google/common/collect/AbstractImmutableSortedMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/AbstractImmutableSortedMapMapInterfaceTest.java new file mode 100644 index 000000000000..30110ad5719e --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/AbstractImmutableSortedMapMapInterfaceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Sets.newHashSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Joiner; +import com.google.common.collect.testing.SortedMapInterfaceTest; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public abstract class AbstractImmutableSortedMapMapInterfaceTest + extends SortedMapInterfaceTest { + public AbstractImmutableSortedMapMapInterfaceTest() { + super(false, false, false, false, false); + } + + @Override + protected SortedMap makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + private static final Joiner JOINER = Joiner.on(", "); + + @Override + protected void assertMoreInvariants(Map map) { + // TODO: can these be moved to MapInterfaceTest? + for (Entry entry : map.entrySet()) { + assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); + } + + assertEquals("{" + JOINER.join(map.entrySet()) + "}", map.toString()); + assertEquals("[" + JOINER.join(map.entrySet()) + "]", map.entrySet().toString()); + assertEquals("[" + JOINER.join(map.keySet()) + "]", map.keySet().toString()); + assertEquals("[" + JOINER.join(map.values()) + "]", map.values().toString()); + + assertEquals(newHashSet(map.entrySet()), map.entrySet()); + assertEquals(newHashSet(map.keySet()), map.keySet()); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/AbstractImmutableTableTest.java b/android/guava-tests/test/com/google/common/collect/AbstractImmutableTableTest.java index ffff9f52b667..6f1b6f7e8c6b 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractImmutableTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractImmutableTableTest.java @@ -16,8 +16,11 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests {@link ImmutableTable} @@ -25,51 +28,34 @@ * @author Gregory Kick */ @GwtCompatible +@NullMarked public abstract class AbstractImmutableTableTest extends TestCase { abstract Iterable> getTestInstances(); public final void testClear() { for (Table testInstance : getTestInstances()) { - try { - testInstance.clear(); - fail(); - } catch (UnsupportedOperationException e) { - // success - } + assertThrows(UnsupportedOperationException.class, () -> testInstance.clear()); } } public final void testPut() { for (Table testInstance : getTestInstances()) { - try { - testInstance.put('a', 1, "blah"); - fail(); - } catch (UnsupportedOperationException e) { - // success - } + assertThrows(UnsupportedOperationException.class, () -> testInstance.put('a', 1, "blah")); } } public final void testPutAll() { for (Table testInstance : getTestInstances()) { - try { - testInstance.putAll(ImmutableTable.of('a', 1, "blah")); - fail(); - } catch (UnsupportedOperationException e) { - // success - } + assertThrows( + UnsupportedOperationException.class, + () -> testInstance.putAll(ImmutableTable.of('a', 1, "blah"))); } } public final void testRemove() { for (Table testInstance : getTestInstances()) { - try { - testInstance.remove('a', 1); - fail(); - } catch (UnsupportedOperationException e) { - // success - } + assertThrows(UnsupportedOperationException.class, () -> testInstance.remove('a', 1)); } } diff --git a/android/guava-tests/test/com/google/common/collect/AbstractIteratorTest.java b/android/guava-tests/test/com/google/common/collect/AbstractIteratorTest.java index a76907cda7c7..8c706a62e346 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractIteratorTest.java @@ -16,13 +16,21 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.SneakyThrows.sneakyThrow; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.collect.TestExceptions.SomeCheckedException; +import com.google.common.collect.TestExceptions.SomeUncheckedException; import com.google.common.testing.GcFinalization; import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code AbstractIterator}. @@ -31,6 +39,7 @@ */ @SuppressWarnings("serial") // No serialization is used in this test @GwtCompatible(emulated = true) +@NullMarked public class AbstractIteratorTest extends TestCase { public void testDefaultBehaviorOfNextAndHasNext() { @@ -42,7 +51,7 @@ public void testDefaultBehaviorOfNextAndHasNext() { private int rep; @Override - public Integer computeNext() { + public @Nullable Integer computeNext() { switch (rep++) { case 0: return 0; @@ -51,8 +60,7 @@ public Integer computeNext() { case 2: return endOfData(); default: - fail("Should not have been invoked again"); - return null; + throw new AssertionError("Should not have been invoked again"); } } }; @@ -71,11 +79,7 @@ public Integer computeNext() { // Make sure computeNext() doesn't get invoked again assertFalse(iter.hasNext()); - try { - iter.next(); - fail("no exception thrown"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, iter::next); } public void testDefaultBehaviorOfPeek() { @@ -88,7 +92,7 @@ public void testDefaultBehaviorOfPeek() { private int rep; @Override - public Integer computeNext() { + public @Nullable Integer computeNext() { switch (rep++) { case 0: return 0; @@ -97,8 +101,7 @@ public Integer computeNext() { case 2: return endOfData(); default: - fail("Should not have been invoked again"); - return null; + throw new AssertionError("Should not have been invoked again"); } } }; @@ -112,33 +115,20 @@ public Integer computeNext() { assertEquals(1, (int) iter.peek()); assertEquals(1, (int) iter.next()); - try { - iter.peek(); - fail("peek() should throw NoSuchElementException at end"); - } catch (NoSuchElementException expected) { - } - - try { - iter.peek(); - fail("peek() should continue to throw NoSuchElementException at end"); - } catch (NoSuchElementException expected) { - } - - try { - iter.next(); - fail("next() should throw NoSuchElementException as usual"); - } catch (NoSuchElementException expected) { - } - - try { - iter.peek(); - fail("peek() should still throw NoSuchElementException after next()"); - } catch (NoSuchElementException expected) { - } + /* + * We test peek() after various calls to make sure that one bad call doesn't interfere with its + * ability to throw the correct exception in the future. + */ + assertThrows(NoSuchElementException.class, iter::peek); + assertThrows(NoSuchElementException.class, iter::peek); + assertThrows(NoSuchElementException.class, iter::next); + assertThrows(NoSuchElementException.class, iter::peek); } + @J2ktIncompatible // weak references, details of GC @GwtIncompatible // weak references + @AndroidIncompatible // depends on details of GC public void testFreesNextReference() { Iterator itr = new AbstractIterator() { @@ -158,7 +148,7 @@ public void testDefaultBehaviorOfPeekForEmptyIteration() { private boolean alreadyCalledEndOfData; @Override - public Integer computeNext() { + public @Nullable Integer computeNext() { if (alreadyCalledEndOfData) { fail("Should not have been invoked again"); } @@ -167,17 +157,12 @@ public Integer computeNext() { } }; - try { - empty.peek(); - fail("peek() should throw NoSuchElementException at end"); - } catch (NoSuchElementException expected) { - } - - try { - empty.peek(); - fail("peek() should continue to throw NoSuchElementException at end"); - } catch (NoSuchElementException expected) { - } + /* + * We test multiple calls to peek() to make sure that one bad call doesn't interfere with its + * ability to throw the correct exception in the future. + */ + assertThrows(NoSuchElementException.class, empty::peek); + assertThrows(NoSuchElementException.class, empty::peek); } public void testSneakyThrow() throws Exception { @@ -188,31 +173,18 @@ public void testSneakyThrow() throws Exception { @Override public Integer computeNext() { if (haveBeenCalled) { - fail("Should not have been called again"); + throw new AssertionError("Should not have been called again"); } else { haveBeenCalled = true; - sneakyThrow(new SomeCheckedException()); + throw sneakyThrow(new SomeCheckedException()); } - return null; // never reached } }; // The first time, the sneakily-thrown exception comes out - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (Exception e) { - if (!(e instanceof SomeCheckedException)) { - throw e; - } - } - + assertThrows(SomeCheckedException.class, iter::hasNext); // But the second time, AbstractIterator itself throws an ISE - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iter::hasNext); } public void testException() { @@ -226,12 +198,8 @@ public Integer computeNext() { }; // It should pass through untouched - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (SomeUncheckedException e) { - assertSame(exception, e); - } + SomeUncheckedException e = assertThrows(SomeUncheckedException.class, iter::hasNext); + assertSame(exception, e); } public void testExceptionAfterEndOfData() { @@ -243,11 +211,7 @@ public Integer computeNext() { throw new SomeUncheckedException(); } }; - try { - iter.hasNext(); - fail("No exception thrown"); - } catch (SomeUncheckedException expected) { - } + assertThrows(SomeUncheckedException.class, iter::hasNext); } @SuppressWarnings("DoNotCall") @@ -268,11 +232,7 @@ public Integer computeNext() { assertEquals(0, (int) iter.next()); - try { - iter.remove(); - fail("No exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, iter::remove); } public void testReentrantHasNext() { @@ -281,32 +241,13 @@ public void testReentrantHasNext() { @Override protected Integer computeNext() { boolean unused = hasNext(); - return null; + throw new AssertionError(); } }; - try { - iter.hasNext(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, iter::hasNext); } // Technically we should test other reentrant scenarios (9 combinations of // hasNext/next/peek), but we'll cop out for now, knowing that peek() and // next() both start by invoking hasNext() anyway. - - /** Throws a undeclared checked exception. */ - private static void sneakyThrow(Throwable t) { - class SneakyThrower { - @SuppressWarnings("unchecked") // not really safe, but that's the point - void throwIt(Throwable t) throws T { - throw (T) t; - } - } - new SneakyThrower().throwIt(t); - } - - private static class SomeCheckedException extends Exception {} - - private static class SomeUncheckedException extends RuntimeException {} } diff --git a/android/guava-tests/test/com/google/common/collect/AbstractMapEntryTest.java b/android/guava-tests/test/com/google/common/collect/AbstractMapEntryTest.java index 63c17dedaeca..7e58e81b4303 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractMapEntryTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractMapEntryTest.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static java.util.Collections.singletonMap; + import com.google.common.annotations.GwtCompatible; -import java.util.Collections; import java.util.Map.Entry; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code AbstractMapEntry}. @@ -27,11 +30,13 @@ * @author Mike Bostock */ @GwtCompatible +@NullMarked public class AbstractMapEntryTest extends TestCase { - private static final String NK = null; - private static final Integer NV = null; + private static final @Nullable String NK = null; + private static final @Nullable Integer NV = null; - private static Entry entry(final K key, final V value) { + private static Entry entry( + final K key, final V value) { return new AbstractMapEntry() { @Override public K getKey() { @@ -45,8 +50,9 @@ public V getValue() { }; } - private static Entry control(K key, V value) { - return Collections.singletonMap(key, value).entrySet().iterator().next(); + private static Entry control( + K key, V value) { + return singletonMap(key, value).entrySet().iterator().next(); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/collect/AbstractMapsTransformValuesTest.java b/android/guava-tests/test/com/google/common/collect/AbstractMapsTransformValuesTest.java new file mode 100644 index 000000000000..e529b1f33660 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/AbstractMapsTransformValuesTest.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Maps.transformValues; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.testing.MapInterfaceTest; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * Superclass for tests for {@link Maps#transformValues} overloads. + * + * @author Isaac Shum + */ +@GwtCompatible +@NullMarked +abstract class AbstractMapsTransformValuesTest extends MapInterfaceTest { + public AbstractMapsTransformValuesTest() { + super(false, true, false, true, true); + } + + @Override + protected String getKeyNotInPopulatedMap() throws UnsupportedOperationException { + return "z"; + } + + @Override + protected String getValueNotInPopulatedMap() throws UnsupportedOperationException { + return "26"; + } + + /** Helper assertion comparing two maps */ + private void assertMapsEqual(Map expected, Map map) { + assertEquals(expected, map); + assertEquals(expected.hashCode(), map.hashCode()); + assertEquals(expected.entrySet(), map.entrySet()); + + // Assert that expectedValues > mapValues and that + // mapValues > expectedValues; i.e. that expectedValues == mapValues. + Collection expectedValues = expected.values(); + Collection mapValues = map.values(); + assertEquals(expectedValues.size(), mapValues.size()); + assertTrue(expectedValues.containsAll(mapValues)); + assertTrue(mapValues.containsAll(expectedValues)); + } + + public void testTransformEmptyMapEquality() { + Map map = + transformValues(ImmutableMap.of(), Functions.toStringFunction()); + assertMapsEqual(Maps.newHashMap(), map); + } + + public void testTransformSingletonMapEquality() { + Map map = + transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); + Map expected = ImmutableMap.of("a", "1"); + assertMapsEqual(expected, map); + assertEquals(expected.get("a"), map.get("a")); + } + + public void testTransformIdentityFunctionEquality() { + Map underlying = ImmutableMap.of("a", 1); + Map map = transformValues(underlying, Functions.identity()); + assertMapsEqual(underlying, map); + } + + public void testTransformPutEntryIsUnsupported() { + Map map = + transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); + assertThrows(UnsupportedOperationException.class, () -> map.put("b", "2")); + + assertThrows(UnsupportedOperationException.class, () -> map.putAll(ImmutableMap.of("b", "2"))); + + assertThrows( + UnsupportedOperationException.class, + () -> map.entrySet().iterator().next().setValue("one")); + } + + public void testTransformRemoveEntry() { + Map underlying = Maps.newHashMap(); + underlying.put("a", 1); + Map map = transformValues(underlying, Functions.toStringFunction()); + assertEquals("1", map.remove("a")); + assertNull(map.remove("b")); + } + + public void testTransformEqualityOfMapsWithNullValues() { + Map underlying = Maps.newHashMap(); + underlying.put("a", null); + underlying.put("b", ""); + + Map map = + transformValues( + underlying, + new Function<@Nullable String, Boolean>() { + @Override + public Boolean apply(@Nullable String from) { + return from == null; + } + }); + Map expected = ImmutableMap.of("a", true, "b", false); + assertMapsEqual(expected, map); + assertEquals(expected.get("a"), map.get("a")); + assertEquals(expected.containsKey("a"), map.containsKey("a")); + assertEquals(expected.get("b"), map.get("b")); + assertEquals(expected.containsKey("b"), map.containsKey("b")); + assertEquals(expected.get("c"), map.get("c")); + assertEquals(expected.containsKey("c"), map.containsKey("c")); + } + + public void testTransformReflectsUnderlyingMap() { + Map underlying = Maps.newHashMap(); + underlying.put("a", 1); + underlying.put("b", 2); + underlying.put("c", 3); + Map map = transformValues(underlying, Functions.toStringFunction()); + assertEquals(underlying.size(), map.size()); + + underlying.put("d", 4); + assertEquals(underlying.size(), map.size()); + assertEquals("4", map.get("d")); + + underlying.remove("c"); + assertEquals(underlying.size(), map.size()); + assertFalse(map.containsKey("c")); + + underlying.clear(); + assertEquals(underlying.size(), map.size()); + } + + public void testTransformChangesAreReflectedInUnderlyingMap() { + Map underlying = Maps.newLinkedHashMap(); + underlying.put("a", 1); + underlying.put("b", 2); + underlying.put("c", 3); + underlying.put("d", 4); + underlying.put("e", 5); + underlying.put("f", 6); + underlying.put("g", 7); + Map map = transformValues(underlying, Functions.toStringFunction()); + + map.remove("a"); + assertFalse(underlying.containsKey("a")); + + Set keys = map.keySet(); + keys.remove("b"); + assertFalse(underlying.containsKey("b")); + + Iterator keyIterator = keys.iterator(); + keyIterator.next(); + keyIterator.remove(); + assertFalse(underlying.containsKey("c")); + + Collection values = map.values(); + values.remove("4"); + assertFalse(underlying.containsKey("d")); + + Iterator valueIterator = values.iterator(); + valueIterator.next(); + valueIterator.remove(); + assertFalse(underlying.containsKey("e")); + + Set> entries = map.entrySet(); + Entry firstEntry = entries.iterator().next(); + entries.remove(firstEntry); + assertFalse(underlying.containsKey("f")); + + Iterator> entryIterator = entries.iterator(); + entryIterator.next(); + entryIterator.remove(); + assertFalse(underlying.containsKey("g")); + + assertTrue(underlying.isEmpty()); + assertTrue(map.isEmpty()); + assertTrue(keys.isEmpty()); + assertTrue(values.isEmpty()); + assertTrue(entries.isEmpty()); + } + + public void testTransformEquals() { + Map underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2); + Map expected = transformValues(underlying, Functions.identity()); + + assertMapsEqual(expected, expected); + + Map equalToUnderlying = Maps.newTreeMap(); + equalToUnderlying.putAll(underlying); + Map map = transformValues(equalToUnderlying, Functions.identity()); + assertMapsEqual(expected, map); + + map = + transformValues( + ImmutableMap.of("a", 1, "b", 2, "c", 3), + new Function() { + @Override + public Integer apply(Integer from) { + return from - 1; + } + }); + assertMapsEqual(expected, map); + } + + public void testTransformEntrySetContains() { + Map<@Nullable String, @Nullable Boolean> underlying = Maps.newHashMap(); + underlying.put("a", null); + underlying.put("b", true); + underlying.put(null, true); + + Map<@Nullable String, @Nullable Boolean> map = + transformValues( + underlying, + new Function<@Nullable Boolean, @Nullable Boolean>() { + @Override + public @Nullable Boolean apply(@Nullable Boolean from) { + return (from == null) ? true : null; + } + }); + + Set> entries = map.entrySet(); + assertTrue(entries.contains(immutableEntry("a", true))); + assertTrue(entries.contains(Maps.immutableEntry("b", null))); + assertTrue( + entries.contains(Maps.<@Nullable String, @Nullable Boolean>immutableEntry(null, null))); + + assertFalse(entries.contains(Maps.immutableEntry("c", null))); + assertFalse(entries.contains(Maps.<@Nullable String, Boolean>immutableEntry(null, true))); + } + + @Override + public void testKeySetRemoveAllNullFromEmpty() { + try { + super.testKeySetRemoveAllNullFromEmpty(); + } catch (RuntimeException tolerated) { + // GWT's HashMap.keySet().removeAll(null) doesn't throws NPE. + } + } + + @Override + public void testEntrySetRemoveAllNullFromEmpty() { + try { + super.testEntrySetRemoveAllNullFromEmpty(); + } catch (RuntimeException tolerated) { + // GWT's HashMap.entrySet().removeAll(null) doesn't throws NPE. + } + } +} diff --git a/android/guava-tests/test/com/google/common/collect/AbstractMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/AbstractMultimapAsMapImplementsMapTest.java index 3e41dec8eb5a..682f16d54d5a 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractMultimapAsMapImplementsMapTest.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@link Multimap#asMap()} for an arbitrary multimap with {@link MapInterfaceTest}. @@ -28,6 +31,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public abstract class AbstractMultimapAsMapImplementsMapTest extends MapInterfaceTest> { @@ -64,13 +68,12 @@ protected Collection getValueNotInPopulatedMap() throws UnsupportedOper @Override public void testRemove() { final Map> map; - final String keyToRemove; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { return; } - keyToRemove = map.keySet().iterator().next(); + final String keyToRemove = map.keySet().iterator().next(); if (supportsRemove) { int initialSize = map.size(); map.get(keyToRemove); @@ -80,11 +83,7 @@ public void testRemove() { assertFalse(map.containsKey(keyToRemove)); assertEquals(initialSize - 1, map.size()); } else { - try { - map.remove(keyToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.remove(keyToRemove)); } assertInvariants(map); } diff --git a/android/guava-tests/test/com/google/common/collect/AbstractRangeSetTest.java b/android/guava-tests/test/com/google/common/collect/AbstractRangeSetTest.java index e53bea1bbcf9..90b8a4792aad 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractRangeSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractRangeSetTest.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Base class for {@link RangeSet} tests. @@ -26,13 +27,14 @@ * @author Louis Wasserman */ @GwtIncompatible // TreeRangeSet +@NullUnmarked public abstract class AbstractRangeSetTest extends TestCase { public static void testInvariants(RangeSet rangeSet) { testInvariantsInternal(rangeSet); testInvariantsInternal(rangeSet.complement()); } - private static void testInvariantsInternal(RangeSet rangeSet) { + private static > void testInvariantsInternal(RangeSet rangeSet) { assertEquals(rangeSet.asRanges().isEmpty(), rangeSet.isEmpty()); assertEquals(rangeSet.asDescendingSetOfRanges().isEmpty(), rangeSet.isEmpty()); assertEquals(!rangeSet.asRanges().iterator().hasNext(), rangeSet.isEmpty()); diff --git a/android/guava-tests/test/com/google/common/collect/AbstractSequentialIteratorTest.java b/android/guava-tests/test/com/google/common/collect/AbstractSequentialIteratorTest.java index c0b7d91c4cf4..3d0b4776be97 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractSequentialIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractSequentialIteratorTest.java @@ -16,19 +16,24 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.TestExceptions.SomeUncheckedException; import com.google.common.collect.testing.IteratorTester; import java.util.Iterator; import java.util.NoSuchElementException; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** Tests for {@link AbstractSequentialIterator}. */ @GwtCompatible(emulated = true) +@NullMarked public class AbstractSequentialIteratorTest extends TestCase { @GwtIncompatible // Too slow public void testDoublerExhaustive() { @@ -59,7 +64,8 @@ public void testSampleCode() { public Iterator iterator() { Iterator powersOfTwo = new AbstractSequentialIterator(1) { - protected Integer computeNext(Integer previous) { + @Override + protected @Nullable Integer computeNext(Integer previous) { return (previous == 1 << 30) ? null : previous * 2; } }; @@ -102,63 +108,52 @@ protected Integer computeNext(Integer previous) { .inOrder(); } + @SuppressWarnings("DoNotCall") public void testEmpty() { - Iterator empty = newEmpty(); + Iterator empty = new EmptyAbstractSequentialIterator<>(); assertFalse(empty.hasNext()); - try { - empty.next(); - fail(); - } catch (NoSuchElementException expected) { - } - try { - empty.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(NoSuchElementException.class, empty::next); + assertThrows(UnsupportedOperationException.class, empty::remove); } public void testBroken() { - Iterator broken = newBroken(); + Iterator broken = new BrokenAbstractSequentialIterator(); assertTrue(broken.hasNext()); // We can't retrieve even the known first element: - try { - broken.next(); - fail(); - } catch (MyException expected) { - } - try { - broken.next(); - fail(); - } catch (MyException expected) { - } + assertThrows(SomeUncheckedException.class, broken::next); + assertThrows(SomeUncheckedException.class, broken::next); } private static Iterator newDoubler(int first, final int last) { return new AbstractSequentialIterator(first) { @Override - protected Integer computeNext(Integer previous) { + protected @Nullable Integer computeNext(Integer previous) { return (previous == last) ? null : previous * 2; } }; } - private static Iterator newEmpty() { - return new AbstractSequentialIterator(null) { - @Override - protected T computeNext(T previous) { - throw new AssertionFailedError(); - } - }; - } + private static class EmptyAbstractSequentialIterator extends AbstractSequentialIterator { - private static Iterator newBroken() { - return new AbstractSequentialIterator("UNUSED") { - @Override - protected Object computeNext(Object previous) { - throw new MyException(); - } - }; + public EmptyAbstractSequentialIterator() { + super(null); + } + + @Override + protected T computeNext(T previous) { + throw new AssertionFailedError(); + } } - private static class MyException extends RuntimeException {} + private static class BrokenAbstractSequentialIterator extends AbstractSequentialIterator { + + public BrokenAbstractSequentialIterator() { + super("UNUSED"); + } + + @Override + protected Object computeNext(Object previous) { + throw new SomeUncheckedException(); + } + } } diff --git a/android/guava-tests/test/com/google/common/collect/AbstractTableReadTest.java b/android/guava-tests/test/com/google/common/collect/AbstractTableReadTest.java index 67d44cb9b680..a613102dac08 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractTableReadTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractTableReadTest.java @@ -16,14 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link Table} read operations. @@ -31,8 +35,9 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -public abstract class AbstractTableReadTest extends TestCase { - protected Table table; +@NullMarked +public abstract class AbstractTableReadTest extends TestCase { + protected Table table; /** * Creates a table with the specified data. @@ -41,7 +46,7 @@ public abstract class AbstractTableReadTest extends TestCase { * @throws IllegalArgumentException if the size of {@code data} isn't a multiple of 3 * @throws ClassCastException if a data element has the wrong type */ - protected abstract Table create(Object... data); + protected abstract Table create(@Nullable Object... data); protected void assertSize(int expectedSize) { assertEquals(expectedSize, table.size()); @@ -118,14 +123,13 @@ public void testSize() { public void testEquals() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Table hashCopy = HashBasedTable.create(table); - Table reordered = - create("foo", 3, 'c', "foo", 1, 'a', "bar", 1, 'b'); - Table smaller = create("foo", 1, 'a', "bar", 1, 'b'); - Table swapOuter = - create("bar", 1, 'a', "foo", 1, 'b', "bar", 3, 'c'); - Table swapValues = - create("foo", 1, 'c', "bar", 1, 'b', "foo", 3, 'a'); + // We know that we have only added non-null Characters. + Table hashCopy = + HashBasedTable.create((Table) table); + Table reordered = create("foo", 3, 'c', "foo", 1, 'a', "bar", 1, 'b'); + Table smaller = create("foo", 1, 'a', "bar", 1, 'b'); + Table swapOuter = create("bar", 1, 'a', "foo", 1, 'b', "bar", 3, 'c'); + Table swapValues = create("foo", 1, 'c', "bar", 1, 'b', "foo", 3, 'a'); new EqualsTester() .addEqualityGroup(table, hashCopy, reordered) @@ -157,11 +161,7 @@ public void testRow() { // This test assumes that the implementation does not support null keys. public void testRowNull() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - try { - table.row(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> table.row(null)); } public void testColumn() { @@ -172,11 +172,7 @@ public void testColumn() { // This test assumes that the implementation does not support null keys. public void testColumnNull() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - try { - table.column(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> table.column(null)); } public void testColumnSetPartialOverlap() { @@ -184,6 +180,7 @@ public void testColumnSetPartialOverlap() { assertThat(table.columnKeySet()).containsExactly(1, 2, 3); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerInstance() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 2, 'c', "bar", 3, 'd'); diff --git a/android/guava-tests/test/com/google/common/collect/AbstractTableTest.java b/android/guava-tests/test/com/google/common/collect/AbstractTableTest.java index f634b6a8c724..c0b267b0bc7d 100644 --- a/android/guava-tests/test/com/google/common/collect/AbstractTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/AbstractTableTest.java @@ -17,9 +17,13 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import java.util.Map; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for a {@link Table} implementation supporting reads and writes. @@ -28,12 +32,15 @@ * @author Louis Wasserman */ @GwtCompatible -public abstract class AbstractTableTest extends AbstractTableReadTest { +@NullMarked +public abstract class AbstractTableTest + extends AbstractTableReadTest { - protected void populate(Table table, Object... data) { + protected void populate(Table table, @Nullable Object... data) { checkArgument(data.length % 3 == 0); for (int i = 0; i < data.length; i += 3) { - table.put((String) data[i], (Integer) data[i + 1], (Character) data[i + 2]); + table.put( + (String) data[i], (Integer) data[i + 1], nullableCellValue((Character) data[i + 2])); } } @@ -52,50 +59,33 @@ public void testClear() { assertEquals(0, table.size()); assertFalse(table.containsRow("foo")); } else { - try { - table.clear(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> table.clear()); } } public void testPut() { - assertNull(table.put("foo", 1, 'a')); - assertNull(table.put("bar", 1, 'b')); - assertNull(table.put("foo", 3, 'c')); - assertEquals((Character) 'a', table.put("foo", 1, 'd')); + assertNull(table.put("foo", 1, cellValue('a'))); + assertNull(table.put("bar", 1, cellValue('b'))); + assertNull(table.put("foo", 3, cellValue('c'))); + assertEquals((Character) 'a', table.put("foo", 1, cellValue('d'))); assertEquals((Character) 'd', table.get("foo", 1)); assertEquals((Character) 'b', table.get("bar", 1)); assertSize(3); - assertEquals((Character) 'd', table.put("foo", 1, 'd')); + assertEquals((Character) 'd', table.put("foo", 1, cellValue('d'))); assertEquals((Character) 'd', table.get("foo", 1)); assertSize(3); } - // This test assumes that the implementation does not support nulls. public void testPutNull() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); assertSize(3); - try { - table.put(null, 2, 'd'); - fail(); - } catch (NullPointerException expected) { - } - try { - table.put("cat", null, 'd'); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> table.put(null, 2, cellValue('d'))); + assertThrows(NullPointerException.class, () -> table.put("cat", null, cellValue('d'))); if (supportsNullValues()) { assertNull(table.put("cat", 2, null)); assertTrue(table.contains("cat", 2)); } else { - try { - table.put("cat", 2, null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> table.put("cat", 2, null)); } assertSize(3); } @@ -104,23 +94,19 @@ public void testPutNullReplace() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); if (supportsNullValues()) { - assertEquals((Character) 'b', table.put("bar", 1, null)); + assertEquals((Character) 'b', table.put("bar", 1, nullableCellValue(null))); assertNull(table.get("bar", 1)); } else { - try { - table.put("bar", 1, null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> table.put("bar", 1, nullableCellValue(null))); } } public void testPutAllTable() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Table other = HashBasedTable.create(); - other.put("foo", 1, 'd'); - other.put("bar", 2, 'e'); - other.put("cat", 2, 'f'); + Table other = HashBasedTable.create(); + other.put("foo", 1, cellValue('d')); + other.put("bar", 2, cellValue('e')); + other.put("cat", 2, cellValue('f')); table.putAll(other); assertEquals((Character) 'd', table.get("foo", 1)); assertEquals((Character) 'b', table.get("bar", 1)); @@ -146,11 +132,7 @@ public void testRemove() { assertNull(table.remove(null, null)); assertSize(2); } else { - try { - table.remove("foo", 3); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> table.remove("foo", 3)); assertEquals((Character) 'c', table.get("foo", 3)); } } @@ -158,18 +140,29 @@ public void testRemove() { public void testRowClearAndPut() { if (supportsRemove()) { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Map row = table.row("foo"); + Map row = table.row("foo"); assertEquals(ImmutableMap.of(1, 'a', 3, 'c'), row); table.remove("foo", 3); assertEquals(ImmutableMap.of(1, 'a'), row); table.remove("foo", 1); assertEquals(ImmutableMap.of(), row); - table.put("foo", 2, 'b'); + table.put("foo", 2, cellValue('b')); assertEquals(ImmutableMap.of(2, 'b'), row); row.clear(); assertEquals(ImmutableMap.of(), row); - table.put("foo", 5, 'x'); + table.put("foo", 5, cellValue('x')); assertEquals(ImmutableMap.of(5, 'x'), row); } } + + @SuppressWarnings("unchecked") // C can only be @Nullable Character or Character + protected @NonNull C cellValue(Character character) { + return (C) character; + } + + // Only safe wrt. ClassCastException. Not null-safe (can be used to test expected Table NPEs) + @SuppressWarnings("unchecked") + protected C nullableCellValue(@Nullable Character character) { + return (C) character; + } } diff --git a/android/guava-tests/test/com/google/common/collect/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/collect/AndroidIncompatible.java index 6600c1f6946f..21fd882d0bed 100644 --- a/android/guava-tests/test/com/google/common/collect/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/collect/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/collect/ArrayListMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ArrayListMultimapTest.java index 08e5ae8ccf2c..b92deb516a58 100644 --- a/android/guava-tests/test/com/google/common/collect/ArrayListMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ArrayListMultimapTest.java @@ -16,11 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -33,6 +35,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@code ArrayListMultimap}. @@ -40,9 +43,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ArrayListMultimapTest extends TestCase { @GwtIncompatible // suite + @J2ktIncompatible + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -116,11 +122,7 @@ public void testSublistConcurrentModificationException() { assertTrue(sublist.isEmpty()); multimap.put("foo", 6); - try { - sublist.isEmpty(); - fail("Expected ConcurrentModificationException"); - } catch (ConcurrentModificationException expected) { - } + assertThrows(ConcurrentModificationException.class, () -> sublist.isEmpty()); } public void testCreateFromMultimap() { @@ -143,17 +145,9 @@ public void testCreateFromSizes() { } public void testCreateFromIllegalSizes() { - try { - ArrayListMultimap.create(15, -2); - fail(); - } catch (IllegalArgumentException expected) { - } - - try { - ArrayListMultimap.create(-15, 2); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ArrayListMultimap.create(15, -2)); + + assertThrows(IllegalArgumentException.class, () -> ArrayListMultimap.create(-15, 2)); } public void testCreateFromHashMultimap() { diff --git a/android/guava-tests/test/com/google/common/collect/ArrayTableColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/ArrayTableColumnMapTest.java new file mode 100644 index 000000000000..f73153084816 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ArrayTableColumnMapTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // TODO(hhchan): ArrayTable +@NullUnmarked +public class ArrayTableColumnMapTest extends ColumnMapTests { + public ArrayTableColumnMapTest() { + super(true, false, false, false); + } + + @Override + Table makeTable() { + return ArrayTable.create(asList(1, 2, 3), asList("foo", "bar", "dog")); + } + + @Override + protected Map> makeEmptyMap() { + throw new UnsupportedOperationException(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ArrayTableColumnTest.java b/android/guava-tests/test/com/google/common/collect/ArrayTableColumnTest.java new file mode 100644 index 000000000000..902e5ea7b00f --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ArrayTableColumnTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // TODO(hhchan): ArrayTable +@NullUnmarked +public class ArrayTableColumnTest extends ColumnTests { + public ArrayTableColumnTest() { + super(true, true, false, false, false); + } + + @Override + protected String getKeyNotInPopulatedMap() { + throw new UnsupportedOperationException(); + } + + @Override + protected Map makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + @Override + Table makeTable() { + return ArrayTable.create(asList("one", "two", "three", "four"), asList('a', 'b', 'c')); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ArrayTableRowMapTest.java b/android/guava-tests/test/com/google/common/collect/ArrayTableRowMapTest.java new file mode 100644 index 000000000000..082a43596f9d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ArrayTableRowMapTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // TODO(hhchan): ArrayTable +@NullUnmarked +public class ArrayTableRowMapTest extends RowMapTests { + public ArrayTableRowMapTest() { + super(true, false, false, false); + } + + @Override + Table makeTable() { + return ArrayTable.create(asList("foo", "bar", "dog"), asList(1, 2, 3)); + } + + @Override + protected Map> makeEmptyMap() { + throw new UnsupportedOperationException(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ArrayTableRowTest.java b/android/guava-tests/test/com/google/common/collect/ArrayTableRowTest.java new file mode 100644 index 000000000000..9d8370717961 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ArrayTableRowTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // TODO(hhchan): ArrayTable +@NullUnmarked +public class ArrayTableRowTest extends RowTests { + public ArrayTableRowTest() { + super(true, true, false, false, false); + } + + @Override + protected String getKeyNotInPopulatedMap() { + throw new UnsupportedOperationException(); + } + + @Override + protected Map makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + @Override + protected Table makeTable() { + return ArrayTable.create(asList('a', 'b', 'c'), asList("one", "two", "three", "four")); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ArrayTableTest.java b/android/guava-tests/test/com/google/common/collect/ArrayTableTest.java index b7622c23137a..a698b357e560 100644 --- a/android/guava-tests/test/com/google/common/collect/ArrayTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/ArrayTableTest.java @@ -16,11 +16,14 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Tables.immutableCell; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.collect.Table.Cell; import com.google.common.testing.EqualsTester; @@ -28,6 +31,8 @@ import com.google.common.testing.SerializableTester; import java.util.Arrays; import java.util.Map; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link ArrayTable}. @@ -35,10 +40,11 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -public class ArrayTableTest extends AbstractTableTest { +@NullMarked +public class ArrayTableTest extends AbstractTableTest<@Nullable Character> { @Override - protected ArrayTable create(Object... data) { + protected ArrayTable create(@Nullable Object... data) { // TODO: Specify different numbers of rows and columns, to detect problems // that arise when the wrong size is used. ArrayTable table = @@ -125,12 +131,12 @@ public void testEquals() { hashCopy.put("foo", 1, 'a'); hashCopy.put("bar", 1, 'b'); hashCopy.put("foo", 3, 'c'); - Table reordered = + Table reordered = create("foo", 3, 'c', "foo", 1, 'a', "bar", 1, 'b'); - Table smaller = create("foo", 1, 'a', "bar", 1, 'b'); - Table swapOuter = + Table smaller = create("foo", 1, 'a', "bar", 1, 'b'); + Table swapOuter = create("bar", 1, 'a', "foo", 1, 'b', "bar", 3, 'c'); - Table swapValues = + Table swapValues = create("foo", 1, 'c', "bar", 1, 'b', "foo", 3, 'a'); new EqualsTester() @@ -159,7 +165,7 @@ public void testHashCode() { @Override public void testRow() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Map expected = Maps.newHashMap(); + Map expected = Maps.newHashMap(); expected.put(1, 'a'); expected.put(3, 'c'); expected.put(2, null); @@ -169,7 +175,7 @@ public void testRow() { @Override public void testColumn() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Map expected = Maps.newHashMap(); + Map expected = Maps.newHashMap(); expected.put("foo", 'a'); expected.put("bar", 'b'); expected.put("cat", null); @@ -184,35 +190,27 @@ public void testToStringSize1() { } public void testCreateDuplicateRows() { - try { - ArrayTable.create(asList("foo", "bar", "foo"), asList(1, 2, 3)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ArrayTable.create(asList("foo", "bar", "foo"), asList(1, 2, 3))); } public void testCreateDuplicateColumns() { - try { - ArrayTable.create(asList("foo", "bar"), asList(1, 2, 3, 2)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ArrayTable.create(asList("foo", "bar"), asList(1, 2, 3, 2))); } public void testCreateEmptyRows() { - try { - ArrayTable.create(Arrays.asList(), asList(1, 2, 3)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ArrayTable.create(Arrays.asList(), asList(1, 2, 3))); } public void testCreateEmptyColumns() { - try { - ArrayTable.create(asList("foo", "bar"), Arrays.asList()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ArrayTable.create(asList("foo", "bar"), Arrays.asList())); } public void testCreateEmptyRowsXColumns() { @@ -224,11 +222,7 @@ public void testCreateEmptyRowsXColumns() { assertThat(table.rowKeyList()).isEmpty(); assertThat(table.columnKeySet()).isEmpty(); assertThat(table.rowKeySet()).isEmpty(); - try { - table.at(0, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> table.at(0, 0)); } @GwtIncompatible // toArray @@ -239,9 +233,9 @@ public void testEmptyToArry() { } public void testCreateCopyArrayTable() { - Table original = + Table original = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - Table copy = ArrayTable.create(original); + Table copy = ArrayTable.create(original); assertEquals(original, copy); original.put("foo", 1, 'd'); assertEquals((Character) 'd', original.get("foo", 1)); @@ -255,7 +249,7 @@ public void testCreateCopyHashBasedTable() { original.put("foo", 1, 'a'); original.put("bar", 1, 'b'); original.put("foo", 3, 'c'); - Table copy = ArrayTable.create(original); + Table copy = ArrayTable.create(original); assertEquals(4, copy.size()); assertEquals((Character) 'a', copy.get("foo", 1)); assertEquals((Character) 'b', copy.get("bar", 1)); @@ -278,7 +272,7 @@ public void testCreateCopyEmptyTable() { } public void testCreateCopyEmptyArrayTable() { - Table original = + Table original = ArrayTable.create(Arrays.asList(), Arrays.asList()); ArrayTable copy = ArrayTable.create(original); assertThat(copy).isEqualTo(original); @@ -290,6 +284,7 @@ public void testSerialization() { SerializableTester.reserializeAndAssert(table); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNullPointerStatic() { new NullPointerTester().testAllPublicStaticMethods(ArrayTable.class); @@ -357,26 +352,10 @@ public void testAt() { assertEquals((Character) 'b', table.at(1, 0)); assertEquals((Character) 'c', table.at(0, 2)); assertNull(table.at(1, 2)); - try { - table.at(1, 3); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.at(1, -1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.at(3, 2); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.at(-1, 2); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> table.at(1, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> table.at(1, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> table.at(3, 2)); + assertThrows(IndexOutOfBoundsException.class, () -> table.at(-1, 2)); } public void testSet() { @@ -388,26 +367,10 @@ public void testSet() { assertEquals((Character) 'e', table.get("cat", 1)); assertEquals((Character) 'a', table.set(0, 0, null)); assertNull(table.get("foo", 1)); - try { - table.set(1, 3, 'z'); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.set(1, -1, 'z'); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.set(3, 2, 'z'); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - table.set(-1, 2, 'z'); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> table.set(1, 3, 'z')); + assertThrows(IndexOutOfBoundsException.class, () -> table.set(1, -1, 'z')); + assertThrows(IndexOutOfBoundsException.class, () -> table.set(3, 2, 'z')); + assertThrows(IndexOutOfBoundsException.class, () -> table.set(-1, 2, 'z')); assertFalse(table.containsValue('z')); } @@ -423,18 +386,11 @@ public void testEraseAll() { public void testPutIllegal() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); - try { - table.put("dog", 1, 'd'); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]"); - } - try { - table.put("foo", 4, 'd'); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> table.put("dog", 1, 'd')); + assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]"); + expected = assertThrows(IllegalArgumentException.class, () -> table.put("foo", 4, 'd')); + assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]"); assertFalse(table.containsValue('d')); } @@ -470,60 +426,48 @@ public void testToArray() { public void testCellReflectsChanges() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Cell cell = table.cellSet().iterator().next(); - assertEquals(Tables.immutableCell("foo", 1, 'a'), cell); + assertEquals(immutableCell("foo", 1, 'a'), cell); assertEquals((Character) 'a', table.put("foo", 1, 'd')); - assertEquals(Tables.immutableCell("foo", 1, 'd'), cell); + assertEquals(immutableCell("foo", 1, 'd'), cell); } public void testRowMissing() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Map row = table.row("dog"); assertTrue(row.isEmpty()); - try { - row.put(1, 'd'); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> row.put(1, 'd')); } public void testColumnMissing() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Map column = table.column(4); assertTrue(column.isEmpty()); - try { - column.put("foo", 'd'); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> column.put("foo", 'd')); } public void testRowPutIllegal() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Map map = table.row("foo"); - try { - map.put(4, 'd'); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> map.put(4, 'd')); + assertThat(expected).hasMessageThat().isEqualTo("Column 4 not in [1, 2, 3]"); } public void testColumnPutIllegal() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Map map = table.column(3); - try { - map.put("dog", 'd'); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> map.put("dog", 'd')); + assertThat(expected).hasMessageThat().isEqualTo("Row dog not in [foo, bar, cat]"); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() { new NullPointerTester().testAllPublicInstanceMethods(create()); } + @J2ktIncompatible @GwtIncompatible // serialize public void testSerializable() { SerializableTester.reserializeAndAssert(create()); diff --git a/android/guava-tests/test/com/google/common/collect/Base.java b/android/guava-tests/test/com/google/common/collect/Base.java new file mode 100644 index 000000000000..1d9e2836202e --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/Base.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.io.Serializable; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; + +/** Simple base class to verify that we handle generics correctly. */ +@GwtCompatible +@NullUnmarked +class Base implements Comparable, Serializable { + private final String s; + + public Base(String s) { + this.s = s; + } + + @Override + public int hashCode() { // delegate to 's' + return s.hashCode(); + } + + @Override + public boolean equals(@Nullable Object other) { + if (other == null) { + return false; + } else if (other instanceof Base) { + return s.equals(((Base) other).s); + } else { + return false; + } + } + + @Override + public int compareTo(Base o) { + return s.compareTo(o.s); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; +} diff --git a/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java b/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java index 65d2ef6f46e7..3489a989a3cc 100644 --- a/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java +++ b/android/guava-tests/test/com/google/common/collect/BenchmarkHelpers.java @@ -17,10 +17,11 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkState; +import static java.util.Collections.synchronizedSet; +import static java.util.Collections.unmodifiableSet; import com.google.common.base.Equivalence; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; @@ -32,12 +33,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; +import org.jspecify.annotations.NullUnmarked; /** * Helper classes for various benchmarks. * * @author Christopher Swenson */ +@NullUnmarked final class BenchmarkHelpers { /** So far, this is the best way to test various implementations of {@link Set} subclasses. */ public interface CollectionsImplEnum { @@ -80,13 +83,13 @@ public > Set create(Collection contents) { UnmodifiableSetImpl { @Override public > Set create(Collection contents) { - return Collections.unmodifiableSet(new HashSet(contents)); + return unmodifiableSet(new HashSet(contents)); } }, SynchronizedSetImpl { @Override public > Set create(Collection contents) { - return Collections.synchronizedSet(new HashSet(contents)); + return synchronizedSet(new HashSet(contents)); } }, ImmutableSetImpl { @@ -386,7 +389,7 @@ public enum InternerImpl implements InternerImplEnum { public Interner create(Collection contents) { Interner interner = Interners.newWeakInterner(); for (E e : contents) { - interner.intern(e); + E unused = interner.intern(e); } return interner; } @@ -396,7 +399,7 @@ public Interner create(Collection contents) { public Interner create(Collection contents) { Interner interner = Interners.newStrongInterner(); for (E e : contents) { - interner.intern(e); + E unused = interner.intern(e); } return interner; } @@ -416,7 +419,7 @@ public enum ListSizeDistribution { final int min; final int max; - private ListSizeDistribution(int min, int max) { + ListSizeDistribution(int min, int max) { this.min = min; this.max = max; } diff --git a/android/guava-tests/test/com/google/common/collect/CollectSpliteratorsTest.java b/android/guava-tests/test/com/google/common/collect/CollectSpliteratorsTest.java new file mode 100644 index 000000000000..16c98c5cae61 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/CollectSpliteratorsTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Lists.charactersOf; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Ascii; +import com.google.common.collect.testing.SpliteratorTester; +import java.util.Arrays; +import java.util.List; +import java.util.Spliterator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; + +/** Tests for {@code CollectSpliterators}. */ +@GwtCompatible +@NullMarked +public class CollectSpliteratorsTest extends TestCase { + public void testMap() { + SpliteratorTester.of( + () -> + CollectSpliterators.map( + Arrays.spliterator(new String[] {"a", "b", "c", "d", "e"}), Ascii::toUpperCase)) + .expect("A", "B", "C", "D", "E"); + } + + public void testFlatMap() { + SpliteratorTester.of( + () -> + CollectSpliterators.flatMap( + Arrays.spliterator(new String[] {"abc", "", "de", "f", "g", ""}), + (String str) -> charactersOf(str).spliterator(), + Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.NONNULL, + 7)) + .expect('a', 'b', 'c', 'd', 'e', 'f', 'g'); + } + + public void testFlatMap_nullStream() { + SpliteratorTester.of( + () -> + CollectSpliterators.flatMap( + Arrays.spliterator(new String[] {"abc", "", "de", "f", "g", ""}), + (String str) -> str.isEmpty() ? null : charactersOf(str).spliterator(), + Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.NONNULL, + 7)) + .expect('a', 'b', 'c', 'd', 'e', 'f', 'g'); + } + + public void testFlatMapToInt_nullStream() { + SpliteratorTester.ofInt( + () -> + CollectSpliterators.flatMapToInt( + Arrays.spliterator(new Integer[] {1, 0, 1, 2, 3}), + (Integer i) -> i == 0 ? null : IntStream.of(i).spliterator(), + Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.NONNULL, + 4)) + .expect(1, 1, 2, 3); + } + + public void testFlatMapToLong_nullStream() { + SpliteratorTester.ofLong( + () -> + CollectSpliterators.flatMapToLong( + Arrays.spliterator(new Long[] {1L, 0L, 1L, 2L, 3L}), + (Long i) -> i == 0L ? null : LongStream.of(i).spliterator(), + Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.NONNULL, + 4)) + .expect(1L, 1L, 2L, 3L); + } + + public void testFlatMapToDouble_nullStream() { + SpliteratorTester.ofDouble( + () -> + CollectSpliterators.flatMapToDouble( + Arrays.spliterator(new Double[] {1.0, 0.0, 1.0, 2.0, 3.0}), + (Double i) -> i == 0.0 ? null : DoubleStream.of(i).spliterator(), + Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.NONNULL, + 4)) + .expect(1.0, 1.0, 2.0, 3.0); + } + + public void testMultisetsSpliterator() { + Multiset multiset = TreeMultiset.create(); + multiset.add("a", 3); + multiset.add("b", 1); + multiset.add("c", 2); + + List actualValues = Lists.newArrayList(); + multiset.spliterator().forEachRemaining(actualValues::add); + assertThat(multiset).containsExactly("a", "a", "a", "b", "c", "c").inOrder(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/CollectionBenchmarkSampleData.java b/android/guava-tests/test/com/google/common/collect/CollectionBenchmarkSampleData.java index cdc7a91cc1e6..6c4a951623ea 100644 --- a/android/guava-tests/test/com/google/common/collect/CollectionBenchmarkSampleData.java +++ b/android/guava-tests/test/com/google/common/collect/CollectionBenchmarkSampleData.java @@ -17,17 +17,19 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.shuffle; -import com.google.common.primitives.Ints; -import java.util.Collections; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Package up sample data for common collections benchmarking. * * @author Nicholaus Shupe */ +@NullUnmarked class CollectionBenchmarkSampleData { private final boolean isUserTypeFast; private final SpecialRandom random; @@ -75,7 +77,7 @@ private Element[] createQueries(Set elementsInSet, int numQueries) { queryList.addAll(elementsInSet); } List tmp = Lists.newArrayList(elementsInSet); - Collections.shuffle(tmp, random); + shuffle(tmp, random); queryList.addAll(tmp.subList(0, extras)); } @@ -86,7 +88,7 @@ private Element[] createQueries(Set elementsInSet, int numQueries) { queryList.add(candidate); } } - Collections.shuffle(queryList, random); + shuffle(queryList, random); return queryList.toArray(new Element[0]); } @@ -111,7 +113,7 @@ static class Element implements Comparable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return this == obj || (obj instanceof Element && ((Element) obj).hash == hash); } @@ -122,7 +124,7 @@ public int hashCode() { @Override public int compareTo(Element that) { - return Ints.compare(hash, that.hash); + return Integer.compare(hash, that.hash); } @Override @@ -137,7 +139,7 @@ static class SlowElement extends Element { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return slowItDown() != 1 && super.equals(obj); } diff --git a/android/guava-tests/test/com/google/common/collect/Collections2FilterArrayListTest.java b/android/guava-tests/test/com/google/common/collect/Collections2FilterArrayListTest.java new file mode 100644 index 000000000000..4c3ef4b26098 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/Collections2FilterArrayListTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.base.Predicate; +import com.google.common.collect.FilteredCollectionsTestUtil.AbstractFilteredCollectionTest; +import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked +public final class Collections2FilterArrayListTest + extends AbstractFilteredCollectionTest> { + @Override + Collection createUnfiltered(Iterable contents) { + return Lists.newArrayList(contents); + } + + @Override + Collection filter(Collection elements, Predicate predicate) { + return Collections2.filter(elements, predicate); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/Collections2Test.java b/android/guava-tests/test/com/google/common/collect/Collections2Test.java index 4b8761893013..a68f90f7bbd5 100644 --- a/android/guava-tests/test/com/google/common/collect/Collections2Test.java +++ b/android/guava-tests/test/com/google/common/collect/Collections2Test.java @@ -16,16 +16,16 @@ package com.google.common.collect; +import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; import static com.google.common.truth.Truth.assertThat; -import static java.util.Arrays.asList; import static java.util.Collections.nCopies; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Function; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Predicate; import com.google.common.collect.testing.CollectionTestSuiteBuilder; import com.google.common.collect.testing.TestStringCollectionGenerator; @@ -37,9 +37,12 @@ import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Collections2}. @@ -48,8 +51,11 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class Collections2Test extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(Collections2Test.class.getSimpleName()); suite.addTest(testsForFilter()); @@ -62,31 +68,14 @@ public static Test suite() { return suite; } - static final Predicate NOT_YYY_ZZZ = - new Predicate() { - @Override - public boolean apply(String input) { - return !"yyy".equals(input) && !"zzz".equals(input); - } - }; - - static final Predicate LENGTH_1 = - new Predicate() { - @Override - public boolean apply(String input) { - return input.length() == 1; - } - }; - - static final Predicate STARTS_WITH_VOWEL = - new Predicate() { - @Override - public boolean apply(String input) { - return asList('a', 'e', 'i', 'o', 'u').contains(input.charAt(0)); - } - }; + static final Predicate<@Nullable String> NOT_YYY_ZZZ = + input -> !Objects.equals(input, "yyy") && !Objects.equals(input, "zzz"); + static final Predicate LENGTH_1 = input -> input.length() == 1; + + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilter() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @@ -109,7 +98,9 @@ public Collection create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterAll() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @@ -130,7 +121,9 @@ public Collection create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterLinkedList() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @@ -153,7 +146,9 @@ public Collection create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterNoNulls() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @@ -176,7 +171,9 @@ public Collection create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterFiltered() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @@ -200,25 +197,20 @@ public Collection create(String[] elements) { .createTestSuite(); } - private static final Function REMOVE_FIRST_CHAR = - new Function() { - @Override - public String apply(String from) { - return ((from == null) || "".equals(from)) ? null : from.substring(1); - } - }; - + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForTransform() { return CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override - public Collection create(String[] elements) { - List list = newArrayList(); + public Collection<@Nullable String> create(@Nullable String[] elements) { + List<@Nullable String> list = newArrayList(); for (String element : elements) { list.add((element == null) ? null : "q" + element); } - return Collections2.transform(list, REMOVE_FIRST_CHAR); + return Collections2.transform( + list, from -> isNullOrEmpty(from) ? null : from.substring(1)); } }) .named("Collections2.transform") @@ -230,6 +222,7 @@ public Collection create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -498,7 +491,7 @@ private void assertPermutationsCount(int expected, Collection> permu } public void testToStringImplWithNullEntries() throws Exception { - List list = Lists.newArrayList(); + List<@Nullable String> list = Lists.newArrayList(); list.add("foo"); list.add(null); diff --git a/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java b/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java index 52c9bed52b15..ec8601fc52a0 100644 --- a/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/CompactHashMapTest.java @@ -18,6 +18,7 @@ import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.TestStringMapGenerator; @@ -29,13 +30,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code CompactHashMap}. * * @author Louis Wasserman */ +@NullUnmarked public class CompactHashMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -111,7 +115,7 @@ public void testAllocArraysExpectedSize() { map.put(1, "1"); assertThat(map.needsAllocArrays()).isFalse(); - int expectedSize = Math.max(1, i); + int expectedSize = max(1, i); assertThat(map.entries).hasLength(expectedSize); assertThat(map.keys).hasLength(expectedSize); assertThat(map.values).hasLength(expectedSize); diff --git a/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java b/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java index 0f0216f3b77d..e472c4039357 100644 --- a/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/CompactHashSetTest.java @@ -17,7 +17,8 @@ package com.google.common.collect; import static com.google.common.truth.Truth.assertThat; -import static java.util.stream.Collectors.*; +import static java.lang.Math.max; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for CompactHashSet. @@ -38,7 +40,9 @@ * @author Dimitris Andreou */ @GwtIncompatible // java.util.Arrays#copyOf(Object[], int), java.lang.reflect.Array +@NullUnmarked public class CompactHashSetTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { List> allFeatures = Arrays.>asList( @@ -58,7 +62,7 @@ public static Test suite() { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return CompactHashSet.create(Arrays.asList(elements)); + return CompactHashSet.create(asList(elements)); } }) .named("CompactHashSet") @@ -69,12 +73,12 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - CompactHashSet set = CompactHashSet.create(Arrays.asList(elements)); + CompactHashSet set = CompactHashSet.create(asList(elements)); for (int i = 0; i < 100; i++) { - set.add(i); + set.add("extra" + i); } for (int i = 0; i < 100; i++) { - set.remove(i); + set.remove("extra" + i); } set.trimToSize(); return set; @@ -104,7 +108,7 @@ public void testAllocArraysExpectedSize() { set.add(1); assertThat(set.needsAllocArrays()).isFalse(); - int expectedSize = Math.max(1, i); + int expectedSize = max(1, i); assertThat(set.elements).hasLength(expectedSize); } } diff --git a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java index d1363bcda669..fbfe0f96c13b 100644 --- a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashMapTest.java @@ -14,7 +14,9 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.TestStringMapGenerator; @@ -27,13 +29,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code CompactLinkedHashMap}. * * @author Louis Wasserman */ +@NullUnmarked public class CompactLinkedHashMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -136,7 +141,7 @@ private void testHasMapEntriesInOrder(Map map, Object... alternatingKeysAn for (int i = 0; i < map.size(); i++) { Object expectedKey = alternatingKeysAndValues[2 * i]; Object expectedValue = alternatingKeysAndValues[2 * i + 1]; - Entry expectedEntry = Maps.immutableEntry(expectedKey, expectedValue); + Entry expectedEntry = immutableEntry(expectedKey, expectedValue); assertEquals(expectedEntry, entries.get(i)); assertEquals(expectedKey, keys.get(i)); assertEquals(expectedValue, values.get(i)); @@ -170,7 +175,7 @@ public void testAllocArraysExpectedSize() { map.put(1, Integer.toString(1)); assertThat(map.needsAllocArrays()).isFalse(); - int expectedSize = Math.max(1, i); + int expectedSize = max(1, i); assertThat(map.entries).hasLength(expectedSize); assertThat(map.keys).hasLength(expectedSize); assertThat(map.values).hasLength(expectedSize); diff --git a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java index 299503e0463e..67853e409484 100644 --- a/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/CompactLinkedHashSetTest.java @@ -17,6 +17,8 @@ package com.google.common.collect; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -30,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for CompactLinkedHashSet. @@ -37,7 +40,9 @@ * @author Dimitris Andreou */ @GwtIncompatible // java.util.Arrays#copyOf(Object[], int), java.lang.reflect.Array +@NullUnmarked public class CompactLinkedHashSetTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { List> allFeatures = Arrays.>asList( @@ -58,7 +63,7 @@ public static Test suite() { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return CompactLinkedHashSet.create(Arrays.asList(elements)); + return CompactLinkedHashSet.create(asList(elements)); } }) .named("CompactLinkedHashSet") @@ -85,7 +90,7 @@ public void testAllocArraysExpectedSize() { set.add(1); assertThat(set.needsAllocArrays()).isFalse(); - int expectedSize = Math.max(1, i); + int expectedSize = max(1, i); assertThat(set.elements).hasLength(expectedSize); } } diff --git a/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java b/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java index b30cb7646d43..be94a9d650b4 100644 --- a/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java +++ b/android/guava-tests/test/com/google/common/collect/ComparatorsTest.java @@ -16,15 +16,28 @@ package com.google.common.collect; +import static com.google.common.collect.Comparators.emptiesFirst; +import static com.google.common.collect.Comparators.emptiesLast; +import static com.google.common.collect.Comparators.isInOrder; +import static com.google.common.collect.Comparators.isInStrictOrder; +import static com.google.common.collect.Comparators.max; +import static com.google.common.collect.Comparators.min; +import static com.google.common.collect.testing.Helpers.testComparator; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static java.util.Comparator.comparing; +import static java.util.Comparator.naturalOrder; +import static java.util.Comparator.reverseOrder; import com.google.common.annotations.GwtCompatible; -import com.google.common.collect.testing.Helpers; import com.google.common.testing.EqualsTester; import java.util.Collections; import java.util.Comparator; +import java.util.Optional; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code Comparators}. @@ -32,8 +45,8 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class ComparatorsTest extends TestCase { - @SuppressWarnings("unchecked") // dang varargs public void testLexicographical() { Comparator comparator = Ordering.natural(); Comparator> lexy = Comparators.lexicographical(comparator); @@ -44,7 +57,7 @@ public void testLexicographical() { ImmutableList ab = ImmutableList.of("a", "b"); ImmutableList b = ImmutableList.of("b"); - Helpers.testComparator(lexy, empty, a, aa, ab, b); + testComparator(lexy, empty, a, aa, ab, b); new EqualsTester() .addEqualityGroup(lexy, Comparators.lexicographical(comparator)) @@ -54,46 +67,85 @@ public void testLexicographical() { } public void testIsInOrder() { - assertFalse(Comparators.isInOrder(asList(5, 3, 0, 9), Ordering.natural())); - assertFalse(Comparators.isInOrder(asList(0, 5, 3, 9), Ordering.natural())); - assertTrue(Comparators.isInOrder(asList(0, 3, 5, 9), Ordering.natural())); - assertTrue(Comparators.isInOrder(asList(0, 0, 3, 3), Ordering.natural())); - assertTrue(Comparators.isInOrder(asList(0, 3), Ordering.natural())); - assertTrue(Comparators.isInOrder(Collections.singleton(1), Ordering.natural())); - assertTrue(Comparators.isInOrder(Collections.emptyList(), Ordering.natural())); + assertFalse(isInOrder(asList(5, 3, 0, 9), Ordering.natural())); + assertFalse(isInOrder(asList(0, 5, 3, 9), Ordering.natural())); + assertTrue(isInOrder(asList(0, 3, 5, 9), Ordering.natural())); + assertTrue(isInOrder(asList(0, 0, 3, 3), Ordering.natural())); + assertTrue(isInOrder(asList(0, 3), Ordering.natural())); + assertTrue(isInOrder(singleton(1), Ordering.natural())); + assertTrue(isInOrder(Collections.emptyList(), Ordering.natural())); } public void testIsInStrictOrder() { - assertFalse(Comparators.isInStrictOrder(asList(5, 3, 0, 9), Ordering.natural())); - assertFalse(Comparators.isInStrictOrder(asList(0, 5, 3, 9), Ordering.natural())); - assertTrue(Comparators.isInStrictOrder(asList(0, 3, 5, 9), Ordering.natural())); - assertFalse(Comparators.isInStrictOrder(asList(0, 0, 3, 3), Ordering.natural())); - assertTrue(Comparators.isInStrictOrder(asList(0, 3), Ordering.natural())); - assertTrue(Comparators.isInStrictOrder(Collections.singleton(1), Ordering.natural())); - assertTrue(Comparators.isInStrictOrder(Collections.emptyList(), Ordering.natural())); + assertFalse(isInStrictOrder(asList(5, 3, 0, 9), Ordering.natural())); + assertFalse(isInStrictOrder(asList(0, 5, 3, 9), Ordering.natural())); + assertTrue(isInStrictOrder(asList(0, 3, 5, 9), Ordering.natural())); + assertFalse(isInStrictOrder(asList(0, 0, 3, 3), Ordering.natural())); + assertTrue(isInStrictOrder(asList(0, 3), Ordering.natural())); + assertTrue(isInStrictOrder(singleton(1), Ordering.natural())); + assertTrue(isInStrictOrder(Collections.emptyList(), Ordering.natural())); + } + + public void testEmptiesFirst() { + Optional empty = Optional.empty(); + Optional abc = Optional.of("abc"); + Optional z = Optional.of("z"); + + Comparator> comparator = emptiesFirst(comparing(String::length)); + testComparator(comparator, empty, z, abc); + + // Just demonstrate that no explicit type parameter is required + Comparator> unused = emptiesFirst(naturalOrder()); + } + + public void testEmptiesLast() { + Optional empty = Optional.empty(); + Optional abc = Optional.of("abc"); + Optional z = Optional.of("z"); + + Comparator> comparator = emptiesLast(comparing(String::length)); + testComparator(comparator, z, abc, empty); + + // Just demonstrate that no explicit type parameter is required + Comparator> unused = emptiesLast(naturalOrder()); } public void testMinMaxNatural() { - assertThat(Comparators.min(1, 2)).isEqualTo(1); - assertThat(Comparators.min(2, 1)).isEqualTo(1); - assertThat(Comparators.max(1, 2)).isEqualTo(2); - assertThat(Comparators.max(2, 1)).isEqualTo(2); + assertThat(min(1, 2)).isEqualTo(1); + assertThat(min(2, 1)).isEqualTo(1); + assertThat(max(1, 2)).isEqualTo(2); + assertThat(max(2, 1)).isEqualTo(2); } public void testMinMaxNatural_equalInstances() { Foo a = new Foo(1); Foo b = new Foo(1); - assertThat(Comparators.min(a, b)).isSameInstanceAs(a); - assertThat(Comparators.max(a, b)).isSameInstanceAs(a); + assertThat(min(a, b)).isSameInstanceAs(a); + assertThat(max(a, b)).isSameInstanceAs(a); } public void testMinMaxComparator() { - Comparator natural = Ordering.natural(); - Comparator reverse = Collections.reverseOrder(natural); - assertThat(Comparators.min(1, 2, reverse)).isEqualTo(2); - assertThat(Comparators.min(2, 1, reverse)).isEqualTo(2); - assertThat(Comparators.max(1, 2, reverse)).isEqualTo(1); - assertThat(Comparators.max(2, 1, reverse)).isEqualTo(1); + Comparator reverse = reverseOrder(); + assertThat(min(1, 2, reverse)).isEqualTo(2); + assertThat(min(2, 1, reverse)).isEqualTo(2); + assertThat(max(1, 2, reverse)).isEqualTo(1); + assertThat(max(2, 1, reverse)).isEqualTo(1); + } + + /** + * Fails compilation if the signature of min and max is changed to take {@code Comparator} + * instead of {@code Comparator}. + */ + public void testMinMaxWithSupertypeComparator() { + Comparator numberComparator = comparing(Number::intValue); + Integer comparand1 = 1; + Integer comparand2 = 2; + + Integer min = min(comparand1, comparand2, numberComparator); + Integer max = max(comparand1, comparand2, numberComparator); + + assertThat(min).isEqualTo(1); + assertThat(max).isEqualTo(2); } public void testMinMaxComparator_equalInstances() { @@ -101,8 +153,8 @@ public void testMinMaxComparator_equalInstances() { Comparator reverse = Collections.reverseOrder(natural); Foo a = new Foo(1); Foo b = new Foo(1); - assertThat(Comparators.min(a, b, reverse)).isSameInstanceAs(a); - assertThat(Comparators.max(a, b, reverse)).isSameInstanceAs(a); + assertThat(min(a, b, reverse)).isSameInstanceAs(a); + assertThat(max(a, b, reverse)).isSameInstanceAs(a); } private static class Foo implements Comparable { @@ -118,7 +170,7 @@ public int hashCode() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof Foo) && ((Foo) o).value.equals(value); } diff --git a/android/guava-tests/test/com/google/common/collect/ComparisonChainTest.java b/android/guava-tests/test/com/google/common/collect/ComparisonChainTest.java index 9a9f98a8cb68..e5f495528734 100644 --- a/android/guava-tests/test/com/google/common/collect/ComparisonChainTest.java +++ b/android/guava-tests/test/com/google/common/collect/ComparisonChainTest.java @@ -16,9 +16,12 @@ package com.google.common.collect; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link ComparisonChain}. @@ -26,6 +29,7 @@ * @author Kevin Bourrillion */ @GwtCompatible +@NullMarked public class ComparisonChainTest extends TestCase { private static final DontCompareMe DONT_COMPARE_ME = new DontCompareMe(); @@ -36,77 +40,84 @@ public int compareTo(DontCompareMe o) { } } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testCompareBooleans() { - assertEquals( - 0, - ComparisonChain.start() - .compare(true, true) - .compare(true, Boolean.TRUE) - .compare(Boolean.TRUE, true) - .compare(Boolean.TRUE, Boolean.TRUE) - .result()); + assertThat( + ComparisonChain.start() + .compare(true, true) + .compare(true, Boolean.TRUE) + .compare(Boolean.TRUE, true) + .compare(Boolean.TRUE, Boolean.TRUE) + .result()) + .isEqualTo(0); } public void testDegenerate() { // kinda bogus, but who cares? - assertEquals(0, ComparisonChain.start().result()); + assertThat(ComparisonChain.start().result()).isEqualTo(0); } public void testOneEqual() { - assertEquals(0, ComparisonChain.start().compare("a", "a").result()); + assertThat(ComparisonChain.start().compare("a", "a").result()).isEqualTo(0); } public void testOneEqualUsingComparator() { - assertEquals( - 0, ComparisonChain.start().compare("a", "A", String.CASE_INSENSITIVE_ORDER).result()); + assertThat(ComparisonChain.start().compare("a", "A", String.CASE_INSENSITIVE_ORDER).result()) + .isEqualTo(0); } public void testManyEqual() { - assertEquals( - 0, - ComparisonChain.start() - .compare(1, 1) - .compare(1L, 1L) - .compareFalseFirst(true, true) - .compare(1.0, 1.0) - .compare(1.0f, 1.0f) - .compare("a", "a", Ordering.usingToString()) - .result()); + assertThat( + ComparisonChain.start() + .compare(1, 1) + .compare(1L, 1L) + .compareFalseFirst(true, true) + .compare(1.0, 1.0) + .compare(1.0f, 1.0f) + .compare("a", "a", Ordering.usingToString()) + .result()) + .isEqualTo(0); } public void testShortCircuitLess() { - assertTrue( - ComparisonChain.start().compare("a", "b").compare(DONT_COMPARE_ME, DONT_COMPARE_ME).result() - < 0); + assertThat( + ComparisonChain.start() + .compare("a", "b") + .compare(DONT_COMPARE_ME, DONT_COMPARE_ME) + .result()) + .isLessThan(0); } public void testShortCircuitGreater() { - assertTrue( - ComparisonChain.start().compare("b", "a").compare(DONT_COMPARE_ME, DONT_COMPARE_ME).result() - > 0); + assertThat( + ComparisonChain.start() + .compare("b", "a") + .compare(DONT_COMPARE_ME, DONT_COMPARE_ME) + .result()) + .isGreaterThan(0); } public void testShortCircuitSecondStep() { - assertTrue( - ComparisonChain.start() + assertThat( + ComparisonChain.start() .compare("a", "a") .compare("a", "b") .compare(DONT_COMPARE_ME, DONT_COMPARE_ME) - .result() - < 0); + .result()) + .isLessThan(0); } public void testCompareFalseFirst() { - assertTrue(ComparisonChain.start().compareFalseFirst(true, true).result() == 0); - assertTrue(ComparisonChain.start().compareFalseFirst(true, false).result() > 0); - assertTrue(ComparisonChain.start().compareFalseFirst(false, true).result() < 0); - assertTrue(ComparisonChain.start().compareFalseFirst(false, false).result() == 0); + assertThat(ComparisonChain.start().compareFalseFirst(true, true).result()).isEqualTo(0); + assertThat(ComparisonChain.start().compareFalseFirst(true, false).result()).isGreaterThan(0); + assertThat(ComparisonChain.start().compareFalseFirst(false, true).result()).isLessThan(0); + assertThat(ComparisonChain.start().compareFalseFirst(false, false).result()).isEqualTo(0); } public void testCompareTrueFirst() { - assertTrue(ComparisonChain.start().compareTrueFirst(true, true).result() == 0); - assertTrue(ComparisonChain.start().compareTrueFirst(true, false).result() < 0); - assertTrue(ComparisonChain.start().compareTrueFirst(false, true).result() > 0); - assertTrue(ComparisonChain.start().compareTrueFirst(false, false).result() == 0); + assertThat(ComparisonChain.start().compareTrueFirst(true, true).result()).isEqualTo(0); + assertThat(ComparisonChain.start().compareTrueFirst(true, false).result()).isLessThan(0); + assertThat(ComparisonChain.start().compareTrueFirst(false, true).result()).isGreaterThan(0); + assertThat(ComparisonChain.start().compareTrueFirst(false, false).result()).isEqualTo(0); } } diff --git a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetBasherTest.java b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetBasherTest.java index ddc2bf1c704b..aa7836dfe39f 100644 --- a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetBasherTest.java +++ b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetBasherTest.java @@ -16,6 +16,10 @@ package com.google.common.collect; +import static com.google.common.collect.Lists.newArrayListWithExpectedSize; +import static com.google.common.collect.Lists.transform; +import static java.lang.Math.min; + import com.google.common.base.Function; import com.google.common.primitives.Ints; import java.util.List; @@ -30,6 +34,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Basher test for {@link ConcurrentHashMultiset}: start a bunch of threads, have each of them do @@ -40,17 +45,18 @@ * @author mike nonemacher */ +@NullUnmarked public class ConcurrentHashMultisetBasherTest extends TestCase { - public void testAddAndRemove_ConcurrentHashMap() throws Exception { + public void testAddAndRemove_concurrentHashMap() throws Exception { testAddAndRemove(new ConcurrentHashMap()); } - public void testAddAndRemove_ConcurrentSkipListMap() throws Exception { + public void testAddAndRemove_concurrentSkipListMap() throws Exception { testAddAndRemove(new ConcurrentSkipListMap()); } - public void testAddAndRemove_MapMakerMap() throws Exception { + public void testAddAndRemove_mapMakerMap() throws Exception { MapMaker mapMaker = new MapMaker(); // force MapMaker to use its own MapMakerInternalMap mapMaker.useCustomMap = true; @@ -67,7 +73,7 @@ private void testAddAndRemove(ConcurrentMap map) ExecutorService pool = Executors.newFixedThreadPool(nThreads); ImmutableList keys = ImmutableList.of("a", "b", "c"); try { - List> futures = Lists.newArrayListWithExpectedSize(nTasks); + List> futures = newArrayListWithExpectedSize(nTasks); for (int i = 0; i < nTasks; i++) { futures.add(pool.submit(new MutateTask(multiset, keys))); } @@ -81,7 +87,7 @@ private void testAddAndRemove(ConcurrentMap map) } List actualCounts = - Lists.transform( + transform( keys, new Function() { @Override @@ -148,7 +154,7 @@ public int[] call() throws Exception { { int delta = random.nextInt(6); // [0, 5] int oldValue = multiset.remove(key, delta); - deltas[keyIndex] -= Math.min(delta, oldValue); + deltas[keyIndex] -= min(delta, oldValue); break; } case REMOVE_EXACTLY: diff --git a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java index 1d38c8600e55..e5b0a3234ff6 100644 --- a/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ConcurrentHashMultisetTest.java @@ -20,6 +20,7 @@ import static com.google.common.collect.MapMakerInternalMap.Strength.WEAK; import static com.google.common.testing.SerializableTester.reserializeAndAssert; import static java.util.Arrays.asList; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -39,6 +40,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test case for {@link ConcurrentHashMultiset}. @@ -46,8 +48,10 @@ * @author Cliff L. Biffle * @author mike nonemacher */ +@NullUnmarked public class ConcurrentHashMultisetTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -73,6 +77,7 @@ public static Test suite() { return suite; } + @AndroidIncompatible // test-suite builders private static TestStringMultisetGenerator concurrentHashMultisetGenerator() { return new TestStringMultisetGenerator() { @Override @@ -82,6 +87,7 @@ protected Multiset create(String[] elements) { }; } + @AndroidIncompatible // test-suite builders private static TestStringMultisetGenerator concurrentSkipListMultisetGenerator() { return new TestStringMultisetGenerator() { @Override @@ -159,11 +165,7 @@ public void testAdd_laterFewWithOverflow() { when(backingMap.get(KEY)).thenReturn(new AtomicInteger(INITIAL_COUNT)); - try { - multiset.add(KEY, COUNT_TO_ADD); - fail("Must reject arguments that would cause counter overflow."); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> multiset.add(KEY, COUNT_TO_ADD)); } /** @@ -246,11 +248,7 @@ public void testRemoveExactly() { cms.add("a", 2); cms.add("b", 3); - try { - cms.removeExactly("a", -2); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> cms.removeExactly("a", -2)); assertTrue(cms.removeExactly("a", 0)); assertEquals(2, cms.count("a")); diff --git a/android/guava-tests/test/com/google/common/collect/ContiguousSetTest.java b/android/guava-tests/test/com/google/common/collect/ContiguousSetTest.java index 6b19b1edcd2d..737656ad32a6 100644 --- a/android/guava-tests/test/com/google/common/collect/ContiguousSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ContiguousSetTest.java @@ -19,6 +19,7 @@ import static com.google.common.collect.BoundType.CLOSED; import static com.google.common.collect.BoundType.OPEN; import static com.google.common.collect.DiscreteDomain.integers; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_QUERIES; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.NON_STANDARD_TOSTRING; @@ -30,6 +31,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.google.SetGenerators.ContiguousSetDescendingGenerator; @@ -43,9 +45,13 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; -/** @author Gregory Kick */ +/** + * @author Gregory Kick + */ @GwtCompatible(emulated = true) +@NullUnmarked public class ContiguousSetTest extends TestCase { private static final DiscreteDomain NOT_EQUAL_TO_INTEGERS = new DiscreteDomain() { @@ -76,29 +82,13 @@ public Integer maxValue() { }; public void testInvalidIntRange() { - try { - ContiguousSet.closed(2, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - ContiguousSet.closedOpen(2, 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ContiguousSet.closed(2, 1)); + assertThrows(IllegalArgumentException.class, () -> ContiguousSet.closedOpen(2, 1)); } public void testInvalidLongRange() { - try { - ContiguousSet.closed(2L, 1L); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - ContiguousSet.closedOpen(2L, 1L); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ContiguousSet.closed(2L, 1L)); + assertThrows(IllegalArgumentException.class, () -> ContiguousSet.closedOpen(2L, 1L)); } public void testEquals() { @@ -155,22 +145,36 @@ public void testSerialization() { assertEquals(enormous, enormousReserialized); } + private static final DiscreteDomain UNBOUNDED_THROWING_DOMAIN = + new DiscreteDomain() { + @Override + public Integer next(Integer value) { + throw new AssertionError(); + } + + @Override + public Integer previous(Integer value) { + throw new AssertionError(); + } + + @Override + public long distance(Integer start, Integer end) { + throw new AssertionError(); + } + }; + public void testCreate_noMin() { Range range = Range.lessThan(0); - try { - ContiguousSet.create(range, RangeTest.UNBOUNDED_DOMAIN); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ContiguousSet.create(range, UNBOUNDED_THROWING_DOMAIN)); } public void testCreate_noMax() { Range range = Range.greaterThan(0); - try { - ContiguousSet.create(range, RangeTest.UNBOUNDED_DOMAIN); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> ContiguousSet.create(range, UNBOUNDED_THROWING_DOMAIN)); } public void testCreate_empty() { @@ -236,11 +240,7 @@ public void testSubSet() { public void testSubSet_outOfOrder() { ImmutableSortedSet set = ContiguousSet.create(Range.closed(1, 3), integers()); - try { - set.subSet(3, 2); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> set.subSet(3, 2)); } public void testSubSet_tooLarge() { @@ -280,6 +280,12 @@ public void testContains() { assertTrue(set.contains(2)); assertTrue(set.contains(3)); assertFalse(set.contains(4)); + } + + // TODO: https://youtrack.jetbrains.com/issue/KT-71001/ - Enable when Kotlin throws expected CCE. + @J2ktIncompatible + public void testContains_typeMismatch() { + ImmutableSortedSet set = ContiguousSet.create(Range.open(0, 4), integers()); assertFalse(set.contains((Object) "blah")); } @@ -291,6 +297,12 @@ public void testContainsAll() { for (Set subset : Sets.powerSet(ImmutableSet.of(1, 2, 3))) { assertFalse(set.containsAll(Sets.union(subset, ImmutableSet.of(9)))); } + } + + // TODO: https://youtrack.jetbrains.com/issue/KT-71001/ - Enable when Kotlin throws expected CCE. + @J2ktIncompatible + public void testContainsAll_typeMismatch() { + ImmutableSortedSet set = ContiguousSet.create(Range.closed(1, 3), integers()); assertFalse(set.containsAll((Collection) ImmutableSet.of("blah"))); } @@ -388,7 +400,9 @@ public void testAsList() { assertEquals(ImmutableList.of(1, 2, 3), ImmutableList.copyOf(list.toArray(new Integer[0]))); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static class BuiltTests extends TestCase { public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/CountTest.java b/android/guava-tests/test/com/google/common/collect/CountTest.java index 0eff420a5818..86323cc8f728 100644 --- a/android/guava-tests/test/com/google/common/collect/CountTest.java +++ b/android/guava-tests/test/com/google/common/collect/CountTest.java @@ -16,6 +16,7 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@code Count}. @@ -23,6 +24,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class CountTest extends TestCase { public void testGet() { assertEquals(20, new Count(20).get()); diff --git a/android/guava-tests/test/com/google/common/collect/Derived.java b/android/guava-tests/test/com/google/common/collect/Derived.java new file mode 100644 index 000000000000..046853509f6b --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/Derived.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.NullUnmarked; + +/** Simple derived class to verify that we handle generics correctly. */ +@GwtCompatible +@NullUnmarked +class Derived extends Base { + public Derived(String s) { + super(s); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; +} diff --git a/android/guava-tests/test/com/google/common/collect/DiscreteDomainTest.java b/android/guava-tests/test/com/google/common/collect/DiscreteDomainTest.java index b9e15fd9ca65..c11828ad39dc 100644 --- a/android/guava-tests/test/com/google/common/collect/DiscreteDomainTest.java +++ b/android/guava-tests/test/com/google/common/collect/DiscreteDomainTest.java @@ -17,10 +17,12 @@ package com.google.common.collect; import static com.google.common.testing.SerializableTester.reserializeAndAssert; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; import java.math.BigInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link DiscreteDomain}. @@ -28,6 +30,7 @@ * @author Chris Povirk */ @GwtIncompatible // SerializableTester +@NullUnmarked public class DiscreteDomainTest extends TestCase { public void testSerialization() { reserializeAndAssert(DiscreteDomain.integers()); @@ -43,16 +46,10 @@ public void testIntegersOffset() { } public void testIntegersOffsetExceptions() { - try { - DiscreteDomain.integers().offset(0, -1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - DiscreteDomain.integers().offset(Integer.MAX_VALUE, 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DiscreteDomain.integers().offset(0, -1)); + assertThrows( + IllegalArgumentException.class, + () -> DiscreteDomain.integers().offset(Integer.MAX_VALUE, 1)); } public void testLongsOffset() { @@ -61,16 +58,9 @@ public void testLongsOffset() { } public void testLongsOffsetExceptions() { - try { - DiscreteDomain.longs().offset(0L, -1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - DiscreteDomain.longs().offset(Long.MAX_VALUE, 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DiscreteDomain.longs().offset(0L, -1)); + assertThrows( + IllegalArgumentException.class, () -> DiscreteDomain.longs().offset(Long.MAX_VALUE, 1)); } public void testBigIntegersOffset() { @@ -81,24 +71,15 @@ public void testBigIntegersOffset() { } public void testBigIntegersOffsetExceptions() { - try { - DiscreteDomain.bigIntegers().offset(BigInteger.ZERO, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> DiscreteDomain.bigIntegers().offset(BigInteger.ZERO, -1)); } public void testCustomOffsetExceptions() { - try { - new MyIntegerDomain().offset(0, -1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - new MyIntegerDomain().offset(Integer.MAX_VALUE, 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> new MyIntegerDomain().offset(0, -1)); + assertThrows( + IllegalArgumentException.class, () -> new MyIntegerDomain().offset(Integer.MAX_VALUE, 1)); } private static final class MyIntegerDomain extends DiscreteDomain { diff --git a/android/guava-tests/test/com/google/common/collect/EmptyImmutableTableTest.java b/android/guava-tests/test/com/google/common/collect/EmptyImmutableTableTest.java index 4fb26e65c8fa..163688c867bc 100644 --- a/android/guava-tests/test/com/google/common/collect/EmptyImmutableTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/EmptyImmutableTableTest.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.EqualsTester; +import org.jspecify.annotations.NullMarked; /** * Tests {@link EmptyImmutableTable} @@ -26,6 +27,7 @@ * @author Gregory Kick */ @GwtCompatible(emulated = true) +@NullMarked public class EmptyImmutableTableTest extends AbstractImmutableTableTest { private static final ImmutableTable INSTANCE = ImmutableTable.of(); diff --git a/android/guava-tests/test/com/google/common/collect/EnumBiMapTest.java b/android/guava-tests/test/com/google/common/collect/EnumBiMapTest.java index 25b057a72d56..6c869447834b 100644 --- a/android/guava-tests/test/com/google/common/collect/EnumBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/EnumBiMapTest.java @@ -16,12 +16,14 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.Helpers.orderEntriesByKey; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -40,6 +42,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Tests for {@code EnumBiMap}. @@ -47,7 +50,9 @@ * @author Mike Bostock * @author Jared Levy */ +@J2ktIncompatible // EnumBimap @GwtCompatible(emulated = true) +@NullMarked public class EnumBiMapTest extends TestCase { private enum Currency { DOLLAR, @@ -65,6 +70,7 @@ private enum Country { UK } + @AndroidIncompatible // test-suite builders public static final class EnumBiMapGenerator implements TestBiMapGenerator { @SuppressWarnings("unchecked") @Override @@ -80,17 +86,17 @@ public BiMap create(Object... entries) { @Override public SampleElements> samples() { return new SampleElements<>( - Helpers.mapEntry(Country.CANADA, Currency.DOLLAR), - Helpers.mapEntry(Country.CHILE, Currency.PESO), - Helpers.mapEntry(Country.UK, Currency.POUND), - Helpers.mapEntry(Country.JAPAN, Currency.YEN), - Helpers.mapEntry(Country.SWITZERLAND, Currency.FRANC)); + mapEntry(Country.CANADA, Currency.DOLLAR), + mapEntry(Country.CHILE, Currency.PESO), + mapEntry(Country.UK, Currency.POUND), + mapEntry(Country.JAPAN, Currency.YEN), + mapEntry(Country.SWITZERLAND, Currency.FRANC)); } @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -109,7 +115,9 @@ public Currency[] createValueArray(int length) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -148,16 +156,12 @@ public void testCreateFromMap() { assertEquals(Currency.DOLLAR, bimap.inverse().get(Country.CANADA)); /* Map must have at least one entry if not an EnumBiMap. */ - try { - EnumBiMap.create(Collections.emptyMap()); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } - try { - EnumBiMap.create(EnumHashBiMap.create(Currency.class)); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> EnumBiMap.create(Collections.emptyMap())); + assertThrows( + IllegalArgumentException.class, + () -> EnumBiMap.create(EnumHashBiMap.create(Currency.class))); /* Map can be empty if it's an EnumBiMap. */ Map emptyBimap = EnumBiMap.create(Currency.class, Country.class); @@ -183,11 +187,13 @@ public void testEnumBiMapConstructor() { assertEquals(bimap3, emptyBimap); } + @GwtIncompatible // keyType public void testKeyType() { EnumBiMap bimap = EnumBiMap.create(Currency.class, Country.class); assertEquals(Currency.class, bimap.keyType()); } + @GwtIncompatible // valueType public void testValueType() { EnumBiMap bimap = EnumBiMap.create(Currency.class, Country.class); assertEquals(Country.class, bimap.valueType()); @@ -285,12 +291,14 @@ public void testEntrySet() { assertEquals(3, uniqueEntries.size()); } + @J2ktIncompatible @GwtIncompatible // serialization public void testSerializable() { SerializableTester.reserializeAndAssert( EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CANADA))); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(EnumBiMap.class); diff --git a/android/guava-tests/test/com/google/common/collect/EnumHashBiMapTest.java b/android/guava-tests/test/com/google/common/collect/EnumHashBiMapTest.java index 2a78f55477da..72075d05d33f 100644 --- a/android/guava-tests/test/com/google/common/collect/EnumHashBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/EnumHashBiMapTest.java @@ -16,8 +16,12 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -34,13 +38,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code EnumHashBiMap}. * * @author Mike Bostock */ +@J2ktIncompatible // EnumHashBiMap @GwtCompatible(emulated = true) +@NullUnmarked public class EnumHashBiMapTest extends TestCase { private enum Currency { DOLLAR, @@ -58,6 +65,7 @@ private enum Country { UK } + @AndroidIncompatible // test-suite builders public static final class EnumHashBiMapGenerator implements TestBiMapGenerator { @SuppressWarnings("unchecked") @Override @@ -73,17 +81,17 @@ public BiMap create(Object... entries) { @Override public SampleElements> samples() { return new SampleElements<>( - Maps.immutableEntry(Country.CANADA, "DOLLAR"), - Maps.immutableEntry(Country.CHILE, "PESO"), - Maps.immutableEntry(Country.UK, "POUND"), - Maps.immutableEntry(Country.JAPAN, "YEN"), - Maps.immutableEntry(Country.SWITZERLAND, "FRANC")); + immutableEntry(Country.CANADA, "DOLLAR"), + immutableEntry(Country.CHILE, "PESO"), + immutableEntry(Country.UK, "POUND"), + immutableEntry(Country.JAPAN, "YEN"), + immutableEntry(Country.SWITZERLAND, "FRANC")); } @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -102,7 +110,9 @@ public String[] createValueArray(int length) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -142,11 +152,9 @@ public void testCreateFromMap() { assertEquals(Currency.DOLLAR, bimap.inverse().get("dollar")); /* Map must have at least one entry if not an EnumHashBiMap. */ - try { - EnumHashBiMap.create(Collections.emptyMap()); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> EnumHashBiMap.create(Collections.emptyMap())); /* Map can be empty if it's an EnumHashBiMap. */ Map emptyBimap = EnumHashBiMap.create(Currency.class); @@ -197,6 +205,7 @@ public void testEnumBiMapConstructor() { assertEquals(bimap3, emptyBimap); } + @GwtIncompatible // keyType public void testKeyType() { EnumHashBiMap bimap = EnumHashBiMap.create(Currency.class); assertEquals(Currency.class, bimap.keyType()); @@ -216,11 +225,13 @@ public void testEntrySet() { assertEquals(3, uniqueEntries.size()); } + @J2ktIncompatible @GwtIncompatible // serialize public void testSerializable() { SerializableTester.reserializeAndAssert(EnumHashBiMap.create(Currency.class)); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(EnumHashBiMap.class); diff --git a/android/guava-tests/test/com/google/common/collect/EnumMultisetTest.java b/android/guava-tests/test/com/google/common/collect/EnumMultisetTest.java index 265db21286b7..da4db3ca1e34 100644 --- a/android/guava-tests/test/com/google/common/collect/EnumMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/EnumMultisetTest.java @@ -16,10 +16,12 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.AnEnum; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -29,12 +31,14 @@ import com.google.common.testing.ClassSanityTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; +import com.google.errorprone.annotations.Keep; import java.util.Collection; import java.util.EnumSet; import java.util.Set; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for an {@link EnumMultiset}. @@ -42,9 +46,13 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@J2ktIncompatible // EnumMultiset +@NullUnmarked public class EnumMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -62,6 +70,7 @@ public static Test suite() { return suite; } + @AndroidIncompatible // test-suite builders private static TestEnumMultisetGenerator enumMultisetGenerator() { return new TestEnumMultisetGenerator() { @Override @@ -105,11 +114,7 @@ public void testCollectionCreate() { public void testIllegalCreate() { Collection empty = EnumSet.noneOf(Color.class); - try { - EnumMultiset.create(empty); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> EnumMultiset.create(empty)); } public void testCreateEmptyWithClass() { @@ -118,11 +123,8 @@ public void testCreateEmptyWithClass() { } public void testCreateEmptyWithoutClassFails() { - try { - EnumMultiset.create(ImmutableList.of()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> EnumMultiset.create(ImmutableList.of())); } public void testToString() { @@ -154,11 +156,13 @@ public void testEntrySet() { // create(Enum1.class) is equal to create(Enum2.class) but testEquals() expects otherwise. // For the same reason, we need to skip create(Iterable, Class). private static class EnumMultisetFactory { + @Keep // used reflectively by testEquals public static > EnumMultiset create(Iterable elements) { return EnumMultiset.create(elements); } } + @J2ktIncompatible @GwtIncompatible // reflection public void testEquals() throws Exception { new ClassSanityTester() @@ -168,6 +172,7 @@ public void testEquals() throws Exception { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { new NullPointerTester() diff --git a/android/guava-tests/test/com/google/common/collect/EvictingQueueTest.java b/android/guava-tests/test/com/google/common/collect/EvictingQueueTest.java index 05fdbd0e860a..1d8cdd383907 100644 --- a/android/guava-tests/test/com/google/common/collect/EvictingQueueTest.java +++ b/android/guava-tests/test/com/google/common/collect/EvictingQueueTest.java @@ -16,14 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import java.util.AbstractList; import java.util.List; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link EvictingQueue}. @@ -31,14 +35,11 @@ * @author Kurt Alfred Kluever */ @GwtCompatible(emulated = true) +@NullMarked public class EvictingQueueTest extends TestCase { public void testCreateWithNegativeSize() throws Exception { - try { - EvictingQueue.create(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> EvictingQueue.create(-1)); } public void testCreateWithZeroSize() throws Exception { @@ -54,19 +55,11 @@ public void testCreateWithZeroSize() throws Exception { assertFalse(queue.remove("hi")); assertEquals(0, queue.size()); - try { - queue.element(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> queue.element()); assertNull(queue.peek()); assertNull(queue.poll()); - try { - queue.remove(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> queue.remove()); } public void testRemainingCapacity_maxSize0() { @@ -187,6 +180,7 @@ public String get(int index) { assertTrue(queue.isEmpty()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/collect/FauxveridesTest.java b/android/guava-tests/test/com/google/common/collect/FauxveridesTest.java index ff0b86bf95a2..f31f28e8d2ea 100644 --- a/android/guava-tests/test/com/google/common/collect/FauxveridesTest.java +++ b/android/guava-tests/test/com/google/common/collect/FauxveridesTest.java @@ -21,6 +21,8 @@ import static com.google.common.collect.Sets.newHashSet; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertThrows; import com.google.common.base.Function; import com.google.common.base.Joiner; @@ -28,12 +30,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests that all {@code public static} methods "inherited" from superclasses are "overridden" in @@ -42,6 +45,7 @@ * * @author Chris Povirk */ +@NullUnmarked public class FauxveridesTest extends TestCase { public void testImmutableBiMap() { doHasAllFauxveridesTest(ImmutableBiMap.class, ImmutableMap.class); @@ -77,31 +81,23 @@ public void testImmutableSortedMapCopyOfMap() { Map original = ImmutableMap.of(new Object(), new Object(), new Object(), new Object()); - try { - ImmutableSortedMap.copyOf(original); - fail(); - } catch (ClassCastException expected) { - } + assertThrows(ClassCastException.class, () -> ImmutableSortedMap.copyOf(original)); } public void testImmutableSortedSetCopyOfIterable() { + // false positive: `new Object()` is not equal to `new Object()` + @SuppressWarnings("DistinctVarargsChecker") Set original = ImmutableSet.of(new Object(), new Object()); - try { - ImmutableSortedSet.copyOf(original); - fail(); - } catch (ClassCastException expected) { - } + assertThrows(ClassCastException.class, () -> ImmutableSortedSet.copyOf(original)); } public void testImmutableSortedSetCopyOfIterator() { + // false positive: `new Object()` is not equal to `new Object()` + @SuppressWarnings("DistinctVarargsChecker") Set original = ImmutableSet.of(new Object(), new Object()); - try { - ImmutableSortedSet.copyOf(original.iterator()); - fail(); - } catch (ClassCastException expected) { - } + assertThrows(ClassCastException.class, () -> ImmutableSortedSet.copyOf(original.iterator())); } private void doHasAllFauxveridesTest(Class descendant, Class ancestor) { @@ -171,12 +167,12 @@ private static final class MethodSignature implements Comparable[] parameters) { parameterSignatures = transform( - Arrays.asList(parameters), + asList(parameters), new Function, TypeParameterSignature>() { @Override public TypeParameterSignature apply(TypeVariable from) { @@ -219,7 +215,7 @@ public TypeParameterSignature apply(TypeVariable from) { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof TypeSignature) { TypeSignature other = (TypeSignature) obj; return parameterSignatures.equals(other.parameterSignatures); @@ -247,11 +243,11 @@ private static final class TypeParameterSignature { TypeParameterSignature(TypeVariable typeParameter) { name = typeParameter.getName(); - bounds = Arrays.asList(typeParameter.getBounds()); + bounds = asList(typeParameter.getBounds()); } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof TypeParameterSignature) { TypeParameterSignature other = (TypeParameterSignature) obj; /* diff --git a/android/guava-tests/test/com/google/common/collect/FilteredBiMapTest.java b/android/guava-tests/test/com/google/common/collect/FilteredBiMapTest.java new file mode 100644 index 000000000000..78d4e958ccc3 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/FilteredBiMapTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class FilteredBiMapTest extends AbstractFilteredMapTest { + @Override + BiMap createUnfiltered() { + return HashBiMap.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/FilteredCollectionsTest.java b/android/guava-tests/test/com/google/common/collect/FilteredCollectionsTestUtil.java similarity index 78% rename from android/guava-tests/test/com/google/common/collect/FilteredCollectionsTest.java rename to android/guava-tests/test/com/google/common/collect/FilteredCollectionsTestUtil.java index 3b05972eeeec..a965aa31f010 100644 --- a/android/guava-tests/test/com/google/common/collect/FilteredCollectionsTest.java +++ b/android/guava-tests/test/com/google/common/collect/FilteredCollectionsTestUtil.java @@ -16,7 +16,10 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -28,15 +31,21 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; -import java.util.TreeSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** - * Tests for filtered collection views. + * Class that contains nested abstract tests for filtered collection views, along with their + * implementation helpers. * * @author Louis Wasserman */ -public class FilteredCollectionsTest extends TestCase { +/* + * TODO(cpovirk): Should all the tests for filtered collections run under GWT, too? Currently, they + * don't. + */ +@NullUnmarked +public final class FilteredCollectionsTestUtil { private static final Predicate EVEN = new Predicate() { @Override @@ -152,11 +161,7 @@ public void testAddAllFailsAtomically() { C filtered = filter(createUnfiltered(contents), EVEN); C filteredToModify = filter(createUnfiltered(contents), EVEN); - try { - filteredToModify.addAll(toAdd); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> filteredToModify.addAll(toAdd)); assertThat(filteredToModify).containsExactlyElementsIn(filtered); } @@ -168,17 +173,9 @@ public void testAddToFilterFiltered() { C filtered1 = filter(unfiltered, EVEN); C filtered2 = filter(filtered1, PRIME_DIGIT); - try { - filtered2.add(4); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> filtered2.add(4)); - try { - filtered2.add(3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> filtered2.add(3)); filtered2.add(2); } @@ -191,7 +188,7 @@ public void testClearFilterFiltered() { C filtered2 = filter(filtered1, PRIME_DIGIT); C inverseFiltered = - filter(createUnfiltered(contents), Predicates.not(Predicates.and(EVEN, PRIME_DIGIT))); + filter(createUnfiltered(contents), not(Predicates.and(EVEN, PRIME_DIGIT))); filtered2.clear(); assertThat(unfiltered).containsExactlyElementsIn(inverseFiltered); @@ -203,7 +200,7 @@ public abstract static class AbstractFilteredSetTest> extends AbstractFilteredCollectionTest { public void testEqualsAndHashCode() { for (List contents : SAMPLE_INPUTS) { - Set expected = Sets.newHashSet(); + Set expected = newHashSet(); for (Integer i : contents) { if (EVEN.apply(i)) { expected.add(i); @@ -374,79 +371,5 @@ public void testNavigation() { } } - // implementation tests - - public static final class IterablesFilterArrayListTest - extends AbstractFilteredIterableTest> { - @Override - Iterable createUnfiltered(Iterable contents) { - return Lists.newArrayList(contents); - } - - @Override - Iterable filter(Iterable elements, Predicate predicate) { - return Iterables.filter(elements, predicate); - } - } - - public static final class Collections2FilterArrayListTest - extends AbstractFilteredCollectionTest> { - @Override - Collection createUnfiltered(Iterable contents) { - return Lists.newArrayList(contents); - } - - @Override - Collection filter(Collection elements, Predicate predicate) { - return Collections2.filter(elements, predicate); - } - } - - public static final class SetsFilterHashSetTest extends AbstractFilteredSetTest> { - @Override - Set createUnfiltered(Iterable contents) { - return Sets.newHashSet(contents); - } - - @Override - Set filter(Set elements, Predicate predicate) { - return Sets.filter(elements, predicate); - } - } - - public static final class SetsFilterSortedSetTest - extends AbstractFilteredSortedSetTest> { - @Override - SortedSet createUnfiltered(Iterable contents) { - final TreeSet result = Sets.newTreeSet(contents); - // we have to make the result not Navigable - return new ForwardingSortedSet() { - @Override - protected SortedSet delegate() { - return result; - } - }; - } - - @Override - SortedSet filter(SortedSet elements, Predicate predicate) { - return Sets.filter(elements, predicate); - } - } - - public static final class SetsFilterNavigableSetTest extends AbstractFilteredNavigableSetTest { - @Override - NavigableSet createUnfiltered(Iterable contents) { - return Sets.newTreeSet(contents); - } - - @Override - NavigableSet filter( - NavigableSet elements, Predicate predicate) { - return Sets.filter(elements, predicate); - } - } - - /** No-op test so that the class has at least one method, making Maven's test runner happy. */ - public void testNoop() {} + private FilteredCollectionsTestUtil() {} } diff --git a/android/guava-tests/test/com/google/common/collect/FilteredMapTest.java b/android/guava-tests/test/com/google/common/collect/FilteredMapTest.java new file mode 100644 index 000000000000..439099b11fda --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/FilteredMapTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class FilteredMapTest extends AbstractFilteredMapTest { + @Override + Map createUnfiltered() { + return Maps.newHashMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/FilteredMultimapTest.java b/android/guava-tests/test/com/google/common/collect/FilteredMultimapTest.java index 0e5e26574821..383dc06a7ba7 100644 --- a/android/guava-tests/test/com/google/common/collect/FilteredMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/FilteredMultimapTest.java @@ -16,11 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.Multimaps.filterKeys; +import static com.google.common.collect.Multimaps.filterValues; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Predicate; -import java.util.Arrays; import java.util.Map.Entry; +import java.util.Objects; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Multimaps} filtering methods. @@ -28,15 +33,12 @@ * @author Jared Levy */ @GwtIncompatible // nottested +@NullUnmarked public class FilteredMultimapTest extends TestCase { private static final Predicate> ENTRY_PREDICATE = - new Predicate>() { - @Override - public boolean apply(Entry entry) { - return !"badkey".equals(entry.getKey()) && !((Integer) 55556).equals(entry.getValue()); - } - }; + entry -> + !Objects.equals(entry.getKey(), "badkey") && !Objects.equals(entry.getValue(), 55556); protected Multimap create() { Multimap unfiltered = HashMultimap.create(); @@ -45,36 +47,24 @@ protected Multimap create() { return Multimaps.filterEntries(unfiltered, ENTRY_PREDICATE); } - private static final Predicate KEY_PREDICATE = - new Predicate() { - @Override - public boolean apply(String key) { - return !"badkey".equals(key); - } - }; + private static final Predicate KEY_PREDICATE = key -> !Objects.equals(key, "badkey"); public void testFilterKeys() { Multimap unfiltered = HashMultimap.create(); unfiltered.put("foo", 55556); unfiltered.put("badkey", 1); - Multimap filtered = Multimaps.filterKeys(unfiltered, KEY_PREDICATE); + Multimap filtered = filterKeys(unfiltered, KEY_PREDICATE); assertEquals(1, filtered.size()); assertTrue(filtered.containsEntry("foo", 55556)); } - private static final Predicate VALUE_PREDICATE = - new Predicate() { - @Override - public boolean apply(Integer value) { - return !((Integer) 55556).equals(value); - } - }; + private static final Predicate VALUE_PREDICATE = value -> !Objects.equals(value, 55556); public void testFilterValues() { Multimap unfiltered = HashMultimap.create(); unfiltered.put("foo", 55556); unfiltered.put("badkey", 1); - Multimap filtered = Multimaps.filterValues(unfiltered, VALUE_PREDICATE); + Multimap filtered = filterValues(unfiltered, VALUE_PREDICATE); assertEquals(1, filtered.size()); assertFalse(filtered.containsEntry("foo", 55556)); assertTrue(filtered.containsEntry("badkey", 1)); @@ -85,11 +75,11 @@ public void testFilterFiltered() { unfiltered.put("foo", 55556); unfiltered.put("badkey", 1); unfiltered.put("foo", 1); - Multimap keyFiltered = Multimaps.filterKeys(unfiltered, KEY_PREDICATE); - Multimap filtered = Multimaps.filterValues(keyFiltered, VALUE_PREDICATE); + Multimap keyFiltered = filterKeys(unfiltered, KEY_PREDICATE); + Multimap filtered = filterValues(keyFiltered, VALUE_PREDICATE); assertEquals(1, filtered.size()); assertTrue(filtered.containsEntry("foo", 1)); - assertTrue(filtered.keySet().retainAll(Arrays.asList("cat", "dog"))); + assertTrue(filtered.keySet().retainAll(asList("cat", "dog"))); assertEquals(0, filtered.size()); } diff --git a/android/guava-tests/test/com/google/common/collect/FilteredSortedMapTest.java b/android/guava-tests/test/com/google/common/collect/FilteredSortedMapTest.java new file mode 100644 index 000000000000..7fb754300d59 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/FilteredSortedMapTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class FilteredSortedMapTest extends AbstractFilteredMapTest { + @Override + SortedMap createUnfiltered() { + return Maps.newTreeMap(); + } + + public void testFirstAndLastKeyFilteredMap() { + SortedMap unfiltered = createUnfiltered(); + unfiltered.put("apple", 2); + unfiltered.put("banana", 6); + unfiltered.put("cat", 3); + unfiltered.put("dog", 5); + + SortedMap filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); + assertEquals("banana", filtered.firstKey()); + assertEquals("cat", filtered.lastKey()); + } + + public void testHeadSubTailMap_filteredMap() { + SortedMap unfiltered = createUnfiltered(); + unfiltered.put("apple", 2); + unfiltered.put("banana", 6); + unfiltered.put("cat", 4); + unfiltered.put("dog", 3); + SortedMap filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); + + assertEquals(ImmutableMap.of("banana", 6), filtered.headMap("dog")); + assertEquals(ImmutableMap.of(), filtered.headMap("banana")); + assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.headMap("emu")); + + assertEquals(ImmutableMap.of("banana", 6), filtered.subMap("banana", "dog")); + assertEquals(ImmutableMap.of("dog", 3), filtered.subMap("cat", "emu")); + + assertEquals(ImmutableMap.of("dog", 3), filtered.tailMap("cat")); + assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.tailMap("banana")); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/FluentIterableTest.java b/android/guava-tests/test/com/google/common/collect/FluentIterableTest.java index b8427b1c8140..049dae5b2760 100644 --- a/android/guava-tests/test/com/google/common/collect/FluentIterableTest.java +++ b/android/guava-tests/test/com/google/common/collect/FluentIterableTest.java @@ -16,9 +16,18 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.collect.Iterables.removeIf; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.nCopies; +import static java.util.Collections.singletonList; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -38,9 +47,10 @@ import java.util.Set; import java.util.SortedSet; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link FluentIterable}. @@ -48,6 +58,7 @@ * @author Marcin Mikosik */ @GwtCompatible(emulated = true) +@NullUnmarked public class FluentIterableTest extends TestCase { @GwtIncompatible // NullPointerTester @@ -57,17 +68,12 @@ public void testNullPointerExceptions() { } public void testFromArrayAndAppend() { - FluentIterable units = - FluentIterable.from(TimeUnit.values()).append(TimeUnit.SECONDS); + FluentIterable unused = FluentIterable.from(TimeUnit.values()).append(SECONDS); } public void testFromArrayAndIteratorRemove() { FluentIterable units = FluentIterable.from(TimeUnit.values()); - try { - Iterables.removeIf(units, Predicates.equalTo(TimeUnit.SECONDS)); - fail("Expected an UnsupportedOperationException to be thrown but it wasn't."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> removeIf(units, equalTo(SECONDS))); } public void testFrom() { @@ -76,7 +82,11 @@ public void testFrom() { Lists.newArrayList(FluentIterable.from(ImmutableList.of(1, 2, 3, 4)))); } - @SuppressWarnings("deprecation") // test of deprecated method + @SuppressWarnings({ + "deprecation", // test of deprecated method + // We need to test that from(FluentIterable) really is just a null check. + "InlineMeInliner", + }) public void testFrom_alreadyFluentIterable() { FluentIterable iterable = FluentIterable.from(asList(1)); assertSame(iterable, FluentIterable.from(iterable)); @@ -101,7 +111,6 @@ public void testConcatIterable() { List list1 = newArrayList(1); List list2 = newArrayList(4); - @SuppressWarnings("unchecked") List> input = newArrayList(list1, list2); FluentIterable result = FluentIterable.concat(input); @@ -123,7 +132,6 @@ public void testConcatVarargs() { List list3 = newArrayList(7, 8); List list4 = newArrayList(9); List list5 = newArrayList(10); - @SuppressWarnings("unchecked") FluentIterable result = FluentIterable.concat(list1, list2, list3, list4, list5); assertEquals(asList(1, 4, 7, 8, 9, 10), newArrayList(result)); assertEquals("[1, 4, 7, 8, 9, 10]", result.toString()); @@ -133,17 +141,13 @@ public void testConcatNullPointerException() { List list1 = newArrayList(1); List list2 = newArrayList(4); - try { - FluentIterable.concat(list1, null, list2); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> FluentIterable.concat(list1, null, list2)); } public void testConcatPeformingFiniteCycle() { Iterable iterable = asList(1, 2, 3); int n = 4; - FluentIterable repeated = FluentIterable.concat(Collections.nCopies(n, iterable)); + FluentIterable repeated = FluentIterable.concat(nCopies(n, iterable)); assertThat(repeated).containsExactly(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3).inOrder(); } @@ -220,7 +224,7 @@ public Iterator iterator() { } public void testContains_nullSetYes() { - Iterable set = Sets.newHashSet("a", null, "b"); + Iterable set = newHashSet("a", null, "b"); assertTrue(FluentIterable.from(set).contains(null)); } @@ -240,12 +244,12 @@ public void testContains_nullIterableNo() { } public void testContains_nonNullSetYes() { - Iterable set = Sets.newHashSet("a", null, "b"); + Iterable set = newHashSet("a", null, "b"); assertTrue(FluentIterable.from(set).contains("b")); } public void testContains_nonNullSetNo() { - Iterable set = Sets.newHashSet("a", "b"); + Iterable set = newHashSet("a", "b"); assertFalse(FluentIterable.from(set).contains("c")); } @@ -264,7 +268,7 @@ public void testOfToString() { } public void testToString() { - assertEquals("[]", FluentIterable.from(Collections.emptyList()).toString()); + assertEquals("[]", FluentIterable.from(emptyList()).toString()); assertEquals("[]", FluentIterable.of().toString()); assertEquals( @@ -328,12 +332,12 @@ public void testAppend_emptyList() { } public void testAppend_nullPointerException() { - try { - FluentIterable unused = - FluentIterable.from(asList(1, 2)).append((List) null); - fail("Appending null iterable should throw NPE."); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> { + FluentIterable unused = + FluentIterable.from(asList(1, 2)).append((List) null); + }); } /* @@ -346,9 +350,9 @@ public void testAppend_nullPointerException() { public void testFilter() { FluentIterable filtered = - FluentIterable.from(asList("foo", "bar")).filter(Predicates.equalTo("foo")); + FluentIterable.from(asList("foo", "bar")).filter(equalTo("foo")); - List expected = Collections.singletonList("foo"); + List expected = singletonList("foo"); List actual = Lists.newArrayList(filtered); assertEquals(expected, actual); assertCanIterateAgain(filtered); @@ -373,7 +377,7 @@ public void testFilterByType() throws Exception { public void testAnyMatch() { ArrayList list = Lists.newArrayList(); FluentIterable iterable = FluentIterable.from(list); - Predicate predicate = Predicates.equalTo("pants"); + Predicate predicate = equalTo("pants"); assertFalse(iterable.anyMatch(predicate)); list.add("cool"); @@ -385,7 +389,7 @@ public void testAnyMatch() { public void testAllMatch() { List list = Lists.newArrayList(); FluentIterable iterable = FluentIterable.from(list); - Predicate predicate = Predicates.equalTo("cool"); + Predicate predicate = equalTo("cool"); assertTrue(iterable.allMatch(predicate)); list.add("cool"); @@ -396,8 +400,8 @@ public void testAllMatch() { public void testFirstMatch() { FluentIterable iterable = FluentIterable.from(Lists.newArrayList("cool", "pants")); - assertThat(iterable.firstMatch(Predicates.equalTo("cool"))).hasValue("cool"); - assertThat(iterable.firstMatch(Predicates.equalTo("pants"))).hasValue("pants"); + assertThat(iterable.firstMatch(equalTo("cool"))).hasValue("cool"); + assertThat(iterable.firstMatch(equalTo("pants"))).hasValue("pants"); assertThat(iterable.firstMatch(Predicates.alwaysFalse())).isAbsent(); assertThat(iterable.firstMatch(Predicates.alwaysTrue())).hasValue("cool"); } @@ -425,11 +429,7 @@ public void testTransformWith_poorlyBehavedTransform() { Iterator resultIterator = iterable.iterator(); resultIterator.next(); - try { - resultIterator.next(); - fail("Transforming null to int should throw NumberFormatException"); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> resultIterator.next()); } private static final class StringValueOfFunction implements Function { @@ -484,15 +484,11 @@ public void testFirst_list() { public void testFirst_null() { List list = Lists.newArrayList(null, "a", "b"); - try { - FluentIterable.from(list).first(); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> FluentIterable.from(list).first()); } public void testFirst_emptyList() { - List list = Collections.emptyList(); + List list = emptyList(); assertThat(FluentIterable.from(list).first()).isAbsent(); } @@ -512,7 +508,7 @@ public void testFirst_iterable() { } public void testFirst_emptyIterable() { - Set set = Sets.newHashSet(); + Set set = newHashSet(); assertThat(FluentIterable.from(set).first()).isAbsent(); } @@ -523,15 +519,11 @@ public void testLast_list() { public void testLast_null() { List list = Lists.newArrayList("a", "b", null); - try { - FluentIterable.from(list).last(); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> FluentIterable.from(list).last()); } public void testLast_emptyList() { - List list = Collections.emptyList(); + List list = emptyList(); assertThat(FluentIterable.from(list).last()).isAbsent(); } @@ -551,7 +543,7 @@ public void testLast_iterable() { } public void testLast_emptyIterable() { - Set set = Sets.newHashSet(); + Set set = newHashSet(); assertThat(FluentIterable.from(set).last()).isAbsent(); } @@ -571,12 +563,12 @@ public void testSkip_simpleList() { public void testSkip_pastEnd() { Collection set = ImmutableSet.of("a", "b"); - assertEquals(Collections.emptyList(), Lists.newArrayList(FluentIterable.from(set).skip(20))); + assertEquals(emptyList(), Lists.newArrayList(FluentIterable.from(set).skip(20))); } public void testSkip_pastEndList() { Collection list = Lists.newArrayList("a", "b"); - assertEquals(Collections.emptyList(), Lists.newArrayList(FluentIterable.from(list).skip(20))); + assertEquals(emptyList(), Lists.newArrayList(FluentIterable.from(list).skip(20))); } public void testSkip_skipNone() { @@ -663,11 +655,8 @@ public void testSkip_structurallyModifiedSkipAllList() throws Exception { } public void testSkip_illegalArgument() { - try { - FluentIterable.from(asList("a", "b", "c")).skip(-1); - fail("Skipping negative number of elements should throw IllegalArgumentException."); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> FluentIterable.from(asList("a", "b", "c")).skip(-1)); } public void testLimit() { @@ -680,12 +669,12 @@ public void testLimit() { } public void testLimit_illegalArgument() { - try { - FluentIterable unused = - FluentIterable.from(Lists.newArrayList("a", "b", "c")).limit(-1); - fail("Passing negative number to limit(...) method should throw IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + FluentIterable unused = + FluentIterable.from(Lists.newArrayList("a", "b", "c")).limit(-1); + }); } public void testIsEmpty() { @@ -747,25 +736,17 @@ public void testToMultiset_empty() { public void testToMap() { assertThat(fluent(1, 2, 3).toMap(Functions.toStringFunction()).entrySet()) - .containsExactly( - Maps.immutableEntry(1, "1"), Maps.immutableEntry(2, "2"), Maps.immutableEntry(3, "3")) + .containsExactly(immutableEntry(1, "1"), immutableEntry(2, "2"), immutableEntry(3, "3")) .inOrder(); } public void testToMap_nullKey() { - try { - fluent(1, null, 2).toMap(Functions.constant("foo")); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> fluent(1, null, 2).toMap(Functions.constant("foo"))); } public void testToMap_nullValue() { - try { - fluent(1, 2, 3).toMap(Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> fluent(1, 2, 3).toMap(Functions.constant(null))); } public void testIndex() { @@ -788,21 +769,21 @@ public Integer apply(String input) { } public void testIndex_nullKey() { - try { - ImmutableListMultimap unused = - fluent(1, 2, 3).index(Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> { + ImmutableListMultimap unused = + fluent(1, 2, 3).index(Functions.constant(null)); + }); } public void testIndex_nullValue() { - try { - ImmutableListMultimap unused = - fluent(1, null, 2).index(Functions.constant("foo")); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> { + ImmutableListMultimap unused = + fluent(1, null, 2).index(Functions.constant("foo")); + }); } public void testUniqueIndex() { @@ -820,60 +801,57 @@ public Integer apply(String input) { } public void testUniqueIndex_duplicateKey() { - try { - ImmutableMap unused = - FluentIterable.from(asList("one", "two", "three", "four")) - .uniqueIndex( - new Function() { - @Override - public Integer apply(String input) { - return input.length(); - } - }); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + ImmutableMap unused = + FluentIterable.from(asList("one", "two", "three", "four")) + .uniqueIndex( + new Function() { + @Override + public Integer apply(String input) { + return input.length(); + } + }); + }); } public void testUniqueIndex_nullKey() { - try { - fluent(1, 2, 3).uniqueIndex(Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> fluent(1, 2, 3).uniqueIndex(Functions.constant(null))); } public void testUniqueIndex_nullValue() { - try { - ImmutableMap unused = - fluent(1, null, 2) - .uniqueIndex( - new Function() { - @Override - public Object apply(@CheckForNull Integer input) { - return String.valueOf(input); - } - }); - fail(); - } catch (NullPointerException expected) { - } - } - - public void testCopyInto_List() { + assertThrows( + NullPointerException.class, + () -> { + ImmutableMap unused = + fluent(1, null, 2) + .uniqueIndex( + new Function() { + @Override + public Object apply(@Nullable Integer input) { + return String.valueOf(input); + } + }); + }); + } + + public void testCopyInto_list() { assertThat(fluent(1, 3, 5).copyInto(Lists.newArrayList(1, 2))) .containsExactly(1, 2, 1, 3, 5) .inOrder(); } - public void testCopyInto_Set() { - assertThat(fluent(1, 3, 5).copyInto(Sets.newHashSet(1, 2))).containsExactly(1, 2, 3, 5); + public void testCopyInto_set() { + assertThat(fluent(1, 3, 5).copyInto(newHashSet(1, 2))).containsExactly(1, 2, 3, 5); } - public void testCopyInto_SetAllDuplicates() { - assertThat(fluent(1, 3, 5).copyInto(Sets.newHashSet(1, 2, 3, 5))).containsExactly(1, 2, 3, 5); + public void testCopyInto_setAllDuplicates() { + assertThat(fluent(1, 3, 5).copyInto(newHashSet(1, 2, 3, 5))).containsExactly(1, 2, 3, 5); } - public void testCopyInto_NonCollection() { + public void testCopyInto_nonCollection() { final ArrayList list = Lists.newArrayList(1, 2, 3); final ArrayList iterList = Lists.newArrayList(9, 8, 7); @@ -905,17 +883,13 @@ public void testGet() { } public void testGet_outOfBounds() { - try { - FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, + () -> FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(-1)); - try { - FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(3); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, + () -> FluentIterable.from(Lists.newArrayList("a", "b", "c")).get(3)); } private static void assertCanIterateAgain(Iterable iterable) { diff --git a/android/guava-tests/test/com/google/common/collect/ForMapMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/ForMapMultimapAsMapImplementsMapTest.java index 402410b297f0..b09fc124a98d 100644 --- a/android/guava-tests/test/com/google/common/collect/ForMapMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForMapMultimapAsMapImplementsMapTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@link Multimap#asMap()} for a {@link Multimaps#forMap} multimap with {@link @@ -28,6 +29,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class ForMapMultimapAsMapImplementsMapTest extends AbstractMultimapAsMapImplementsMapTest { public ForMapMultimapAsMapImplementsMapTest() { diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingCollectionTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingCollectionTest.java index 63dd135afaec..a92edb28f458 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingCollectionTest.java @@ -29,6 +29,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ForwardingCollection}. @@ -37,6 +38,7 @@ * @author Hayward Chan * @author Louis Wasserman */ +@NullUnmarked public class ForwardingCollectionTest extends TestCase { static final class StandardImplForwardingCollection extends ForwardingCollection { private final Collection backingCollection; @@ -101,6 +103,7 @@ public String toString() { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingConcurrentMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingConcurrentMapTest.java index aa1c61c88d35..3bd70757f696 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingConcurrentMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingConcurrentMapTest.java @@ -19,12 +19,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ForwardingConcurrentMap}. * * @author Jared Levy */ +@NullUnmarked public class ForwardingConcurrentMapTest extends TestCase { private static class TestMap extends ForwardingConcurrentMap { diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingDequeTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingDequeTest.java index 7541f91a2dd2..6811b1c2393a 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingDequeTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingDequeTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.ForwardingWrapperTester; import java.util.Deque; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code ForwardingDeque}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class ForwardingDequeTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( Deque.class, - new Function() { + new Function>() { @Override - public Deque apply(Deque delegate) { - return wrap(delegate); + public Deque apply(Deque delegate) { + return wrap((Deque) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingListIteratorTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingListIteratorTest.java index df6fe5003bd0..e369de4254cd 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingListIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingListIteratorTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.ForwardingWrapperTester; import java.util.ListIterator; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code ForwardingListIterator}. * * @author Robert Konigsberg */ +@NullUnmarked public class ForwardingListIteratorTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( ListIterator.class, - new Function() { + new Function>() { @Override - public ListIterator apply(ListIterator delegate) { - return wrap(delegate); + public ListIterator apply(ListIterator delegate) { + return wrap((ListIterator) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingListMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingListMultimapTest.java index 3c5ebae76e46..ba974e0dc9bd 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingListMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingListMultimapTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingListMultimap}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class ForwardingListMultimapTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( ListMultimap.class, - new Function() { + new Function>() { @Override - public ListMultimap apply(ListMultimap delegate) { - return wrap(delegate); + public ListMultimap apply(ListMultimap delegate) { + return wrap((ListMultimap) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingListTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingListTest.java index 7d3466eedd2c..32b9d1f499a1 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingListTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingListTest.java @@ -31,6 +31,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingList}. @@ -38,6 +40,7 @@ * @author Robert Konigsberg * @author Louis Wasserman */ +@NullUnmarked public class ForwardingListTest extends TestCase { static final class StandardImplForwardingList extends ForwardingList { private final List backingList; @@ -112,7 +115,7 @@ public String toString() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -152,6 +155,7 @@ public List subList(int fromIndex, int toIndex) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java index cc5d73967607..ab305710a9f0 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingMapTest.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import static com.google.common.collect.Iterators.emptyIterator; import static java.lang.reflect.Modifier.STATIC; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; @@ -29,9 +30,7 @@ import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; -import com.google.common.reflect.AbstractInvocationHandler; import com.google.common.reflect.Parameter; -import com.google.common.reflect.Reflection; import com.google.common.reflect.TypeToken; import com.google.common.testing.ArbitraryInstances; import com.google.common.testing.EqualsTester; @@ -44,9 +43,14 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.IntFunction; +import java.util.function.Predicate; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link ForwardingMap}. @@ -54,6 +58,7 @@ * @author Hayward Chan * @author Louis Wasserman */ +@NullUnmarked public class ForwardingMapTest extends TestCase { static class StandardImplForwardingMap extends ForwardingMap { private final Map backingMap; @@ -83,12 +88,12 @@ public void putAll(Map map) { } @Override - public V remove(Object object) { + public @Nullable V remove(Object object) { return standardRemove(object); } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -133,6 +138,7 @@ public boolean isEmpty() { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -170,7 +176,7 @@ protected Map create(Entry[] entries) { for (Entry entry : entries) { builder.put(entry.getKey(), entry.getValue()); } - return new StandardImplForwardingMap<>(builder.build()); + return new StandardImplForwardingMap<>(builder.buildOrThrow()); } }) .named("ForwardingMap[ImmutableMap] with standard implementations") @@ -222,7 +228,7 @@ public Set> entrySet() { return new StandardEntrySet() { @Override public Iterator> iterator() { - return Iterators.emptyIterator(); + return emptyIterator(); } }; } @@ -329,38 +335,25 @@ protected Map delegate() { }; } - private static final ImmutableMap JUF_METHODS = - ImmutableMap.of( - "java.util.function.Predicate", "test", - "java.util.function.Consumer", "accept", - "java.util.function.IntFunction", "apply"); - - private static Object getDefaultValue(final TypeToken type) { + private static @Nullable Object getDefaultValue(final TypeToken type) { Class rawType = type.getRawType(); Object defaultValue = ArbitraryInstances.get(rawType); if (defaultValue != null) { return defaultValue; } - final String typeName = rawType.getCanonicalName(); - if (JUF_METHODS.containsKey(typeName)) { - // Generally, methods that accept java.util.function.* instances - // don't like to get null values. We generate them dynamically - // using Proxy so that we can have Java 7 compliant code. - return Reflection.newProxy( - rawType, - new AbstractInvocationHandler() { - @Override - public Object handleInvocation(Object proxy, Method method, Object[] args) { - // Crude, but acceptable until we can use Java 8. Other - // methods have default implementations, and it is hard to - // distinguish. - if (method.getName().equals(JUF_METHODS.get(typeName))) { - return getDefaultValue(type.method(method).getReturnType()); - } - throw new IllegalStateException("Unexpected " + method + " invoked on " + proxy); - } - }); + // TODO(cpovirk): Support these types in ArbitraryInstances itself? + if (rawType.equals(Predicate.class)) { + return (Predicate) v -> (boolean) getDefaultValue(TypeToken.of(boolean.class)); + } else if (rawType.equals(IntFunction.class)) { + try { + Method method = IntFunction.class.getMethod("apply", int.class); + return (IntFunction) v -> getDefaultValue(type.method(method).getReturnType()); + } catch (NoSuchMethodException e) { + throw newLinkageError(e); + } + } else if (rawType.equals(Consumer.class)) { + return (Consumer) v -> {}; } else { return null; } @@ -392,4 +385,8 @@ private static void callAllPublicMethods(TypeToken type, T object) } } } + + private static LinkageError newLinkageError(Throwable cause) { + return new LinkageError(cause.toString(), cause); + } } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingMultimapTest.java index b842feae10b2..ec93966ea344 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingMultimapTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingMultimap}. * * @author Hayward Chan */ +@NullUnmarked public class ForwardingMultimapTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( Multimap.class, - new Function() { + new Function>() { @Override - public Multimap apply(Multimap delegate) { - return wrap(delegate); + public Multimap apply(Multimap delegate) { + return wrap((Multimap) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingMultisetTest.java index 327885d538f4..c3a541f758b3 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingMultisetTest.java @@ -16,6 +16,8 @@ package com.google.common.collect; +import static java.util.Arrays.asList; + import com.google.common.base.Function; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -26,13 +28,14 @@ import com.google.common.collect.testing.google.TestStringMultisetGenerator; import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Set; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ForwardingMultiset}. @@ -40,6 +43,7 @@ * @author Hayward Chan * @author Louis Wasserman */ +@NullUnmarked public class ForwardingMultisetTest extends TestCase { static final class StandardImplForwardingMultiset extends ForwardingMultiset { @@ -115,7 +119,7 @@ public String toString() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -155,6 +159,7 @@ public int size() { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -166,7 +171,7 @@ public static Test suite() { @Override protected Multiset create(String[] elements) { return new StandardImplForwardingMultiset<>( - LinkedHashMultiset.create(Arrays.asList(elements))); + LinkedHashMultiset.create(asList(elements))); } }) .named("ForwardingMultiset[LinkedHashMultiset] with standard " + "implementations") @@ -197,8 +202,7 @@ protected Multiset create(String[] elements) { */ @Override protected Set create(String[] elements) { - final Multiset inner = - LinkedHashMultiset.create(Arrays.asList(elements)); + final Multiset inner = LinkedHashMultiset.create(asList(elements)); return new ForwardingMultiset() { @Override protected Multiset delegate() { @@ -277,7 +281,7 @@ public boolean retainAll(Collection collection) { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { throw new UnsupportedOperationException(); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java index b70cd0718ef1..2bcccef303c3 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableMapTest.java @@ -39,6 +39,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingNavigableMap}. @@ -46,6 +48,7 @@ * @author Robert Konigsberg * @author Louis Wasserman */ +@NullUnmarked public class ForwardingNavigableMapTest extends TestCase { static class StandardImplForwardingNavigableMap extends ForwardingNavigableMap { private final NavigableMap backingMap; @@ -75,12 +78,12 @@ public void putAll(Map map) { } @Override - public V remove(Object object) { + public @Nullable V remove(Object object) { return standardRemove(object); } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -134,47 +137,47 @@ public SortedMap subMap(K fromKey, K toKey) { } @Override - public Entry lowerEntry(K key) { + public @Nullable Entry lowerEntry(K key) { return standardLowerEntry(key); } @Override - public K lowerKey(K key) { + public @Nullable K lowerKey(K key) { return standardLowerKey(key); } @Override - public Entry floorEntry(K key) { + public @Nullable Entry floorEntry(K key) { return standardFloorEntry(key); } @Override - public K floorKey(K key) { + public @Nullable K floorKey(K key) { return standardFloorKey(key); } @Override - public Entry ceilingEntry(K key) { + public @Nullable Entry ceilingEntry(K key) { return standardCeilingEntry(key); } @Override - public K ceilingKey(K key) { + public @Nullable K ceilingKey(K key) { return standardCeilingKey(key); } @Override - public Entry higherEntry(K key) { + public @Nullable Entry higherEntry(K key) { return standardHigherEntry(key); } @Override - public K higherKey(K key) { + public @Nullable K higherKey(K key) { return standardHigherKey(key); } @Override - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return standardFirstEntry(); } @@ -184,12 +187,12 @@ public Entry firstEntry() { */ @Override - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return standardPollFirstEntry(); } @Override - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return standardPollLastEntry(); } @@ -242,11 +245,12 @@ protected NavigableMap delegate() { } @Override - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return standardLastEntry(); } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableSetTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableSetTest.java index fd47bf8fd53c..537629afe38c 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingNavigableSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingNavigableSetTest.java @@ -16,6 +16,8 @@ package com.google.common.collect; +import static java.util.Arrays.asList; + import com.google.common.base.Function; import com.google.common.collect.testing.SafeTreeSet; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -24,7 +26,6 @@ import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -34,12 +35,15 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingNavigableSet}. * * @author Louis Wasserman */ +@NullUnmarked public class ForwardingNavigableSetTest extends TestCase { static class StandardImplForwardingNavigableSet extends ForwardingNavigableSet { private final NavigableSet backingSet; @@ -54,7 +58,7 @@ protected NavigableSet delegate() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -119,32 +123,32 @@ public SortedSet subSet(T fromElement, T toElement) { } @Override - public T lower(T e) { + public @Nullable T lower(T e) { return standardLower(e); } @Override - public T floor(T e) { + public @Nullable T floor(T e) { return standardFloor(e); } @Override - public T ceiling(T e) { + public @Nullable T ceiling(T e) { return standardCeiling(e); } @Override - public T higher(T e) { + public @Nullable T higher(T e) { return standardHigher(e); } @Override - public T pollFirst() { + public @Nullable T pollFirst() { return standardPollFirst(); } @Override - public T pollLast() { + public @Nullable T pollLast() { return standardPollLast(); } @@ -159,6 +163,7 @@ public SortedSet tailSet(T fromElement) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -169,7 +174,7 @@ public static Test suite() { @Override protected Set create(String[] elements) { return new StandardImplForwardingNavigableSet<>( - new SafeTreeSet(Arrays.asList(elements))); + new SafeTreeSet(asList(elements))); } @Override diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingObjectTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingObjectTest.java index 05bd9f5ca5ed..efcdf0e87d5e 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingObjectTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingObjectTest.java @@ -16,15 +16,19 @@ package com.google.common.collect; +import static com.google.common.collect.Sets.newHashSet; + import com.google.common.testing.EqualsTester; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code ForwardingObject}. * * @author Mike Bostock */ +@NullUnmarked public class ForwardingObjectTest extends TestCase { public void testEqualsReflexive() { @@ -40,7 +44,7 @@ protected Object delegate() { } public void testEqualsSymmetric() { - final Set delegate = Sets.newHashSet("foo"); + final Set delegate = newHashSet("foo"); ForwardingObject forward = new ForwardingObject() { @Override diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingQueueTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingQueueTest.java index 248f132777a4..1e645cdad84b 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingQueueTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingQueueTest.java @@ -29,6 +29,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingQueue}. @@ -36,6 +38,7 @@ * @author Robert Konigsberg * @author Louis Wasserman */ +@NullUnmarked public class ForwardingQueueTest extends TestCase { static final class StandardImplForwardingQueue extends ForwardingQueue { @@ -106,16 +109,17 @@ public boolean offer(T o) { } @Override - public T peek() { + public @Nullable T peek() { return standardPeek(); } @Override - public T poll() { + public @Nullable T poll() { return standardPoll(); } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSetMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSetMultimapTest.java index 03f0f3151efa..f25b2aa3ee85 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSetMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSetMultimapTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingSetMultimap}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class ForwardingSetMultimapTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( SetMultimap.class, - new Function() { + new Function>() { @Override - public SetMultimap apply(SetMultimap delegate) { - return wrap(delegate); + public SetMultimap apply(SetMultimap delegate) { + return wrap((SetMultimap) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSetTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSetTest.java index 89bf7b31d728..837abe561a30 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSetTest.java @@ -31,6 +31,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingSet}. @@ -38,6 +40,7 @@ * @author Robert Konigsberg * @author Louis Wasserman */ +@NullUnmarked public class ForwardingSetTest extends TestCase { static class StandardImplForwardingSet extends ForwardingSet { private final Set backingSet; @@ -52,7 +55,7 @@ protected Set delegate() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -112,6 +115,7 @@ public String toString() { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapImplementsMapTest.java index b694d5d68988..a2f6c8739265 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapImplementsMapTest.java @@ -17,10 +17,12 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.MapInterfaceTest; import com.google.common.collect.testing.SortedMapInterfaceTest; import java.util.SortedMap; import java.util.TreeMap; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link ForwardingSortedMap} using {@link MapInterfaceTest}. @@ -28,6 +30,7 @@ * @author George van den Driessche */ @GwtCompatible +@NullMarked public class ForwardingSortedMapImplementsMapTest extends SortedMapInterfaceTest { private static class SimpleForwardingSortedMap extends ForwardingSortedMap { @@ -50,7 +53,7 @@ public ForwardingSortedMapImplementsMapTest() { @Override protected SortedMap makeEmptyMap() { return new SimpleForwardingSortedMap<>( - new TreeMap(Ordering.natural().nullsFirst())); + new TreeMap(Ordering.natural().nullsFirst())); } @Override @@ -72,6 +75,7 @@ protected Integer getValueNotInPopulatedMap() throws UnsupportedOperationExcepti return -1; } + @J2ktIncompatible // https://youtrack.jetbrains.com/issue/KT-58242/ undefined behavior (crash) @Override public void testContainsKey() { try { @@ -80,6 +84,7 @@ public void testContainsKey() { } } + @J2ktIncompatible // https://youtrack.jetbrains.com/issue/KT-58242/ undefined behavior (crash) @Override public void testEntrySetContainsEntryIncompatibleKey() { try { diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapTest.java index 59e7ece53eac..1ae585db65af 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMapTest.java @@ -36,12 +36,15 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingSortedMap}. * * @author Robert KonigsbergSortedMapFeature */ +@NullUnmarked public class ForwardingSortedMapTest extends TestCase { static class StandardImplForwardingSortedMap extends ForwardingSortedMap { private final SortedMap backingSortedMap; @@ -71,12 +74,12 @@ public void putAll(Map map) { } @Override - public V remove(Object object) { + public @Nullable V remove(Object object) { return standardRemove(object); } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -126,6 +129,7 @@ public SortedMap subMap(K fromKey, K toKey) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMultisetTest.java index 2b77cd5e4b74..cf6a8d85c1b1 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSortedMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSortedMultisetTest.java @@ -14,6 +14,8 @@ package com.google.common.collect; +import static java.util.Arrays.asList; + import com.google.common.base.Function; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.testing.features.CollectionFeature; @@ -22,21 +24,22 @@ import com.google.common.collect.testing.google.TestStringMultisetGenerator; import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NavigableSet; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ForwardingSortedMultiset}. * * @author Louis Wasserman */ +@NullUnmarked public class ForwardingSortedMultisetTest extends TestCase { static class StandardImplForwardingSortedMultiset extends ForwardingSortedMultiset { private final SortedMultiset backingMultiset; @@ -93,12 +96,12 @@ public SortedMultiset subMultiset( } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { return standardCount(element); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -123,7 +126,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return standardContains(object); } @@ -143,7 +146,7 @@ public Iterator iterator() { } @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { return standardRemove(object); } @@ -173,6 +176,7 @@ public T[] toArray(T[] array) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -183,7 +187,7 @@ public static Test suite() { @Override protected Multiset create(String[] elements) { return new StandardImplForwardingSortedMultiset<>( - TreeMultiset.create(Arrays.asList(elements))); + TreeMultiset.create(asList(elements))); } @Override diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetMultimapTest.java index 7f411c7da667..237eb0570eac 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetMultimapTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ForwardingSortedSetMultimap}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class ForwardingSortedSetMultimapTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( SortedSetMultimap.class, - new Function() { + new Function>() { @Override - public SortedSetMultimap apply(SortedSetMultimap delegate) { - return wrap(delegate); + public SortedSetMultimap apply(SortedSetMultimap delegate) { + return wrap((SortedSetMultimap) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetTest.java index b83ee6b97112..166aaa177bfe 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingSortedSetTest.java @@ -16,6 +16,8 @@ package com.google.common.collect; +import static java.util.Arrays.asList; + import com.google.common.base.Function; import com.google.common.collect.testing.SafeTreeSet; import com.google.common.collect.testing.SortedSetTestSuiteBuilder; @@ -24,19 +26,21 @@ import com.google.common.collect.testing.features.CollectionSize; import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.SortedSet; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code ForwardingSortedSet}. * * @author Louis Wasserman */ +@NullUnmarked public class ForwardingSortedSetTest extends TestCase { static class StandardImplForwardingSortedSet extends ForwardingSortedSet { private final SortedSet backingSortedSet; @@ -51,7 +55,7 @@ protected SortedSet delegate() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return standardEquals(object); } @@ -116,6 +120,7 @@ public SortedSet subSet(T fromElement, T toElement) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -126,7 +131,7 @@ public static Test suite() { @Override protected SortedSet create(String[] elements) { return new StandardImplForwardingSortedSet<>( - new SafeTreeSet(Arrays.asList(elements))); + new SafeTreeSet(asList(elements))); } @Override diff --git a/android/guava-tests/test/com/google/common/collect/ForwardingTableTest.java b/android/guava-tests/test/com/google/common/collect/ForwardingTableTest.java index 91374e6c9b0a..79e6ad3d16a9 100644 --- a/android/guava-tests/test/com/google/common/collect/ForwardingTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/ForwardingTableTest.java @@ -20,12 +20,14 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.ForwardingWrapperTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests {@link ForwardingTable}. * * @author Gregory Kick */ +@NullUnmarked public class ForwardingTableTest extends TestCase { @SuppressWarnings("rawtypes") @@ -33,10 +35,10 @@ public void testForwarding() { new ForwardingWrapperTester() .testForwarding( Table.class, - new Function() { + new Function>() { @Override - public Table apply(Table delegate) { - return wrap(delegate); + public Table apply(Table delegate) { + return wrap((Table) delegate); } }); } diff --git a/android/guava-tests/test/com/google/common/collect/GeneralRangeTest.java b/android/guava-tests/test/com/google/common/collect/GeneralRangeTest.java index dfa73d62ba88..214513297506 100644 --- a/android/guava-tests/test/com/google/common/collect/GeneralRangeTest.java +++ b/android/guava-tests/test/com/google/common/collect/GeneralRangeTest.java @@ -16,14 +16,19 @@ import static com.google.common.collect.BoundType.CLOSED; import static com.google.common.collect.BoundType.OPEN; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.testing.NullPointerTester; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code GeneralRange}. @@ -31,36 +36,35 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class GeneralRangeTest extends TestCase { - private static final Ordering ORDERING = Ordering.natural().nullsFirst(); + private static final Ordering<@Nullable Integer> ORDERING = + Ordering.natural().nullsFirst(); - private static final List IN_ORDER_VALUES = Arrays.asList(null, 1, 2, 3, 4, 5); + private static final List<@Nullable Integer> IN_ORDER_VALUES = + unmodifiableList(Arrays.<@Nullable Integer>asList(null, 1, 2, 3, 4, 5)); public void testCreateEmptyRangeFails() { for (BoundType lboundType : BoundType.values()) { for (BoundType uboundType : BoundType.values()) { - try { - GeneralRange.range(ORDERING, 4, lboundType, 2, uboundType); - fail("Expected IAE"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> GeneralRange.range(ORDERING, 4, lboundType, 2, uboundType)); } } } public void testCreateEmptyRangeOpenOpenFails() { for (Integer i : IN_ORDER_VALUES) { - try { - GeneralRange.range(ORDERING, i, OPEN, i, OPEN); - fail("Expected IAE"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> GeneralRange.<@Nullable Integer>range(ORDERING, i, OPEN, i, OPEN)); } } public void testCreateEmptyRangeClosedOpenSucceeds() { for (Integer i : IN_ORDER_VALUES) { - GeneralRange range = GeneralRange.range(ORDERING, i, CLOSED, i, OPEN); + GeneralRange<@Nullable Integer> range = GeneralRange.range(ORDERING, i, CLOSED, i, OPEN); for (Integer j : IN_ORDER_VALUES) { assertFalse(range.contains(j)); } @@ -69,7 +73,7 @@ public void testCreateEmptyRangeClosedOpenSucceeds() { public void testCreateEmptyRangeOpenClosedSucceeds() { for (Integer i : IN_ORDER_VALUES) { - GeneralRange range = GeneralRange.range(ORDERING, i, OPEN, i, CLOSED); + GeneralRange<@Nullable Integer> range = GeneralRange.range(ORDERING, i, OPEN, i, CLOSED); for (Integer j : IN_ORDER_VALUES) { assertFalse(range.contains(j)); } @@ -78,7 +82,7 @@ public void testCreateEmptyRangeOpenClosedSucceeds() { public void testCreateSingletonRangeSucceeds() { for (Integer i : IN_ORDER_VALUES) { - GeneralRange range = GeneralRange.range(ORDERING, i, CLOSED, i, CLOSED); + GeneralRange<@Nullable Integer> range = GeneralRange.range(ORDERING, i, CLOSED, i, CLOSED); for (Integer j : IN_ORDER_VALUES) { assertEquals(Objects.equal(i, j), range.contains(j)); } @@ -86,7 +90,7 @@ public void testCreateSingletonRangeSucceeds() { } public void testSingletonRange() { - GeneralRange range = GeneralRange.range(ORDERING, 3, CLOSED, 3, CLOSED); + GeneralRange<@Nullable Integer> range = GeneralRange.range(ORDERING, 3, CLOSED, 3, CLOSED); for (Integer i : IN_ORDER_VALUES) { assertEquals(ORDERING.compare(i, 3) == 0, range.contains(i)); } @@ -94,7 +98,7 @@ public void testSingletonRange() { public void testLowerRange() { for (BoundType lBoundType : BoundType.values()) { - GeneralRange range = GeneralRange.downTo(ORDERING, 3, lBoundType); + GeneralRange<@Nullable Integer> range = GeneralRange.downTo(ORDERING, 3, lBoundType); for (Integer i : IN_ORDER_VALUES) { assertEquals( ORDERING.compare(i, 3) > 0 || (ORDERING.compare(i, 3) == 0 && lBoundType == CLOSED), @@ -109,7 +113,7 @@ public void testLowerRange() { public void testUpperRange() { for (BoundType lBoundType : BoundType.values()) { - GeneralRange range = GeneralRange.upTo(ORDERING, 3, lBoundType); + GeneralRange<@Nullable Integer> range = GeneralRange.upTo(ORDERING, 3, lBoundType); for (Integer i : IN_ORDER_VALUES) { assertEquals( ORDERING.compare(i, 3) < 0 || (ORDERING.compare(i, 3) == 0 && lBoundType == CLOSED), @@ -126,7 +130,8 @@ public void testDoublyBoundedAgainstRange() { for (BoundType lboundType : BoundType.values()) { for (BoundType uboundType : BoundType.values()) { Range range = Range.range(2, lboundType, 4, uboundType); - GeneralRange gRange = GeneralRange.range(ORDERING, 2, lboundType, 4, uboundType); + GeneralRange<@Nullable Integer> gRange = + GeneralRange.range(ORDERING, 2, lboundType, 4, uboundType); for (Integer i : IN_ORDER_VALUES) { assertEquals(i != null && range.contains(i), gRange.contains(i)); } @@ -146,7 +151,7 @@ public void testIntersectAgainstBiggerRange() { assertEquals( GeneralRange.range(ORDERING, 2, CLOSED, 4, OPEN), - range.intersect(GeneralRange.range(ORDERING, null, OPEN, 5, CLOSED))); + range.intersect(GeneralRange.<@Nullable Integer>range(ORDERING, null, OPEN, 5, CLOSED))); assertEquals( GeneralRange.range(ORDERING, 2, OPEN, 4, OPEN), @@ -219,6 +224,7 @@ public void testReverse() { GeneralRange.range(ORDERING, 3, CLOSED, 5, OPEN).reverse()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { new NullPointerTester().testAllPublicStaticMethods(GeneralRange.class); diff --git a/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnMapTest.java new file mode 100644 index 000000000000..16657ccc4799 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnMapTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class HashBasedTableColumnMapTest extends ColumnMapTests { + public HashBasedTableColumnMapTest() { + super(false, true, true, false); + } + + @Override + Table makeTable() { + return HashBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnTest.java b/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnTest.java new file mode 100644 index 000000000000..238c04bba9d6 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/HashBasedTableColumnTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class HashBasedTableColumnTest extends ColumnTests { + public HashBasedTableColumnTest() { + super(false, true, true, true, false); + } + + @Override + Table makeTable() { + return HashBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/HashBasedTableRowMapTest.java b/android/guava-tests/test/com/google/common/collect/HashBasedTableRowMapTest.java new file mode 100644 index 000000000000..923ffe197faa --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/HashBasedTableRowMapTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class HashBasedTableRowMapTest extends RowMapTests { + public HashBasedTableRowMapTest() { + super(false, true, true, true); + } + + @Override + Table makeTable() { + return HashBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/HashBasedTableRowTest.java b/android/guava-tests/test/com/google/common/collect/HashBasedTableRowTest.java new file mode 100644 index 000000000000..1b6f4a44a97f --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/HashBasedTableRowTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class HashBasedTableRowTest extends RowTests { + public HashBasedTableRowTest() { + super(false, true, true, true, true); + } + + @Override + Table makeTable() { + return HashBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/HashBasedTableTest.java b/android/guava-tests/test/com/google/common/collect/HashBasedTableTest.java index 8a608aeeea60..d0d3950d9bc4 100644 --- a/android/guava-tests/test/com/google/common/collect/HashBasedTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/HashBasedTableTest.java @@ -16,12 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link HashBasedTable}. @@ -29,10 +33,11 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -public class HashBasedTableTest extends AbstractTableTest { +@NullMarked +public class HashBasedTableTest extends AbstractTableTest { @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { Table table = HashBasedTable.create(); table.put("foo", 4, 'a'); table.put("cat", 1, 'b'); @@ -70,17 +75,9 @@ public void testCreateWithValidSizes() { } public void testCreateWithInvalidSizes() { - try { - HashBasedTable.create(100, -5); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashBasedTable.create(100, -5)); - try { - HashBasedTable.create(-5, 20); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashBasedTable.create(-5, 20)); } public void testCreateCopy() { @@ -91,12 +88,14 @@ public void testCreateCopy() { assertEquals((Character) 'a', copy.get("foo", 1)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); SerializableTester.reserializeAndAssert(table); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerStatic() { new NullPointerTester().testAllPublicStaticMethods(HashBasedTable.class); diff --git a/android/guava-tests/test/com/google/common/collect/HashBiMapTest.java b/android/guava-tests/test/com/google/common/collect/HashBiMapTest.java index 9541434286d4..f4bee7bf9830 100644 --- a/android/guava-tests/test/com/google/common/collect/HashBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/HashBiMapTest.java @@ -16,10 +16,12 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -32,6 +34,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link HashBiMap}. @@ -39,8 +42,11 @@ * @author Mike Bostock */ @GwtCompatible(emulated = true) +@NullMarked public class HashBiMapTest extends TestCase { + @J2ktIncompatible + @AndroidIncompatible // test-suite builders public static final class HashBiMapGenerator extends TestStringBiMapGenerator { @Override protected BiMap create(Entry[] entries) { @@ -52,7 +58,9 @@ protected BiMap create(Entry[] entries) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -138,9 +146,7 @@ public void testInsertionOrder() { map.put("quux", 3); assertThat(map.entrySet()) .containsExactly( - Maps.immutableEntry("foo", 1), - Maps.immutableEntry("bar", 2), - Maps.immutableEntry("quux", 3)) + immutableEntry("foo", 1), immutableEntry("bar", 2), immutableEntry("quux", 3)) .inOrder(); } @@ -152,7 +158,7 @@ public void testInsertionOrderAfterRemoveFirst() { map.remove("foo"); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("bar", 2), Maps.immutableEntry("quux", 3)) + .containsExactly(immutableEntry("bar", 2), immutableEntry("quux", 3)) .inOrder(); } @@ -164,7 +170,7 @@ public void testInsertionOrderAfterRemoveMiddle() { map.remove("bar"); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("foo", 1), Maps.immutableEntry("quux", 3)) + .containsExactly(immutableEntry("foo", 1), immutableEntry("quux", 3)) .inOrder(); } @@ -176,7 +182,7 @@ public void testInsertionOrderAfterRemoveLast() { map.remove("quux"); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("foo", 1), Maps.immutableEntry("bar", 2)) + .containsExactly(immutableEntry("foo", 1), immutableEntry("bar", 2)) .inOrder(); } @@ -188,7 +194,7 @@ public void testInsertionOrderAfterForcePut() { map.forcePut("quux", 1); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("bar", 2), Maps.immutableEntry("quux", 1)) + .containsExactly(immutableEntry("bar", 2), immutableEntry("quux", 1)) .inOrder(); } @@ -200,7 +206,7 @@ public void testInsertionOrderAfterInverseForcePut() { map.inverse().forcePut(1, "quux"); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("bar", 2), Maps.immutableEntry("quux", 1)) + .containsExactly(immutableEntry("bar", 2), immutableEntry("quux", 1)) .inOrder(); } @@ -210,7 +216,7 @@ public void testInverseInsertionOrderAfterInverse() { map.put("quux", 1); assertThat(map.inverse().entrySet()) - .containsExactly(Maps.immutableEntry(2, "bar"), Maps.immutableEntry(1, "quux")) + .containsExactly(immutableEntry(2, "bar"), immutableEntry(1, "quux")) .inOrder(); } @@ -222,7 +228,7 @@ public void testInverseInsertionOrderAfterInverseForcePut() { map.inverse().forcePut(1, "quux"); assertThat(map.inverse().entrySet()) - .containsExactly(Maps.immutableEntry(2, "bar"), Maps.immutableEntry(1, "quux")) + .containsExactly(immutableEntry(2, "bar"), immutableEntry(1, "quux")) .inOrder(); } @@ -236,9 +242,7 @@ public void testInverseInsertionOrderAfterInverseForcePutPresentKey() { map.inverse().forcePut(4, "bar"); assertThat(map.entrySet()) .containsExactly( - Maps.immutableEntry("foo", 1), - Maps.immutableEntry("bar", 4), - Maps.immutableEntry("quux", 3)) + immutableEntry("foo", 1), immutableEntry("bar", 4), immutableEntry("quux", 3)) .inOrder(); } @@ -249,10 +253,10 @@ public void testInverseEntrySetValueNewKey() { Iterator> inverseEntryItr = map.inverse().entrySet().iterator(); Entry entry = inverseEntryItr.next(); entry.setValue(3); - assertEquals(Maps.immutableEntry("b", 2), inverseEntryItr.next()); + assertEquals(immutableEntry("b", 2), inverseEntryItr.next()); assertFalse(inverseEntryItr.hasNext()); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry(2, "b"), Maps.immutableEntry(3, "a")) + .containsExactly(immutableEntry(2, "b"), immutableEntry(3, "a")) .inOrder(); } } diff --git a/android/guava-tests/test/com/google/common/collect/HashMultimapTest.java b/android/guava-tests/test/com/google/common/collect/HashMultimapTest.java index 6cde6c56fda9..77554381bf72 100644 --- a/android/guava-tests/test/com/google/common/collect/HashMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/HashMultimapTest.java @@ -16,8 +16,11 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -27,6 +30,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@link HashMultimap}. @@ -34,9 +38,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class HashMultimapTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -99,17 +106,9 @@ public void testCreateFromSizes() { } public void testCreateFromIllegalSizes() { - try { - HashMultimap.create(-20, 15); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashMultimap.create(-20, 15)); - try { - HashMultimap.create(20, -15); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashMultimap.create(20, -15)); } public void testEmptyMultimapsEqual() { diff --git a/android/guava-tests/test/com/google/common/collect/HashMultisetTest.java b/android/guava-tests/test/com/google/common/collect/HashMultisetTest.java index 030c927a209c..f978ebb4d321 100644 --- a/android/guava-tests/test/com/google/common/collect/HashMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/HashMultisetTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.google.MultisetFeature; @@ -27,10 +28,10 @@ import com.google.common.collect.testing.google.TestStringMultisetGenerator; import com.google.common.testing.SerializableTester; import java.io.Serializable; -import java.util.Arrays; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link HashMultiset}. @@ -39,9 +40,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class HashMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -59,6 +63,8 @@ public static Test suite() { return suite; } + @J2ktIncompatible + @AndroidIncompatible // test-suite builders private static TestStringMultisetGenerator hashMultisetGenerator() { return new TestStringMultisetGenerator() { @Override @@ -85,11 +91,12 @@ public void testCreateWithSize() { } public void testCreateFromIterable() { - Multiset multiset = HashMultiset.create(Arrays.asList("foo", "bar", "foo")); + Multiset multiset = HashMultiset.create(asList("foo", "bar", "foo")); assertEquals(3, multiset.size()); assertEquals(2, multiset.count("foo")); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializationContainingSelf() { Multiset> multiset = HashMultiset.create(); @@ -99,6 +106,7 @@ public void testSerializationContainingSelf() { assertSame(copy, copy.iterator().next()); } + @J2ktIncompatible @GwtIncompatible // Only used by @GwtIncompatible code private static class MultisetHolder implements Serializable { public Multiset member; @@ -107,9 +115,10 @@ private static class MultisetHolder implements Serializable { this.member = multiset; } - private static final long serialVersionUID = 1L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1L; } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializationIndirectSelfReference() { Multiset multiset = HashMultiset.create(); diff --git a/android/guava-tests/test/com/google/common/collect/HashingTest.java b/android/guava-tests/test/com/google/common/collect/HashingTest.java index 5dfac4726ab9..07162702e118 100644 --- a/android/guava-tests/test/com/google/common/collect/HashingTest.java +++ b/android/guava-tests/test/com/google/common/collect/HashingTest.java @@ -18,9 +18,11 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** Tests for {@code Hashing}. */ @GwtCompatible +@NullMarked public class HashingTest extends TestCase { public void testSmear() { assertEquals(1459320713, smear(754102528)); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapInverseMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapInverseMapInterfaceTest.java new file mode 100644 index 000000000000..2bcf2a7e0793 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapInverseMapInterfaceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableBiMapInverseMapInterfaceTest + extends AbstractImmutableBiMapMapInterfaceTest { + @Override + protected Map makeEmptyMap() { + return ImmutableBiMap.of(); + } + + @Override + protected Map makePopulatedMap() { + return ImmutableBiMap.of(1, "one", 2, "two", 3, "three").inverse(); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapMapInterfaceTest.java new file mode 100644 index 000000000000..63bd444579db --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapMapInterfaceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableBiMapMapInterfaceTest + extends AbstractImmutableBiMapMapInterfaceTest { + @Override + protected Map makeEmptyMap() { + return ImmutableBiMap.of(); + } + + @Override + protected Map makePopulatedMap() { + return ImmutableBiMap.of("one", 1, "two", 2, "three", 3); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java index a3a406cec255..1e4de4c798b6 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableBiMapTest.java @@ -16,13 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Joiner; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableBiMap.Builder; -import com.google.common.collect.testing.MapInterfaceTest; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -41,6 +44,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableBiMap}. @@ -48,19 +53,17 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableBiMapTest extends TestCase { // TODO: Reduce duplication of ImmutableMapTest code + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); - suite.addTestSuite(MapTests.class); - suite.addTestSuite(InverseMapTests.class); - suite.addTestSuite(CreationTests.class); - suite.addTestSuite(BiMapSpecificTests.class); - suite.addTest( BiMapTestSuiteBuilder.using(new ImmutableBiMapGenerator()) .named("ImmutableBiMap") @@ -93,667 +96,541 @@ public static Test suite() { MapFeature.ALLOWS_ANY_NULL_QUERIES) .suppressing(BiMapInverseTester.getInverseSameAfterSerializingMethods()) .createTestSuite()); + suite.addTestSuite(ImmutableBiMapTest.class); return suite; } - public abstract static class AbstractMapTests extends MapInterfaceTest { - public AbstractMapTests() { - super(false, false, false, false, false); - } - - @Override - protected Map makeEmptyMap() { - throw new UnsupportedOperationException(); - } - - private static final Joiner joiner = Joiner.on(", "); - - @Override - protected void assertMoreInvariants(Map map) { - - BiMap bimap = (BiMap) map; + // Creation tests - for (Entry entry : map.entrySet()) { - assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); - assertEquals(entry.getKey(), bimap.inverse().get(entry.getValue())); - } - - assertEquals("{" + joiner.join(map.entrySet()) + "}", map.toString()); - assertEquals("[" + joiner.join(map.entrySet()) + "]", map.entrySet().toString()); - assertEquals("[" + joiner.join(map.keySet()) + "]", map.keySet().toString()); - assertEquals("[" + joiner.join(map.values()) + "]", map.values().toString()); - - assertEquals(Sets.newHashSet(map.entrySet()), map.entrySet()); - assertEquals(Sets.newHashSet(map.keySet()), map.keySet()); - } + public void testEmptyBuilder() { + ImmutableBiMap map = new Builder().build(); + assertEquals(Collections.emptyMap(), map); + assertEquals(Collections.emptyMap(), map.inverse()); + assertSame(ImmutableBiMap.of(), map); } - public static class MapTests extends AbstractMapTests { - @Override - protected Map makeEmptyMap() { - return ImmutableBiMap.of(); - } - - @Override - protected Map makePopulatedMap() { - return ImmutableBiMap.of("one", 1, "two", 2, "three", 3); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testSingletonBuilder() { + ImmutableBiMap map = new Builder().put("one", 1).build(); + assertMapEquals(map, "one", 1); + assertMapEquals(map.inverse(), 1, "one"); } - public static class InverseMapTests extends AbstractMapTests { - @Override - protected Map makeEmptyMap() { - return ImmutableBiMap.of(); - } - - @Override - protected Map makePopulatedMap() { - return ImmutableBiMap.of(1, "one", 2, "two", 3, "three").inverse(); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testBuilder_withImmutableEntry() { + ImmutableBiMap map = + new Builder().put(immutableEntry("one", 1)).build(); + assertMapEquals(map, "one", 1); } - public static class CreationTests extends TestCase { - public void testEmptyBuilder() { - ImmutableBiMap map = new Builder().build(); - assertEquals(Collections.emptyMap(), map); - assertEquals(Collections.emptyMap(), map.inverse()); - assertSame(ImmutableBiMap.of(), map); - } + public void testBuilder() { + ImmutableBiMap map = + ImmutableBiMap.builder() + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .build(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); + } - public void testSingletonBuilder() { - ImmutableBiMap map = new Builder().put("one", 1).build(); - assertMapEquals(map, "one", 1); - assertMapEquals(map.inverse(), 1, "one"); - } + @GwtIncompatible + public void testBuilderExactlySizedReusesArray() { + ImmutableBiMap.Builder builder = ImmutableBiMap.builderWithExpectedSize(10); + Object[] builderArray = builder.alternatingKeysAndValues; + for (int i = 0; i < 10; i++) { + builder.put(i, i); + } + Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues; + RegularImmutableBiMap map = + (RegularImmutableBiMap) builder.build(); + Object[] mapInternalArray = map.alternatingKeysAndValues; + assertSame(builderArray, builderArrayAfterPuts); + assertSame(builderArray, mapInternalArray); + } - public void testBuilder_withImmutableEntry() { - ImmutableBiMap map = - new Builder().put(Maps.immutableEntry("one", 1)).build(); - assertMapEquals(map, "one", 1); - } + public void testBuilder_orderEntriesByValue() { + ImmutableBiMap map = + ImmutableBiMap.builder() + .orderEntriesByValue(Ordering.natural()) + .put("three", 3) + .put("one", 1) + .put("five", 5) + .put("four", 4) + .put("two", 2) + .build(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); + } - public void testBuilder() { - ImmutableBiMap map = - ImmutableBiMap.builder() - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .build(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); - } + public void testBuilder_orderEntriesByValueAfterExactSizeBuild() { + ImmutableBiMap.Builder builder = + new ImmutableBiMap.Builder(2).put("four", 4).put("one", 1); + ImmutableMap keyOrdered = builder.build(); + ImmutableMap valueOrdered = + builder.orderEntriesByValue(Ordering.natural()).build(); + assertMapEquals(keyOrdered, "four", 4, "one", 1); + assertMapEquals(valueOrdered, "one", 1, "four", 4); + } - @GwtIncompatible - public void testBuilderExactlySizedReusesArray() { - ImmutableBiMap.Builder builder = ImmutableBiMap.builderWithExpectedSize(10); - Object[] builderArray = builder.alternatingKeysAndValues; - for (int i = 0; i < 10; i++) { - builder.put(i, i); - } - Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues; - RegularImmutableBiMap map = - (RegularImmutableBiMap) builder.build(); - Object[] mapInternalArray = map.alternatingKeysAndValues; - assertSame(builderArray, builderArrayAfterPuts); - assertSame(builderArray, mapInternalArray); - } + public void testBuilder_orderEntriesByValue_usedTwiceFails() { + ImmutableBiMap.Builder builder = + new Builder().orderEntriesByValue(Ordering.natural()); + assertThrows( + IllegalStateException.class, () -> builder.orderEntriesByValue(Ordering.natural())); + } - public void testBuilder_orderEntriesByValue() { - ImmutableBiMap map = - ImmutableBiMap.builder() - .orderEntriesByValue(Ordering.natural()) - .put("three", 3) - .put("one", 1) - .put("five", 5) - .put("four", 4) - .put("two", 2) - .build(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); - } + public void testBuilderPutAllWithEmptyMap() { + ImmutableBiMap map = + new Builder().putAll(Collections.emptyMap()).build(); + assertEquals(Collections.emptyMap(), map); + } - public void testBuilder_orderEntriesByValueAfterExactSizeBuild() { - ImmutableBiMap.Builder builder = - new ImmutableBiMap.Builder(2).put("four", 4).put("one", 1); - ImmutableMap keyOrdered = builder.build(); - ImmutableMap valueOrdered = - builder.orderEntriesByValue(Ordering.natural()).build(); - assertMapEquals(keyOrdered, "four", 4, "one", 1); - assertMapEquals(valueOrdered, "one", 1, "four", 4); - } + public void testBuilderPutAll() { + Map toPut = new LinkedHashMap<>(); + toPut.put("one", 1); + toPut.put("two", 2); + toPut.put("three", 3); + Map moreToPut = new LinkedHashMap<>(); + moreToPut.put("four", 4); + moreToPut.put("five", 5); + + ImmutableBiMap map = + new Builder().putAll(toPut).putAll(moreToPut).build(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); + } - public void testBuilder_orderEntriesByValue_usedTwiceFails() { - ImmutableBiMap.Builder builder = - new Builder().orderEntriesByValue(Ordering.natural()); - try { - builder.orderEntriesByValue(Ordering.natural()); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - } + public void testBuilderReuse() { + Builder builder = new Builder<>(); + ImmutableBiMap mapOne = builder.put("one", 1).put("two", 2).build(); + ImmutableBiMap mapTwo = builder.put("three", 3).put("four", 4).build(); - public void testBuilderPutAllWithEmptyMap() { - ImmutableBiMap map = - new Builder().putAll(Collections.emptyMap()).build(); - assertEquals(Collections.emptyMap(), map); - } + assertMapEquals(mapOne, "one", 1, "two", 2); + assertMapEquals(mapOne.inverse(), 1, "one", 2, "two"); + assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); + assertMapEquals(mapTwo.inverse(), 1, "one", 2, "two", 3, "three", 4, "four"); + } - public void testBuilderPutAll() { - Map toPut = new LinkedHashMap<>(); - toPut.put("one", 1); - toPut.put("two", 2); - toPut.put("three", 3); - Map moreToPut = new LinkedHashMap<>(); - moreToPut.put("four", 4); - moreToPut.put("five", 5); - - ImmutableBiMap map = - new Builder().putAll(toPut).putAll(moreToPut).build(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - assertMapEquals(map.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); - } + public void testBuilderPutNullKey() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + } - public void testBuilderReuse() { - Builder builder = new Builder<>(); - ImmutableBiMap mapOne = builder.put("one", 1).put("two", 2).build(); - ImmutableBiMap mapTwo = builder.put("three", 3).put("four", 4).build(); + public void testBuilderPutNullValue() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put("one", null)); + } - assertMapEquals(mapOne, "one", 1, "two", 2); - assertMapEquals(mapOne.inverse(), 1, "one", 2, "two"); - assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); - assertMapEquals(mapTwo.inverse(), 1, "one", 2, "two", 3, "three", 4, "four"); - } + public void testBuilderPutNullKeyViaPutAll() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap(null, 1))); + } - public void testBuilderPutNullKey() { - Builder builder = new Builder<>(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } - } + public void testBuilderPutNullValueViaPutAll() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap("one", null))); + } - public void testBuilderPutNullValue() { - Builder builder = new Builder<>(); - try { - builder.put("one", null); - fail(); - } catch (NullPointerException expected) { - } - } + @SuppressWarnings("AlwaysThrows") + public void testPuttingTheSameKeyTwiceThrowsOnBuild() { + Builder builder = + new Builder() + .put("one", 1) + .put("one", 1); // throwing on this line would be even better - public void testBuilderPutNullKeyViaPutAll() { - Builder builder = new Builder<>(); - try { - builder.putAll(Collections.singletonMap(null, 1)); - fail(); - } catch (NullPointerException expected) { - } - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> builder.build()); + assertThat(expected).hasMessageThat().contains("one"); + } - public void testBuilderPutNullValueViaPutAll() { - Builder builder = new Builder<>(); - try { - builder.putAll(Collections.singletonMap("one", null)); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOf() { + assertMapEquals(ImmutableBiMap.of("one", 1), "one", 1); + assertMapEquals(ImmutableBiMap.of("one", 1).inverse(), 1, "one"); + assertMapEquals(ImmutableBiMap.of("one", 1, "two", 2), "one", 1, "two", 2); + assertMapEquals(ImmutableBiMap.of("one", 1, "two", 2).inverse(), 1, "one", 2, "two"); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3).inverse(), + 1, + "one", + 2, + "two", + 3, + "three"); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4).inverse(), + 1, + "one", + 2, + "two", + 3, + "three", + 4, + "four"); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5); + assertMapEquals( + ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5).inverse(), + 1, + "one", + 2, + "two", + 3, + "three", + 4, + "four", + 5, + "five"); + assertMapEquals( + ImmutableBiMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6); + assertMapEquals( + ImmutableBiMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7); + assertMapEquals( + ImmutableBiMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8); + assertMapEquals( + ImmutableBiMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8, + "nine", + 9); + assertMapEquals( + ImmutableBiMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9, + "ten", 10), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8, + "nine", + 9, + "ten", + 10); + } - public void testPuttingTheSameKeyTwiceThrowsOnBuild() { - Builder builder = - new Builder() - .put("one", 1) - .put("one", 1); // throwing on this line would be even better - - try { - builder.build(); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("one"); - } - } + public void testOfNullKey() { + assertThrows(NullPointerException.class, () -> ImmutableBiMap.of(null, 1)); - public void testOf() { - assertMapEquals(ImmutableBiMap.of("one", 1), "one", 1); - assertMapEquals(ImmutableBiMap.of("one", 1).inverse(), 1, "one"); - assertMapEquals(ImmutableBiMap.of("one", 1, "two", 2), "one", 1, "two", 2); - assertMapEquals(ImmutableBiMap.of("one", 1, "two", 2).inverse(), 1, "one", 2, "two"); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3).inverse(), - 1, - "one", - 2, - "two", - 3, - "three"); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4).inverse(), - 1, - "one", - 2, - "two", - 3, - "three", - 4, - "four"); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5); - assertMapEquals( - ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5).inverse(), - 1, - "one", - 2, - "two", - 3, - "three", - 4, - "four", - 5, - "five"); - assertMapEquals( - ImmutableBiMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6); - assertMapEquals( - ImmutableBiMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7); - assertMapEquals( - ImmutableBiMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8); - assertMapEquals( - ImmutableBiMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8, - "nine", - 9); - assertMapEquals( - ImmutableBiMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9, - "ten", 10), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8, - "nine", - 9, - "ten", - 10); - } + assertThrows(NullPointerException.class, () -> ImmutableBiMap.of("one", 1, null, 2)); + } - public void testOfNullKey() { - try { - ImmutableBiMap.of(null, 1); - fail(); - } catch (NullPointerException expected) { - } - - try { - ImmutableBiMap.of("one", 1, null, 2); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOfNullValue() { + assertThrows(NullPointerException.class, () -> ImmutableBiMap.of("one", null)); - public void testOfNullValue() { - try { - ImmutableBiMap.of("one", null); - fail(); - } catch (NullPointerException expected) { - } - - try { - ImmutableBiMap.of("one", 1, "two", null); - fail(); - } catch (NullPointerException expected) { - } - } + assertThrows(NullPointerException.class, () -> ImmutableBiMap.of("one", 1, "two", null)); + } - public void testOfWithDuplicateKey() { - try { - ImmutableBiMap.of("one", 1, "one", 1); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("one"); - } - } + @SuppressWarnings({"AlwaysThrows", "DistinctVarargsChecker"}) + public void testOfWithDuplicateKey() { + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> ImmutableBiMap.of("one", 1, "one", 1)); + assertThat(expected).hasMessageThat().contains("one"); + } - public void testOfEntries() { - assertMapEquals( - ImmutableBiMap.ofEntries(entry("one", 1), entry("two", 2)), "one", 1, "two", 2); - } + public void testOfEntries() { + assertMapEquals(ImmutableBiMap.ofEntries(entry("one", 1), entry("two", 2)), "one", 1, "two", 2); + } - public void testOfEntriesNull() { - Entry nullKey = entry(null, 23); - try { - ImmutableBiMap.ofEntries(nullKey); - fail(); - } catch (NullPointerException expected) { - } - Entry nullValue = entry(23, null); - try { - ImmutableBiMap.ofEntries(nullValue); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOfEntriesNull() { + Entry<@Nullable Integer, Integer> nullKey = entry(null, 23); + assertThrows( + NullPointerException.class, + () -> ImmutableBiMap.ofEntries((Entry) nullKey)); + Entry nullValue = + ImmutableBiMapTest.<@Nullable Integer>entry(23, null); + assertThrows( + NullPointerException.class, + () -> ImmutableBiMap.ofEntries((Entry) nullValue)); + } - private static Entry entry(T key, T value) { - return new AbstractMap.SimpleImmutableEntry<>(key, value); - } + private static Entry entry(T key, T value) { + return new AbstractMap.SimpleImmutableEntry<>(key, value); + } - public void testCopyOfEmptyMap() { - ImmutableBiMap copy = - ImmutableBiMap.copyOf(Collections.emptyMap()); - assertEquals(Collections.emptyMap(), copy); - assertSame(copy, ImmutableBiMap.copyOf(copy)); - assertSame(ImmutableBiMap.of(), copy); - } + public void testCopyOfEmptyMap() { + ImmutableBiMap copy = + ImmutableBiMap.copyOf(Collections.emptyMap()); + assertEquals(Collections.emptyMap(), copy); + assertSame(copy, ImmutableBiMap.copyOf(copy)); + assertSame(ImmutableBiMap.of(), copy); + } - public void testCopyOfSingletonMap() { - ImmutableBiMap copy = - ImmutableBiMap.copyOf(Collections.singletonMap("one", 1)); - assertMapEquals(copy, "one", 1); - assertSame(copy, ImmutableBiMap.copyOf(copy)); - } + public void testCopyOfSingletonMap() { + ImmutableBiMap copy = ImmutableBiMap.copyOf(singletonMap("one", 1)); + assertMapEquals(copy, "one", 1); + assertSame(copy, ImmutableBiMap.copyOf(copy)); + } - public void testCopyOf() { - Map original = new LinkedHashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); + public void testCopyOf() { + Map original = new LinkedHashMap<>(); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); - ImmutableBiMap copy = ImmutableBiMap.copyOf(original); - assertMapEquals(copy, "one", 1, "two", 2, "three", 3); - assertSame(copy, ImmutableBiMap.copyOf(copy)); - } + ImmutableBiMap copy = ImmutableBiMap.copyOf(original); + assertMapEquals(copy, "one", 1, "two", 2, "three", 3); + assertSame(copy, ImmutableBiMap.copyOf(copy)); + } - public void testEmpty() { - ImmutableBiMap bimap = ImmutableBiMap.of(); - assertEquals(Collections.emptyMap(), bimap); - assertEquals(Collections.emptyMap(), bimap.inverse()); - } + public void testEmpty() { + ImmutableBiMap bimap = ImmutableBiMap.of(); + assertEquals(Collections.emptyMap(), bimap); + assertEquals(Collections.emptyMap(), bimap.inverse()); + } - public void testFromHashMap() { - Map hashMap = Maps.newLinkedHashMap(); - hashMap.put("one", 1); - hashMap.put("two", 2); - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); - assertMapEquals(bimap, "one", 1, "two", 2); - assertMapEquals(bimap.inverse(), 1, "one", 2, "two"); - } + public void testFromHashMap() { + Map hashMap = Maps.newLinkedHashMap(); + hashMap.put("one", 1); + hashMap.put("two", 2); + ImmutableBiMap bimap = ImmutableBiMap.copyOf(hashMap); + assertMapEquals(bimap, "one", 1, "two", 2); + assertMapEquals(bimap.inverse(), 1, "one", 2, "two"); + } - public void testFromImmutableMap() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf( - new ImmutableMap.Builder() - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .build()); - assertMapEquals(bimap, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - assertMapEquals(bimap.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); - } + public void testFromImmutableMap() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf( + new ImmutableMap.Builder() + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .buildOrThrow()); + assertMapEquals(bimap, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + assertMapEquals(bimap.inverse(), 1, "one", 2, "two", 3, "three", 4, "four", 5, "five"); + } - public void testDuplicateValues() { - ImmutableMap map = - new ImmutableMap.Builder() - .put("one", 1) - .put("two", 2) - .put("uno", 1) - .put("dos", 2) - .build(); - - try { - ImmutableBiMap.copyOf(map); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("1"); - } - } + public void testDuplicateValues() { + ImmutableMap map = + new ImmutableMap.Builder() + .put("one", 1) + .put("two", 2) + .put("uno", 1) + .put("dos", 2) + .buildOrThrow(); + + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> ImmutableBiMap.copyOf(map)); + assertThat(expected).hasMessageThat().containsMatch("1|2"); + } - // TODO(b/172823566): Use mainline testToImmutableBiMap once CollectorTester is usable to java7. - public void testToImmutableBiMap_java7_combine() { - ImmutableBiMap.Builder zis = - ImmutableBiMap.builder().put("one", 1); - ImmutableBiMap.Builder zat = - ImmutableBiMap.builder().put("two", 2).put("three", 3); - ImmutableBiMap biMap = zis.combine(zat).build(); - assertMapEquals(biMap, "one", 1, "two", 2, "three", 3); - } + // TODO(b/172823566): Use mainline testToImmutableBiMap once CollectorTester is usable to java7. + public void testToImmutableBiMap_java7_combine() { + ImmutableBiMap.Builder zis = + ImmutableBiMap.builder().put("one", 1); + ImmutableBiMap.Builder zat = + ImmutableBiMap.builder().put("two", 2).put("three", 3); + ImmutableBiMap biMap = zis.combine(zat).build(); + assertMapEquals(biMap, "one", 1, "two", 2, "three", 3); + } - // TODO(b/172823566): Use mainline testToImmutableBiMap once CollectorTester is usable to java7. - public void testToImmutableBiMap_exceptionOnDuplicateKey_java7_combine() { - ImmutableBiMap.Builder zis = - ImmutableBiMap.builder().put("one", 1).put("two", 2); - ImmutableBiMap.Builder zat = - ImmutableBiMap.builder().put("two", 22).put("three", 3); - try { - zis.combine(zat).build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // expected - } - } + // TODO(b/172823566): Use mainline testToImmutableBiMap once CollectorTester is usable to java7. + public void testToImmutableBiMap_exceptionOnDuplicateKey_java7_combine() { + ImmutableBiMap.Builder zis = + ImmutableBiMap.builder().put("one", 1).put("two", 2); + ImmutableBiMap.Builder zat = + ImmutableBiMap.builder().put("two", 22).put("three", 3); + assertThrows(IllegalArgumentException.class, () -> zis.combine(zat).build()); } - public static class BiMapSpecificTests extends TestCase { + // BiMap-specific tests - @SuppressWarnings("DoNotCall") - public void testForcePut() { - BiMap bimap = ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); - try { - bimap.forcePut("three", 3); - fail(); - } catch (UnsupportedOperationException expected) { - } - } + @SuppressWarnings("DoNotCall") + public void testForcePut() { + BiMap bimap = ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); + assertThrows(UnsupportedOperationException.class, () -> bimap.forcePut("three", 3)); + } - public void testKeySet() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4)); - Set keys = bimap.keySet(); - assertEquals(Sets.newHashSet("one", "two", "three", "four"), keys); - assertThat(keys).containsExactly("one", "two", "three", "four").inOrder(); - } + public void testKeySet() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4)); + Set keys = bimap.keySet(); + assertEquals(newHashSet("one", "two", "three", "four"), keys); + assertThat(keys).containsExactly("one", "two", "three", "four").inOrder(); + } - public void testValues() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4)); - Set values = bimap.values(); - assertEquals(Sets.newHashSet(1, 2, 3, 4), values); - assertThat(values).containsExactly(1, 2, 3, 4).inOrder(); - } + public void testValues() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4)); + Set values = bimap.values(); + assertEquals(newHashSet(1, 2, 3, 4), values); + assertThat(values).containsExactly(1, 2, 3, 4).inOrder(); + } - public void testDoubleInverse() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); - assertSame(bimap, bimap.inverse().inverse()); - } + public void testDoubleInverse() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); + assertSame(bimap, bimap.inverse().inverse()); + } - @GwtIncompatible // SerializableTester - public void testEmptySerialization() { - ImmutableBiMap bimap = ImmutableBiMap.of(); - assertSame(bimap, SerializableTester.reserializeAndAssert(bimap)); - } + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testEmptySerialization() { + ImmutableBiMap bimap = ImmutableBiMap.of(); + assertSame(bimap, SerializableTester.reserializeAndAssert(bimap)); + } - @GwtIncompatible // SerializableTester - public void testSerialization() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); - ImmutableBiMap copy = SerializableTester.reserializeAndAssert(bimap); - assertEquals(Integer.valueOf(1), copy.get("one")); - assertEquals("one", copy.inverse().get(1)); - assertSame(copy, copy.inverse().inverse()); - } + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testSerialization() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf(ImmutableMap.of("one", 1, "two", 2)); + ImmutableBiMap copy = SerializableTester.reserializeAndAssert(bimap); + assertEquals(Integer.valueOf(1), copy.get("one")); + assertEquals("one", copy.inverse().get(1)); + assertSame(copy, copy.inverse().inverse()); + } - @GwtIncompatible // SerializableTester - public void testInverseSerialization() { - ImmutableBiMap bimap = - ImmutableBiMap.copyOf(ImmutableMap.of(1, "one", 2, "two")).inverse(); - ImmutableBiMap copy = SerializableTester.reserializeAndAssert(bimap); - assertEquals(Integer.valueOf(1), copy.get("one")); - assertEquals("one", copy.inverse().get(1)); - assertSame(copy, copy.inverse().inverse()); - } + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testInverseSerialization() { + ImmutableBiMap bimap = + ImmutableBiMap.copyOf(ImmutableMap.of(1, "one", 2, "two")).inverse(); + ImmutableBiMap copy = SerializableTester.reserializeAndAssert(bimap); + assertEquals(Integer.valueOf(1), copy.get("one")); + assertEquals("one", copy.inverse().get(1)); + assertSame(copy, copy.inverse().inverse()); } private static void assertMapEquals(Map map, Object... alternatingKeysAndValues) { - int i = 0; - for (Entry entry : map.entrySet()) { - assertEquals(alternatingKeysAndValues[i++], entry.getKey()); - assertEquals(alternatingKeysAndValues[i++], entry.getValue()); + Map expected = new LinkedHashMap<>(); + for (int i = 0; i < alternatingKeysAndValues.length; i += 2) { + expected.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]); } + assertThat(map).containsExactlyEntriesIn(expected).inOrder(); } /** No-op test so that the class has at least one method, making Maven's test runner happy. */ diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableClassToInstanceMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableClassToInstanceMapTest.java index bce73b8f759b..c991bde6e6d4 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableClassToInstanceMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableClassToInstanceMapTest.java @@ -17,6 +17,10 @@ package com.google.common.collect; import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.junit.Assert.assertThrows; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -26,20 +30,23 @@ import com.google.common.collect.testing.features.MapFeature; import com.google.common.testing.SerializableTester; import java.io.Serializable; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link ImmutableClassToInstanceMap}. * * @author Kevin Bourrillion */ +@NullUnmarked public class ImmutableClassToInstanceMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableClassToInstanceMapTest.class); @@ -50,13 +57,13 @@ public static Test suite() { // Other tests will verify what real, warning-free usage looks like // but here we have to do some serious fudging @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public Map create(Object... elements) { ImmutableClassToInstanceMap.Builder builder = ImmutableClassToInstanceMap.builder(); for (Object object : elements) { - Entry entry = (Entry) object; - builder.put(entry.getKey(), entry.getValue()); + Entry entry = (Entry) object; + builder.put((Class) entry.getKey(), (Impl) entry.getValue()); } return (Map) builder.build(); } @@ -81,7 +88,7 @@ public void testSerialization_empty() { } public void testCopyOf_map_empty() { - Map, Object> in = Collections.emptyMap(); + Map, Object> in = emptyMap(); ClassToInstanceMap map = ImmutableClassToInstanceMap.copyOf(in); assertTrue(map.isEmpty()); assertSame(map, ImmutableClassToInstanceMap.of()); @@ -108,30 +115,21 @@ public void testCopyOf_map_valid() { assertEquals(0, zero); Double pi = map.getInstance(Double.class); - assertEquals(Math.PI, pi, 0.0); + assertThat(pi).isEqualTo(Math.PI); assertSame(map, ImmutableClassToInstanceMap.copyOf(map)); } public void testCopyOf_map_nulls() { - Map, Number> nullKey = Collections.singletonMap(null, (Number) 1.0); - try { - ImmutableClassToInstanceMap.copyOf(nullKey); - fail(); - } catch (NullPointerException expected) { - } + Map, Number> nullKey = singletonMap(null, (Number) 1.0); + assertThrows(NullPointerException.class, () -> ImmutableClassToInstanceMap.copyOf(nullKey)); - Map, Number> nullValue = - Collections.singletonMap(Number.class, null); - try { - ImmutableClassToInstanceMap.copyOf(nullValue); - fail(); - } catch (NullPointerException expected) { - } + Map, Number> nullValue = singletonMap(Number.class, null); + assertThrows(NullPointerException.class, () -> ImmutableClassToInstanceMap.copyOf(nullValue)); } public void testCopyOf_imap_empty() { - Map, Object> in = Collections.emptyMap(); + Map, Object> in = emptyMap(); ClassToInstanceMap map = ImmutableClassToInstanceMap.copyOf(in); assertTrue(map.isEmpty()); } @@ -146,7 +144,7 @@ public void testCopyOf_imap_valid() { assertEquals(0, zero); Double pi = map.getInstance(Double.class); - assertEquals(Math.PI, pi, 0.0); + assertThat(pi).isEqualTo(Math.PI); } public void testPrimitiveAndWrapper() { @@ -161,11 +159,12 @@ public void testPrimitiveAndWrapper() { assertEquals(1, (int) ictim.getInstance(int.class)); } + @SuppressWarnings("rawtypes") // TODO(cpovirk): Can we at least use Class in some places? abstract static class TestClassToInstanceMapGenerator implements TestMapGenerator { @Override - public Class[] createKeyArray(int length) { - return new Class[length]; + public Class[] createKeyArray(int length) { + return new Class[length]; } @Override @@ -186,7 +185,7 @@ public SampleElements> samples() { @Override @SuppressWarnings("unchecked") public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -213,7 +212,7 @@ static final class Impl implements One, Two, Three, Four, Five, Serializable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof Impl && value == ((Impl) obj).value; } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableCollectionTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableCollectionTest.java index 260c43459b3d..59d794dfa6f2 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableCollectionTest.java @@ -17,12 +17,14 @@ package com.google.common.collect; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code ImmutableCollection}. * * @author Louis Wasserman */ +@NullUnmarked public class ImmutableCollectionTest extends TestCase { public void testCapacityExpansion() { assertEquals(1, ImmutableCollection.Builder.expandedCapacity(0, 1)); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableEnumMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableEnumMapTest.java index 189db72e6b5e..8c02927ee04f 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableEnumMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableEnumMapTest.java @@ -17,15 +17,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_QUERIES; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.collect.testing.AnEnum; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.TestEnumMapGenerator; import com.google.common.collect.testing.features.CollectionSize; @@ -34,6 +35,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Tests for {@code ImmutableEnumMap}. @@ -41,7 +43,10 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableEnumMapTest extends TestCase { + @J2ktIncompatible + @AndroidIncompatible // test-suite builders public static class ImmutableEnumMapGenerator extends TestEnumMapGenerator { @Override protected Map create(Entry[] entries) { @@ -53,7 +58,9 @@ protected Map create(Entry[] entries) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -80,7 +87,7 @@ public AnEnum apply(AnEnum ae) { } }); ImmutableMap copy = Maps.immutableEnumMap(map); - assertThat(copy.entrySet()).containsExactly(Helpers.mapEntry(AnEnum.A, AnEnum.A)); + assertThat(copy.entrySet()).containsExactly(mapEntry(AnEnum.A, AnEnum.A)); } public void testEmptyImmutableEnumMap() { @@ -93,10 +100,7 @@ public void testImmutableEnumMapOrdering() { Maps.immutableEnumMap(ImmutableMap.of(AnEnum.C, "c", AnEnum.A, "a", AnEnum.E, "e")); assertThat(map.entrySet()) - .containsExactly( - Helpers.mapEntry(AnEnum.A, "a"), - Helpers.mapEntry(AnEnum.C, "c"), - Helpers.mapEntry(AnEnum.E, "e")) + .containsExactly(mapEntry(AnEnum.A, "a"), mapEntry(AnEnum.C, "c"), mapEntry(AnEnum.E, "e")) .inOrder(); } } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableListCopyOfConcurrentlyModifiedInputTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableListCopyOfConcurrentlyModifiedInputTest.java new file mode 100644 index 000000000000..f758b8daf3c2 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableListCopyOfConcurrentlyModifiedInputTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Iterables.unmodifiableIterable; +import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.reflect.Reflection.newProxy; +import static java.util.Arrays.asList; + +import com.google.common.annotations.GwtIncompatible; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // reflection +@NullUnmarked +public class ImmutableListCopyOfConcurrentlyModifiedInputTest extends TestCase { + enum WrapWithIterable { + WRAP, + NO_WRAP + } + + private static void runConcurrentlyMutatedTest( + Collection initialContents, + Iterable actionsToPerformConcurrently, + WrapWithIterable wrap) { + ConcurrentlyMutatedList concurrentlyMutatedList = + newConcurrentlyMutatedList(initialContents, actionsToPerformConcurrently); + + Iterable iterableToCopy = + wrap == WrapWithIterable.WRAP + ? unmodifiableIterable(concurrentlyMutatedList) + : concurrentlyMutatedList; + + ImmutableList copyOfIterable = ImmutableList.copyOf(iterableToCopy); + + assertTrue(concurrentlyMutatedList.getAllStates().contains(copyOfIterable)); + } + + private static void runConcurrentlyMutatedTest(WrapWithIterable wrap) { + /* + * TODO: Iterate over many array sizes and all possible operation lists, + * performing adds and removes in different ways. + */ + runConcurrentlyMutatedTest(elements(), ops(add(1), add(2)), wrap); + + runConcurrentlyMutatedTest(elements(), ops(add(1), nop()), wrap); + + runConcurrentlyMutatedTest(elements(), ops(add(1), remove()), wrap); + + runConcurrentlyMutatedTest(elements(), ops(nop(), add(1)), wrap); + + runConcurrentlyMutatedTest(elements(1), ops(remove(), nop()), wrap); + + runConcurrentlyMutatedTest(elements(1), ops(remove(), add(2)), wrap); + + runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), remove()), wrap); + + runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), nop()), wrap); + + runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), add(3)), wrap); + + runConcurrentlyMutatedTest(elements(1, 2), ops(nop(), remove()), wrap); + + runConcurrentlyMutatedTest(elements(1, 2, 3), ops(remove(), remove()), wrap); + } + + private static ImmutableList elements(Integer... elements) { + return ImmutableList.copyOf(elements); + } + + private static ImmutableList ops(ListFrobber... elements) { + return ImmutableList.copyOf(elements); + } + + public void testCopyOf_concurrentlyMutatedList() { + runConcurrentlyMutatedTest(WrapWithIterable.NO_WRAP); + } + + public void testCopyOf_concurrentlyMutatedIterable() { + runConcurrentlyMutatedTest(WrapWithIterable.WRAP); + } + + /** An operation to perform on a list. */ + interface ListFrobber { + void perform(List list); + } + + static ListFrobber add(final int element) { + return new ListFrobber() { + @Override + public void perform(List list) { + list.add(0, element); + } + }; + } + + static ListFrobber remove() { + return new ListFrobber() { + @Override + public void perform(List list) { + list.remove(0); + } + }; + } + + static ListFrobber nop() { + return new ListFrobber() { + @Override + public void perform(List list) {} + }; + } + + /** A list that mutates itself after every call to each of its {@link List} methods. */ + interface ConcurrentlyMutatedList extends List { + /** + * The elements of a {@link ConcurrentlyMutatedList} are added and removed over time. This + * method returns every state that the list has passed through at some point. + */ + Set> getAllStates(); + } + + /** + * Returns a {@link ConcurrentlyMutatedList} that performs the given operations as its concurrent + * modifications. The mutations occur in the same thread as the triggering method call. + */ + private static ConcurrentlyMutatedList newConcurrentlyMutatedList( + final Collection initialContents, + final Iterable actionsToPerformConcurrently) { + InvocationHandler invocationHandler = + new InvocationHandler() { + final CopyOnWriteArrayList delegate = + new CopyOnWriteArrayList<>(initialContents); + + final Method getAllStatesMethod = + getOnlyElement(asList(ConcurrentlyMutatedList.class.getDeclaredMethods())); + + final Iterator remainingActions = actionsToPerformConcurrently.iterator(); + + final Set> allStates = newHashSet(); + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.equals(getAllStatesMethod) + ? getAllStates() + : invokeListMethod(method, args); + } + + private Set> getAllStates() { + return allStates; + } + + private Object invokeListMethod(Method method, Object[] args) throws Throwable { + try { + Object returnValue = method.invoke(delegate, args); + mutateDelegate(); + return returnValue; + } catch (InvocationTargetException e) { + throw e.getCause(); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private void mutateDelegate() { + allStates.add(ImmutableList.copyOf(delegate)); + remainingActions.next().perform(delegate); + allStates.add(ImmutableList.copyOf(delegate)); + } + }; + + @SuppressWarnings("unchecked") + ConcurrentlyMutatedList list = + newProxy(ConcurrentlyMutatedList.class, invocationHandler); + return list; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableListMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableListMultimapTest.java index 126074004224..40cf746ee6ef 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableListMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableListMultimapTest.java @@ -16,13 +16,17 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_ANY_NULL_QUERIES; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableListMultimap.Builder; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.google.ListMultimapTestSuiteBuilder; @@ -38,6 +42,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableListMultimap}. @@ -45,7 +51,10 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableListMultimapTest extends TestCase { + @J2ktIncompatible + @AndroidIncompatible // test-suite builders public static class ImmutableListMultimapGenerator extends TestStringListMultimapGenerator { @Override protected ListMultimap create(Entry[] entries) { @@ -57,6 +66,8 @@ protected ListMultimap create(Entry[] entries) { } } + @J2ktIncompatible + @AndroidIncompatible // test-suite builders public static class ImmutableListMultimapCopyOfEntriesGenerator extends TestStringListMultimapGenerator { @Override @@ -65,7 +76,9 @@ protected ListMultimap create(Entry[] entries) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -82,6 +95,44 @@ public static Test suite() { return suite; } + public void testBuilderWithExpectedKeysNegative() { + assertThrows( + IllegalArgumentException.class, () -> ImmutableListMultimap.builderWithExpectedKeys(-1)); + } + + public void testBuilderWithExpectedKeysZero() { + ImmutableListMultimap.Builder builder = + ImmutableListMultimap.builderWithExpectedKeys(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedKeysPositive() { + ImmutableListMultimap.Builder builder = + ImmutableListMultimap.builderWithExpectedKeys(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyNegative() { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + assertThrows(IllegalArgumentException.class, () -> builder.expectedValuesPerKey(-1)); + } + + public void testBuilderWithExpectedValuesPerKeyZero() { + ImmutableListMultimap.Builder builder = + ImmutableListMultimap.builder().expectedValuesPerKey(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyPositive() { + ImmutableListMultimap.Builder builder = + ImmutableListMultimap.builder().expectedValuesPerKey(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + public void testBuilder_withImmutableEntry() { ImmutableListMultimap multimap = new Builder().put(Maps.immutableEntry("one", 1)).build(); @@ -90,20 +141,14 @@ public void testBuilder_withImmutableEntry() { public void testBuilder_withImmutableEntryAndNullContents() { Builder builder = new Builder<>(); - try { - builder.put(Maps.immutableEntry("one", (Integer) null)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> builder.put(Maps.immutableEntry("one", (Integer) null))); + assertThrows( + NullPointerException.class, () -> builder.put(Maps.immutableEntry((String) null, 1))); } private static class StringHolder { - String string; + @Nullable String string; } public void testBuilder_withMutableEntry() { @@ -134,8 +179,8 @@ public void testBuilderPutAllIterable() { builder.putAll("bar", Arrays.asList(4, 5)); builder.putAll("foo", Arrays.asList(6, 7)); Multimap multimap = builder.build(); - assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo")); - assertEquals(Arrays.asList(4, 5), multimap.get("bar")); + assertThat(multimap.get("foo")).containsExactly(1, 2, 3, 6, 7).inOrder(); + assertThat(multimap.get("bar")).containsExactly(4, 5).inOrder(); assertEquals(7, multimap.size()); } @@ -144,9 +189,9 @@ public void testBuilderPutAllVarargs() { builder.putAll("foo", 1, 2, 3); builder.putAll("bar", 4, 5); builder.putAll("foo", 6, 7); - Multimap multimap = builder.build(); - assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo")); - assertEquals(Arrays.asList(4, 5), multimap.get("bar")); + ImmutableListMultimap multimap = builder.build(); + assertThat(multimap.get("foo")).containsExactly(1, 2, 3, 6, 7).inOrder(); + assertThat(multimap.get("bar")).containsExactly(4, 5).inOrder(); assertEquals(7, multimap.size()); } @@ -163,9 +208,9 @@ public void testBuilderPutAllMultimap() { ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); builder.putAll(toPut); builder.putAll(moreToPut); - Multimap multimap = builder.build(); - assertEquals(Arrays.asList(1, 2, 3, 6, 7), multimap.get("foo")); - assertEquals(Arrays.asList(4, 5), multimap.get("bar")); + ImmutableListMultimap multimap = builder.build(); + assertThat(multimap.get("foo")).containsExactly(1, 2, 3, 6, 7).inOrder(); + assertThat(multimap.get("bar")).containsExactly(4, 5).inOrder(); assertEquals(7, multimap.size()); } @@ -206,62 +251,33 @@ public void testBuilderPutAllMultimapWithDuplicates() { ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); builder.putAll(toPut); builder.putAll(moreToPut); - Multimap multimap = builder.build(); - assertEquals(Arrays.asList(1, 2, 1, 6, 7, 2), multimap.get("foo")); - assertEquals(Arrays.asList(4, 5, 4), multimap.get("bar")); + ImmutableListMultimap multimap = builder.build(); + assertThat(multimap.get("foo")).containsExactly(1, 2, 1, 6, 7, 2).inOrder(); + assertThat(multimap.get("bar")).containsExactly(4, 5, 4).inOrder(); assertEquals(9, multimap.size()); } public void testBuilderPutNullKey() { - Multimap toPut = LinkedListMultimap.create(); - toPut.put("foo", null); + Multimap<@Nullable String, Integer> toPut = LinkedListMultimap.create(); + toPut.put(null, 1); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(null, Arrays.asList(1, 2, 3)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(null, 1, 2, 3); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(toPut); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + assertThrows(NullPointerException.class, () -> builder.putAll(null, Arrays.asList(1, 2, 3))); + assertThrows(NullPointerException.class, () -> builder.putAll(null, 1, 2, 3)); + assertThrows( + NullPointerException.class, () -> builder.putAll((Multimap) toPut)); } public void testBuilderPutNullValue() { - Multimap toPut = LinkedListMultimap.create(); - toPut.put(null, 1); + Multimap toPut = LinkedListMultimap.create(); + toPut.put("foo", null); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); - try { - builder.put("foo", null); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll("foo", Arrays.asList(1, null, 3)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll("foo", 1, null, 3); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(toPut); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.put("foo", null)); + assertThrows( + NullPointerException.class, () -> builder.putAll("foo", Arrays.asList(1, null, 3))); + assertThrows(NullPointerException.class, () -> builder.putAll("foo", 1, null, 3)); + assertThrows( + NullPointerException.class, () -> builder.putAll((Multimap) toPut)); } public void testBuilderOrderKeysBy() { @@ -340,9 +356,8 @@ public void testCopyOf() { input.put("foo", 1); input.put("bar", 2); input.put("foo", 3); - Multimap multimap = ImmutableListMultimap.copyOf(input); - assertEquals(multimap, input); - assertEquals(input, multimap); + ImmutableListMultimap multimap = ImmutableListMultimap.copyOf(input); + new EqualsTester().addEqualityGroup(input, multimap).testEquals(); } public void testCopyOfWithDuplicates() { @@ -351,16 +366,14 @@ public void testCopyOfWithDuplicates() { input.put("bar", 2); input.put("foo", 3); input.put("foo", 1); - Multimap multimap = ImmutableListMultimap.copyOf(input); - assertEquals(multimap, input); - assertEquals(input, multimap); + ImmutableListMultimap multimap = ImmutableListMultimap.copyOf(input); + new EqualsTester().addEqualityGroup(input, multimap).testEquals(); } public void testCopyOfEmpty() { ArrayListMultimap input = ArrayListMultimap.create(); - Multimap multimap = ImmutableListMultimap.copyOf(input); - assertEquals(multimap, input); - assertEquals(input, multimap); + ImmutableListMultimap multimap = ImmutableListMultimap.copyOf(input); + new EqualsTester().addEqualityGroup(input, multimap).testEquals(); } public void testCopyOfImmutableListMultimap() { @@ -369,23 +382,19 @@ public void testCopyOfImmutableListMultimap() { } public void testCopyOfNullKey() { - ArrayListMultimap input = ArrayListMultimap.create(); + ArrayListMultimap<@Nullable String, Integer> input = ArrayListMultimap.create(); input.put(null, 1); - try { - ImmutableListMultimap.copyOf(input); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> ImmutableListMultimap.copyOf((ArrayListMultimap) input)); } public void testCopyOfNullValue() { - ArrayListMultimap input = ArrayListMultimap.create(); - input.putAll("foo", Arrays.asList(1, null, 3)); - try { - ImmutableListMultimap.copyOf(input); - fail(); - } catch (NullPointerException expected) { - } + ArrayListMultimap input = ArrayListMultimap.create(); + input.putAll("foo", Arrays.<@Nullable Integer>asList(1, null, 3)); + assertThrows( + NullPointerException.class, + () -> ImmutableListMultimap.copyOf((ArrayListMultimap) input)); } // TODO(b/172823566): Use mainline testToImmutableListMultimap once CollectorTester is usable. @@ -403,17 +412,17 @@ public void testToImmutableListMultimap_java7_combine() { } public void testEmptyMultimapReads() { - Multimap multimap = ImmutableListMultimap.of(); + ImmutableListMultimap multimap = ImmutableListMultimap.of(); assertFalse(multimap.containsKey("foo")); assertFalse(multimap.containsValue(1)); assertFalse(multimap.containsEntry("foo", 1)); assertTrue(multimap.entries().isEmpty()); assertTrue(multimap.equals(ArrayListMultimap.create())); - assertEquals(Collections.emptyList(), multimap.get("foo")); + assertEquals(emptyList(), multimap.get("foo")); assertEquals(0, multimap.hashCode()); assertTrue(multimap.isEmpty()); assertEquals(HashMultiset.create(), multimap.keys()); - assertEquals(Collections.emptySet(), multimap.keySet()); + assertEquals(emptySet(), multimap.keySet()); assertEquals(0, multimap.size()); assertTrue(multimap.values().isEmpty()); assertEquals("{}", multimap.toString()); @@ -554,6 +563,7 @@ private static void assertMultimapEquals( } } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { Multimap multimap = createMultimap(); @@ -567,12 +577,14 @@ public void testSerialization() { assertEquals(HashMultiset.create(multimap.values()), HashMultiset.create(valuesCopy)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testEmptySerialization() { Multimap multimap = ImmutableListMultimap.of(); assertSame(multimap, SerializableTester.reserialize(multimap)); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableListTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableListTest.java index 52ed7fa3ffe2..ca538883395f 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableListTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableListTest.java @@ -16,17 +16,21 @@ package com.google.common.collect; -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Iterables.unmodifiableIterable; -import static com.google.common.collect.Sets.newHashSet; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Iterators.singletonIterator; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.testing.Helpers.misleadingSizeCollection; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_QUERIES; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; -import static java.lang.reflect.Proxy.newProxyInstance; +import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.nCopies; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.Helpers; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.MinimalIterable; @@ -41,19 +45,15 @@ import com.google.common.collect.testing.testers.ListHashCodeTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link ImmutableList}. @@ -63,9 +63,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableListTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -112,727 +115,523 @@ public static Test suite() { return suite; } - public static class CreationTests extends TestCase { - public void testCreation_noArgs() { - List list = ImmutableList.of(); - assertEquals(Collections.emptyList(), list); - } - - public void testCreation_oneElement() { - List list = ImmutableList.of("a"); - assertEquals(Collections.singletonList("a"), list); - } - - public void testCreation_twoElements() { - List list = ImmutableList.of("a", "b"); - assertEquals(Lists.newArrayList("a", "b"), list); - } - - public void testCreation_threeElements() { - List list = ImmutableList.of("a", "b", "c"); - assertEquals(Lists.newArrayList("a", "b", "c"), list); - } - - public void testCreation_fourElements() { - List list = ImmutableList.of("a", "b", "c", "d"); - assertEquals(Lists.newArrayList("a", "b", "c", "d"), list); - } - - public void testCreation_fiveElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e"), list); - } - - public void testCreation_sixElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f"), list); - } + // Creation tests - public void testCreation_sevenElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g"), list); - } - - public void testCreation_eightElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h"), list); - } - - public void testCreation_nineElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i"), list); - } + public void testCreation_noArgs() { + List list = ImmutableList.of(); + assertEquals(emptyList(), list); + } - public void testCreation_tenElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), list); - } + public void testCreation_oneElement() { + List list = ImmutableList.of("a"); + assertEquals(singletonList("a"), list); + } - public void testCreation_elevenElements() { - List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"); - assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), list); - } + public void testCreation_twoElements() { + List list = ImmutableList.of("a", "b"); + assertEquals(Lists.newArrayList("a", "b"), list); + } - // Varargs versions + public void testCreation_threeElements() { + List list = ImmutableList.of("a", "b", "c"); + assertEquals(Lists.newArrayList("a", "b", "c"), list); + } - public void testCreation_twelveElements() { - List list = - ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"); - assertEquals( - Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"), list); - } + public void testCreation_fourElements() { + List list = ImmutableList.of("a", "b", "c", "d"); + assertEquals(Lists.newArrayList("a", "b", "c", "d"), list); + } - public void testCreation_thirteenElements() { - List list = - ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"); - assertEquals( - Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"), - list); - } + public void testCreation_fiveElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e"), list); + } - public void testCreation_fourteenElements() { - List list = - ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"); - assertEquals( - Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"), - list); - } + public void testCreation_sixElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f"), list); + } - public void testCreation_singletonNull() { - try { - ImmutableList.of((String) null); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCreation_sevenElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g"), list); + } - public void testCreation_withNull() { - try { - ImmutableList.of("a", null, "b"); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCreation_eightElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h"), list); + } - public void testCreation_generic() { - List a = ImmutableList.of("a"); - // only verify that there is no compile warning - ImmutableList> unused = ImmutableList.of(a, a); - } + public void testCreation_nineElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i"), list); + } - public void testCreation_arrayOfArray() { - String[] array = new String[] {"a"}; - List list = ImmutableList.of(array); - assertEquals(Collections.singletonList(array), list); - } + public void testCreation_tenElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), list); + } - public void testCopyOf_emptyArray() { - String[] array = new String[0]; - List list = ImmutableList.copyOf(array); - assertEquals(Collections.emptyList(), list); - } + public void testCreation_elevenElements() { + List list = ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"); + assertEquals(Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), list); + } - public void testCopyOf_arrayOfOneElement() { - String[] array = new String[] {"a"}; - List list = ImmutableList.copyOf(array); - assertEquals(Collections.singletonList("a"), list); - } + // Varargs versions - public void testCopyOf_nullArray() { - try { - ImmutableList.copyOf((String[]) null); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCreation_twelveElements() { + List list = + ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"); + assertEquals( + Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"), list); + } - public void testCopyOf_arrayContainingOnlyNull() { - String[] array = new String[] {null}; - try { - ImmutableList.copyOf(array); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCreation_thirteenElements() { + List list = + ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"); + assertEquals( + Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"), list); + } - public void testCopyOf_collection_empty() { - // "" is required to work around a javac 1.5 bug. - Collection c = MinimalCollection.of(); - List list = ImmutableList.copyOf(c); - assertEquals(Collections.emptyList(), list); - } + public void testCreation_fourteenElements() { + List list = + ImmutableList.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"); + assertEquals( + Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"), + list); + } - public void testCopyOf_collection_oneElement() { - Collection c = MinimalCollection.of("a"); - List list = ImmutableList.copyOf(c); - assertEquals(Collections.singletonList("a"), list); - } + public void testCreation_singletonNull() { + assertThrows(NullPointerException.class, () -> ImmutableList.of((String) null)); + } - public void testCopyOf_collection_general() { - Collection c = MinimalCollection.of("a", "b", "a"); - List list = ImmutableList.copyOf(c); - assertEquals(asList("a", "b", "a"), list); - List mutableList = asList("a", "b"); - list = ImmutableList.copyOf(mutableList); - mutableList.set(0, "c"); - assertEquals(asList("a", "b"), list); - } + public void testCreation_withNull() { + assertThrows(NullPointerException.class, () -> ImmutableList.of("a", null, "b")); + } - public void testCopyOf_collectionContainingNull() { - Collection c = MinimalCollection.of("a", null, "b"); - try { - ImmutableList.copyOf(c); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCreation_generic() { + List a = ImmutableList.of("a"); + // only verify that there is no compile warning + ImmutableList> unused = ImmutableList.of(a, a); + } - public void testCopyOf_iterator_empty() { - Iterator iterator = Iterators.emptyIterator(); - List list = ImmutableList.copyOf(iterator); - assertEquals(Collections.emptyList(), list); - } + public void testCreation_arrayOfArray() { + String[] array = new String[] {"a"}; + List list = ImmutableList.of(array); + assertEquals(singletonList(array), list); + } - public void testCopyOf_iterator_oneElement() { - Iterator iterator = Iterators.singletonIterator("a"); - List list = ImmutableList.copyOf(iterator); - assertEquals(Collections.singletonList("a"), list); - } + public void testCopyOf_emptyArray() { + String[] array = new String[0]; + List list = ImmutableList.copyOf(array); + assertEquals(emptyList(), list); + } - public void testCopyOf_iterator_general() { - Iterator iterator = asList("a", "b", "a").iterator(); - List list = ImmutableList.copyOf(iterator); - assertEquals(asList("a", "b", "a"), list); - } + public void testCopyOf_arrayOfOneElement() { + String[] array = new String[] {"a"}; + List list = ImmutableList.copyOf(array); + assertEquals(singletonList("a"), list); + } - public void testCopyOf_iteratorContainingNull() { - Iterator iterator = asList("a", null, "b").iterator(); - try { - ImmutableList.copyOf(iterator); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCopyOf_nullArray() { + assertThrows(NullPointerException.class, () -> ImmutableList.copyOf((String[]) null)); + } - public void testCopyOf_iteratorNull() { - try { - ImmutableList.copyOf((Iterator) null); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCopyOf_arrayContainingOnlyNull() { + @Nullable String[] array = new @Nullable String[] {null}; + assertThrows(NullPointerException.class, () -> ImmutableList.copyOf((String[]) array)); + } - public void testCopyOf_concurrentlyMutating() { - List sample = Lists.newArrayList("a", "b", "c"); - for (int delta : new int[] {-1, 0, 1}) { - for (int i = 0; i < sample.size(); i++) { - Collection misleading = Helpers.misleadingSizeCollection(delta); - List expected = sample.subList(0, i); - misleading.addAll(expected); - assertEquals(expected, ImmutableList.copyOf(misleading)); - assertEquals(expected, ImmutableList.copyOf((Iterable) misleading)); - } - } - } + public void testCopyOf_collection_empty() { + Collection c = MinimalCollection.of(); + List list = ImmutableList.copyOf(c); + assertEquals(emptyList(), list); + } - private static class CountingIterable implements Iterable { - int count = 0; + public void testCopyOf_collection_oneElement() { + Collection c = MinimalCollection.of("a"); + List list = ImmutableList.copyOf(c); + assertEquals(singletonList("a"), list); + } - @Override - public Iterator iterator() { - count++; - return asList("a", "b", "a").iterator(); - } - } + public void testCopyOf_collection_general() { + Collection c = MinimalCollection.of("a", "b", "a"); + List list = ImmutableList.copyOf(c); + assertEquals(asList("a", "b", "a"), list); + List mutableList = asList("a", "b"); + list = ImmutableList.copyOf(mutableList); + mutableList.set(0, "c"); + assertEquals(asList("a", "b"), list); + } - public void testCopyOf_plainIterable() { - CountingIterable iterable = new CountingIterable(); - List list = ImmutableList.copyOf(iterable); - assertEquals(asList("a", "b", "a"), list); - } + public void testCopyOf_collectionContainingNull() { + Collection<@Nullable String> c = MinimalCollection.of("a", null, "b"); + assertThrows(NullPointerException.class, () -> ImmutableList.copyOf((Collection) c)); + } - public void testCopyOf_plainIterable_iteratesOnce() { - CountingIterable iterable = new CountingIterable(); - ImmutableList unused = ImmutableList.copyOf(iterable); - assertEquals(1, iterable.count); - } + public void testCopyOf_iterator_empty() { + Iterator iterator = emptyIterator(); + List list = ImmutableList.copyOf(iterator); + assertEquals(emptyList(), list); + } - public void testCopyOf_shortcut_empty() { - Collection c = ImmutableList.of(); - assertSame(c, ImmutableList.copyOf(c)); - } + public void testCopyOf_iterator_oneElement() { + Iterator iterator = singletonIterator("a"); + List list = ImmutableList.copyOf(iterator); + assertEquals(singletonList("a"), list); + } - public void testCopyOf_shortcut_singleton() { - Collection c = ImmutableList.of("a"); - assertSame(c, ImmutableList.copyOf(c)); - } + public void testCopyOf_iterator_general() { + Iterator iterator = asList("a", "b", "a").iterator(); + List list = ImmutableList.copyOf(iterator); + assertEquals(asList("a", "b", "a"), list); + } - public void testCopyOf_shortcut_immutableList() { - Collection c = ImmutableList.of("a", "b", "c"); - assertSame(c, ImmutableList.copyOf(c)); - } + public void testCopyOf_iteratorContainingNull() { + Iterator<@Nullable String> iterator = + Arrays.<@Nullable String>asList("a", null, "b").iterator(); + assertThrows( + NullPointerException.class, () -> ImmutableList.copyOf((Iterator) iterator)); + } - public void testBuilderAddArrayHandlesNulls() { - String[] elements = {"a", null, "b"}; - ImmutableList.Builder builder = ImmutableList.builder(); - try { - builder.add(elements); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } - ImmutableList result = builder.build(); - - /* - * Maybe it rejects all elements, or maybe it adds "a" before failing. - * Either way is fine with us. - */ - if (result.isEmpty()) { - return; - } - assertTrue(ImmutableList.of("a").equals(result)); - assertEquals(1, result.size()); - } + public void testCopyOf_iteratorNull() { + assertThrows(NullPointerException.class, () -> ImmutableList.copyOf((Iterator) null)); + } - public void testBuilderAddCollectionHandlesNulls() { - List elements = Arrays.asList("a", null, "b"); - ImmutableList.Builder builder = ImmutableList.builder(); - try { - builder.addAll(elements); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { + public void testCopyOf_concurrentlyMutating() { + List sample = Lists.newArrayList("a", "b", "c"); + for (int delta : new int[] {-1, 0, 1}) { + for (int i = 0; i < sample.size(); i++) { + Collection misleading = misleadingSizeCollection(delta); + List expected = sample.subList(0, i); + misleading.addAll(expected); + assertEquals(expected, ImmutableList.copyOf(misleading)); + assertEquals(expected, ImmutableList.copyOf((Iterable) misleading)); } - ImmutableList result = builder.build(); - assertEquals(ImmutableList.of("a"), result); - assertEquals(1, result.size()); } + } - public void testSortedCopyOf_natural() { - Collection c = MinimalCollection.of(4, 16, 10, -1, 5); - ImmutableList list = ImmutableList.sortedCopyOf(c); - assertEquals(asList(-1, 4, 5, 10, 16), list); - } + private static class CountingIterable implements Iterable { + int count = 0; - public void testSortedCopyOf_natural_empty() { - Collection c = MinimalCollection.of(); - ImmutableList list = ImmutableList.sortedCopyOf(c); - assertEquals(asList(), list); + @Override + public Iterator iterator() { + count++; + return asList("a", "b", "a").iterator(); } + } - public void testSortedCopyOf_natural_singleton() { - Collection c = MinimalCollection.of(100); - ImmutableList list = ImmutableList.sortedCopyOf(c); - assertEquals(asList(100), list); - } + public void testCopyOf_plainIterable() { + CountingIterable iterable = new CountingIterable(); + List list = ImmutableList.copyOf(iterable); + assertEquals(asList("a", "b", "a"), list); + } - public void testSortedCopyOf_natural_containsNull() { - Collection c = MinimalCollection.of(1, 3, null, 2); - try { - ImmutableList.sortedCopyOf(c); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } - } + public void testCopyOf_plainIterable_iteratesOnce() { + CountingIterable iterable = new CountingIterable(); + ImmutableList unused = ImmutableList.copyOf(iterable); + assertEquals(1, iterable.count); + } - public void testSortedCopyOf() { - Collection c = MinimalCollection.of("a", "b", "A", "c"); - List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); - assertEquals(asList("a", "A", "b", "c"), list); - } + public void testCopyOf_shortcut_empty() { + Collection c = ImmutableList.of(); + assertSame(c, ImmutableList.copyOf(c)); + } - public void testSortedCopyOf_empty() { - Collection c = MinimalCollection.of(); - List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); - assertEquals(asList(), list); - } + public void testCopyOf_shortcut_singleton() { + Collection c = ImmutableList.of("a"); + assertSame(c, ImmutableList.copyOf(c)); + } - public void testSortedCopyOf_singleton() { - Collection c = MinimalCollection.of("a"); - List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); - assertEquals(asList("a"), list); - } + public void testCopyOf_shortcut_immutableList() { + Collection c = ImmutableList.of("a", "b", "c"); + assertSame(c, ImmutableList.copyOf(c)); + } - public void testSortedCopyOf_containsNull() { - Collection c = MinimalCollection.of("a", "b", "A", null, "c"); - try { - ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } - } + public void testBuilderAddArrayHandlesNulls() { + @Nullable String[] elements = new @Nullable String[] {"a", null, "b"}; + ImmutableList.Builder builder = ImmutableList.builder(); + assertThrows(NullPointerException.class, () -> builder.add((String[]) elements)); + ImmutableList result = builder.build(); - // TODO(b/172823566): Use mainline testToImmutableList once CollectorTester is usable to java7. - public void testToImmutableList_java7_combine() { - ImmutableList.Builder zis = ImmutableList.builder().add("a", "b"); - ImmutableList.Builder zat = ImmutableList.builder().add("c", "d"); - ImmutableList list = zis.combine(zat).build(); - assertEquals(asList("a", "b", "c", "d"), list); + /* + * Maybe it rejects all elements, or maybe it adds "a" before failing. + * Either way is fine with us. + */ + if (result.isEmpty()) { + return; } + assertTrue(ImmutableList.of("a").equals(result)); + assertEquals(1, result.size()); } - @GwtIncompatible // reflection - public static class ConcurrentTests extends TestCase { - enum WrapWithIterable { - WRAP, - NO_WRAP - } + public void testBuilderAddCollectionHandlesNulls() { + List<@Nullable String> elements = asList("a", null, "b"); + ImmutableList.Builder builder = ImmutableList.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((List) elements)); + ImmutableList result = builder.build(); + assertEquals(ImmutableList.of("a"), result); + assertEquals(1, result.size()); + } - private static void runConcurrentlyMutatedTest( - Collection initialContents, - Iterable actionsToPerformConcurrently, - WrapWithIterable wrap) { - ConcurrentlyMutatedList concurrentlyMutatedList = - newConcurrentlyMutatedList(initialContents, actionsToPerformConcurrently); + public void testSortedCopyOf_natural() { + Collection c = MinimalCollection.of(4, 16, 10, -1, 5); + ImmutableList list = ImmutableList.sortedCopyOf(c); + assertEquals(asList(-1, 4, 5, 10, 16), list); + } - Iterable iterableToCopy = - wrap == WrapWithIterable.WRAP - ? unmodifiableIterable(concurrentlyMutatedList) - : concurrentlyMutatedList; + public void testSortedCopyOf_natural_empty() { + Collection c = MinimalCollection.of(); + ImmutableList list = ImmutableList.sortedCopyOf(c); + assertEquals(asList(), list); + } - ImmutableList copyOfIterable = ImmutableList.copyOf(iterableToCopy); + public void testSortedCopyOf_natural_singleton() { + Collection c = MinimalCollection.of(100); + ImmutableList list = ImmutableList.sortedCopyOf(c); + assertEquals(asList(100), list); + } - assertTrue(concurrentlyMutatedList.getAllStates().contains(copyOfIterable)); - } + public void testSortedCopyOf_natural_containsNull() { + Collection<@Nullable Integer> c = MinimalCollection.of(1, 3, null, 2); + assertThrows( + NullPointerException.class, () -> ImmutableList.sortedCopyOf((Collection) c)); + } - private static void runConcurrentlyMutatedTest(WrapWithIterable wrap) { - /* - * TODO: Iterate over many array sizes and all possible operation lists, - * performing adds and removes in different ways. - */ - runConcurrentlyMutatedTest(elements(), ops(add(1), add(2)), wrap); + public void testSortedCopyOf() { + Collection c = MinimalCollection.of("a", "b", "A", "c"); + List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); + assertEquals(asList("a", "A", "b", "c"), list); + } - runConcurrentlyMutatedTest(elements(), ops(add(1), nop()), wrap); + public void testSortedCopyOf_empty() { + Collection c = MinimalCollection.of(); + List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); + assertEquals(asList(), list); + } - runConcurrentlyMutatedTest(elements(), ops(add(1), remove()), wrap); + public void testSortedCopyOf_singleton() { + Collection c = MinimalCollection.of("a"); + List list = ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, c); + assertEquals(asList("a"), list); + } - runConcurrentlyMutatedTest(elements(), ops(nop(), add(1)), wrap); + public void testSortedCopyOf_containsNull() { + Collection<@Nullable String> c = MinimalCollection.of("a", "b", "A", null, "c"); + assertThrows( + NullPointerException.class, + () -> ImmutableList.sortedCopyOf(String.CASE_INSENSITIVE_ORDER, (Collection) c)); + } - runConcurrentlyMutatedTest(elements(1), ops(remove(), nop()), wrap); + // TODO(b/172823566): Use mainline testToImmutableList once CollectorTester is usable to java7. + public void testToImmutableList_java7_combine() { + ImmutableList.Builder zis = ImmutableList.builder().add("a", "b"); + ImmutableList.Builder zat = ImmutableList.builder().add("c", "d"); + ImmutableList list = zis.combine(zat).build(); + assertEquals(asList("a", "b", "c", "d"), list); + } - runConcurrentlyMutatedTest(elements(1), ops(remove(), add(2)), wrap); + // Basic tests - runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), remove()), wrap); + @J2ktIncompatible + @GwtIncompatible // NullPointerTester + public void testNullPointers() { + NullPointerTester tester = new NullPointerTester(); + tester.testAllPublicStaticMethods(ImmutableList.class); + tester.testAllPublicInstanceMethods(ImmutableList.of(1, 2, 3)); + } - runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), nop()), wrap); + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testSerialization_empty() { + Collection c = ImmutableList.of(); + assertSame(c, SerializableTester.reserialize(c)); + } - runConcurrentlyMutatedTest(elements(1, 2), ops(remove(), add(3)), wrap); + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testSerialization_singleton() { + Collection c = ImmutableList.of("a"); + SerializableTester.reserializeAndAssert(c); + } - runConcurrentlyMutatedTest(elements(1, 2), ops(nop(), remove()), wrap); + @J2ktIncompatible + @GwtIncompatible // SerializableTester + public void testSerialization_multiple() { + Collection c = ImmutableList.of("a", "b", "c"); + SerializableTester.reserializeAndAssert(c); + } - runConcurrentlyMutatedTest(elements(1, 2, 3), ops(remove(), remove()), wrap); - } + public void testEquals_immutableList() { + Collection c = ImmutableList.of("a", "b", "c"); + assertTrue(c.equals(ImmutableList.of("a", "b", "c"))); + assertFalse(c.equals(ImmutableList.of("a", "c", "b"))); + assertFalse(c.equals(ImmutableList.of("a", "b"))); + assertFalse(c.equals(ImmutableList.of("a", "b", "c", "d"))); + } - private static ImmutableList elements(Integer... elements) { - return ImmutableList.copyOf(elements); - } + public void testBuilderAdd() { + ImmutableList list = + new ImmutableList.Builder().add("a").add("b").add("a").add("c").build(); + assertEquals(asList("a", "b", "a", "c"), list); + } - private static ImmutableList ops(ListFrobber... elements) { - return ImmutableList.copyOf(elements); + @GwtIncompatible("Builder impl") + public void testBuilderForceCopy() { + ImmutableList.Builder builder = ImmutableList.builder(); + Object[] prevArray = null; + for (int i = 0; i < 10; i++) { + builder.add(i); + assertNotSame(builder.contents, prevArray); + prevArray = builder.contents; + ImmutableList unused = builder.build(); } + } - public void testCopyOf_concurrentlyMutatedList() { - runConcurrentlyMutatedTest(WrapWithIterable.NO_WRAP); - } + @GwtIncompatible + public void testBuilderExactlySizedReusesArray() { + ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(10); + Object[] builderArray = builder.contents; + for (int i = 0; i < 10; i++) { + builder.add(i); + } + Object[] builderArrayAfterAdds = builder.contents; + RegularImmutableList list = (RegularImmutableList) builder.build(); + Object[] listInternalArray = list.array; + assertSame(builderArray, builderArrayAfterAdds); + assertSame(builderArray, listInternalArray); + } - public void testCopyOf_concurrentlyMutatedIterable() { - runConcurrentlyMutatedTest(WrapWithIterable.WRAP); - } + public void testBuilderAdd_varargs() { + ImmutableList list = + new ImmutableList.Builder().add("a", "b", "a", "c").build(); + assertEquals(asList("a", "b", "a", "c"), list); + } - /** An operation to perform on a list. */ - interface ListFrobber { - void perform(List list); - } + public void testBuilderAddAll_iterable() { + List a = asList("a", "b"); + List b = asList("c", "d"); + ImmutableList list = new ImmutableList.Builder().addAll(a).addAll(b).build(); + assertEquals(asList("a", "b", "c", "d"), list); + b.set(0, "f"); + assertEquals(asList("a", "b", "c", "d"), list); + } - static ListFrobber add(final int element) { - return new ListFrobber() { - @Override - public void perform(List list) { - list.add(0, element); - } - }; - } + public void testBuilderAddAll_iterator() { + List a = asList("a", "b"); + List b = asList("c", "d"); + ImmutableList list = + new ImmutableList.Builder().addAll(a.iterator()).addAll(b.iterator()).build(); + assertEquals(asList("a", "b", "c", "d"), list); + b.set(0, "f"); + assertEquals(asList("a", "b", "c", "d"), list); + } - static ListFrobber remove() { - return new ListFrobber() { - @Override - public void perform(List list) { - list.remove(0); + public void testComplexBuilder() { + List colorElem = asList(0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF); + ImmutableList.Builder webSafeColorsBuilder = ImmutableList.builder(); + for (Integer red : colorElem) { + for (Integer green : colorElem) { + for (Integer blue : colorElem) { + webSafeColorsBuilder.add((red << 16) + (green << 8) + blue); } - }; - } - - static ListFrobber nop() { - return new ListFrobber() { - @Override - public void perform(List list) {} - }; - } - - /** A list that mutates itself after every call to each of its {@link List} methods. */ - interface ConcurrentlyMutatedList extends List { - /** - * The elements of a {@link ConcurrentlyMutatedList} are added and removed over time. This - * method returns every state that the list has passed through at some point. - */ - Set> getAllStates(); - } - - /** - * Returns a {@link ConcurrentlyMutatedList} that performs the given operations as its - * concurrent modifications. The mutations occur in the same thread as the triggering method - * call. - */ - private static ConcurrentlyMutatedList newConcurrentlyMutatedList( - final Collection initialContents, - final Iterable actionsToPerformConcurrently) { - InvocationHandler invocationHandler = - new InvocationHandler() { - final CopyOnWriteArrayList delegate = - new CopyOnWriteArrayList<>(initialContents); - - final Method getAllStatesMethod = - getOnlyElement(asList(ConcurrentlyMutatedList.class.getDeclaredMethods())); - - final Iterator remainingActions = actionsToPerformConcurrently.iterator(); - - final Set> allStates = newHashSet(); - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return method.equals(getAllStatesMethod) - ? getAllStates() - : invokeListMethod(method, args); - } - - private Set> getAllStates() { - return allStates; - } - - private Object invokeListMethod(Method method, Object[] args) throws Throwable { - try { - Object returnValue = method.invoke(delegate, args); - mutateDelegate(); - return returnValue; - } catch (InvocationTargetException e) { - throw e.getCause(); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - - private void mutateDelegate() { - allStates.add(ImmutableList.copyOf(delegate)); - remainingActions.next().perform(delegate); - allStates.add(ImmutableList.copyOf(delegate)); - } - }; - - @SuppressWarnings("unchecked") - ConcurrentlyMutatedList list = - (ConcurrentlyMutatedList) - newProxyInstance( - ImmutableListTest.CreationTests.class.getClassLoader(), - new Class[] {ConcurrentlyMutatedList.class}, - invocationHandler); - return list; + } } + ImmutableList webSafeColors = webSafeColorsBuilder.build(); + assertEquals(216, webSafeColors.size()); + Integer[] webSafeColorArray = webSafeColors.toArray(new Integer[webSafeColors.size()]); + assertEquals(0x000000, (int) webSafeColorArray[0]); + assertEquals(0x000033, (int) webSafeColorArray[1]); + assertEquals(0x000066, (int) webSafeColorArray[2]); + assertEquals(0x003300, (int) webSafeColorArray[6]); + assertEquals(0x330000, (int) webSafeColorArray[36]); + assertEquals(0x000066, (int) webSafeColors.get(2)); + assertEquals(0x003300, (int) webSafeColors.get(6)); + ImmutableList addedColor = webSafeColorsBuilder.add(0x00BFFF).build(); + assertEquals( + "Modifying the builder should not have changed any already" + " built sets", + 216, + webSafeColors.size()); + assertEquals("the new array should be one bigger than webSafeColors", 217, addedColor.size()); + Integer[] appendColorArray = addedColor.toArray(new Integer[addedColor.size()]); + assertEquals(0x00BFFF, (int) appendColorArray[216]); } - public static class BasicTests extends TestCase { - - @GwtIncompatible // NullPointerTester - public void testNullPointers() { - NullPointerTester tester = new NullPointerTester(); - tester.testAllPublicStaticMethods(ImmutableList.class); - tester.testAllPublicInstanceMethods(ImmutableList.of(1, 2, 3)); - } - - @GwtIncompatible // SerializableTester - public void testSerialization_empty() { - Collection c = ImmutableList.of(); - assertSame(c, SerializableTester.reserialize(c)); - } - - @GwtIncompatible // SerializableTester - public void testSerialization_singleton() { - Collection c = ImmutableList.of("a"); - SerializableTester.reserializeAndAssert(c); - } - - @GwtIncompatible // SerializableTester - public void testSerialization_multiple() { - Collection c = ImmutableList.of("a", "b", "c"); - SerializableTester.reserializeAndAssert(c); - } - - public void testEquals_immutableList() { - Collection c = ImmutableList.of("a", "b", "c"); - assertTrue(c.equals(ImmutableList.of("a", "b", "c"))); - assertFalse(c.equals(ImmutableList.of("a", "c", "b"))); - assertFalse(c.equals(ImmutableList.of("a", "b"))); - assertFalse(c.equals(ImmutableList.of("a", "b", "c", "d"))); - } - - public void testBuilderAdd() { - ImmutableList list = - new ImmutableList.Builder().add("a").add("b").add("a").add("c").build(); - assertEquals(asList("a", "b", "a", "c"), list); - } - - @GwtIncompatible("Builder impl") - public void testBuilderForceCopy() { - ImmutableList.Builder builder = ImmutableList.builder(); - Object[] prevArray = null; - for (int i = 0; i < 10; i++) { - builder.add(i); - assertNotSame(builder.contents, prevArray); - prevArray = builder.contents; - ImmutableList unused = builder.build(); - } - } + public void testBuilderAddHandlesNullsCorrectly() { + ImmutableList.Builder builder = ImmutableList.builder(); + assertThrows(NullPointerException.class, () -> builder.add((String) null)); - @GwtIncompatible - public void testBuilderExactlySizedReusesArray() { - ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize(10); - Object[] builderArray = builder.contents; - for (int i = 0; i < 10; i++) { - builder.add(i); - } - Object[] builderArrayAfterAdds = builder.contents; - RegularImmutableList list = (RegularImmutableList) builder.build(); - Object[] listInternalArray = list.array; - assertSame(builderArray, builderArrayAfterAdds); - assertSame(builderArray, listInternalArray); - } + assertThrows(NullPointerException.class, () -> builder.add((String[]) null)); - public void testBuilderAdd_varargs() { - ImmutableList list = - new ImmutableList.Builder().add("a", "b", "a", "c").build(); - assertEquals(asList("a", "b", "a", "c"), list); - } + assertThrows(NullPointerException.class, () -> builder.add("a", null, "b")); + } - public void testBuilderAddAll_iterable() { - List a = asList("a", "b"); - List b = asList("c", "d"); - ImmutableList list = new ImmutableList.Builder().addAll(a).addAll(b).build(); - assertEquals(asList("a", "b", "c", "d"), list); - b.set(0, "f"); - assertEquals(asList("a", "b", "c", "d"), list); + public void testBuilderAddAllHandlesNullsCorrectly() { + { + ImmutableList.Builder builder = ImmutableList.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Iterable) null)); } - public void testBuilderAddAll_iterator() { - List a = asList("a", "b"); - List b = asList("c", "d"); - ImmutableList list = - new ImmutableList.Builder().addAll(a.iterator()).addAll(b.iterator()).build(); - assertEquals(asList("a", "b", "c", "d"), list); - b.set(0, "f"); - assertEquals(asList("a", "b", "c", "d"), list); + { + ImmutableList.Builder builder = ImmutableList.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Iterator) null)); } - public void testComplexBuilder() { - List colorElem = asList(0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF); - ImmutableList.Builder webSafeColorsBuilder = ImmutableList.builder(); - for (Integer red : colorElem) { - for (Integer green : colorElem) { - for (Integer blue : colorElem) { - webSafeColorsBuilder.add((red << 16) + (green << 8) + blue); - } - } - } - ImmutableList webSafeColors = webSafeColorsBuilder.build(); - assertEquals(216, webSafeColors.size()); - Integer[] webSafeColorArray = webSafeColors.toArray(new Integer[webSafeColors.size()]); - assertEquals(0x000000, (int) webSafeColorArray[0]); - assertEquals(0x000033, (int) webSafeColorArray[1]); - assertEquals(0x000066, (int) webSafeColorArray[2]); - assertEquals(0x003300, (int) webSafeColorArray[6]); - assertEquals(0x330000, (int) webSafeColorArray[36]); - assertEquals(0x000066, (int) webSafeColors.get(2)); - assertEquals(0x003300, (int) webSafeColors.get(6)); - ImmutableList addedColor = webSafeColorsBuilder.add(0x00BFFF).build(); - assertEquals( - "Modifying the builder should not have changed any already" + " built sets", - 216, - webSafeColors.size()); - assertEquals("the new array should be one bigger than webSafeColors", 217, addedColor.size()); - Integer[] appendColorArray = addedColor.toArray(new Integer[addedColor.size()]); - assertEquals(0x00BFFF, (int) appendColorArray[216]); + { + ImmutableList.Builder builder = ImmutableList.builder(); + List<@Nullable String> listWithNulls = asList("a", null, "b"); + assertThrows(NullPointerException.class, () -> builder.addAll((List) listWithNulls)); } - public void testBuilderAddHandlesNullsCorrectly() { + { ImmutableList.Builder builder = ImmutableList.builder(); - try { - builder.add((String) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - try { - builder.add((String[]) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - try { - builder.add("a", null, "b"); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + Iterator<@Nullable String> iteratorWithNulls = + Arrays.<@Nullable String>asList("a", null, "b").iterator(); + assertThrows( + NullPointerException.class, () -> builder.addAll((Iterator) iteratorWithNulls)); } - public void testBuilderAddAllHandlesNullsCorrectly() { + { ImmutableList.Builder builder = ImmutableList.builder(); - try { - builder.addAll((Iterable) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - try { - builder.addAll((Iterator) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - builder = ImmutableList.builder(); - List listWithNulls = asList("a", null, "b"); - try { - builder.addAll(listWithNulls); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - builder = ImmutableList.builder(); - Iterator iteratorWithNulls = asList("a", null, "b").iterator(); - try { - builder.addAll(iteratorWithNulls); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } - - Iterable iterableWithNulls = MinimalIterable.of("a", null, "b"); - try { - builder.addAll(iterableWithNulls); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + Iterable<@Nullable String> iterableWithNulls = MinimalIterable.of("a", null, "b"); + assertThrows( + NullPointerException.class, () -> builder.addAll((Iterable) iterableWithNulls)); } + } - public void testAsList() { - ImmutableList list = ImmutableList.of("a", "b"); - assertSame(list, list.asList()); - } + // We need to test that asList() really does return the original list. + @SuppressWarnings("InlineMeInliner") + public void testAsList() { + ImmutableList list = ImmutableList.of("a", "b"); + assertSame(list, list.asList()); + } - @GwtIncompatible("builder internals") - public void testReusedBuilder() { - ImmutableList.Builder builder = new ImmutableList.Builder(); - for (int i = 0; i < 10; i++) { - builder.add("foo"); - } - builder.add("bar"); - RegularImmutableList list = (RegularImmutableList) builder.build(); - builder.add("baz"); - assertTrue(list.array != builder.contents); + @GwtIncompatible("builder internals") + public void testReusedBuilder() { + ImmutableList.Builder builder = new ImmutableList.Builder(); + for (int i = 0; i < 10; i++) { + builder.add("foo"); } + builder.add("bar"); + RegularImmutableList list = (RegularImmutableList) builder.build(); + builder.add("baz"); + assertTrue(list.array != builder.contents); + } + + @SuppressWarnings("ModifiedButNotUsed") + @GwtIncompatible // actually allocates nCopies + @J2ktIncompatible // actually allocates nCopies + public void testAddOverflowCollection() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < 100; i++) { + builder.add("a"); + } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> builder.addAll(nCopies(Integer.MAX_VALUE - 50, "a"))); + assertThat(expected) + .hasMessageThat() + .contains("cannot store more than Integer.MAX_VALUE elements"); } } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java index ecb84cc68e84..a85266620561 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableMapTest.java @@ -16,22 +16,21 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Joiner; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.testing.CollectionTestSuiteBuilder; import com.google.common.collect.testing.ListTestSuiteBuilder; -import com.google.common.collect.testing.MapInterfaceTest; import com.google.common.collect.testing.MapTestSuiteBuilder; -import com.google.common.collect.testing.MinimalSet; -import com.google.common.collect.testing.SampleElements.Colliders; -import com.google.common.collect.testing.SampleElements.Unhashables; -import com.google.common.collect.testing.UnhashableObject; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -46,7 +45,6 @@ import com.google.common.collect.testing.google.MapGenerators.ImmutableMapValuesAsSingletonSetGenerator; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; -import com.google.common.testing.SerializableTester; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -54,12 +52,17 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableMap}. @@ -68,9 +71,13 @@ * @author Jesse Wilson */ @GwtCompatible(emulated = true) +@SuppressWarnings("AlwaysThrows") +@NullMarked public class ImmutableMapTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableMapTest.class); @@ -168,680 +175,641 @@ public static Test suite() { return suite; } - public abstract static class AbstractMapTests extends MapInterfaceTest { - public AbstractMapTests() { - super(false, false, false, false, false); - } + // Creation tests - @Override - protected Map makeEmptyMap() { - throw new UnsupportedOperationException(); - } + public void testEmptyBuilder() { + ImmutableMap map = new Builder().buildOrThrow(); + assertEquals(Collections.emptyMap(), map); + } - private static final Joiner JOINER = Joiner.on(", "); + public void testSingletonBuilder() { + ImmutableMap map = new Builder().put("one", 1).buildOrThrow(); + assertMapEquals(map, "one", 1); + } - @Override - protected void assertMoreInvariants(Map map) { - // TODO: can these be moved to MapInterfaceTest? - for (Entry entry : map.entrySet()) { - assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); - } + public void testBuilder() { + ImmutableMap map = + new Builder() + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .buildOrThrow(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + } - assertEquals("{" + JOINER.join(map.entrySet()) + "}", map.toString()); - assertEquals("[" + JOINER.join(map.entrySet()) + "]", map.entrySet().toString()); - assertEquals("[" + JOINER.join(map.keySet()) + "]", map.keySet().toString()); - assertEquals("[" + JOINER.join(map.values()) + "]", map.values().toString()); + @GwtIncompatible + public void testBuilderExactlySizedReusesArray() { + ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(10); + Object[] builderArray = builder.alternatingKeysAndValues; + for (int i = 0; i < 10; i++) { + builder.put(i, i); + } + Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues; + RegularImmutableMap map = + (RegularImmutableMap) builder.buildOrThrow(); + Object[] mapInternalArray = map.alternatingKeysAndValues; + assertSame(builderArray, builderArrayAfterPuts); + assertSame(builderArray, mapInternalArray); + } - assertEquals(MinimalSet.from(map.entrySet()), map.entrySet()); - assertEquals(Sets.newHashSet(map.keySet()), map.keySet()); - } + public void testBuilder_orderEntriesByValue() { + ImmutableMap map = + new Builder() + .orderEntriesByValue(Ordering.natural()) + .put("three", 3) + .put("one", 1) + .put("five", 5) + .put("four", 4) + .put("two", 2) + .buildOrThrow(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); } - public static class MapTests extends AbstractMapTests { - @Override - protected Map makeEmptyMap() { - return ImmutableMap.of(); - } + public void testBuilder_orderEntriesByValueAfterExactSizeBuild() { + Builder builder = new Builder(2).put("four", 4).put("one", 1); + ImmutableMap keyOrdered = builder.buildOrThrow(); + ImmutableMap valueOrdered = + builder.orderEntriesByValue(Ordering.natural()).buildOrThrow(); + assertMapEquals(keyOrdered, "four", 4, "one", 1); + assertMapEquals(valueOrdered, "one", 1, "four", 4); + } - @Override - protected Map makePopulatedMap() { - return ImmutableMap.of("one", 1, "two", 2, "three", 3); - } + public void testBuilder_orderEntriesByValue_usedTwiceFails() { + ImmutableMap.Builder builder = + new Builder().orderEntriesByValue(Ordering.natural()); + assertThrows( + IllegalStateException.class, () -> builder.orderEntriesByValue(Ordering.natural())); + } - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } + @GwtIncompatible // we haven't implemented this + public void testBuilder_orderEntriesByValue_keepingLast() { + ImmutableMap.Builder builder = + new Builder() + .orderEntriesByValue(Ordering.natural()) + .put("three", 3) + .put("one", 1) + .put("five", 5) + .put("four", 3) + .put("four", 5) + .put("four", 4) // this should win because it's last + .put("two", 2); + assertMapEquals( + builder.buildKeepingLast(), "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + assertThrows(IllegalArgumentException.class, () -> builder.buildOrThrow()); + } - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + @GwtIncompatible // we haven't implemented this + public void testBuilder_orderEntriesByValueAfterExactSizeBuild_keepingLastWithoutDuplicates() { + ImmutableMap.Builder builder = + new Builder(3) + .orderEntriesByValue(Ordering.natural()) + .put("three", 3) + .put("one", 1); + assertMapEquals(builder.buildKeepingLast(), "one", 1, "three", 3); } - public static class SingletonMapTests extends AbstractMapTests { - @Override - protected Map makePopulatedMap() { - return ImmutableMap.of("one", 1); - } + @GwtIncompatible // we haven't implemented this + public void testBuilder_orderEntriesByValue_keepingLast_builderSizeFieldPreserved() { + ImmutableMap.Builder builder = + new Builder() + .orderEntriesByValue(Ordering.natural()) + .put("one", 1) + .put("one", 1); + assertMapEquals(builder.buildKeepingLast(), "one", 1); + assertThrows(IllegalArgumentException.class, () -> builder.buildOrThrow()); + } - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } + public void testBuilder_withImmutableEntry() { + ImmutableMap map = + new Builder().put(immutableEntry("one", 1)).buildOrThrow(); + assertMapEquals(map, "one", 1); + } - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testBuilder_withImmutableEntryAndNullContents() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, () -> builder.put(immutableEntry("one", (Integer) null))); + assertThrows(NullPointerException.class, () -> builder.put(immutableEntry((String) null, 1))); } - @GwtIncompatible // SerializableTester - public static class ReserializedMapTests extends AbstractMapTests { - @Override - protected Map makePopulatedMap() { - return SerializableTester.reserialize(ImmutableMap.of("one", 1, "two", 2, "three", 3)); - } + private static class StringHolder { + @Nullable String string; + } - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } + public void testBuilder_withMutableEntry() { + ImmutableMap.Builder builder = new Builder<>(); + final StringHolder holder = new StringHolder(); + holder.string = "one"; + Entry entry = + new AbstractMapEntry() { + @Override + public String getKey() { + return holder.string; + } + + @Override + public Integer getValue() { + return 1; + } + }; + + builder.put(entry); + holder.string = "two"; + assertMapEquals(builder.buildOrThrow(), "one", 1); + } - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testBuilderPutAllWithEmptyMap() { + ImmutableMap map = + new Builder() + .putAll(Collections.emptyMap()) + .buildOrThrow(); + assertEquals(Collections.emptyMap(), map); } - public static class MapTestsWithBadHashes extends AbstractMapTests { + public void testBuilderPutAll() { + Map toPut = new LinkedHashMap<>(); + toPut.put("one", 1); + toPut.put("two", 2); + toPut.put("three", 3); + Map moreToPut = new LinkedHashMap<>(); + moreToPut.put("four", 4); + moreToPut.put("five", 5); - @Override - protected Map makeEmptyMap() { - throw new UnsupportedOperationException(); - } + ImmutableMap map = + new Builder().putAll(toPut).putAll(moreToPut).buildOrThrow(); + assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); + } - @Override - protected Map makePopulatedMap() { - Colliders colliders = new Colliders(); - return ImmutableMap.of( - colliders.e0(), 0, - colliders.e1(), 1, - colliders.e2(), 2, - colliders.e3(), 3); - } + public void testBuilderReuse() { + Builder builder = new Builder<>(); + ImmutableMap mapOne = builder.put("one", 1).put("two", 2).buildOrThrow(); + ImmutableMap mapTwo = builder.put("three", 3).put("four", 4).buildOrThrow(); - @Override - protected Object getKeyNotInPopulatedMap() { - return new Colliders().e4(); - } + assertMapEquals(mapOne, "one", 1, "two", 2); + assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); + } - @Override - protected Integer getValueNotInPopulatedMap() { - return 4; - } + public void testBuilderPutNullKeyFailsAtomically() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + builder.put("foo", 2); + assertMapEquals(builder.buildOrThrow(), "foo", 2); } - @GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. - public static class MapTestsWithUnhashableValues - extends AbstractMapTests { - @Override - protected Map makeEmptyMap() { - return ImmutableMap.of(); - } + public void testBuilderPutImmutableEntryWithNullKeyFailsAtomically() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put(immutableEntry((String) null, 1))); + builder.put("foo", 2); + assertMapEquals(builder.buildOrThrow(), "foo", 2); + } - @Override - protected Map makePopulatedMap() { - Unhashables unhashables = new Unhashables(); - return ImmutableMap.of(0, unhashables.e0(), 1, unhashables.e1(), 2, unhashables.e2()); - } + // for GWT compatibility + static class SimpleEntry extends AbstractMapEntry { + public K key; + public V value; - @Override - protected Integer getKeyNotInPopulatedMap() { - return 3; + SimpleEntry(K key, V value) { + this.key = key; + this.value = value; } @Override - protected UnhashableObject getValueNotInPopulatedMap() { - return new Unhashables().e3(); + public K getKey() { + return key; } - } - @GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. - public static class MapTestsWithSingletonUnhashableValue extends MapTestsWithUnhashableValues { @Override - protected Map makePopulatedMap() { - Unhashables unhashables = new Unhashables(); - return ImmutableMap.of(0, unhashables.e0()); + public V getValue() { + return value; } } - public static class CreationTests extends TestCase { - public void testEmptyBuilder() { - ImmutableMap map = new Builder().buildOrThrow(); - assertEquals(Collections.emptyMap(), map); - } - - public void testSingletonBuilder() { - ImmutableMap map = - new Builder().put("one", 1).buildOrThrow(); - assertMapEquals(map, "one", 1); - } - - public void testBuilder() { - ImmutableMap map = - new Builder() - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .buildOrThrow(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - } - - @GwtIncompatible - public void testBuilderExactlySizedReusesArray() { - ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(10); - Object[] builderArray = builder.alternatingKeysAndValues; - for (int i = 0; i < 10; i++) { - builder.put(i, i); - } - Object[] builderArrayAfterPuts = builder.alternatingKeysAndValues; - RegularImmutableMap map = - (RegularImmutableMap) builder.buildOrThrow(); - Object[] mapInternalArray = map.alternatingKeysAndValues; - assertSame(builderArray, builderArrayAfterPuts); - assertSame(builderArray, mapInternalArray); - } - - public void testBuilder_orderEntriesByValue() { - ImmutableMap map = - new Builder() - .orderEntriesByValue(Ordering.natural()) - .put("three", 3) - .put("one", 1) - .put("five", 5) - .put("four", 4) - .put("two", 2) - .buildOrThrow(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - } - - public void testBuilder_orderEntriesByValueAfterExactSizeBuild() { - Builder builder = - new Builder(2).put("four", 4).put("one", 1); - ImmutableMap keyOrdered = builder.buildOrThrow(); - ImmutableMap valueOrdered = - builder.orderEntriesByValue(Ordering.natural()).buildOrThrow(); - assertMapEquals(keyOrdered, "four", 4, "one", 1); - assertMapEquals(valueOrdered, "one", 1, "four", 4); - } - - public void testBuilder_orderEntriesByValue_usedTwiceFails() { - ImmutableMap.Builder builder = - new Builder().orderEntriesByValue(Ordering.natural()); - try { - builder.orderEntriesByValue(Ordering.natural()); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - } + public void testBuilderPutMutableEntryWithNullKeyFailsAtomically() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, () -> builder.put(new SimpleEntry(null, 1))); + builder.put("foo", 2); + assertMapEquals(builder.buildOrThrow(), "foo", 2); + } - public void testBuilder_withImmutableEntry() { - ImmutableMap map = - new Builder().put(Maps.immutableEntry("one", 1)).buildOrThrow(); - assertMapEquals(map, "one", 1); - } + public void testBuilderPutNullKey() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + } - public void testBuilder_withImmutableEntryAndNullContents() { - Builder builder = new Builder<>(); - try { - builder.put(Maps.immutableEntry("one", (Integer) null)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } - } + public void testBuilderPutNullValue() { + Builder builder = new Builder<>(); + assertThrows(NullPointerException.class, () -> builder.put("one", null)); + } - private static class StringHolder { - String string; - } + public void testBuilderPutNullKeyViaPutAll() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap(null, 1))); + } - public void testBuilder_withMutableEntry() { - ImmutableMap.Builder builder = new Builder<>(); - final StringHolder holder = new StringHolder(); - holder.string = "one"; - Entry entry = - new AbstractMapEntry() { - @Override - public String getKey() { - return holder.string; - } - - @Override - public Integer getValue() { - return 1; - } - }; - - builder.put(entry); - holder.string = "two"; - assertMapEquals(builder.buildOrThrow(), "one", 1); - } + public void testBuilderPutNullValueViaPutAll() { + Builder builder = new Builder<>(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap("one", null))); + } - public void testBuilderPutAllWithEmptyMap() { - ImmutableMap map = - new Builder() - .putAll(Collections.emptyMap()) - .buildOrThrow(); - assertEquals(Collections.emptyMap(), map); - } + public void testPuttingTheSameKeyTwiceThrowsOnBuild() { + Builder builder = + new Builder() + .put("one", 1) + .put("one", 1); // throwing on this line might be better but it's too late to change - public void testBuilderPutAll() { - Map toPut = new LinkedHashMap<>(); - toPut.put("one", 1); - toPut.put("two", 2); - toPut.put("three", 3); - Map moreToPut = new LinkedHashMap<>(); - moreToPut.put("four", 4); - moreToPut.put("five", 5); - - ImmutableMap map = - new Builder().putAll(toPut).putAll(moreToPut).buildOrThrow(); - assertMapEquals(map, "one", 1, "two", 2, "three", 3, "four", 4, "five", 5); - } + assertThrows(IllegalArgumentException.class, () -> builder.buildOrThrow()); + } - public void testBuilderReuse() { - Builder builder = new Builder<>(); - ImmutableMap mapOne = builder.put("one", 1).put("two", 2).buildOrThrow(); - ImmutableMap mapTwo = builder.put("three", 3).put("four", 4).buildOrThrow(); + public void testBuildKeepingLast_allowsOverwrite() { + Builder builder = + new Builder() + .put(1, "un") + .put(2, "deux") + .put(70, "soixante-dix") + .put(70, "septante") + .put(70, "seventy") + .put(1, "one") + .put(2, "two"); + ImmutableMap map = builder.buildKeepingLast(); + assertMapEquals(map, 1, "one", 2, "two", 70, "seventy"); + } - assertMapEquals(mapOne, "one", 1, "two", 2); - assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4); - } + public void testBuildKeepingLast_smallTableSameHash() { + String key1 = "QED"; + String key2 = "R&D"; + assertThat(key1.hashCode()).isEqualTo(key2.hashCode()); + ImmutableMap map = + ImmutableMap.builder() + .put(key1, 1) + .put(key2, 2) + .put(key1, 3) + .put(key2, 4) + .buildKeepingLast(); + assertMapEquals(map, key1, 3, key2, 4); + } - public void testBuilderPutNullKeyFailsAtomically() { - Builder builder = new Builder<>(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } - builder.put("foo", 2); - assertMapEquals(builder.buildOrThrow(), "foo", 2); + // The java7 branch has different code depending on whether the entry indexes fit in a byte, + // short, or int. The small table in testBuildKeepingLast_allowsOverwrite will test the byte + // case. This method tests the short case. + public void testBuildKeepingLast_shortTable() { + Builder builder = ImmutableMap.builder(); + Map expected = new LinkedHashMap<>(); + for (int i = 0; i < 1000; i++) { + // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are + // duplicates. + Integer key = i & ~1; + String value = String.valueOf(i); + builder.put(key, value); + expected.put(key, value); } + ImmutableMap map = builder.buildKeepingLast(); + assertThat(map).hasSize(500); + assertThat(map).containsExactlyEntriesIn(expected).inOrder(); + } - public void testBuilderPutImmutableEntryWithNullKeyFailsAtomically() { - Builder builder = new Builder<>(); - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } - builder.put("foo", 2); - assertMapEquals(builder.buildOrThrow(), "foo", 2); + // This method tests the int case. + public void testBuildKeepingLast_bigTable() { + Builder builder = ImmutableMap.builder(); + Map expected = new LinkedHashMap<>(); + for (int i = 0; i < 200_000; i++) { + // Truncate to even key, so we have put(0, "0") then put(0, "1"). Half the entries are + // duplicates. + Integer key = i & ~1; + String value = String.valueOf(i); + builder.put(key, value); + expected.put(key, value); } + ImmutableMap map = builder.buildKeepingLast(); + assertThat(map).hasSize(100_000); + assertThat(map).containsExactlyEntriesIn(expected).inOrder(); + } - // for GWT compatibility - static class SimpleEntry extends AbstractMapEntry { - public K key; - public V value; - - SimpleEntry(K key, V value) { - this.key = key; - this.value = value; - } - - @Override - public K getKey() { - return key; - } + private static class ClassWithTerribleHashCode implements Comparable { + private final int value; - @Override - public V getValue() { - return value; - } + ClassWithTerribleHashCode(int value) { + this.value = value; } - public void testBuilderPutMutableEntryWithNullKeyFailsAtomically() { - Builder builder = new Builder<>(); - try { - builder.put(new SimpleEntry(null, 1)); - fail(); - } catch (NullPointerException expected) { - } - builder.put("foo", 2); - assertMapEquals(builder.buildOrThrow(), "foo", 2); + @Override + public int compareTo(ClassWithTerribleHashCode that) { + return Integer.compare(this.value, that.value); } - public void testBuilderPutNullKey() { - Builder builder = new Builder<>(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } + @Override + public boolean equals(@Nullable Object x) { + return x instanceof ClassWithTerribleHashCode + && ((ClassWithTerribleHashCode) x).value == value; } - public void testBuilderPutNullValue() { - Builder builder = new Builder<>(); - try { - builder.put("one", null); - fail(); - } catch (NullPointerException expected) { - } + @Override + public int hashCode() { + return 23; } - public void testBuilderPutNullKeyViaPutAll() { - Builder builder = new Builder<>(); - try { - builder.putAll(Collections.singletonMap(null, 1)); - fail(); - } catch (NullPointerException expected) { - } + @Override + public String toString() { + return "ClassWithTerribleHashCode(" + value + ")"; } + } - public void testBuilderPutNullValueViaPutAll() { - Builder builder = new Builder<>(); - try { - builder.putAll(Collections.singletonMap("one", null)); - fail(); - } catch (NullPointerException expected) { - } - } + @GwtIncompatible + public void testBuildKeepingLast_collisions() { + Map expected = new LinkedHashMap<>(); + Builder builder = new Builder<>(); + int size = 18; + for (int i = 0; i < size; i++) { + ClassWithTerribleHashCode key = new ClassWithTerribleHashCode(i); + builder.put(key, i); + builder.put(key, -i); + expected.put(key, -i); + } + ImmutableMap map = builder.buildKeepingLast(); + assertThat(map).containsExactlyEntriesIn(expected).inOrder(); + } - public void testPuttingTheSameKeyTwiceThrowsOnBuild() { - Builder builder = - new Builder() - .put("one", 1) - .put("one", 1); // throwing on this line would be even better + @GwtIncompatible // Pattern, Matcher + public void testBuilder_keepingLast_thenOrThrow() { + ImmutableMap.Builder builder = + new Builder() + .put("three", 3) + .put("one", 1) + .put("five", 5) + .put("four", 3) + .put("four", 5) + .put("four", 4) // this should win because it's last + .put("two", 2); + assertMapEquals( + builder.buildKeepingLast(), "three", 3, "one", 1, "five", 5, "four", 4, "two", 2); + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> builder.buildOrThrow()); + // We don't really care which values the exception message contains, but they should be + // different from each other. If buildKeepingLast() collapsed duplicates, that might end up not + // being true. + Pattern pattern = Pattern.compile("Multiple entries with same key: four=(.*) and four=(.*)"); + assertThat(expected).hasMessageThat().matches(pattern); + Matcher matcher = pattern.matcher(expected.getMessage()); + assertThat(matcher.matches()).isTrue(); + assertThat(matcher.group(1)).isNotEqualTo(matcher.group(2)); + } - try { - builder.buildOrThrow(); - fail(); - } catch (IllegalArgumentException expected) { - } - } + public void testOf() { + assertMapEquals(ImmutableMap.of("one", 1), "one", 1); + assertMapEquals(ImmutableMap.of("one", 1, "two", 2), "one", 1, "two", 2); + assertMapEquals( + ImmutableMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3); + assertMapEquals( + ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4); + assertMapEquals( + ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5); + assertMapEquals( + ImmutableMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6); + assertMapEquals( + ImmutableMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7); + assertMapEquals( + ImmutableMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8); + assertMapEquals( + ImmutableMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8, + "nine", + 9); + assertMapEquals( + ImmutableMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9, + "ten", 10), + "one", + 1, + "two", + 2, + "three", + 3, + "four", + 4, + "five", + 5, + "six", + 6, + "seven", + 7, + "eight", + 8, + "nine", + 9, + "ten", + 10); + } - public void testOf() { - assertMapEquals(ImmutableMap.of("one", 1), "one", 1); - assertMapEquals(ImmutableMap.of("one", 1, "two", 2), "one", 1, "two", 2); - assertMapEquals( - ImmutableMap.of("one", 1, "two", 2, "three", 3), "one", 1, "two", 2, "three", 3); - assertMapEquals( - ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4); - assertMapEquals( - ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5); - assertMapEquals( - ImmutableMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6); - assertMapEquals( - ImmutableMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7); - assertMapEquals( - ImmutableMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8); - assertMapEquals( - ImmutableMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8, - "nine", - 9); - assertMapEquals( - ImmutableMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9, - "ten", 10), - "one", - 1, - "two", - 2, - "three", - 3, - "four", - 4, - "five", - 5, - "six", - 6, - "seven", - 7, - "eight", - 8, - "nine", - 9, - "ten", - 10); - } + public void testOfNullKey() { + assertThrows(NullPointerException.class, () -> ImmutableMap.of(null, 1)); - public void testOfNullKey() { - try { - ImmutableMap.of(null, 1); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableMap.of("one", 1, null, 2)); + } - try { - ImmutableMap.of("one", 1, null, 2); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOfNullValue() { + assertThrows(NullPointerException.class, () -> ImmutableMap.of("one", null)); - public void testOfNullValue() { - try { - ImmutableMap.of("one", null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableMap.of("one", 1, "two", null)); + } - try { - ImmutableMap.of("one", 1, "two", null); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOfWithDuplicateKey() { + assertThrows(IllegalArgumentException.class, () -> ImmutableMap.of("one", 1, "one", 1)); + } - public void testOfWithDuplicateKey() { - try { - ImmutableMap.of("one", 1, "one", 1); - fail(); - } catch (IllegalArgumentException expected) { - } - } + public void testCopyOfEmptyMap() { + ImmutableMap copy = + ImmutableMap.copyOf(Collections.emptyMap()); + assertEquals(Collections.emptyMap(), copy); + assertSame(copy, ImmutableMap.copyOf(copy)); + } - public void testCopyOfEmptyMap() { - ImmutableMap copy = - ImmutableMap.copyOf(Collections.emptyMap()); - assertEquals(Collections.emptyMap(), copy); - assertSame(copy, ImmutableMap.copyOf(copy)); - } + public void testCopyOfSingletonMap() { + ImmutableMap copy = ImmutableMap.copyOf(singletonMap("one", 1)); + assertMapEquals(copy, "one", 1); + assertSame(copy, ImmutableMap.copyOf(copy)); + } - public void testCopyOfSingletonMap() { - ImmutableMap copy = ImmutableMap.copyOf(Collections.singletonMap("one", 1)); - assertMapEquals(copy, "one", 1); - assertSame(copy, ImmutableMap.copyOf(copy)); - } + public void testCopyOf() { + Map original = new LinkedHashMap<>(); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); - public void testCopyOf() { - Map original = new LinkedHashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); + ImmutableMap copy = ImmutableMap.copyOf(original); + assertMapEquals(copy, "one", 1, "two", 2, "three", 3); + assertSame(copy, ImmutableMap.copyOf(copy)); + } - ImmutableMap copy = ImmutableMap.copyOf(original); - assertMapEquals(copy, "one", 1, "two", 2, "three", 3); - assertSame(copy, ImmutableMap.copyOf(copy)); - } + // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. + public void testToImmutableMap_java7_combine() { + ImmutableMap.Builder zis = + ImmutableMap.builder().put("one", 1); + ImmutableMap.Builder zat = + ImmutableMap.builder().put("two", 2).put("three", 3); + assertMapEquals(zis.combine(zat).build(), "one", 1, "two", 2, "three", 3); + } - // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. - public void testToImmutableMap_java7_combine() { - ImmutableMap.Builder zis = - ImmutableMap.builder().put("one", 1); - ImmutableMap.Builder zat = - ImmutableMap.builder().put("two", 2).put("three", 3); - assertMapEquals(zis.combine(zat).build(), "one", 1, "two", 2, "three", 3); - } + // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. + public void testToImmutableMap_exceptionOnDuplicateKey_java7_combine() { + ImmutableMap.Builder zis = + ImmutableMap.builder().put("one", 1).put("two", 2); + ImmutableMap.Builder zat = + ImmutableMap.builder().put("two", 22).put("three", 3); + assertThrows(IllegalArgumentException.class, () -> zis.combine(zat).build()); + } - // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. - public void testToImmutableMap_exceptionOnDuplicateKey_java7_combine() { - ImmutableMap.Builder zis = - ImmutableMap.builder().put("one", 1).put("two", 2); - ImmutableMap.Builder zat = - ImmutableMap.builder().put("two", 22).put("three", 3); - try { - zis.combine(zat).build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // expected + public static void hashtableTestHelper(ImmutableList sizes) { + for (int size : sizes) { + Builder builder = ImmutableMap.builderWithExpectedSize(size); + for (int i = 0; i < size; i++) { + Integer integer = i; + builder.put(integer, integer); } - } - - public static void hashtableTestHelper(ImmutableList sizes) { - for (int size : sizes) { - Builder builder = ImmutableMap.builderWithExpectedSize(size); - for (int i = 0; i < size; i++) { - Integer integer = i; - builder.put(integer, integer); - } - ImmutableMap map = builder.build(); - assertEquals(size, map.size()); - int entries = 0; - for (Integer key : map.keySet()) { - assertEquals(entries, key.intValue()); - assertSame(key, map.get(key)); - entries++; - } - assertEquals(size, entries); + ImmutableMap map = builder.build(); + assertEquals(size, map.size()); + int entries = 0; + for (Integer key : map.keySet()) { + assertEquals(entries, key.intValue()); + assertSame(key, map.get(key)); + entries++; } + assertEquals(size, entries); } + } - public void testByteArrayHashtable() { - hashtableTestHelper(ImmutableList.of(2, 89)); - } + public void testByteArrayHashtable() { + hashtableTestHelper(ImmutableList.of(2, 89)); + } - public void testShortArrayHashtable() { - hashtableTestHelper(ImmutableList.of(90, 22937)); - } + public void testShortArrayHashtable() { + hashtableTestHelper(ImmutableList.of(90, 22937)); + } - public void testIntArrayHashtable() { - hashtableTestHelper(ImmutableList.of(22938)); - } + public void testIntArrayHashtable() { + hashtableTestHelper(ImmutableList.of(22938)); } + // Non-creation tests + public void testNullGet() { ImmutableMap map = ImmutableMap.of("one", 1); assertNull(map.get(null)); @@ -869,6 +837,7 @@ public void testAsMultimapCaches() { assertSame(multimap1, multimap2); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -880,13 +849,9 @@ public void testNullPointers() { } private static void assertMapEquals(Map map, Object... alternatingKeysAndValues) { - Map expected = new LinkedHashMap<>(); + Map expected = new LinkedHashMap<>(); for (int i = 0; i < alternatingKeysAndValues.length; i += 2) { - @SuppressWarnings("unchecked") - K key = (K) alternatingKeysAndValues[i]; - @SuppressWarnings("unchecked") - V value = (V) alternatingKeysAndValues[i + 1]; - expected.put(key, value); + expected.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]); } assertThat(map).containsExactlyEntriesIn(expected).inOrder(); } @@ -899,7 +864,7 @@ public IntHolder(int value) { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof IntHolder) && ((IntHolder) o).value == value; } @@ -908,7 +873,7 @@ public int hashCode() { return value; } - private static final long serialVersionUID = 5; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 5; } public void testMutableValues() { @@ -916,12 +881,13 @@ public void testMutableValues() { IntHolder holderB = new IntHolder(2); Map map = ImmutableMap.of("a", holderA, "b", holderB); holderA.value = 3; - assertTrue(map.entrySet().contains(Maps.immutableEntry("a", new IntHolder(3)))); + assertTrue(map.entrySet().contains(immutableEntry("a", new IntHolder(3)))); Map intMap = ImmutableMap.of("a", 3, "b", 2); assertEquals(intMap.hashCode(), map.entrySet().hashCode()); assertEquals(intMap.hashCode(), map.hashCode()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testViewSerialization() { Map map = ImmutableMap.of("one", 1, "two", 2, "three", 3); @@ -933,6 +899,7 @@ public void testViewSerialization() { assertTrue(reserializedValues instanceof ImmutableCollection); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testKeySetIsSerializable_regularImmutableMap() { class NonSerializableClass {} @@ -944,6 +911,7 @@ class NonSerializableClass {} LenientSerializableTester.reserializeAndAssertLenient(set); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testValuesCollectionIsSerializable_regularImmutableMap() { class NonSerializableClass {} @@ -956,10 +924,11 @@ class NonSerializableClass {} } // TODO: Re-enable this test after moving to new serialization format in ImmutableMap. + @J2ktIncompatible @GwtIncompatible // SerializableTester @SuppressWarnings("unchecked") public void ignore_testSerializationNoDuplication_regularImmutableMap() throws Exception { - // Tests that searializing a map, its keySet, and values only writes the underlying data once. + // Tests that serializing a map, its keySet, and values only writes the underlying data once. Object[] entries = new Object[2000]; for (int i = 0; i < entries.length; i++) { @@ -1041,18 +1010,14 @@ public void testEquals() { } public void testOfEntriesNull() { - Entry nullKey = entry(null, 23); - try { - ImmutableMap.ofEntries(nullKey); - fail(); - } catch (NullPointerException expected) { - } - Entry nullValue = entry(23, null); - try { - ImmutableMap.ofEntries(nullValue); - fail(); - } catch (NullPointerException expected) { - } + Entry<@Nullable Integer, @Nullable Integer> nullKey = entry(null, 23); + assertThrows( + NullPointerException.class, + () -> ImmutableMap.ofEntries((Entry) nullKey)); + Entry<@Nullable Integer, @Nullable Integer> nullValue = entry(23, null); + assertThrows( + NullPointerException.class, + () -> ImmutableMap.ofEntries((Entry) nullValue)); } private static Map map(T... keysAndValues) { @@ -1067,7 +1032,26 @@ private static Map map(T... keysAndValues) { return map; } - private static Entry entry(T key, T value) { + private static Entry entry(T key, T value) { return new AbstractMap.SimpleImmutableEntry<>(key, value); } + + public void testCopyOfMutableEntryList() { + List> entryList = + asList(new AbstractMap.SimpleEntry<>("a", "1"), new AbstractMap.SimpleEntry<>("b", "2")); + ImmutableMap map = ImmutableMap.copyOf(entryList); + assertThat(map).containsExactly("a", "1", "b", "2").inOrder(); + entryList.get(0).setValue("3"); + assertThat(map).containsExactly("a", "1", "b", "2").inOrder(); + } + + public void testBuilderPutAllEntryList() { + List> entryList = + asList(new AbstractMap.SimpleEntry<>("a", "1"), new AbstractMap.SimpleEntry<>("b", "2")); + ImmutableMap map = + ImmutableMap.builder().putAll(entryList).buildOrThrow(); + assertThat(map).containsExactly("a", "1", "b", "2").inOrder(); + entryList.get(0).setValue("3"); + assertThat(map).containsExactly("a", "1", "b", "2").inOrder(); + } } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMapWithBadHashesMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMapWithBadHashesMapInterfaceTest.java new file mode 100644 index 000000000000..d7e4910b2af8 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableMapWithBadHashesMapInterfaceTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.SampleElements.Colliders; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableMapWithBadHashesMapInterfaceTest + extends AbstractImmutableMapMapInterfaceTest { + @Override + protected Map makeEmptyMap() { + throw new UnsupportedOperationException(); + } + + @Override + protected Map makePopulatedMap() { + Colliders colliders = new Colliders(); + return ImmutableMap.of( + colliders.e0(), 0, + colliders.e1(), 1, + colliders.e2(), 2, + colliders.e3(), 3); + } + + @Override + protected Object getKeyNotInPopulatedMap() { + return new Colliders().e4(); + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 4; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMultimapAsMapImplementsMapTest.java index 8c1f020b5ca8..0d7889408235 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableMultimapAsMapImplementsMapTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@link Multimap#asMap()} for an {@link ImmutableMultimap} with {@link MapInterfaceTest}. @@ -27,6 +28,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class ImmutableMultimapAsMapImplementsMapTest extends AbstractMultimapAsMapImplementsMapTest { diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMultimapTest.java index ac09593bac1d..2e4ea543f74d 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableMultimapTest.java @@ -16,17 +16,24 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMultimap.Builder; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SampleElements.Unhashables; import com.google.common.collect.testing.UnhashableObject; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; -import java.util.Arrays; import java.util.Map.Entry; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableMultimap}. @@ -34,32 +41,66 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableMultimapTest extends TestCase { + @SuppressWarnings("JUnitIncompatibleType") public void testBuilder_withImmutableEntry() { ImmutableMultimap multimap = - new Builder().put(Maps.immutableEntry("one", 1)).build(); - assertEquals(Arrays.asList(1), multimap.get("one")); + new Builder().put(immutableEntry("one", 1)).build(); + assertEquals(asList(1), multimap.get("one")); } public void testBuilder_withImmutableEntryAndNullContents() { Builder builder = new Builder<>(); - try { - builder.put(Maps.immutableEntry("one", (Integer) null)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> builder.put(immutableEntry("one", (Integer) null))); + assertThrows(NullPointerException.class, () -> builder.put(immutableEntry((String) null, 1))); + } + + public void testBuilderWithExpectedKeysNegative() { + assertThrows( + IllegalArgumentException.class, () -> ImmutableMultimap.builderWithExpectedKeys(-1)); + } + + public void testBuilderWithExpectedKeysZero() { + ImmutableMultimap.Builder builder = + ImmutableMultimap.builderWithExpectedKeys(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedKeysPositive() { + ImmutableMultimap.Builder builder = + ImmutableMultimap.builderWithExpectedKeys(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyNegative() { + assertThrows( + IllegalArgumentException.class, () -> ImmutableMultimap.builder().expectedValuesPerKey(-1)); + } + + public void testBuilderWithExpectedValuesPerKeyZero() { + ImmutableMultimap.Builder builder = + ImmutableMultimap.builder().expectedValuesPerKey(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyPositive() { + ImmutableMultimap.Builder builder = + ImmutableMultimap.builder().expectedValuesPerKey(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(immutableEntry("key", "value")); } private static class StringHolder { - String string; + @Nullable String string; } + @SuppressWarnings("JUnitIncompatibleType") public void testBuilder_withMutableEntry() { ImmutableMultimap.Builder builder = new Builder<>(); final StringHolder holder = new StringHolder(); @@ -79,7 +120,7 @@ public Integer getValue() { builder.put(entry); holder.string = "two"; - assertEquals(Arrays.asList(1), builder.build().get("one")); + assertEquals(asList(1), builder.build().get("one")); } // TODO: test ImmutableMultimap builder and factory methods @@ -127,6 +168,7 @@ public void testEquals() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableMultisetTest.java index 2b320ce1eac0..3e1dfe2be9fd 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableMultisetTest.java @@ -17,11 +17,15 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Iterators.singletonIterator; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.MinimalCollection; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -36,6 +40,7 @@ import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -44,6 +49,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableMultiset}. @@ -51,9 +58,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite // TODO(cpovirk): add to collect/gwt/suites + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableMultisetTest.class); @@ -196,6 +206,7 @@ public void testCreation_arrayOfOneElement() { assertEquals(HashMultiset.create(asList("a")), multiset); } + @SuppressWarnings("ArrayAsKeyOfSetOrMap") public void testCreation_arrayOfArray() { String[] array = new String[] {"a"}; Multiset multiset = ImmutableMultiset.of(array); @@ -205,17 +216,12 @@ public void testCreation_arrayOfArray() { } public void testCreation_arrayContainingOnlyNull() { - String[] array = new String[] {null}; - try { - ImmutableMultiset.copyOf(array); - fail(); - } catch (NullPointerException expected) { - } + @Nullable String[] array = new @Nullable String[] {null}; + assertThrows(NullPointerException.class, () -> ImmutableMultiset.copyOf((String[]) array)); } public void testCopyOf_collection_empty() { - // "" is required to work around a javac 1.5 bug. - Collection c = MinimalCollection.of(); + Collection c = MinimalCollection.of(); Multiset multiset = ImmutableMultiset.copyOf(c); assertTrue(multiset.isEmpty()); } @@ -233,12 +239,9 @@ public void testCopyOf_collection_general() { } public void testCopyOf_collectionContainingNull() { - Collection c = MinimalCollection.of("a", null, "b"); - try { - ImmutableMultiset.copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + Collection<@Nullable String> c = MinimalCollection.of("a", null, "b"); + assertThrows( + NullPointerException.class, () -> ImmutableMultiset.copyOf((Collection) c)); } public void testCopyOf_multiset_empty() { @@ -260,22 +263,19 @@ public void testCopyOf_multiset_general() { } public void testCopyOf_multisetContainingNull() { - Multiset c = HashMultiset.create(asList("a", null, "b")); - try { - ImmutableMultiset.copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + Multiset<@Nullable String> c = + HashMultiset.create(Arrays.<@Nullable String>asList("a", null, "b")); + assertThrows(NullPointerException.class, () -> ImmutableMultiset.copyOf((Multiset) c)); } public void testCopyOf_iterator_empty() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); Multiset multiset = ImmutableMultiset.copyOf(iterator); assertTrue(multiset.isEmpty()); } public void testCopyOf_iterator_oneElement() { - Iterator iterator = Iterators.singletonIterator("a"); + Iterator iterator = singletonIterator("a"); Multiset multiset = ImmutableMultiset.copyOf(iterator); assertEquals(HashMultiset.create(asList("a")), multiset); } @@ -287,12 +287,10 @@ public void testCopyOf_iterator_general() { } public void testCopyOf_iteratorContainingNull() { - Iterator iterator = asList("a", null, "b").iterator(); - try { - ImmutableMultiset.copyOf(iterator); - fail(); - } catch (NullPointerException expected) { - } + Iterator<@Nullable String> iterator = + Arrays.<@Nullable String>asList("a", null, "b").iterator(); + assertThrows( + NullPointerException.class, () -> ImmutableMultiset.copyOf((Iterator) iterator)); } private static class CountingIterable implements Iterable { @@ -402,86 +400,65 @@ public void testBuilderSetCount() { public void testBuilderAddHandlesNullsCorrectly() { ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.add((String) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.add((String) null)); } public void testBuilderAddAllHandlesNullsCorrectly() { - ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.addAll((Collection) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Collection) null)); } - builder = ImmutableMultiset.builder(); - List listWithNulls = asList("a", null, "b"); - try { - builder.addAll(listWithNulls); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); + List<@Nullable String> listWithNulls = asList("a", null, "b"); + assertThrows(NullPointerException.class, () -> builder.addAll((List) listWithNulls)); } - builder = ImmutableMultiset.builder(); - Multiset multisetWithNull = LinkedHashMultiset.create(asList("a", null, "b")); - try { - builder.addAll(multisetWithNull); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); + Multiset<@Nullable String> multisetWithNull = + LinkedHashMultiset.create(Arrays.<@Nullable String>asList("a", null, "b")); + assertThrows( + NullPointerException.class, () -> builder.addAll((Multiset) multisetWithNull)); } } public void testBuilderAddCopiesHandlesNullsCorrectly() { ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.addCopies(null, 2); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.addCopies(null, 2)); } public void testBuilderAddCopiesIllegal() { ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.addCopies("a", -2); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.addCopies("a", -2)); } public void testBuilderSetCountHandlesNullsCorrectly() { ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.setCount(null, 2); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.setCount(null, 2)); } public void testBuilderSetCountIllegal() { ImmutableMultiset.Builder builder = ImmutableMultiset.builder(); - try { - builder.setCount("a", -2); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.setCount("a", -2)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(ImmutableMultiset.class); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization_empty() { Collection c = ImmutableMultiset.of(); assertSame(c, SerializableTester.reserialize(c)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization_multiple() { Collection c = ImmutableMultiset.of("a", "b", "a"); @@ -489,6 +466,7 @@ public void testSerialization_multiple() { assertThat(copy).containsExactly("a", "a", "b").inOrder(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization_elementSet() { Multiset c = ImmutableMultiset.of("a", "b", "a"); @@ -496,6 +474,7 @@ public void testSerialization_elementSet() { assertThat(copy).containsExactly("a", "b").inOrder(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization_entrySet() { Multiset c = ImmutableMultiset.of("a", "b", "c"); @@ -503,11 +482,14 @@ public void testSerialization_entrySet() { } public void testEquals_immutableMultiset() { - Collection c = ImmutableMultiset.of("a", "b", "a"); - assertEquals(c, ImmutableMultiset.of("a", "b", "a")); - assertEquals(c, ImmutableMultiset.of("a", "a", "b")); - assertThat(c).isNotEqualTo(ImmutableMultiset.of("a", "b")); - assertThat(c).isNotEqualTo(ImmutableMultiset.of("a", "b", "c", "d")); + new EqualsTester() + .addEqualityGroup( + ImmutableMultiset.of("a", "b", "a"), + ImmutableMultiset.of("a", "b", "a"), + ImmutableMultiset.of("a", "a", "b")) + .addEqualityGroup(ImmutableMultiset.of("a", "b")) + .addEqualityGroup(ImmutableMultiset.of("a", "b", "c", "d")) + .testEquals(); } public void testIterationOrder() { @@ -531,6 +513,7 @@ public void testAsList() { assertEquals(4, list.lastIndexOf("b")); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization_asList() { ImmutableMultiset multiset = ImmutableMultiset.of("a", "a", "b", "b", "b"); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableRangeMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableRangeMapTest.java index 59f3a1b59cf9..f6f87d4dd56e 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableRangeMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableRangeMapTest.java @@ -15,13 +15,16 @@ package com.google.common.collect; import static com.google.common.collect.BoundType.OPEN; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.SerializableTester; import java.util.Map.Entry; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code ImmutableRangeMap}. @@ -29,6 +32,7 @@ * @author Louis Wasserman */ @GwtIncompatible // NavigableMap +@NullUnmarked public class ImmutableRangeMapTest extends TestCase { private static final ImmutableList> RANGES; private static final int MIN_BOUND = 0; @@ -65,18 +69,10 @@ public class ImmutableRangeMapTest extends TestCase { public void testBuilderRejectsEmptyRanges() { for (int i = MIN_BOUND; i <= MAX_BOUND; i++) { + final int ii = i; ImmutableRangeMap.Builder builder = ImmutableRangeMap.builder(); - try { - builder.put(Range.closedOpen(i, i), 1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // success - } - try { - builder.put(Range.openClosed(i, i), 1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.put(Range.closedOpen(ii, ii), 1)); + assertThrows(IllegalArgumentException.class, () -> builder.put(Range.openClosed(ii, ii), 1)); } } @@ -120,11 +116,7 @@ public void testGet() { } public void testSpanEmpty() { - try { - ImmutableRangeMap.of().span(); - fail("Expected NoSuchElementException"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> ImmutableRangeMap.of().span()); } public void testSpanSingleRange() { @@ -157,9 +149,9 @@ public void testGetEntry() { for (int i = MIN_BOUND; i <= MAX_BOUND; i++) { Entry, Integer> expectedEntry = null; if (range1.contains(i)) { - expectedEntry = Maps.immutableEntry(range1, 1); + expectedEntry = immutableEntry(range1, 1); } else if (range2.contains(i)) { - expectedEntry = Maps.immutableEntry(range2, 2); + expectedEntry = immutableEntry(range2, 2); } assertEquals(expectedEntry, rangeMap.getEntry(i)); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableRangeSetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableRangeSetTest.java index 4c2bf3f92a83..ab764372ec2a 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableRangeSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableRangeSetTest.java @@ -15,6 +15,7 @@ package com.google.common.collect; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; @@ -28,6 +29,7 @@ import java.util.Set; import junit.framework.Test; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ImmutableRangeSet}. @@ -35,8 +37,10 @@ * @author Louis Wasserman */ @GwtIncompatible // ImmutableRangeSet +@NullUnmarked public class ImmutableRangeSetTest extends AbstractRangeSetTest { + @AndroidIncompatible // test-suite builders static final class ImmutableRangeSetIntegerAsSetGenerator implements TestSetGenerator { @Override public SampleElements samples() { @@ -64,6 +68,7 @@ public Set create(Object... elements) { } } + @AndroidIncompatible // test-suite builders static final class ImmutableRangeSetBigIntegerAsSetGenerator implements TestSetGenerator { @Override @@ -97,6 +102,7 @@ public Set create(Object... elements) { } } + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableRangeSetTest.class); @@ -320,12 +326,7 @@ public void testAddUnsupported() { .add(Range.closedOpen(1, 3)) .build(); - try { - rangeSet.add(Range.open(3, 4)); - fail(); - } catch (UnsupportedOperationException expected) { - // success - } + assertThrows(UnsupportedOperationException.class, () -> rangeSet.add(Range.open(3, 4))); } @SuppressWarnings("DoNotCall") @@ -336,12 +337,9 @@ public void testAddAllUnsupported() { .add(Range.closedOpen(1, 3)) .build(); - try { - rangeSet.addAll(ImmutableRangeSet.of()); - fail(); - } catch (UnsupportedOperationException expected) { - // success - } + assertThrows( + UnsupportedOperationException.class, + () -> rangeSet.addAll(ImmutableRangeSet.of())); } @SuppressWarnings("DoNotCall") @@ -352,12 +350,7 @@ public void testRemoveUnsupported() { .add(Range.closedOpen(1, 3)) .build(); - try { - rangeSet.remove(Range.closed(6, 7)); - fail(); - } catch (UnsupportedOperationException expected) { - // success - } + assertThrows(UnsupportedOperationException.class, () -> rangeSet.remove(Range.closed(6, 7))); } @SuppressWarnings("DoNotCall") @@ -368,24 +361,17 @@ public void testRemoveAllUnsupported() { .add(Range.closedOpen(1, 3)) .build(); - try { - rangeSet.removeAll(ImmutableRangeSet.of()); - fail(); - } catch (UnsupportedOperationException expected) { - // success - } + assertThrows( + UnsupportedOperationException.class, + () -> rangeSet.removeAll(ImmutableRangeSet.of())); - try { - rangeSet.removeAll(ImmutableRangeSet.of(Range.closed(6, 8))); - fail(); - } catch (UnsupportedOperationException expected) { - // success - } + assertThrows( + UnsupportedOperationException.class, + () -> rangeSet.removeAll(ImmutableRangeSet.of(Range.closed(6, 8)))); } @AndroidIncompatible // slow public void testExhaustive() { - @SuppressWarnings("unchecked") ImmutableSet> ranges = ImmutableSet.of( Range.all(), @@ -428,11 +414,7 @@ public void testExhaustive() { } if (anyOverlaps) { - try { - RangeSet copy = ImmutableRangeSet.copyOf(subset); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ImmutableRangeSet.copyOf(subset)); } else { RangeSet copy = ImmutableRangeSet.copyOf(subset); assertEquals(mutable, copy); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapAsMapImplementsMapTest.java index c503a17573ef..f8dfc39a2990 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapAsMapImplementsMapTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@link Multimap#asMap()} for an {@link ImmutableSetMultimap} with {@link MapInterfaceTest}. @@ -27,6 +28,7 @@ * @author Mike Ward */ @GwtCompatible +@NullMarked public class ImmutableSetMultimapAsMapImplementsMapTest extends AbstractMultimapAsMapImplementsMapTest { diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapTest.java index 33d4ec1aa356..b7b8d6f8a2c9 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSetMultimapTest.java @@ -16,13 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.features.CollectionFeature.KNOWN_ORDER; import static com.google.common.collect.testing.features.CollectionFeature.SERIALIZABLE; import static com.google.common.collect.testing.features.MapFeature.ALLOWS_ANY_NULL_QUERIES; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.emptySet; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSetMultimap.Builder; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.google.SetMultimapTestSuiteBuilder; @@ -38,6 +41,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableSetMultimap}. @@ -45,7 +50,10 @@ * @author Mike Ward */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableSetMultimapTest extends TestCase { + @J2ktIncompatible + @AndroidIncompatible // test-suite builders private static final class ImmutableSetMultimapGenerator extends TestStringSetMultimapGenerator { @Override protected SetMultimap create(Entry[] entries) { @@ -57,6 +65,8 @@ protected SetMultimap create(Entry[] entries) { } } + @J2ktIncompatible + @AndroidIncompatible // test-suite builders private static final class ImmutableSetMultimapCopyOfEntriesGenerator extends TestStringSetMultimapGenerator { @Override @@ -65,7 +75,9 @@ protected SetMultimap create(Entry[] entries) { } } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableSetMultimapTest.class); @@ -82,6 +94,98 @@ public static Test suite() { return suite; } + public void testBuilderWithExpectedKeysNegative() { + assertThrows( + IllegalArgumentException.class, () -> ImmutableSetMultimap.builderWithExpectedKeys(-1)); + } + + public void testBuilderWithExpectedKeysZero() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builderWithExpectedKeys(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedKeysPositive() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builderWithExpectedKeys(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyNegative() { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + assertThrows(IllegalArgumentException.class, () -> builder.expectedValuesPerKey(-1)); + } + + public void testBuilderWithExpectedValuesPerKeyZero() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder().expectedValuesPerKey(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyPositive() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder().expectedValuesPerKey(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyNegativeOrderValuesBy() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder().orderValuesBy(Ordering.natural()); + assertThrows(IllegalArgumentException.class, () -> builder.expectedValuesPerKey(-1)); + } + + public void testBuilderWithExpectedValuesPerKeyZeroOrderValuesBy() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder() + .orderValuesBy(Ordering.natural()) + .expectedValuesPerKey(0); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + public void testBuilderWithExpectedValuesPerKeyPositiveOrderValuesBy() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder() + .orderValuesBy(Ordering.natural()) + .expectedValuesPerKey(1); + builder.put("key", "value"); + assertThat(builder.build().entries()).containsExactly(Maps.immutableEntry("key", "value")); + } + + static class HashHostileComparable implements Comparable { + final String string; + + public HashHostileComparable(String string) { + this.string = string; + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } + + @Override + public int compareTo(HashHostileComparable o) { + return string.compareTo(o.string); + } + } + + public void testSortedBuilderWithExpectedValuesPerKeyPositive() { + ImmutableSetMultimap.Builder builder = + ImmutableSetMultimap.builder() + .expectedValuesPerKey(2) + .orderValuesBy(Ordering.natural()); + HashHostileComparable v1 = new HashHostileComparable("value1"); + HashHostileComparable v2 = new HashHostileComparable("value2"); + builder.put("key", v1); + builder.put("key", v2); + assertThat(builder.build().entries()).hasSize(2); + } + public void testBuilder_withImmutableEntry() { ImmutableSetMultimap multimap = new Builder().put(Maps.immutableEntry("one", 1)).build(); @@ -90,20 +194,14 @@ public void testBuilder_withImmutableEntry() { public void testBuilder_withImmutableEntryAndNullContents() { Builder builder = new Builder<>(); - try { - builder.put(Maps.immutableEntry("one", (Integer) null)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> builder.put(Maps.immutableEntry("one", (Integer) null))); + assertThrows( + NullPointerException.class, () -> builder.put(Maps.immutableEntry((String) null, 1))); } private static class StringHolder { - String string; + @Nullable String string; } public void testBuilder_withMutableEntry() { @@ -201,55 +299,26 @@ public void testBuilderPutAllMultimapWithDuplicates() { } public void testBuilderPutNullKey() { - Multimap toPut = LinkedListMultimap.create(); - toPut.put("foo", null); + Multimap<@Nullable String, Integer> toPut = LinkedListMultimap.create(); + toPut.put(null, 1); ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(null, Arrays.asList(1, 2, 3)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(null, 1, 2, 3); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(toPut); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + assertThrows(NullPointerException.class, () -> builder.putAll(null, Arrays.asList(1, 2, 3))); + assertThrows(NullPointerException.class, () -> builder.putAll(null, 1, 2, 3)); + assertThrows( + NullPointerException.class, () -> builder.putAll((Multimap) toPut)); } public void testBuilderPutNullValue() { - Multimap toPut = LinkedListMultimap.create(); - toPut.put(null, 1); + Multimap toPut = LinkedListMultimap.create(); + toPut.put("foo", null); ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); - try { - builder.put("foo", null); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll("foo", Arrays.asList(1, null, 3)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll("foo", 4, null, 6); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.putAll(toPut); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.put("foo", null)); + assertThrows( + NullPointerException.class, () -> builder.putAll("foo", Arrays.asList(1, null, 3))); + assertThrows(NullPointerException.class, () -> builder.putAll("foo", 4, null, 6)); + assertThrows( + NullPointerException.class, () -> builder.putAll((Multimap) toPut)); } public void testBuilderOrderKeysBy() { @@ -382,23 +451,19 @@ public void testCopyOfImmutableSetMultimap() { } public void testCopyOfNullKey() { - HashMultimap input = HashMultimap.create(); + HashMultimap<@Nullable String, Integer> input = HashMultimap.create(); input.put(null, 1); - try { - ImmutableSetMultimap.copyOf(input); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> ImmutableSetMultimap.copyOf((Multimap) input)); } public void testCopyOfNullValue() { - HashMultimap input = HashMultimap.create(); - input.putAll("foo", Arrays.asList(1, null, 3)); - try { - ImmutableSetMultimap.copyOf(input); - fail(); - } catch (NullPointerException expected) { - } + HashMultimap input = HashMultimap.create(); + input.putAll("foo", Arrays.<@Nullable Integer>asList(1, null, 3)); + assertThrows( + NullPointerException.class, + () -> ImmutableSetMultimap.copyOf((Multimap) input)); } // TODO(b/172823566): Use mainline testToImmutableSetMultimap once CollectorTester is usable. @@ -422,11 +487,11 @@ public void testEmptyMultimapReads() { assertFalse(multimap.containsEntry("foo", 1)); assertTrue(multimap.entries().isEmpty()); assertTrue(multimap.equals(HashMultimap.create())); - assertEquals(Collections.emptySet(), multimap.get("foo")); + assertEquals(emptySet(), multimap.get("foo")); assertEquals(0, multimap.hashCode()); assertTrue(multimap.isEmpty()); assertEquals(HashMultiset.create(), multimap.keys()); - assertEquals(Collections.emptySet(), multimap.keySet()); + assertEquals(emptySet(), multimap.keySet()); assertEquals(0, multimap.size()); assertTrue(multimap.values().isEmpty()); assertEquals("{}", multimap.toString()); @@ -548,6 +613,7 @@ private static void assertMultimapEquals( } } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { Multimap multimap = createMultimap(); @@ -561,12 +627,14 @@ public void testSerialization() { assertEquals(HashMultiset.create(multimap.values()), HashMultiset.create(valuesCopy)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testEmptySerialization() { Multimap multimap = ImmutableSetMultimap.of(); assertSame(multimap, SerializableTester.reserialize(multimap)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSortedSerialization() { Multimap multimap = @@ -594,6 +662,7 @@ private ImmutableSetMultimap createMultimap() { .build(); } + @J2ktIncompatible @GwtIncompatible // reflection public void testNulls() throws Exception { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java index 2ddd1ee6c184..5056e3b1f755 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSetTest.java @@ -17,9 +17,12 @@ package com.google.common.collect; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.singleton; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SetTestSuiteBuilder; @@ -35,11 +38,11 @@ import com.google.common.collect.testing.google.SetGenerators.ImmutableSetWithBadHashesGenerator; import com.google.common.testing.EqualsTester; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.Set; import junit.framework.Test; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link ImmutableSet}. @@ -49,9 +52,12 @@ * @author Nick Kralevich */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableSetTest extends AbstractImmutableSetTest { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -168,7 +174,6 @@ protected > Set of(E e1, E e2, E e3, E e4, E return ImmutableSet.of(e1, e2, e3, e4, e5); } - @SuppressWarnings("unchecked") @Override protected > Set of( E e1, E e2, E e3, E e4, E e5, E e6, E... rest) { @@ -203,6 +208,7 @@ public void testCreation_allDuplicates() { public void testCreation_oneDuplicate() { // now we'll get the varargs overload + @SuppressWarnings("DistinctVarargsChecker") // deliberately testing deduplication ImmutableSet set = ImmutableSet.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "a"); assertEquals( @@ -212,6 +218,7 @@ public void testCreation_oneDuplicate() { public void testCreation_manyDuplicates() { // now we'll get the varargs overload + @SuppressWarnings("DistinctVarargsChecker") // deliberately testing deduplication ImmutableSet set = ImmutableSet.of("a", "b", "c", "c", "c", "c", "b", "b", "a", "a", "c", "c", "c", "a"); assertThat(set).containsExactly("a", "b", "c").inOrder(); @@ -263,7 +270,7 @@ public void testPresizedBuilderForceCopy() { public void testCreation_arrayOfArray() { String[] array = new String[] {"a"}; Set set = ImmutableSet.of(array); - assertEquals(Collections.singleton(array), set); + assertEquals(singleton(array), set); } @GwtIncompatible // ImmutableSet.chooseTableSize @@ -279,11 +286,7 @@ public void testChooseTableSize() { assertEquals(1 << 30, ImmutableSet.chooseTableSize((1 << 30) - 1)); // Now we've gone too far - try { - ImmutableSet.chooseTableSize(1 << 30); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ImmutableSet.chooseTableSize(1 << 30)); } @GwtIncompatible // RegularImmutableSet.table not in emulation @@ -329,11 +332,6 @@ public void testToImmutableSet_java7() { assertThat(set).containsExactly("a", "b", "c", "d").inOrder(); } - @GwtIncompatible // GWT is single threaded - public void testCopyOf_threadSafe() { - verifyThreadSafe(); - } - @Override > Builder builder() { return ImmutableSet.builder(); @@ -344,6 +342,7 @@ int getComplexBuilderSetLastElement() { return LAST_COLOR_ADDED; } + @SuppressWarnings("DistinctVarargsChecker") // deliberately testing deduplication public void testEquals() { new EqualsTester() .addEqualityGroup(ImmutableSet.of(), ImmutableSet.of()) diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapInclusiveMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapInclusiveMapInterfaceTest.java new file mode 100644 index 000000000000..40848428bf64 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapInclusiveMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableSortedMapHeadMapInclusiveMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).headMap("c", true); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "d"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 4; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapMapInterfaceTest.java new file mode 100644 index 000000000000..c22d8063072d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapHeadMapMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableSortedMapHeadMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).headMap("d"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "d"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 4; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapSubMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapSubMapMapInterfaceTest.java new file mode 100644 index 000000000000..31faf4bf4c26 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapSubMapMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableSortedMapSubMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).subMap("b", "d"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "a"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 4; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapExclusiveMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapExclusiveMapInterfaceTest.java new file mode 100644 index 000000000000..8752bd4f79bd --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapExclusiveMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableSortedMapTailMapExclusiveMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).tailMap("a", false); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "a"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapMapInterfaceTest.java new file mode 100644 index 000000000000..5a31f11b46fc --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTailMapMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class ImmutableSortedMapTailMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).tailMap("b"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "a"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return 1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTest.java index 850a8e73b2c4..e1d4139b0c9d 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMapTest.java @@ -16,16 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Joiner; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSortedMap.Builder; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.NavigableMapTestSuiteBuilder; -import com.google.common.collect.testing.SortedMapInterfaceTest; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; @@ -42,11 +44,14 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link ImmutableSortedMap}. @@ -56,10 +61,14 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@SuppressWarnings("AlwaysThrows") +@NullMarked public class ImmutableSortedMapTest extends TestCase { // TODO: Avoid duplicating code in ImmutableMapTest + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableSortedMapTest.class); @@ -118,718 +127,490 @@ public static Test suite() { return suite; } - public abstract static class AbstractMapTests extends SortedMapInterfaceTest { - public AbstractMapTests() { - super(false, false, false, false, false); - } - - @Override - protected SortedMap makeEmptyMap() { - throw new UnsupportedOperationException(); - } - - private static final Joiner joiner = Joiner.on(", "); + // Creation tests - @Override - protected void assertMoreInvariants(Map map) { - // TODO: can these be moved to MapInterfaceTest? - for (Entry entry : map.entrySet()) { - assertEquals(entry.getKey() + "=" + entry.getValue(), entry.toString()); - } - - assertEquals("{" + joiner.join(map.entrySet()) + "}", map.toString()); - assertEquals("[" + joiner.join(map.entrySet()) + "]", map.entrySet().toString()); - assertEquals("[" + joiner.join(map.keySet()) + "]", map.keySet().toString()); - assertEquals("[" + joiner.join(map.values()) + "]", map.values().toString()); - - assertEquals(Sets.newHashSet(map.entrySet()), map.entrySet()); - assertEquals(Sets.newHashSet(map.keySet()), map.keySet()); - } + public void testEmptyBuilder() { + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder().build(); + assertEquals(Collections.emptyMap(), map); } - public static class MapTests extends AbstractMapTests { - @Override - protected SortedMap makeEmptyMap() { - return ImmutableSortedMap.of(); - } - - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("one", 1, "two", 2, "three", 3); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testSingletonBuilder() { + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder().put("one", 1).build(); + assertMapEquals(map, "one", 1); } - public static class SingletonMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("one", 1); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + public void testBuilder() { + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder() + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .build(); + assertMapEquals(map, "five", 5, "four", 4, "one", 1, "three", 3, "two", 2); } - @GwtIncompatible // SerializableTester - public static class ReserializedMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return SerializableTester.reserialize(ImmutableSortedMap.of("one", 1, "two", 2, "three", 3)); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "minus one"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return -1; - } + @SuppressWarnings("DoNotCall") + public void testBuilder_orderEntriesByValueFails() { + ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows( + UnsupportedOperationException.class, () -> builder.orderEntriesByValue(Ordering.natural())); } - public static class HeadMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).headMap("d"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "d"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return 4; - } + public void testBuilder_withImmutableEntry() { + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder().put(immutableEntry("one", 1)).build(); + assertMapEquals(map, "one", 1); } - public static class HeadMapInclusiveTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).headMap("c", true); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "d"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return 4; - } + public void testBuilder_withImmutableEntryAndNullContents() { + Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows( + NullPointerException.class, () -> builder.put(immutableEntry("one", (Integer) null))); + assertThrows(NullPointerException.class, () -> builder.put(immutableEntry((String) null, 1))); } - public static class TailMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).tailMap("b"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "a"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return 1; - } + private static class StringHolder { + @Nullable String string; } - public static class TailExclusiveMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).tailMap("a", false); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "a"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return 1; - } + public void testBuilder_withMutableEntry() { + ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); + final StringHolder holder = new StringHolder(); + holder.string = "one"; + Entry entry = + new AbstractMapEntry() { + @Override + public String getKey() { + return holder.string; + } + + @Override + public Integer getValue() { + return 1; + } + }; + + builder.put(entry); + holder.string = "two"; + assertMapEquals(builder.build(), "one", 1); } - public static class SubMapTests extends AbstractMapTests { - @Override - protected SortedMap makePopulatedMap() { - return ImmutableSortedMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5).subMap("b", "d"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "a"; - } - - @Override - protected Integer getValueNotInPopulatedMap() { - return 4; - } + public void testBuilderPutAllWithEmptyMap() { + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder() + .putAll(Collections.emptyMap()) + .build(); + assertEquals(Collections.emptyMap(), map); } - public static class CreationTests extends TestCase { - public void testEmptyBuilder() { - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder().build(); - assertEquals(Collections.emptyMap(), map); - } - - public void testSingletonBuilder() { - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder().put("one", 1).build(); - assertMapEquals(map, "one", 1); - } - - public void testBuilder() { - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder() - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .build(); - assertMapEquals(map, "five", 5, "four", 4, "one", 1, "three", 3, "two", 2); - } - - @SuppressWarnings("DoNotCall") - public void testBuilder_orderEntriesByValueFails() { - ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.orderEntriesByValue(Ordering.natural()); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } - } + public void testBuilderPutAll() { + Map toPut = new LinkedHashMap<>(); + toPut.put("one", 1); + toPut.put("two", 2); + toPut.put("three", 3); + Map moreToPut = new LinkedHashMap<>(); + moreToPut.put("four", 4); + moreToPut.put("five", 5); + + ImmutableSortedMap map = + ImmutableSortedMap.naturalOrder().putAll(toPut).putAll(moreToPut).build(); + assertMapEquals(map, "five", 5, "four", 4, "one", 1, "three", 3, "two", 2); + } - public void testBuilder_withImmutableEntry() { - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder() - .put(Maps.immutableEntry("one", 1)) - .build(); - assertMapEquals(map, "one", 1); - } + public void testBuilderReuse() { + Builder builder = ImmutableSortedMap.naturalOrder(); + ImmutableSortedMap mapOne = builder.put("one", 1).put("two", 2).build(); + ImmutableSortedMap mapTwo = builder.put("three", 3).put("four", 4).build(); - public void testBuilder_withImmutableEntryAndNullContents() { - Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.put(Maps.immutableEntry("one", (Integer) null)); - fail(); - } catch (NullPointerException expected) { - } - try { - builder.put(Maps.immutableEntry((String) null, 1)); - fail(); - } catch (NullPointerException expected) { - } - } - - private static class StringHolder { - String string; - } + assertMapEquals(mapOne, "one", 1, "two", 2); + assertMapEquals(mapTwo, "four", 4, "one", 1, "three", 3, "two", 2); + } - public void testBuilder_withMutableEntry() { - ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); - final StringHolder holder = new StringHolder(); - holder.string = "one"; - Entry entry = - new AbstractMapEntry() { - @Override - public String getKey() { - return holder.string; - } - - @Override - public Integer getValue() { - return 1; - } - }; - - builder.put(entry); - holder.string = "two"; - assertMapEquals(builder.build(), "one", 1); - } + public void testBuilderPutNullKey() { + Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows(NullPointerException.class, () -> builder.put(null, 1)); + } - public void testBuilderPutAllWithEmptyMap() { - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder() - .putAll(Collections.emptyMap()) - .build(); - assertEquals(Collections.emptyMap(), map); - } + public void testBuilderPutNullValue() { + Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows(NullPointerException.class, () -> builder.put("one", null)); + } - public void testBuilderPutAll() { - Map toPut = new LinkedHashMap<>(); - toPut.put("one", 1); - toPut.put("two", 2); - toPut.put("three", 3); - Map moreToPut = new LinkedHashMap<>(); - moreToPut.put("four", 4); - moreToPut.put("five", 5); - - ImmutableSortedMap map = - ImmutableSortedMap.naturalOrder() - .putAll(toPut) - .putAll(moreToPut) - .build(); - assertMapEquals(map, "five", 5, "four", 4, "one", 1, "three", 3, "two", 2); - } + public void testBuilderPutNullKeyViaPutAll() { + Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap(null, 1))); + } - public void testBuilderReuse() { - Builder builder = ImmutableSortedMap.naturalOrder(); - ImmutableSortedMap mapOne = builder.put("one", 1).put("two", 2).build(); - ImmutableSortedMap mapTwo = builder.put("three", 3).put("four", 4).build(); + public void testBuilderPutNullValueViaPutAll() { + Builder builder = ImmutableSortedMap.naturalOrder(); + assertThrows( + NullPointerException.class, + () -> builder.putAll(Collections.singletonMap("one", null))); + } - assertMapEquals(mapOne, "one", 1, "two", 2); - assertMapEquals(mapTwo, "four", 4, "one", 1, "three", 3, "two", 2); - } + public void testPuttingTheSameKeyTwiceThrowsOnBuild() { + Builder builder = + ImmutableSortedMap.naturalOrder() + .put("one", 1) + .put("one", 2); // throwing on this line would be even better - public void testBuilderPutNullKey() { - Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.put(null, 1); - fail(); - } catch (NullPointerException expected) { - } - } + assertThrows(IllegalArgumentException.class, () -> builder.build()); + } - public void testBuilderPutNullValue() { - Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.put("one", null); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOf() { + assertMapEquals(ImmutableSortedMap.of("one", 1), "one", 1); + assertMapEquals(ImmutableSortedMap.of("one", 1, "two", 2), "one", 1, "two", 2); + assertMapEquals( + ImmutableSortedMap.of("one", 1, "two", 2, "three", 3), "one", 1, "three", 3, "two", 2); + assertMapEquals( + ImmutableSortedMap.of("one", 1, "two", 2, "three", 3, "four", 4), + "four", + 4, + "one", + 1, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), + "five", + 5, + "four", + 4, + "one", + 1, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6), + "five", + 5, + "four", + 4, + "one", + 1, + "six", + 6, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7), + "five", + 5, + "four", + 4, + "one", + 1, + "seven", + 7, + "six", + 6, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8), + "eight", + 8, + "five", + 5, + "four", + 4, + "one", + 1, + "seven", + 7, + "six", + 6, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9), + "eight", + 8, + "five", + 5, + "four", + 4, + "nine", + 9, + "one", + 1, + "seven", + 7, + "six", + 6, + "three", + 3, + "two", + 2); + assertMapEquals( + ImmutableSortedMap.of( + "one", 1, + "two", 2, + "three", 3, + "four", 4, + "five", 5, + "six", 6, + "seven", 7, + "eight", 8, + "nine", 9, + "ten", 10), + "eight", + 8, + "five", + 5, + "four", + 4, + "nine", + 9, + "one", + 1, + "seven", + 7, + "six", + 6, + "ten", + 10, + "three", + 3, + "two", + 2); + } - public void testBuilderPutNullKeyViaPutAll() { - Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.putAll(Collections.singletonMap(null, 1)); - fail(); - } catch (NullPointerException expected) { - } - } + public void testOfNullKey() { + Integer n = null; + assertThrows(NullPointerException.class, () -> ImmutableSortedMap.of(n, 1)); - public void testBuilderPutNullValueViaPutAll() { - Builder builder = ImmutableSortedMap.naturalOrder(); - try { - builder.putAll(Collections.singletonMap("one", null)); - fail(); - } catch (NullPointerException expected) { - } - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMap.of("one", 1, null, 2)); + } - public void testPuttingTheSameKeyTwiceThrowsOnBuild() { - Builder builder = - ImmutableSortedMap.naturalOrder() - .put("one", 1) - .put("one", 2); // throwing on this line would be even better + public void testOfNullValue() { + assertThrows(NullPointerException.class, () -> ImmutableSortedMap.of("one", null)); - try { - builder.build(); - fail(); - } catch (IllegalArgumentException expected) { - } - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMap.of("one", 1, "two", null)); + } - public void testOf() { - assertMapEquals(ImmutableSortedMap.of("one", 1), "one", 1); - assertMapEquals(ImmutableSortedMap.of("one", 1, "two", 2), "one", 1, "two", 2); - assertMapEquals( - ImmutableSortedMap.of("one", 1, "two", 2, "three", 3), "one", 1, "three", 3, "two", 2); - assertMapEquals( - ImmutableSortedMap.of("one", 1, "two", 2, "three", 3, "four", 4), - "four", - 4, - "one", - 1, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of("one", 1, "two", 2, "three", 3, "four", 4, "five", 5), - "five", - 5, - "four", - 4, - "one", - 1, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6), - "five", - 5, - "four", - 4, - "one", - 1, - "six", - 6, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7), - "five", - 5, - "four", - 4, - "one", - 1, - "seven", - 7, - "six", - 6, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8), - "eight", - 8, - "five", - 5, - "four", - 4, - "one", - 1, - "seven", - 7, - "six", - 6, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9), - "eight", - 8, - "five", - 5, - "four", - 4, - "nine", - 9, - "one", - 1, - "seven", - 7, - "six", - 6, - "three", - 3, - "two", - 2); - assertMapEquals( - ImmutableSortedMap.of( - "one", 1, - "two", 2, - "three", 3, - "four", 4, - "five", 5, - "six", 6, - "seven", 7, - "eight", 8, - "nine", 9, - "ten", 10), - "eight", - 8, - "five", - 5, - "four", - 4, - "nine", - 9, - "one", - 1, - "seven", - 7, - "six", - 6, - "ten", - 10, - "three", - 3, - "two", - 2); - } + @SuppressWarnings("DistinctVarargsChecker") + public void testOfWithDuplicateKey() { + assertThrows(IllegalArgumentException.class, () -> ImmutableSortedMap.of("one", 1, "one", 1)); + } - public void testOfNullKey() { - Integer n = null; - try { - ImmutableSortedMap.of(n, 1); - fail(); - } catch (NullPointerException expected) { - } + public void testCopyOfEmptyMap() { + ImmutableSortedMap copy = + ImmutableSortedMap.copyOf(Collections.emptyMap()); + assertEquals(Collections.emptyMap(), copy); + assertSame(copy, ImmutableSortedMap.copyOf(copy)); + assertSame(Ordering.natural(), copy.comparator()); + } - try { - ImmutableSortedMap.of("one", 1, null, 2); - fail(); - } catch (NullPointerException expected) { - } - } + public void testCopyOfSingletonMap() { + ImmutableSortedMap copy = ImmutableSortedMap.copyOf(singletonMap("one", 1)); + assertMapEquals(copy, "one", 1); + assertSame(copy, ImmutableSortedMap.copyOf(copy)); + assertSame(Ordering.natural(), copy.comparator()); + } - public void testOfNullValue() { - try { - ImmutableSortedMap.of("one", null); - fail(); - } catch (NullPointerException expected) { - } + public void testCopyOf() { + Map original = new LinkedHashMap<>(); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); - try { - ImmutableSortedMap.of("one", 1, "two", null); - fail(); - } catch (NullPointerException expected) { - } - } + ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original); + assertMapEquals(copy, "one", 1, "three", 3, "two", 2); + assertSame(copy, ImmutableSortedMap.copyOf(copy)); + assertSame(Ordering.natural(), copy.comparator()); + } - public void testOfWithDuplicateKey() { - try { - ImmutableSortedMap.of("one", 1, "one", 1); - fail(); - } catch (IllegalArgumentException expected) { - } - } + public void testCopyOfExplicitComparator() { + Comparator comparator = Ordering.natural().reverse(); + Map original = new LinkedHashMap<>(); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); + + ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original, comparator); + assertMapEquals(copy, "two", 2, "three", 3, "one", 1); + assertSame(copy, ImmutableSortedMap.copyOf(copy, comparator)); + assertSame(comparator, copy.comparator()); + } - public void testCopyOfEmptyMap() { - ImmutableSortedMap copy = - ImmutableSortedMap.copyOf(Collections.emptyMap()); - assertEquals(Collections.emptyMap(), copy); - assertSame(copy, ImmutableSortedMap.copyOf(copy)); - assertSame(Ordering.natural(), copy.comparator()); - } + public void testCopyOfImmutableSortedSetDifferentComparator() { + Comparator comparator = Ordering.natural().reverse(); + Map original = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3); + ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original, comparator); + assertMapEquals(copy, "two", 2, "three", 3, "one", 1); + assertSame(copy, ImmutableSortedMap.copyOf(copy, comparator)); + assertSame(comparator, copy.comparator()); + } - public void testCopyOfSingletonMap() { - ImmutableSortedMap copy = - ImmutableSortedMap.copyOf(Collections.singletonMap("one", 1)); - assertMapEquals(copy, "one", 1); - assertSame(copy, ImmutableSortedMap.copyOf(copy)); - assertSame(Ordering.natural(), copy.comparator()); - } + public void testCopyOfSortedNatural() { + SortedMap original = Maps.newTreeMap(); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); - public void testCopyOf() { - Map original = new LinkedHashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); + ImmutableSortedMap copy = ImmutableSortedMap.copyOfSorted(original); + assertMapEquals(copy, "one", 1, "three", 3, "two", 2); + assertSame(copy, ImmutableSortedMap.copyOfSorted(copy)); + assertSame(Ordering.natural(), copy.comparator()); + } - ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original); - assertMapEquals(copy, "one", 1, "three", 3, "two", 2); - assertSame(copy, ImmutableSortedMap.copyOf(copy)); - assertSame(Ordering.natural(), copy.comparator()); - } + public void testCopyOfSortedExplicit() { + Comparator comparator = Ordering.natural().reverse(); + SortedMap original = Maps.newTreeMap(comparator); + original.put("one", 1); + original.put("two", 2); + original.put("three", 3); + + ImmutableSortedMap copy = ImmutableSortedMap.copyOfSorted(original); + assertMapEquals(copy, "two", 2, "three", 3, "one", 1); + assertSame(copy, ImmutableSortedMap.copyOfSorted(copy)); + assertSame(comparator, copy.comparator()); + } - public void testCopyOfExplicitComparator() { - Comparator comparator = Ordering.natural().reverse(); - Map original = new LinkedHashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); - - ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original, comparator); - assertMapEquals(copy, "two", 2, "three", 3, "one", 1); - assertSame(copy, ImmutableSortedMap.copyOf(copy, comparator)); - assertSame(comparator, copy.comparator()); - } + private static class IntegerDiv10 implements Comparable { + final int value; - public void testCopyOfImmutableSortedSetDifferentComparator() { - Comparator comparator = Ordering.natural().reverse(); - Map original = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3); - ImmutableSortedMap copy = ImmutableSortedMap.copyOf(original, comparator); - assertMapEquals(copy, "two", 2, "three", 3, "one", 1); - assertSame(copy, ImmutableSortedMap.copyOf(copy, comparator)); - assertSame(comparator, copy.comparator()); + IntegerDiv10(int value) { + this.value = value; } - public void testCopyOfSortedNatural() { - SortedMap original = Maps.newTreeMap(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); - - ImmutableSortedMap copy = ImmutableSortedMap.copyOfSorted(original); - assertMapEquals(copy, "one", 1, "three", 3, "two", 2); - assertSame(copy, ImmutableSortedMap.copyOfSorted(copy)); - assertSame(Ordering.natural(), copy.comparator()); + @Override + public int compareTo(IntegerDiv10 o) { + return value / 10 - o.value / 10; } - public void testCopyOfSortedExplicit() { - Comparator comparator = Ordering.natural().reverse(); - SortedMap original = Maps.newTreeMap(comparator); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); - - ImmutableSortedMap copy = ImmutableSortedMap.copyOfSorted(original); - assertMapEquals(copy, "two", 2, "three", 3, "one", 1); - assertSame(copy, ImmutableSortedMap.copyOfSorted(copy)); - assertSame(comparator, copy.comparator()); + @Override + public String toString() { + return Integer.toString(value); } + } - private static class IntegerDiv10 implements Comparable { - final int value; - - IntegerDiv10(int value) { - this.value = value; - } - - @Override - public int compareTo(IntegerDiv10 o) { - return value / 10 - o.value / 10; - } - - @Override - public String toString() { - return Integer.toString(value); - } - } + public void testCopyOfDuplicateKey() { + Map original = + ImmutableMap.of( + new IntegerDiv10(3), "three", + new IntegerDiv10(20), "twenty", + new IntegerDiv10(11), "eleven", + new IntegerDiv10(35), "thirty five", + new IntegerDiv10(12), "twelve"); - public void testCopyOfDuplicateKey() { - Map original = - ImmutableMap.of( - new IntegerDiv10(3), "three", - new IntegerDiv10(20), "twenty", - new IntegerDiv10(11), "eleven", - new IntegerDiv10(35), "thirty five", - new IntegerDiv10(12), "twelve"); - - try { - ImmutableSortedMap.copyOf(original); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - } + assertThrows(IllegalArgumentException.class, () -> ImmutableSortedMap.copyOf(original)); + } - public void testImmutableMapCopyOfImmutableSortedMap() { - IntegerDiv10 three = new IntegerDiv10(3); - IntegerDiv10 eleven = new IntegerDiv10(11); - IntegerDiv10 twelve = new IntegerDiv10(12); - IntegerDiv10 twenty = new IntegerDiv10(20); - Map original = - ImmutableSortedMap.of(three, "three", eleven, "eleven", twenty, "twenty"); - Map copy = ImmutableMap.copyOf(original); - assertTrue(original.containsKey(twelve)); - assertFalse(copy.containsKey(twelve)); - } + public void testImmutableMapCopyOfImmutableSortedMap() { + IntegerDiv10 three = new IntegerDiv10(3); + IntegerDiv10 eleven = new IntegerDiv10(11); + IntegerDiv10 twelve = new IntegerDiv10(12); + IntegerDiv10 twenty = new IntegerDiv10(20); + Map original = + ImmutableSortedMap.of(three, "three", eleven, "eleven", twenty, "twenty"); + Map copy = ImmutableMap.copyOf(original); + assertTrue(original.containsKey(twelve)); + assertFalse(copy.containsKey(twelve)); + } - public void testBuilderReverseOrder() { - ImmutableSortedMap map = - ImmutableSortedMap.reverseOrder() - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .build(); - assertMapEquals(map, "two", 2, "three", 3, "one", 1, "four", 4, "five", 5); - assertEquals(Ordering.natural().reverse(), map.comparator()); - } + public void testBuilderReverseOrder() { + ImmutableSortedMap map = + ImmutableSortedMap.reverseOrder() + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .build(); + assertMapEquals(map, "two", 2, "three", 3, "one", 1, "four", 4, "five", 5); + assertEquals(Ordering.natural().reverse(), map.comparator()); + } - public void testBuilderComparator() { - Comparator comparator = Ordering.natural().reverse(); - ImmutableSortedMap map = - new ImmutableSortedMap.Builder(comparator) - .put("one", 1) - .put("two", 2) - .put("three", 3) - .put("four", 4) - .put("five", 5) - .build(); - assertMapEquals(map, "two", 2, "three", 3, "one", 1, "four", 4, "five", 5); - assertSame(comparator, map.comparator()); - } + public void testBuilderComparator() { + Comparator comparator = Ordering.natural().reverse(); + ImmutableSortedMap map = + new ImmutableSortedMap.Builder(comparator) + .put("one", 1) + .put("two", 2) + .put("three", 3) + .put("four", 4) + .put("five", 5) + .build(); + assertMapEquals(map, "two", 2, "three", 3, "one", 1, "four", 4, "five", 5); + assertSame(comparator, map.comparator()); + } - // TODO(b/172823566): Use mainline testToImmutableSortedMap once CollectorTester is usable. - public void testToImmutableSortedMap_java7_combine() { - ImmutableSortedMap.Builder zis = - ImmutableSortedMap.naturalOrder().put("one", 1).put("four", 4); - ImmutableSortedMap.Builder zat = - ImmutableSortedMap.naturalOrder().put("two", 2).put("three", 3); - ImmutableSortedMap sortedMap = zis.combine(zat).build(); - assertMapEquals(sortedMap, "four", 4, "one", 1, "three", 3, "two", 2); - } + // TODO(b/172823566): Use mainline testToImmutableSortedMap once CollectorTester is usable. + public void testToImmutableSortedMap_java7_combine() { + ImmutableSortedMap.Builder zis = + ImmutableSortedMap.naturalOrder().put("one", 1).put("four", 4); + ImmutableSortedMap.Builder zat = + ImmutableSortedMap.naturalOrder().put("two", 2).put("three", 3); + ImmutableSortedMap sortedMap = zis.combine(zat).build(); + assertMapEquals(sortedMap, "four", 4, "one", 1, "three", 3, "two", 2); + } - // TODO(b/172823566): Use mainline testToImmutableSortedMap once CollectorTester is usable. - public void testToImmutableSortedMap_exceptionOnDuplicateKey_java7_combine() { - ImmutableSortedMap.Builder zis = - ImmutableSortedMap.naturalOrder().put("one", 1).put("two", 2); - ImmutableSortedMap.Builder zat = - ImmutableSortedMap.naturalOrder().put("two", 22).put("three", 3); - try { - ImmutableSortedMap.Builder combined = zis.combine(zat); - combined.build(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // expected - } - } + // TODO(b/172823566): Use mainline testToImmutableSortedMap once CollectorTester is usable. + public void testToImmutableSortedMap_exceptionOnDuplicateKey_java7_combine() { + ImmutableSortedMap.Builder zis = + ImmutableSortedMap.naturalOrder().put("one", 1).put("two", 2); + ImmutableSortedMap.Builder zat = + ImmutableSortedMap.naturalOrder().put("two", 22).put("three", 3); + ImmutableSortedMap.Builder combined = zis.combine(zat); + assertThrows(IllegalArgumentException.class, () -> combined.build()); } + // Other tests + public void testNullGet() { ImmutableSortedMap map = ImmutableSortedMap.of("one", 1); assertNull(map.get(null)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -843,16 +624,14 @@ public void testNullPointers() { public void testNullValuesInCopyOfMap() { for (int i = 1; i <= 10; i++) { for (int j = 0; j < i; j++) { - Map source = new TreeMap<>(); + Map source = new TreeMap<>(); for (int k = 0; k < i; k++) { source.put(k, k); } source.put(j, null); - try { - ImmutableSortedMap.copyOf(source); - fail("Expected NullPointerException in copyOf(" + source + ")"); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> ImmutableSortedMap.copyOf((Map) source)); } } } @@ -860,27 +639,24 @@ public void testNullValuesInCopyOfMap() { public void testNullValuesInCopyOfEntries() { for (int i = 1; i <= 10; i++) { for (int j = 0; j < i; j++) { - Map source = new TreeMap<>(); + Map source = new TreeMap<>(); for (int k = 0; k < i; k++) { source.put(k, k); } source.put(j, null); - try { - ImmutableSortedMap.copyOf(source.entrySet()); - fail("Expected NullPointerException in copyOf(" + source.entrySet() + ")"); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> ImmutableSortedMap.copyOf((Set>) source.entrySet())); } } } private static void assertMapEquals(Map map, Object... alternatingKeysAndValues) { - assertEquals(map.size(), alternatingKeysAndValues.length / 2); - int i = 0; - for (Entry entry : map.entrySet()) { - assertEquals(alternatingKeysAndValues[i++], entry.getKey()); - assertEquals(alternatingKeysAndValues[i++], entry.getValue()); + Map expected = new LinkedHashMap<>(); + for (int i = 0; i < alternatingKeysAndValues.length; i += 2) { + expected.put(alternatingKeysAndValues[i], alternatingKeysAndValues[i + 1]); } + assertThat(map).containsExactlyEntriesIn(expected).inOrder(); } private static class IntHolder implements Serializable { @@ -891,7 +667,7 @@ public IntHolder(int value) { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof IntHolder) && ((IntHolder) o).value == value; } @@ -900,7 +676,7 @@ public int hashCode() { return value; } - private static final long serialVersionUID = 5; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 5; } public void testMutableValues() { @@ -908,12 +684,13 @@ public void testMutableValues() { IntHolder holderB = new IntHolder(2); Map map = ImmutableSortedMap.of("a", holderA, "b", holderB); holderA.value = 3; - assertTrue(map.entrySet().contains(Maps.immutableEntry("a", new IntHolder(3)))); + assertTrue(map.entrySet().contains(immutableEntry("a", new IntHolder(3)))); Map intMap = ImmutableSortedMap.of("a", 3, "b", 2); assertEquals(intMap.hashCode(), map.entrySet().hashCode()); assertEquals(intMap.hashCode(), map.hashCode()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testViewSerialization() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3); @@ -924,72 +701,62 @@ public void testViewSerialization() { Lists.newArrayList(SerializableTester.reserialize(map.values()))); } - @SuppressWarnings("unchecked") // varargs public void testHeadMapInclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).headMap("three", true); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("one", 1), Maps.immutableEntry("three", 3)) + .containsExactly(immutableEntry("one", 1), immutableEntry("three", 3)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs public void testHeadMapExclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).headMap("three", false); - assertThat(map.entrySet()).containsExactly(Maps.immutableEntry("one", 1)); + assertThat(map.entrySet()).containsExactly(immutableEntry("one", 1)); } - @SuppressWarnings("unchecked") // varargs public void testTailMapInclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).tailMap("three", true); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("three", 3), Maps.immutableEntry("two", 2)) + .containsExactly(immutableEntry("three", 3), immutableEntry("two", 2)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs public void testTailMapExclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).tailMap("three", false); - assertThat(map.entrySet()).containsExactly(Maps.immutableEntry("two", 2)); + assertThat(map.entrySet()).containsExactly(immutableEntry("two", 2)); } - @SuppressWarnings("unchecked") // varargs public void testSubMapExclusiveExclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).subMap("one", false, "two", false); - assertThat(map.entrySet()).containsExactly(Maps.immutableEntry("three", 3)); + assertThat(map.entrySet()).containsExactly(immutableEntry("three", 3)); } - @SuppressWarnings("unchecked") // varargs public void testSubMapInclusiveExclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).subMap("one", true, "two", false); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("one", 1), Maps.immutableEntry("three", 3)) + .containsExactly(immutableEntry("one", 1), immutableEntry("three", 3)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs public void testSubMapExclusiveInclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).subMap("one", false, "two", true); assertThat(map.entrySet()) - .containsExactly(Maps.immutableEntry("three", 3), Maps.immutableEntry("two", 2)) + .containsExactly(immutableEntry("three", 3), immutableEntry("two", 2)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs public void testSubMapInclusiveInclusive() { Map map = ImmutableSortedMap.of("one", 1, "two", 2, "three", 3).subMap("one", true, "two", true); assertThat(map.entrySet()) .containsExactly( - Maps.immutableEntry("one", 1), - Maps.immutableEntry("three", 3), - Maps.immutableEntry("two", 2)) + immutableEntry("one", 1), immutableEntry("three", 3), immutableEntry("two", 2)) .inOrder(); } @@ -1000,21 +767,21 @@ public int compareTo(SelfComparableExample o) { } } - public void testBuilderGenerics_SelfComparable() { - ImmutableSortedMap.Builder natural = + public void testBuilderGenerics_selfComparable() { + ImmutableSortedMap.Builder unusedNatural = ImmutableSortedMap.naturalOrder(); - ImmutableSortedMap.Builder reverse = + ImmutableSortedMap.Builder unusedReverse = ImmutableSortedMap.reverseOrder(); } private static class SuperComparableExample extends SelfComparableExample {} - public void testBuilderGenerics_SuperComparable() { - ImmutableSortedMap.Builder natural = + public void testBuilderGenerics_superComparable() { + ImmutableSortedMap.Builder unusedNatural = ImmutableSortedMap.naturalOrder(); - ImmutableSortedMap.Builder reverse = + ImmutableSortedMap.Builder unusedReverse = ImmutableSortedMap.reverseOrder(); } } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMultisetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMultisetTest.java index cda23ddf27b5..023f8877ba6a 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSortedMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedMultisetTest.java @@ -15,10 +15,13 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Iterators.singletonIterator; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Function; import com.google.common.collect.ImmutableSortedMultiset.Builder; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.MinimalCollection; @@ -39,13 +42,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ImmutableSortedMultiset}. * * @author Louis Wasserman */ +@NullUnmarked public class ImmutableSortedMultisetTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ImmutableSortedMultisetTest.class); @@ -95,7 +101,7 @@ public List order(List insertionOrder) { new TestStringListGenerator() { @Override protected List create(String[] elements) { - Set set = Sets.newHashSet(); + Set set = newHashSet(); ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); for (String s : elements) { @@ -175,15 +181,7 @@ public void testCreation_arrayOfOneElement() { public void testCreation_arrayOfArray() { Comparator comparator = - Ordering.natural() - .lexicographical() - .onResultOf( - new Function>() { - @Override - public Iterable apply(String[] input) { - return Arrays.asList(input); - } - }); + Ordering.natural().lexicographical().onResultOf(Arrays::asList); String[] array = new String[] {"a"}; Multiset multiset = ImmutableSortedMultiset.orderedBy(comparator).add(array).build(); Multiset expected = HashMultiset.create(); @@ -193,16 +191,11 @@ public Iterable apply(String[] input) { public void testCreation_arrayContainingOnlyNull() { String[] array = new String[] {null}; - try { - ImmutableSortedMultiset.copyOf(array); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMultiset.copyOf(array)); } public void testCopyOf_collection_empty() { - // "" is required to work around a javac 1.5 bug. - Collection c = MinimalCollection.of(); + Collection c = MinimalCollection.of(); Multiset multiset = ImmutableSortedMultiset.copyOf(c); assertTrue(multiset.isEmpty()); } @@ -221,11 +214,7 @@ public void testCopyOf_collection_general() { public void testCopyOf_collectionContainingNull() { Collection c = MinimalCollection.of("a", null, "b"); - try { - ImmutableSortedMultiset.copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMultiset.copyOf(c)); } public void testCopyOf_multiset_empty() { @@ -248,21 +237,17 @@ public void testCopyOf_multiset_general() { public void testCopyOf_multisetContainingNull() { Multiset c = HashMultiset.create(asList("a", null, "b")); - try { - ImmutableSortedMultiset.copyOf(c); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMultiset.copyOf(c)); } public void testCopyOf_iterator_empty() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); Multiset multiset = ImmutableSortedMultiset.copyOf(iterator); assertTrue(multiset.isEmpty()); } public void testCopyOf_iterator_oneElement() { - Iterator iterator = Iterators.singletonIterator("a"); + Iterator iterator = singletonIterator("a"); Multiset multiset = ImmutableSortedMultiset.copyOf(iterator); assertEquals(HashMultiset.create(asList("a")), multiset); } @@ -275,11 +260,7 @@ public void testCopyOf_iterator_general() { public void testCopyOf_iteratorContainingNull() { Iterator iterator = asList("a", null, "b").iterator(); - try { - ImmutableSortedMultiset.copyOf(iterator); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ImmutableSortedMultiset.copyOf(iterator)); } private static class CountingIterable implements Iterable { @@ -403,73 +384,47 @@ public void testBuilderSetCountThenAdd() { public void testBuilderAddHandlesNullsCorrectly() { ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); - try { - builder.add((String) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.add((String) null)); } public void testBuilderAddAllHandlesNullsCorrectly() { - ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); - try { - builder.addAll((Collection) null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); + assertThrows(NullPointerException.class, () -> builder.addAll((Collection) null)); } - builder = ImmutableSortedMultiset.naturalOrder(); - List listWithNulls = asList("a", null, "b"); - try { - builder.addAll(listWithNulls); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); + List listWithNulls = asList("a", null, "b"); + assertThrows(NullPointerException.class, () -> builder.addAll(listWithNulls)); } - builder = ImmutableSortedMultiset.naturalOrder(); - Multiset multisetWithNull = LinkedHashMultiset.create(asList("a", null, "b")); - try { - builder.addAll(multisetWithNull); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { + { + ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); + Multiset multisetWithNull = LinkedHashMultiset.create(asList("a", null, "b")); + assertThrows(NullPointerException.class, () -> builder.addAll(multisetWithNull)); } } public void testBuilderAddCopiesHandlesNullsCorrectly() { ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); - try { - builder.addCopies(null, 2); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.addCopies(null, 2)); } public void testBuilderAddCopiesIllegal() { ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); - try { - builder.addCopies("a", -2); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.addCopies("a", -2)); } public void testBuilderSetCountHandlesNullsCorrectly() { ImmutableSortedMultiset.Builder builder = new ImmutableSortedMultiset.Builder<>(Ordering.natural().nullsFirst()); - try { - builder.setCount(null, 2); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> builder.setCount(null, 2)); } public void testBuilderSetCountIllegal() { ImmutableSortedMultiset.Builder builder = ImmutableSortedMultiset.naturalOrder(); - try { - builder.setCount("a", -2); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.setCount("a", -2)); } public void testNullPointers() { diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java index ea0e3a8d16bc..5a8efba271e3 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableSortedSetTest.java @@ -16,11 +16,19 @@ package com.google.common.collect; +import static com.google.common.collect.Comparators.isInOrder; +import static com.google.common.collect.Iterables.elementsEqual; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.min; import static java.util.Arrays.asList; +import static java.util.Arrays.sort; +import static java.util.Collections.emptyList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; import com.google.common.collect.testing.features.CollectionFeature; @@ -37,7 +45,6 @@ import com.google.common.collect.testing.testers.SetHashCodeTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -48,6 +55,7 @@ import java.util.TreeSet; import junit.framework.Test; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@link ImmutableSortedSet}. @@ -55,9 +63,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ImmutableSortedSetTest extends AbstractImmutableSetTest { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -201,7 +212,6 @@ protected > SortedSet of(E e1, E e2, E e3, E return ImmutableSortedSet.of(e1, e2, e3, e4, e5); } - @SuppressWarnings("unchecked") @Override protected > SortedSet of( E e1, E e2, E e3, E e4, E e5, E e6, E... rest) { @@ -229,6 +239,7 @@ protected > SortedSet copyOf(Iterator set = of(); - try { - set.first(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> set.first()); } public void testEmpty_last() { SortedSet set = of(); - try { - set.last(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> set.last()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testEmpty_serialization() { SortedSet set = of(); @@ -288,8 +292,8 @@ public void testSingle_headSet() { SortedSet set = of("e"); assertTrue(set.headSet("g") instanceof ImmutableSortedSet); assertThat(set.headSet("g")).contains("e"); - assertSame(of(), set.headSet("c")); - assertSame(of(), set.headSet("e")); + assertSame(this.of(), set.headSet("c")); + assertSame(this.of(), set.headSet("e")); } public void testSingle_tailSet() { @@ -297,7 +301,7 @@ public void testSingle_tailSet() { assertTrue(set.tailSet("c") instanceof ImmutableSortedSet); assertThat(set.tailSet("c")).contains("e"); assertThat(set.tailSet("e")).contains("e"); - assertSame(of(), set.tailSet("g")); + assertSame(this.of(), set.tailSet("g")); } public void testSingle_subSet() { @@ -305,9 +309,9 @@ public void testSingle_subSet() { assertTrue(set.subSet("c", "g") instanceof ImmutableSortedSet); assertThat(set.subSet("c", "g")).contains("e"); assertThat(set.subSet("e", "g")).contains("e"); - assertSame(of(), set.subSet("f", "g")); - assertSame(of(), set.subSet("c", "e")); - assertSame(of(), set.subSet("c", "d")); + assertSame(this.of(), set.subSet("f", "g")); + assertSame(this.of(), set.subSet("c", "e")); + assertSame(this.of(), set.subSet("c", "d")); } public void testSingle_first() { @@ -320,6 +324,7 @@ public void testSingle_last() { assertEquals("e", set.last()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSingle_serialization() { SortedSet set = of("e"); @@ -392,8 +397,8 @@ public void testOf_headSet() { assertTrue(set.headSet("e") instanceof ImmutableSortedSet); assertThat(set.headSet("e")).containsExactly("b", "c", "d").inOrder(); assertThat(set.headSet("g")).containsExactly("b", "c", "d", "e", "f").inOrder(); - assertSame(of(), set.headSet("a")); - assertSame(of(), set.headSet("b")); + assertSame(this.of(), set.headSet("a")); + assertSame(this.of(), set.headSet("b")); } public void testOf_tailSet() { @@ -401,7 +406,7 @@ public void testOf_tailSet() { assertTrue(set.tailSet("e") instanceof ImmutableSortedSet); assertThat(set.tailSet("e")).containsExactly("e", "f").inOrder(); assertThat(set.tailSet("a")).containsExactly("b", "c", "d", "e", "f").inOrder(); - assertSame(of(), set.tailSet("g")); + assertSame(this.of(), set.tailSet("g")); } public void testOf_subSet() { @@ -409,16 +414,13 @@ public void testOf_subSet() { assertTrue(set.subSet("c", "e") instanceof ImmutableSortedSet); assertThat(set.subSet("c", "e")).containsExactly("c", "d").inOrder(); assertThat(set.subSet("a", "g")).containsExactly("b", "c", "d", "e", "f").inOrder(); - assertSame(of(), set.subSet("a", "b")); - assertSame(of(), set.subSet("g", "h")); - assertSame(of(), set.subSet("c", "c")); - try { - set.subSet("e", "c"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertSame(this.of(), set.subSet("a", "b")); + assertSame(this.of(), set.subSet("g", "h")); + assertSame(this.of(), set.subSet("c", "c")); + assertThrows(IllegalArgumentException.class, () -> set.subSet("e", "c")); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testOf_subSetSerialization() { SortedSet set = of("e", "f", "b", "d", "c"); @@ -435,11 +437,12 @@ public void testOf_last() { assertEquals("f", set.last()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testOf_serialization() { SortedSet set = of("e", "f", "b", "d", "c"); SortedSet copy = SerializableTester.reserializeAndAssert(set); - assertTrue(Iterables.elementsEqual(set, copy)); + assertTrue(elementsEqual(set, copy)); assertEquals(set.comparator(), copy.comparator()); } @@ -533,11 +536,7 @@ public void testExplicit_subSet() { assertTrue(set.subSet("", "b").isEmpty()); assertTrue(set.subSet("vermont", "california").isEmpty()); assertTrue(set.subSet("aaa", "zzz").isEmpty()); - try { - set.subSet("quick", "the"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> set.subSet("quick", "the")); } public void testExplicit_first() { @@ -556,6 +555,7 @@ public void testExplicit_last() { assertEquals("jumped", set.last()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testExplicitEmpty_serialization() { SortedSet set = ImmutableSortedSet.orderedBy(STRING_LENGTH).build(); @@ -565,6 +565,7 @@ public void testExplicitEmpty_serialization() { assertSame(set.comparator(), copy.comparator()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testExplicit_serialization() { SortedSet set = @@ -572,7 +573,7 @@ public void testExplicit_serialization() { .add("in", "the", "quick", "jumped", "over", "a") .build(); SortedSet copy = SerializableTester.reserializeAndAssert(set); - assertTrue(Iterables.elementsEqual(set, copy)); + assertTrue(elementsEqual(set, copy)); assertSame(set.comparator(), copy.comparator()); } @@ -738,8 +739,8 @@ public void testEquals_bothDefaultOrdering() { assertEquals(Sets.newTreeSet(asList("a", "b", "c")), set); assertFalse(set.equals(Sets.newTreeSet(asList("a", "b", "d")))); assertFalse(Sets.newTreeSet(asList("a", "b", "d")).equals(set)); - assertFalse(set.equals(Sets.newHashSet(4, 5, 6))); - assertFalse(Sets.newHashSet(4, 5, 6).equals(set)); + assertFalse(set.equals(newHashSet(4, 5, 6))); + assertFalse(newHashSet(4, 5, 6).equals(set)); } public void testEquals_bothExplicitOrdering() { @@ -747,21 +748,21 @@ public void testEquals_bothExplicitOrdering() { assertEquals(Sets.newTreeSet(asList("in", "the", "a")), set); assertFalse(set.equals(Sets.newTreeSet(asList("in", "the", "house")))); assertFalse(Sets.newTreeSet(asList("in", "the", "house")).equals(set)); - assertFalse(set.equals(Sets.newHashSet(4, 5, 6))); - assertFalse(Sets.newHashSet(4, 5, 6).equals(set)); + assertFalse(set.equals(newHashSet(4, 5, 6))); + assertFalse(newHashSet(4, 5, 6).equals(set)); Set complex = Sets.newTreeSet(STRING_LENGTH); Collections.addAll(complex, "in", "the", "a"); assertEquals(set, complex); } - public void testEquals_bothDefaultOrdering_StringVsInt() { + public void testEquals_bothDefaultOrdering_stringVsInt() { SortedSet set = of("a", "b", "c"); assertFalse(set.equals(Sets.newTreeSet(asList(4, 5, 6)))); assertNotEqualLenient(Sets.newTreeSet(asList(4, 5, 6)), set); } - public void testEquals_bothExplicitOrdering_StringVsInt() { + public void testEquals_bothExplicitOrdering_stringVsInt() { SortedSet set = of("in", "the", "a"); assertFalse(set.equals(Sets.newTreeSet(asList(4, 5, 6)))); assertNotEqualLenient(Sets.newTreeSet(asList(4, 5, 6)), set); @@ -769,7 +770,7 @@ public void testEquals_bothExplicitOrdering_StringVsInt() { public void testContainsAll_notSortedSet() { SortedSet set = of("a", "b", "f"); - assertTrue(set.containsAll(Collections.emptyList())); + assertTrue(set.containsAll(emptyList())); assertTrue(set.containsAll(asList("b"))); assertTrue(set.containsAll(asList("b", "b"))); assertTrue(set.containsAll(asList("b", "f"))); @@ -793,7 +794,7 @@ public void testContainsAll_sameComparator() { } @SuppressWarnings("CollectionIncompatibleType") // testing incompatible types - public void testContainsAll_sameComparator_StringVsInt() { + public void testContainsAll_sameComparator_stringVsInt() { SortedSet set = of("a", "b", "f"); SortedSet unexpected = Sets.newTreeSet(Ordering.natural()); unexpected.addAll(asList(1, 2, 3)); @@ -814,6 +815,7 @@ public void testContainsAll_differentComparator() { assertFalse(set.containsAll(Sets.newTreeSet(asList("f", "d", "a")))); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testDifferentComparator_serialization() { // don't use Collections.reverseOrder(); it didn't reserialize to the same instance in JDK5 @@ -821,14 +823,14 @@ public void testDifferentComparator_serialization() { SortedSet set = new ImmutableSortedSet.Builder(comparator).add("a", "b", "c").build(); SortedSet copy = SerializableTester.reserializeAndAssert(set); - assertTrue(Iterables.elementsEqual(set, copy)); + assertTrue(elementsEqual(set, copy)); assertEquals(set.comparator(), copy.comparator()); } public void testReverseOrder() { SortedSet set = ImmutableSortedSet.reverseOrder().add("a", "b", "c").build(); assertThat(set).containsExactly("c", "b", "a").inOrder(); - assertTrue(Comparators.isInOrder(Arrays.asList("c", "b", "a"), set.comparator())); + assertTrue(isInOrder(asList("c", "b", "a"), set.comparator())); } private static final Comparator TO_STRING = @@ -878,17 +880,16 @@ public void testLegacyComparable_of() { public void testLegacyComparable_copyOf_collection() { ImmutableSortedSet set = ImmutableSortedSet.copyOf(LegacyComparable.VALUES_BACKWARD); - assertTrue(Iterables.elementsEqual(LegacyComparable.VALUES_FORWARD, set)); + assertTrue(elementsEqual(LegacyComparable.VALUES_FORWARD, set)); } public void testLegacyComparable_copyOf_iterator() { ImmutableSortedSet set = ImmutableSortedSet.copyOf(LegacyComparable.VALUES_BACKWARD.iterator()); - assertTrue(Iterables.elementsEqual(LegacyComparable.VALUES_FORWARD, set)); + assertTrue(elementsEqual(LegacyComparable.VALUES_FORWARD, set)); } public void testLegacyComparable_builder_natural() { - @SuppressWarnings("unchecked") // Note: IntelliJ wrongly reports an error for this statement ImmutableSortedSet.Builder builder = ImmutableSortedSet.naturalOrder(); @@ -898,11 +899,10 @@ public void testLegacyComparable_builder_natural() { builder.add(LegacyComparable.Y, LegacyComparable.Z); ImmutableSortedSet set = builder.build(); - assertTrue(Iterables.elementsEqual(LegacyComparable.VALUES_FORWARD, set)); + assertTrue(elementsEqual(LegacyComparable.VALUES_FORWARD, set)); } public void testLegacyComparable_builder_reverse() { - @SuppressWarnings("unchecked") // Note: IntelliJ wrongly reports an error for this statement ImmutableSortedSet.Builder builder = ImmutableSortedSet.reverseOrder(); @@ -912,16 +912,12 @@ public void testLegacyComparable_builder_reverse() { builder.add(LegacyComparable.Y, LegacyComparable.Z); ImmutableSortedSet set = builder.build(); - assertTrue(Iterables.elementsEqual(LegacyComparable.VALUES_BACKWARD, set)); + assertTrue(elementsEqual(LegacyComparable.VALUES_BACKWARD, set)); } @SuppressWarnings({"deprecation", "static-access", "DoNotCall"}) public void testBuilderMethod() { - try { - ImmutableSortedSet.builder(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> ImmutableSortedSet.builder()); } public void testAsList() { @@ -931,6 +927,7 @@ public void testAsList() { assertSame(list, ImmutableList.copyOf(set)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester, ImmutableSortedAsList public void testAsListReturnTypeAndSerialization() { ImmutableSet set = ImmutableSortedSet.of("a", "e", "i", "o", "u"); @@ -946,6 +943,7 @@ public void testSubsetAsList() { assertEquals(list, ImmutableList.copyOf(set)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester, ImmutableSortedAsList public void testSubsetAsListReturnTypeAndSerialization() { ImmutableSet set = ImmutableSortedSet.of("a", "e", "i", "o", "u").subSet("c", "r"); @@ -954,7 +952,7 @@ public void testSubsetAsListReturnTypeAndSerialization() { assertEquals(list, copy); } - public void testAsListInconsistentComprator() { + public void testAsListInconsistentComparator() { ImmutableSet set = ImmutableSortedSet.orderedBy(STRING_LENGTH) .add("in", "the", "quick", "jumped", "over", "a") @@ -976,7 +974,7 @@ private static Iterator asIterator(E... elements) { } // In GWT, java.util.TreeSet throws ClassCastException when the comparator - // throws it, unlike JDK6. Therefore, we accept ClassCastException as a + // throws it, unlike the JDK. Therefore, we accept ClassCastException as a // valid result thrown by java.util.TreeSet#equals. private static void assertNotEqualLenient(TreeSet unexpected, SortedSet actual) { try { @@ -988,7 +986,7 @@ private static void assertNotEqualLenient(TreeSet unexpected, SortedSet ac public void testHeadSetInclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { assertThat(set.headSet(strings[i], true)) .containsExactlyElementsIn(sortedNumberNames(0, i + 1)) @@ -999,7 +997,7 @@ public void testHeadSetInclusive() { public void testHeadSetExclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { assertThat(set.headSet(strings[i], false)) .containsExactlyElementsIn(sortedNumberNames(0, i)) @@ -1010,7 +1008,7 @@ public void testHeadSetExclusive() { public void testTailSetInclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { assertThat(set.tailSet(strings[i], true)) .containsExactlyElementsIn(sortedNumberNames(i, strings.length)) @@ -1021,7 +1019,7 @@ public void testTailSetInclusive() { public void testTailSetExclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { assertThat(set.tailSet(strings[i], false)) .containsExactlyElementsIn(sortedNumberNames(i + 1, strings.length)) @@ -1070,11 +1068,11 @@ public void testCeiling_elementAbsent() { public void testSubSetExclusiveExclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { for (int j = i; j < strings.length; j++) { assertThat(set.subSet(strings[i], false, strings[j], false)) - .containsExactlyElementsIn(sortedNumberNames(Math.min(i + 1, j), j)) + .containsExactlyElementsIn(sortedNumberNames(min(i + 1, j), j)) .inOrder(); } } @@ -1083,7 +1081,7 @@ public void testSubSetExclusiveExclusive() { public void testSubSetInclusiveExclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { for (int j = i; j < strings.length; j++) { assertThat(set.subSet(strings[i], true, strings[j], false)) @@ -1096,7 +1094,7 @@ public void testSubSetInclusiveExclusive() { public void testSubSetExclusiveInclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { for (int j = i; j < strings.length; j++) { assertThat(set.subSet(strings[i], false, strings[j], true)) @@ -1109,7 +1107,7 @@ public void testSubSetExclusiveInclusive() { public void testSubSetInclusiveInclusive() { String[] strings = NUMBER_NAMES.toArray(new String[0]); ImmutableSortedSet set = ImmutableSortedSet.copyOf(strings); - Arrays.sort(strings); + sort(strings); for (int i = 0; i < strings.length; i++) { for (int j = i; j < strings.length; j++) { assertThat(set.subSet(strings[i], true, strings[j], true)) @@ -1127,7 +1125,7 @@ private static ImmutableList sortedNumberNames(int i, int j) { ImmutableList.of("one", "two", "three", "four", "five", "six", "seven"); private static final ImmutableList SORTED_NUMBER_NAMES = - Ordering.natural().immutableSortedCopy(NUMBER_NAMES); + Ordering.natural().immutableSortedCopy(NUMBER_NAMES); private static class SelfComparableExample implements Comparable { @Override @@ -1136,7 +1134,7 @@ public int compareTo(SelfComparableExample o) { } } - public void testBuilderGenerics_SelfComparable() { + public void testBuilderGenerics_selfComparable() { // testing simple creation ImmutableSortedSet.Builder natural = ImmutableSortedSet.naturalOrder(); assertThat(natural).isNotNull(); @@ -1146,7 +1144,7 @@ public void testBuilderGenerics_SelfComparable() { private static class SuperComparableExample extends SelfComparableExample {} - public void testBuilderGenerics_SuperComparable() { + public void testBuilderGenerics_superComparable() { // testing simple creation ImmutableSortedSet.Builder natural = ImmutableSortedSet.naturalOrder(); assertThat(natural).isNotNull(); @@ -1178,4 +1176,24 @@ public void testReusedBuilder() { builder.add("baz"); assertTrue(list.array != builder.contents); } + + public void testBuilderAsymptotics() { + int[] compares = {0}; + Comparator countingComparator = + (i, j) -> { + compares[0]++; + return i.compareTo(j); + }; + ImmutableSortedSet.Builder builder = + new ImmutableSortedSet.Builder(countingComparator, 10); + for (int i = 0; i < 9; i++) { + builder.add(i); + } + for (int j = 0; j < 1000; j++) { + builder.add(9); + } + ImmutableSortedSet unused = builder.build(); + assertThat(compares[0]).isAtMost(10000); + // hopefully something quadratic would have more digits + } } diff --git a/android/guava-tests/test/com/google/common/collect/ImmutableTableTest.java b/android/guava-tests/test/com/google/common/collect/ImmutableTableTest.java index 9bdc99cab25f..b194d2abcd05 100644 --- a/android/guava-tests/test/com/google/common/collect/ImmutableTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/ImmutableTableTest.java @@ -16,11 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Tables.immutableCell; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.SerializableTester; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests common methods in {@link ImmutableTable} @@ -28,14 +33,15 @@ * @author Gregory Kick */ @GwtCompatible(emulated = true) -public class ImmutableTableTest extends AbstractTableReadTest { +@NullMarked +public class ImmutableTableTest extends AbstractTableReadTest { @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { ImmutableTable.Builder builder = ImmutableTable.builder(); for (int i = 0; i < data.length; i = i + 3) { builder.put((String) data[i], (Integer) data[i + 1], (Character) data[i + 2]); } - return builder.build(); + return builder.buildOrThrow(); } // TODO(b/172823566): Use mainline testToImmutableMap once CollectorTester is usable to java7. @@ -59,8 +65,8 @@ public void testToImmutableTable_java7_combine() { public void testBuilder() { ImmutableTable.Builder builder = new ImmutableTable.Builder<>(); - assertEquals(ImmutableTable.of(), builder.build()); - assertEquals(ImmutableTable.of('a', 1, "foo"), builder.put('a', 1, "foo").build()); + assertEquals(ImmutableTable.of(), builder.buildOrThrow()); + assertEquals(ImmutableTable.of('a', 1, "foo"), builder.put('a', 1, "foo").buildOrThrow()); Table expectedTable = HashBasedTable.create(); expectedTable.put('a', 1, "foo"); expectedTable.put('b', 1, "bar"); @@ -68,39 +74,27 @@ public void testBuilder() { Table otherTable = HashBasedTable.create(); otherTable.put('b', 1, "bar"); otherTable.put('a', 2, "baz"); - assertEquals(expectedTable, builder.putAll(otherTable).build()); + assertEquals(expectedTable, builder.putAll(otherTable).buildOrThrow()); } public void testBuilder_withImmutableCell() { ImmutableTable.Builder builder = new ImmutableTable.Builder<>(); assertEquals( - ImmutableTable.of('a', 1, "foo"), builder.put(Tables.immutableCell('a', 1, "foo")).build()); + ImmutableTable.of('a', 1, "foo"), builder.put(immutableCell('a', 1, "foo")).buildOrThrow()); } public void testBuilder_withImmutableCellAndNullContents() { ImmutableTable.Builder builder = new ImmutableTable.Builder<>(); - try { - builder.put(Tables.immutableCell((Character) null, 1, "foo")); - fail(); - } catch (NullPointerException e) { - // success - } - try { - builder.put(Tables.immutableCell('a', (Integer) null, "foo")); - fail(); - } catch (NullPointerException e) { - // success - } - try { - builder.put(Tables.immutableCell('a', 1, (String) null)); - fail(); - } catch (NullPointerException e) { - // success - } + assertThrows( + NullPointerException.class, () -> builder.put(immutableCell((Character) null, 1, "foo"))); + assertThrows( + NullPointerException.class, () -> builder.put(immutableCell('a', (Integer) null, "foo"))); + assertThrows( + NullPointerException.class, () -> builder.put(immutableCell('a', 1, (String) null))); } private static class StringHolder { - String string; + @Nullable String string; } public void testBuilder_withMutableCell() { @@ -133,7 +127,7 @@ public String getValue() { holder.string = "bar"; // Make sure it uses the original value. - assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build()); + assertEquals(ImmutableTable.of('K', 42, "foo"), builder.buildOrThrow()); } public void testBuilder_noDuplicates() { @@ -141,34 +135,14 @@ public void testBuilder_noDuplicates() { new ImmutableTable.Builder() .put('a', 1, "foo") .put('a', 1, "bar"); - try { - builder.build(); - fail(); - } catch (IllegalArgumentException e) { - // success - } + assertThrows(IllegalArgumentException.class, () -> builder.buildOrThrow()); } public void testBuilder_noNulls() { ImmutableTable.Builder builder = new ImmutableTable.Builder<>(); - try { - builder.put(null, 1, "foo"); - fail(); - } catch (NullPointerException e) { - // success - } - try { - builder.put('a', null, "foo"); - fail(); - } catch (NullPointerException e) { - // success - } - try { - builder.put('a', 1, null); - fail(); - } catch (NullPointerException e) { - // success - } + assertThrows(NullPointerException.class, () -> builder.put(null, 1, "foo")); + assertThrows(NullPointerException.class, () -> builder.put('a', null, "foo")); + assertThrows(NullPointerException.class, () -> builder.put('a', 1, null)); } private static void validateTableCopies(Table original) { @@ -176,7 +150,7 @@ private static void validateTableCopies(Table original) { assertEquals(original, copy); validateViewOrdering(original, copy); - Table built = ImmutableTable.builder().putAll(original).build(); + Table built = ImmutableTable.builder().putAll(original).buildOrThrow(); assertEquals(original, built); validateViewOrdering(original, built); } @@ -239,7 +213,7 @@ public void testBuilder_orderRowsAndColumnsBy_putAll() { .orderRowsBy(Ordering.natural()) .orderColumnsBy(Ordering.natural()) .putAll(table) - .build(); + .buildOrThrow(); assertThat(copy.rowKeySet()).containsExactly('a', 'b').inOrder(); assertThat(copy.columnKeySet()).containsExactly(1, 2).inOrder(); assertThat(copy.values()).containsExactly("baz", "bar", "foo").inOrder(); @@ -259,7 +233,7 @@ public void testBuilder_orderRowsAndColumnsBy_sparse() { builder.put('e', 3, "tub"); builder.put('r', 4, "foo"); builder.put('x', 5, "bar"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder(); assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder(); assertThat(table.values()) @@ -281,7 +255,7 @@ public void testBuilder_orderRowsAndColumnsBy_dense() { builder.put('a', 3, "foo"); builder.put('a', 2, "bar"); builder.put('a', 1, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder(); assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder(); assertThat(table.values()) @@ -303,7 +277,7 @@ public void testBuilder_orderRowsBy_sparse() { builder.put('e', 3, "tub"); builder.put('r', 4, "foo"); builder.put('x', 5, "bar"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.rowKeySet()).containsExactly('b', 'c', 'e', 'r', 'x').inOrder(); assertThat(table.column(5).keySet()).containsExactly('e', 'x').inOrder(); } @@ -319,7 +293,7 @@ public void testBuilder_orderRowsBy_dense() { builder.put('a', 3, "foo"); builder.put('a', 2, "bar"); builder.put('a', 1, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.rowKeySet()).containsExactly('a', 'b', 'c').inOrder(); assertThat(table.column(1).keySet()).containsExactly('a', 'b', 'c').inOrder(); } @@ -336,7 +310,7 @@ public void testBuilder_orderColumnsBy_sparse() { builder.put('e', 3, "tub"); builder.put('r', 4, "foo"); builder.put('x', 5, "bar"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.columnKeySet()).containsExactly(0, 1, 2, 3, 4, 5, 7).inOrder(); assertThat(table.row('c').keySet()).containsExactly(0, 3).inOrder(); } @@ -352,7 +326,7 @@ public void testBuilder_orderColumnsBy_dense() { builder.put('a', 3, "foo"); builder.put('a', 2, "bar"); builder.put('a', 1, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table.columnKeySet()).containsExactly(1, 2, 3).inOrder(); assertThat(table.row('c').keySet()).containsExactly(1, 2, 3).inOrder(); } @@ -370,7 +344,7 @@ public void testDenseSerialization_manualOrder() { builder.put('b', 2, "foo"); builder.put('b', 1, "bar"); builder.put('a', 2, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(DenseImmutableTable.class); validateReserialization(table); } @@ -381,7 +355,7 @@ public void testDenseSerialization_rowOrder() { builder.put('b', 2, "foo"); builder.put('b', 1, "bar"); builder.put('a', 2, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(DenseImmutableTable.class); validateReserialization(table); } @@ -392,7 +366,7 @@ public void testDenseSerialization_columnOrder() { builder.put('b', 2, "foo"); builder.put('b', 1, "bar"); builder.put('a', 2, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(DenseImmutableTable.class); validateReserialization(table); } @@ -404,7 +378,7 @@ public void testDenseSerialization_bothOrders() { builder.put('b', 2, "foo"); builder.put('b', 1, "bar"); builder.put('a', 2, "baz"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(DenseImmutableTable.class); validateReserialization(table); } @@ -416,7 +390,7 @@ public void testSparseSerialization_manualOrder() { builder.put('a', 2, "baz"); builder.put('c', 3, "cat"); builder.put('d', 4, "dog"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(SparseImmutableTable.class); validateReserialization(table); } @@ -429,7 +403,7 @@ public void testSparseSerialization_rowOrder() { builder.put('a', 2, "baz"); builder.put('c', 3, "cat"); builder.put('d', 4, "dog"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(SparseImmutableTable.class); validateReserialization(table); } @@ -442,7 +416,7 @@ public void testSparseSerialization_columnOrder() { builder.put('a', 2, "baz"); builder.put('c', 3, "cat"); builder.put('d', 4, "dog"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(SparseImmutableTable.class); validateReserialization(table); } @@ -456,7 +430,7 @@ public void testSparseSerialization_bothOrders() { builder.put('a', 2, "baz"); builder.put('c', 3, "cat"); builder.put('d', 4, "dog"); - Table table = builder.build(); + Table table = builder.buildOrThrow(); assertThat(table).isInstanceOf(SparseImmutableTable.class); validateReserialization(table); } @@ -468,15 +442,16 @@ private static void validateReserialization(Table original) { assertThat(copy.columnKeySet()).containsExactlyElementsIn(original.columnKeySet()).inOrder(); } + @J2ktIncompatible @GwtIncompatible // Mind-bogglingly slow in GWT @AndroidIncompatible // slow public void testOverflowCondition() { - // See https://code.google.com/p/guava-libraries/issues/detail?id=1322 for details. + // See https://github.com/google/guava/issues/1322 for details. ImmutableTable.Builder builder = ImmutableTable.builder(); for (int i = 1; i < 0x10000; i++) { builder.put(i, 0, "foo"); builder.put(0, i, "bar"); } - assertTrue(builder.build() instanceof SparseImmutableTable); + assertTrue(builder.buildOrThrow() instanceof SparseImmutableTable); } } diff --git a/android/guava-tests/test/com/google/common/collect/InternersTest.java b/android/guava-tests/test/com/google/common/collect/InternersTest.java index cfa14b892244..bd28f8a85696 100644 --- a/android/guava-tests/test/com/google/common/collect/InternersTest.java +++ b/android/guava-tests/test/com/google/common/collect/InternersTest.java @@ -16,6 +16,8 @@ package com.google.common.collect; +import static org.junit.Assert.assertThrows; + import com.google.common.base.Function; import com.google.common.collect.Interners.InternerImpl; import com.google.common.collect.MapMakerInternalMap.Strength; @@ -23,12 +25,14 @@ import com.google.common.testing.NullPointerTester; import java.lang.ref.WeakReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Interners}. * * @author Kevin Bourrillion */ +@NullUnmarked public class InternersTest extends TestCase { public void testStrong_simplistic() { @@ -42,11 +46,7 @@ public void testStrong_simplistic() { public void testStrong_null() { Interner pool = Interners.newStrongInterner(); - try { - pool.intern(null); - fail(); - } catch (NullPointerException ok) { - } + assertThrows(NullPointerException.class, () -> pool.intern(null)); } public void testStrong_builder() { @@ -68,11 +68,7 @@ public void testWeak_simplistic() { public void testWeak_null() { Interner pool = Interners.newWeakInterner(); - try { - pool.intern(null); - fail(); - } catch (NullPointerException ok) { - } + assertThrows(NullPointerException.class, () -> pool.intern(null)); } public void testWeak_builder() { @@ -114,21 +110,13 @@ public void testNullPointerExceptions() { new NullPointerTester().testAllPublicStaticMethods(Interners.class); } - public void testConcurrencyLevel_Zero() { + public void testConcurrencyLevel_zero() { Interners.InternerBuilder builder = Interners.newBuilder(); - try { - builder.concurrencyLevel(0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.concurrencyLevel(0)); } - public void testConcurrencyLevel_Negative() { + public void testConcurrencyLevel_negative() { Interners.InternerBuilder builder = Interners.newBuilder(); - try { - builder.concurrencyLevel(-42); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> builder.concurrencyLevel(-42)); } } diff --git a/android/guava-tests/test/com/google/common/collect/IterablesFilterArrayListTest.java b/android/guava-tests/test/com/google/common/collect/IterablesFilterArrayListTest.java new file mode 100644 index 000000000000..ff8706a22071 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/IterablesFilterArrayListTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.base.Predicate; +import com.google.common.collect.FilteredCollectionsTestUtil.AbstractFilteredIterableTest; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked +public final class IterablesFilterArrayListTest + extends AbstractFilteredIterableTest> { + @Override + Iterable createUnfiltered(Iterable contents) { + return Lists.newArrayList(contents); + } + + @Override + Iterable filter(Iterable elements, Predicate predicate) { + return Iterables.filter(elements, predicate); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/IterablesTest.java b/android/guava-tests/test/com/google/common/collect/IterablesTest.java index 41772100b8a4..f8cdc3e9f18c 100644 --- a/android/guava-tests/test/com/google/common/collect/IterablesTest.java +++ b/android/guava-tests/test/com/google/common/collect/IterablesTest.java @@ -16,17 +16,35 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.collect.Iterables.all; +import static com.google.common.collect.Iterables.any; +import static com.google.common.collect.Iterables.elementsEqual; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.find; +import static com.google.common.collect.Iterables.frequency; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Iterables.mergeSorted; +import static com.google.common.collect.Iterables.removeIf; import static com.google.common.collect.Iterables.skip; +import static com.google.common.collect.Iterables.tryFind; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newLinkedHashSet; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.nCopies; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -47,6 +65,8 @@ import java.util.SortedSet; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code Iterables}. @@ -55,15 +75,16 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class IterablesTest extends TestCase { public void testSize0() { - Iterable iterable = Collections.emptySet(); + Iterable iterable = emptySet(); assertEquals(0, Iterables.size(iterable)); } public void testSize1Collection() { - Iterable iterable = Collections.singleton("a"); + Iterable iterable = singleton("a"); assertEquals(1, Iterables.size(iterable)); } @@ -91,28 +112,28 @@ public Iterator iterator() { assertEquals(5, Iterables.size(collection)); } - private static Iterable iterable(String... elements) { - final List list = asList(elements); - return new Iterable() { + private static Iterable iterable(T... elements) { + final List list = asList(elements); + return new Iterable() { @Override - public Iterator iterator() { + public Iterator iterator() { return list.iterator(); } }; } public void test_contains_null_set_yes() { - Iterable set = Sets.newHashSet("a", null, "b"); + Iterable<@Nullable String> set = newHashSet("a", null, "b"); assertTrue(Iterables.contains(set, null)); } public void test_contains_null_set_no() { - Iterable set = Sets.newHashSet("a", "b"); + Iterable set = newHashSet("a", "b"); assertFalse(Iterables.contains(set, null)); } public void test_contains_null_iterable_yes() { - Iterable set = iterable("a", null, "b"); + Iterable<@Nullable String> set = iterable("a", null, "b"); assertTrue(Iterables.contains(set, null)); } @@ -122,17 +143,17 @@ public void test_contains_null_iterable_no() { } public void test_contains_nonnull_set_yes() { - Iterable set = Sets.newHashSet("a", null, "b"); + Iterable<@Nullable String> set = newHashSet("a", null, "b"); assertTrue(Iterables.contains(set, "b")); } public void test_contains_nonnull_set_no() { - Iterable set = Sets.newHashSet("a", "b"); + Iterable set = newHashSet("a", "b"); assertFalse(Iterables.contains(set, "c")); } public void test_contains_nonnull_iterable_yes() { - Iterable set = iterable("a", null, "b"); + Iterable<@Nullable String> set = iterable("a", null, "b"); assertTrue(Iterables.contains(set, "b")); } @@ -142,62 +163,50 @@ public void test_contains_nonnull_iterable_no() { } public void testGetOnlyElement_noDefault_valid() { - Iterable iterable = Collections.singletonList("foo"); - assertEquals("foo", Iterables.getOnlyElement(iterable)); + Iterable iterable = singletonList("foo"); + assertEquals("foo", getOnlyElement(iterable)); } public void testGetOnlyElement_noDefault_empty() { - Iterable iterable = Collections.emptyList(); - try { - Iterables.getOnlyElement(iterable); - fail(); - } catch (NoSuchElementException expected) { - } + Iterable iterable = emptyList(); + assertThrows(NoSuchElementException.class, () -> getOnlyElement(iterable)); } public void testGetOnlyElement_noDefault_multiple() { Iterable iterable = asList("foo", "bar"); - try { - Iterables.getOnlyElement(iterable); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterable)); } public void testGetOnlyElement_withDefault_singleton() { - Iterable iterable = Collections.singletonList("foo"); - assertEquals("foo", Iterables.getOnlyElement(iterable, "bar")); + Iterable iterable = singletonList("foo"); + assertEquals("foo", getOnlyElement(iterable, "bar")); } public void testGetOnlyElement_withDefault_empty() { - Iterable iterable = Collections.emptyList(); - assertEquals("bar", Iterables.getOnlyElement(iterable, "bar")); + Iterable iterable = emptyList(); + assertEquals("bar", getOnlyElement(iterable, "bar")); } public void testGetOnlyElement_withDefault_empty_null() { - Iterable iterable = Collections.emptyList(); - assertNull(Iterables.getOnlyElement(iterable, null)); + Iterable iterable = emptyList(); + assertNull(Iterables.<@Nullable String>getOnlyElement(iterable, null)); } public void testGetOnlyElement_withDefault_multiple() { Iterable iterable = asList("foo", "bar"); - try { - Iterables.getOnlyElement(iterable, "x"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterable, "x")); } @GwtIncompatible // Iterables.toArray(Iterable, Class) public void testToArrayEmpty() { - Iterable iterable = Collections.emptyList(); + Iterable iterable = emptyList(); String[] array = Iterables.toArray(iterable, String.class); assertTrue(Arrays.equals(new String[0], array)); } @GwtIncompatible // Iterables.toArray(Iterable, Class) public void testToArraySingleton() { - Iterable iterable = Collections.singletonList("a"); + Iterable iterable = singletonList("a"); String[] array = Iterables.toArray(iterable, String.class); assertTrue(Arrays.equals(new String[] {"a"}, array)); } @@ -212,55 +221,51 @@ public void testToArray() { public void testAny() { List list = newArrayList(); - Predicate predicate = Predicates.equalTo("pants"); + Predicate predicate = equalTo("pants"); - assertFalse(Iterables.any(list, predicate)); + assertFalse(any(list, predicate)); list.add("cool"); - assertFalse(Iterables.any(list, predicate)); + assertFalse(any(list, predicate)); list.add("pants"); - assertTrue(Iterables.any(list, predicate)); + assertTrue(any(list, predicate)); } public void testAll() { List list = newArrayList(); - Predicate predicate = Predicates.equalTo("cool"); + Predicate predicate = equalTo("cool"); - assertTrue(Iterables.all(list, predicate)); + assertTrue(all(list, predicate)); list.add("cool"); - assertTrue(Iterables.all(list, predicate)); + assertTrue(all(list, predicate)); list.add("pants"); - assertFalse(Iterables.all(list, predicate)); + assertFalse(all(list, predicate)); } public void testFind() { Iterable list = newArrayList("cool", "pants"); - assertEquals("cool", Iterables.find(list, Predicates.equalTo("cool"))); - assertEquals("pants", Iterables.find(list, Predicates.equalTo("pants"))); - try { - Iterables.find(list, Predicates.alwaysFalse()); - fail(); - } catch (NoSuchElementException e) { - } - assertEquals("cool", Iterables.find(list, Predicates.alwaysTrue())); + assertEquals("cool", find(list, equalTo("cool"))); + assertEquals("pants", find(list, equalTo("pants"))); + assertThrows(NoSuchElementException.class, () -> find(list, Predicates.alwaysFalse())); + assertEquals("cool", find(list, Predicates.alwaysTrue())); assertCanIterateAgain(list); } public void testFind_withDefault() { Iterable list = Lists.newArrayList("cool", "pants"); - assertEquals("cool", Iterables.find(list, Predicates.equalTo("cool"), "woot")); - assertEquals("pants", Iterables.find(list, Predicates.equalTo("pants"), "woot")); - assertEquals("woot", Iterables.find(list, Predicates.alwaysFalse(), "woot")); - assertNull(Iterables.find(list, Predicates.alwaysFalse(), null)); - assertEquals("cool", Iterables.find(list, Predicates.alwaysTrue(), "woot")); + assertEquals("cool", find(list, equalTo("cool"), "woot")); + assertEquals("pants", find(list, equalTo("pants"), "woot")); + assertEquals("woot", find(list, Predicates.alwaysFalse(), "woot")); + assertNull(find(list, Predicates.alwaysFalse(), null)); + assertEquals("cool", find(list, Predicates.alwaysTrue(), "woot")); assertCanIterateAgain(list); } public void testTryFind() { Iterable list = newArrayList("cool", "pants"); - assertThat(Iterables.tryFind(list, Predicates.equalTo("cool"))).hasValue("cool"); - assertThat(Iterables.tryFind(list, Predicates.equalTo("pants"))).hasValue("pants"); - assertThat(Iterables.tryFind(list, Predicates.alwaysTrue())).hasValue("cool"); - assertThat(Iterables.tryFind(list, Predicates.alwaysFalse())).isAbsent(); + assertThat(tryFind(list, equalTo("cool"))).hasValue("cool"); + assertThat(tryFind(list, equalTo("pants"))).hasValue("pants"); + assertThat(tryFind(list, Predicates.alwaysTrue())).hasValue("cool"); + assertThat(tryFind(list, Predicates.alwaysFalse())).isAbsent(); assertCanIterateAgain(list); } @@ -274,7 +279,7 @@ private static class HasBoth extends TypeA implements TypeB {} public void testFilterByType_iterator() throws Exception { HasBoth hasBoth = new HasBoth(); Iterable alist = Lists.newArrayList(new TypeA(), new TypeA(), hasBoth, new TypeA()); - Iterable blist = Iterables.filter(alist, TypeB.class); + Iterable blist = filter(alist, TypeB.class); assertThat(blist).containsExactly(hasBoth).inOrder(); } @@ -298,7 +303,7 @@ public Integer apply(String from) { } public void testPoorlyBehavedTransform() { - List input = asList("1", null, "3"); + List input = asList("1", "not a number", "3"); Iterable result = Iterables.transform( input, @@ -312,21 +317,17 @@ public Integer apply(String from) { Iterator resultIterator = result.iterator(); resultIterator.next(); - try { - resultIterator.next(); - fail("Expected NFE"); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> resultIterator.next()); } public void testNullFriendlyTransform() { - List input = asList(1, 2, null, 3); + List<@Nullable Integer> input = asList(1, 2, null, 3); Iterable result = Iterables.transform( input, - new Function() { + new Function<@Nullable Integer, String>() { @Override - public String apply(Integer from) { + public String apply(@Nullable Integer from) { return String.valueOf(from); } }); @@ -364,7 +365,6 @@ public void testConcatIterable() { List list1 = newArrayList(1); List list2 = newArrayList(4); - @SuppressWarnings("unchecked") List> input = newArrayList(list1, list2); Iterable result = Iterables.concat(input); @@ -386,7 +386,6 @@ public void testConcatVarargs() { List list3 = newArrayList(7, 8); List list4 = newArrayList(9); List list5 = newArrayList(10); - @SuppressWarnings("unchecked") Iterable result = Iterables.concat(list1, list2, list3, list4, list5); assertEquals(asList(1, 4, 7, 8, 9, 10), newArrayList(result)); assertEquals("[1, 4, 7, 8, 9, 10]", result.toString()); @@ -396,40 +395,32 @@ public void testConcatNullPointerException() { List list1 = newArrayList(1); List list2 = newArrayList(4); - try { - Iterables.concat(list1, null, list2); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Iterables.concat(list1, null, list2)); } public void testConcatPeformingFiniteCycle() { Iterable iterable = asList(1, 2, 3); int n = 4; - Iterable repeated = Iterables.concat(Collections.nCopies(n, iterable)); + Iterable repeated = Iterables.concat(nCopies(n, iterable)); assertThat(repeated).containsExactly(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3).inOrder(); } public void testPartition_badSize() { - Iterable source = Collections.singleton(1); - try { - Iterables.partition(source, 0); - fail(); - } catch (IllegalArgumentException expected) { - } + Iterable source = singleton(1); + assertThrows(IllegalArgumentException.class, () -> Iterables.partition(source, 0)); } public void testPartition_empty() { - Iterable source = Collections.emptySet(); + Iterable source = emptySet(); Iterable> partitions = Iterables.partition(source, 1); assertTrue(Iterables.isEmpty(partitions)); } public void testPartition_singleton1() { - Iterable source = Collections.singleton(1); + Iterable source = singleton(1); Iterable> partitions = Iterables.partition(source, 1); assertEquals(1, Iterables.size(partitions)); - assertEquals(Collections.singletonList(1), partitions.iterator().next()); + assertEquals(singletonList(1), partitions.iterator().next()); } public void testPartition_view() { @@ -452,8 +443,8 @@ public void testPartition_view() { assertEquals(ImmutableList.of(3, 4), first); } - @GwtIncompatible // ? - // TODO: Figure out why this is failing in GWT. + @J2ktIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in J2KT. + @GwtIncompatible // Arrays.asList(...).subList doesn't implement RandomAccess in GWT public void testPartitionRandomAccessInput() { Iterable source = asList(1, 2, 3); Iterable> partitions = Iterables.partition(source, 2); @@ -462,8 +453,8 @@ public void testPartitionRandomAccessInput() { assertTrue(iterator.next() instanceof RandomAccess); } - @GwtIncompatible // ? - // TODO: Figure out why this is failing in GWT. + @J2ktIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in J2KT. + @GwtIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in GWT public void testPartitionNonRandomAccessInput() { Iterable source = Lists.newLinkedList(asList(1, 2, 3)); Iterable> partitions = Iterables.partition(source, 2); @@ -476,9 +467,9 @@ public void testPartitionNonRandomAccessInput() { public void testPaddedPartition_basic() { List list = asList(1, 2, 3, 4, 5); - Iterable> partitions = Iterables.paddedPartition(list, 2); + Iterable> partitions = Iterables.paddedPartition(list, 2); assertEquals(3, Iterables.size(partitions)); - assertEquals(asList(5, null), Iterables.getLast(partitions)); + assertEquals(Arrays.<@Nullable Integer>asList(5, null), Iterables.getLast(partitions)); } public void testPaddedPartitionRandomAccessInput() { @@ -513,6 +504,7 @@ private static void assertCanIterateAgain(Iterable iterable) { for (@SuppressWarnings("unused") Object obj : iterable) {} } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -527,28 +519,28 @@ public void testElementsEqual() throws Exception { // A few elements. a = asList(4, 8, 15, 16, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertTrue(Iterables.elementsEqual(a, b)); + assertTrue(elementsEqual(a, b)); // An element differs. a = asList(4, 8, 15, 12, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterables.elementsEqual(a, b)); + assertFalse(elementsEqual(a, b)); // null versus non-null. - a = asList(4, 8, 15, null, 23, 42); + a = Arrays.<@Nullable Integer>asList(4, 8, 15, null, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterables.elementsEqual(a, b)); - assertFalse(Iterables.elementsEqual(b, a)); + assertFalse(elementsEqual(a, b)); + assertFalse(elementsEqual(b, a)); // Different lengths. a = asList(4, 8, 15, 16, 23); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterables.elementsEqual(a, b)); - assertFalse(Iterables.elementsEqual(b, a)); + assertFalse(elementsEqual(a, b)); + assertFalse(elementsEqual(b, a)); } public void testToString() { - List list = Collections.emptyList(); + List list = emptyList(); assertEquals("[]", Iterables.toString(list)); list = newArrayList("yam", "bam", "jam", "ham"); @@ -568,18 +560,14 @@ public void testLimit() { public void testLimit_illegalArgument() { List list = newArrayList("a", "b", "c"); - try { - Iterables.limit(list, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Iterables.limit(list, -1)); } public void testIsEmpty() { - Iterable emptyList = Collections.emptyList(); + Iterable emptyList = emptyList(); assertTrue(Iterables.isEmpty(emptyList)); - Iterable singletonList = Collections.singletonList("foo"); + Iterable singletonList = singletonList("foo"); assertFalse(Iterables.isEmpty(singletonList)); } @@ -616,38 +604,26 @@ public void testSkip_skipNoneList() { } public void testSkip_removal() { - Collection set = Sets.newHashSet("a", "b"); + Collection set = newHashSet("a", "b"); Iterator iterator = skip(set, 2).iterator(); try { iterator.next(); } catch (NoSuchElementException suppressed) { // We want remove() to fail even after a failed call to next(). } - try { - iterator.remove(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> iterator.remove()); } public void testSkip_allOfMutableList_modifiable() { List list = newArrayList("a", "b"); Iterator iterator = skip(list, 2).iterator(); - try { - iterator.remove(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> iterator.remove()); } public void testSkip_allOfImmutableList_modifiable() { List list = ImmutableList.of("a", "b"); Iterator iterator = skip(list, 2).iterator(); - try { - iterator.remove(); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); } @GwtIncompatible // slow (~35s) @@ -715,11 +691,7 @@ public void testSkip_structurallyModifiedSkipAllList() throws Exception { public void testSkip_illegalArgument() { List list = newArrayList("a", "b", "c"); - try { - skip(list, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> skip(list, -1)); } private void testGetOnAbc(Iterable iterable) { @@ -776,12 +748,8 @@ public void testGet_emptyIterable() { } public void testGet_withDefault_negativePosition() { - try { - Iterables.get(newArrayList("a", "b", "c"), -1, "d"); - fail(); - } catch (IndexOutOfBoundsException expected) { - // pass - } + assertThrows( + IndexOutOfBoundsException.class, () -> Iterables.get(newArrayList("a", "b", "c"), -1, "d")); } public void testGet_withDefault_simple() { @@ -811,18 +779,18 @@ public void testGet_withDefault_doesntIterate() { } public void testGetFirst_withDefault_singleton() { - Iterable iterable = Collections.singletonList("foo"); + Iterable iterable = singletonList("foo"); assertEquals("foo", Iterables.getFirst(iterable, "bar")); } public void testGetFirst_withDefault_empty() { - Iterable iterable = Collections.emptyList(); + Iterable iterable = emptyList(); assertEquals("bar", Iterables.getFirst(iterable, "bar")); } public void testGetFirst_withDefault_empty_null() { - Iterable iterable = Collections.emptyList(); - assertNull(Iterables.getFirst(iterable, null)); + Iterable iterable = emptyList(); + assertNull(Iterables.<@Nullable String>getFirst(iterable, null)); } public void testGetFirst_withDefault_multiple() { @@ -836,12 +804,8 @@ public void testGetLast_list() { } public void testGetLast_emptyList() { - List list = Collections.emptyList(); - try { - Iterables.getLast(list); - fail(); - } catch (NoSuchElementException e) { - } + List list = emptyList(); + assertThrows(NoSuchElementException.class, () -> Iterables.getLast(list)); } public void testGetLast_sortedSet() { @@ -850,18 +814,18 @@ public void testGetLast_sortedSet() { } public void testGetLast_withDefault_singleton() { - Iterable iterable = Collections.singletonList("foo"); + Iterable iterable = singletonList("foo"); assertEquals("foo", Iterables.getLast(iterable, "bar")); } public void testGetLast_withDefault_empty() { - Iterable iterable = Collections.emptyList(); + Iterable iterable = emptyList(); assertEquals("bar", Iterables.getLast(iterable, "bar")); } public void testGetLast_withDefault_empty_null() { - Iterable iterable = Collections.emptyList(); - assertNull(Iterables.getLast(iterable, null)); + Iterable iterable = emptyList(); + assertNull(Iterables.<@Nullable String>getLast(iterable, null)); } public void testGetLast_withDefault_multiple() { @@ -874,7 +838,9 @@ public void testGetLast_withDefault_multiple() { * need to prove that it isn't called. */ private static class DiesOnIteratorArrayList extends ArrayList { - /** @throws UnsupportedOperationException all the time */ + /** + * @throws UnsupportedOperationException all the time + */ @Override public Iterator iterator() { throw new UnsupportedOperationException(); @@ -891,11 +857,7 @@ public void testGetLast_withDefault_not_empty_list() { public void testGetLast_emptySortedSet() { SortedSet sortedSet = ImmutableSortedSet.of(); - try { - Iterables.getLast(sortedSet); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> Iterables.getLast(sortedSet)); } public void testGetLast_iterable() { @@ -904,12 +866,8 @@ public void testGetLast_iterable() { } public void testGetLast_emptyIterable() { - Set set = Sets.newHashSet(); - try { - Iterables.getLast(set); - fail(); - } catch (NoSuchElementException e) { - } + Set set = newHashSet(); + assertThrows(NoSuchElementException.class, () -> Iterables.getLast(set)); } public void testUnmodifiableIterable() { @@ -917,15 +875,11 @@ public void testUnmodifiableIterable() { Iterable iterable = Iterables.unmodifiableIterable(list); Iterator iterator = iterable.iterator(); iterator.next(); - try { - iterator.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); assertEquals("[a, b, c]", iterable.toString()); } - @SuppressWarnings("deprecation") // test of deprecated method + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableIterableShortCircuit() { List list = newArrayList("a", "b", "c"); Iterable iterable = Iterables.unmodifiableIterable(list); @@ -938,32 +892,32 @@ public void testUnmodifiableIterableShortCircuit() { public void testFrequency_multiset() { Multiset multiset = ImmutableMultiset.of("a", "b", "a", "c", "b", "a"); - assertEquals(3, Iterables.frequency(multiset, "a")); - assertEquals(2, Iterables.frequency(multiset, "b")); - assertEquals(1, Iterables.frequency(multiset, "c")); - assertEquals(0, Iterables.frequency(multiset, "d")); - assertEquals(0, Iterables.frequency(multiset, 4.2)); - assertEquals(0, Iterables.frequency(multiset, null)); + assertEquals(3, frequency(multiset, "a")); + assertEquals(2, frequency(multiset, "b")); + assertEquals(1, frequency(multiset, "c")); + assertEquals(0, frequency(multiset, "d")); + assertEquals(0, frequency(multiset, 4.2)); + assertEquals(0, frequency(multiset, null)); } public void testFrequency_set() { - Set set = Sets.newHashSet("a", "b", "c"); - assertEquals(1, Iterables.frequency(set, "a")); - assertEquals(1, Iterables.frequency(set, "b")); - assertEquals(1, Iterables.frequency(set, "c")); - assertEquals(0, Iterables.frequency(set, "d")); - assertEquals(0, Iterables.frequency(set, 4.2)); - assertEquals(0, Iterables.frequency(set, null)); + Set set = newHashSet("a", "b", "c"); + assertEquals(1, frequency(set, "a")); + assertEquals(1, frequency(set, "b")); + assertEquals(1, frequency(set, "c")); + assertEquals(0, frequency(set, "d")); + assertEquals(0, frequency(set, 4.2)); + assertEquals(0, frequency(set, null)); } public void testFrequency_list() { List list = newArrayList("a", "b", "a", "c", "b", "a"); - assertEquals(3, Iterables.frequency(list, "a")); - assertEquals(2, Iterables.frequency(list, "b")); - assertEquals(1, Iterables.frequency(list, "c")); - assertEquals(0, Iterables.frequency(list, "d")); - assertEquals(0, Iterables.frequency(list, 4.2)); - assertEquals(0, Iterables.frequency(list, null)); + assertEquals(3, frequency(list, "a")); + assertEquals(2, frequency(list, "b")); + assertEquals(1, frequency(list, "c")); + assertEquals(0, frequency(list, "d")); + assertEquals(0, frequency(list, 4.2)); + assertEquals(0, frequency(list, null)); } public void testRemoveAll_collection() { @@ -1015,7 +969,7 @@ public Iterator iterator() { public void testRemoveIf_randomAccess() { List list = newArrayList("a", "b", "c", "d", "e"); assertTrue( - Iterables.removeIf( + removeIf( list, new Predicate() { @Override @@ -1025,7 +979,7 @@ public boolean apply(String s) { })); assertEquals(newArrayList("a", "c", "e"), list); assertFalse( - Iterables.removeIf( + removeIf( list, new Predicate() { @Override @@ -1043,7 +997,7 @@ public void testRemoveIf_randomAccess_notPermittingDuplicates() { assertTrue(uniqueList instanceof RandomAccess); assertTrue( - Iterables.removeIf( + removeIf( uniqueList, new Predicate() { @Override @@ -1053,7 +1007,7 @@ public boolean apply(String s) { })); assertEquals(newArrayList("a", "c", "e"), uniqueList); assertFalse( - Iterables.removeIf( + removeIf( uniqueList, new Predicate() { @Override @@ -1076,7 +1030,7 @@ public Integer apply(String s) { } }); assertTrue( - Iterables.removeIf( + removeIf( transformed, new Predicate() { @Override @@ -1086,7 +1040,7 @@ public boolean apply(Integer n) { })); assertEquals(newArrayList("1", "3", "5"), list); assertFalse( - Iterables.removeIf( + removeIf( transformed, new Predicate() { @Override @@ -1100,7 +1054,7 @@ public boolean apply(Integer n) { public void testRemoveIf_noRandomAccess() { List list = Lists.newLinkedList(asList("a", "b", "c", "d", "e")); assertTrue( - Iterables.removeIf( + removeIf( list, new Predicate() { @Override @@ -1110,7 +1064,7 @@ public boolean apply(String s) { })); assertEquals(newArrayList("a", "c", "e"), list); assertFalse( - Iterables.removeIf( + removeIf( list, new Predicate() { @Override @@ -1131,7 +1085,7 @@ public Iterator iterator() { } }; assertTrue( - Iterables.removeIf( + removeIf( iterable, new Predicate() { @Override @@ -1141,7 +1095,7 @@ public boolean apply(String s) { })); assertEquals(newArrayList("a", "c", "e"), list); assertFalse( - Iterables.removeIf( + removeIf( iterable, new Predicate() { @Override @@ -1157,30 +1111,6 @@ public boolean apply(String s) { // Iterable. Those returned by Iterators.filter() and Iterables.filter() // are not tested because they are unmodifiable. - public void testIterableWithToString() { - assertEquals("[]", create().toString()); - assertEquals("[a]", create("a").toString()); - assertEquals("[a, b, c]", create("a", "b", "c").toString()); - assertEquals("[c, a, a]", create("c", "a", "a").toString()); - } - - public void testIterableWithToStringNull() { - assertEquals("[null]", create((String) null).toString()); - assertEquals("[null, null]", create(null, null).toString()); - assertEquals("[, null, a]", create("", null, "a").toString()); - } - - /** Returns a new iterable over the specified strings. */ - private static Iterable create(String... strings) { - final List list = asList(strings); - return new FluentIterable() { - @Override - public Iterator iterator() { - return list.iterator(); - } - }; - } - public void testConsumingIterable() { // Test data List list = Lists.newArrayList(asList("a", "b")); @@ -1215,12 +1145,7 @@ public void testConsumingIterable_duelingIterators() { Iterator i2 = Iterables.consumingIterable(list).iterator(); i1.next(); - try { - i2.next(); - fail("Concurrent modification should throw an exception."); - } catch (ConcurrentModificationException cme) { - // Pass - } + assertThrows(ConcurrentModificationException.class, () -> i2.next()); } public void testConsumingIterable_queue_iterator() { @@ -1277,28 +1202,28 @@ protected Queue delegate() { public void testIndexOf_empty() { List list = new ArrayList<>(); - assertEquals(-1, Iterables.indexOf(list, Predicates.equalTo(""))); + assertEquals(-1, Iterables.indexOf(list, equalTo(""))); } public void testIndexOf_oneElement() { List list = Lists.newArrayList("bob"); - assertEquals(0, Iterables.indexOf(list, Predicates.equalTo("bob"))); - assertEquals(-1, Iterables.indexOf(list, Predicates.equalTo("jack"))); + assertEquals(0, Iterables.indexOf(list, equalTo("bob"))); + assertEquals(-1, Iterables.indexOf(list, equalTo("jack"))); } public void testIndexOf_twoElements() { List list = Lists.newArrayList("mary", "bob"); - assertEquals(0, Iterables.indexOf(list, Predicates.equalTo("mary"))); - assertEquals(1, Iterables.indexOf(list, Predicates.equalTo("bob"))); - assertEquals(-1, Iterables.indexOf(list, Predicates.equalTo("jack"))); + assertEquals(0, Iterables.indexOf(list, equalTo("mary"))); + assertEquals(1, Iterables.indexOf(list, equalTo("bob"))); + assertEquals(-1, Iterables.indexOf(list, equalTo("jack"))); } public void testIndexOf_withDuplicates() { List list = Lists.newArrayList("mary", "bob", "bob", "bob", "sam"); - assertEquals(0, Iterables.indexOf(list, Predicates.equalTo("mary"))); - assertEquals(1, Iterables.indexOf(list, Predicates.equalTo("bob"))); - assertEquals(4, Iterables.indexOf(list, Predicates.equalTo("sam"))); - assertEquals(-1, Iterables.indexOf(list, Predicates.equalTo("jack"))); + assertEquals(0, Iterables.indexOf(list, equalTo("mary"))); + assertEquals(1, Iterables.indexOf(list, equalTo("bob"))); + assertEquals(4, Iterables.indexOf(list, equalTo("sam"))); + assertEquals(-1, Iterables.indexOf(list, equalTo("jack"))); } private static final Predicate STARTSWITH_A = @@ -1309,11 +1234,12 @@ public boolean apply(CharSequence input) { } }; + @SuppressWarnings("UnnecessaryStringBuilder") // false positive in a weird case public void testIndexOf_genericPredicate() { List sequences = Lists.newArrayList(); sequences.add("bob"); sequences.add(new StringBuilder("charlie")); - sequences.add(new StringBuffer("henry")); + sequences.add(new StringBuilder("henry")); sequences.add(new StringBuilder("apple")); sequences.add("lemon"); @@ -1330,17 +1256,12 @@ public void testMergeSorted_empty() { Iterable> elements = ImmutableList.of(); // Test - Iterable iterable = Iterables.mergeSorted(elements, Ordering.natural()); + Iterable iterable = mergeSorted(elements, Ordering.natural()); // Verify Iterator iterator = iterable.iterator(); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail("next() on empty iterator should throw NoSuchElementException"); - } catch (NoSuchElementException e) { - // Huzzah! - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); } public void testMergeSorted_single_empty() { @@ -1372,7 +1293,7 @@ public void testMergeSorted_pyramid() { list.add(j); allIntegers.add(j); } - iterables.add(Ordering.natural().sortedCopy(list)); + iterables.add(Ordering.natural().sortedCopy(list)); } verifyMergeSorted(iterables, allIntegers); @@ -1389,12 +1310,13 @@ public void testMergeSorted_skipping_pyramid() { list.add(j * i); allIntegers.add(j * i); } - iterables.add(Ordering.natural().sortedCopy(list)); + iterables.add(Ordering.natural().sortedCopy(list)); } verifyMergeSorted(iterables, allIntegers); } + @J2ktIncompatible @GwtIncompatible // reflection public void testIterables_nullCheck() throws Exception { new ClassSanityTester() @@ -1405,9 +1327,9 @@ public void testIterables_nullCheck() throws Exception { private static void verifyMergeSorted( Iterable> iterables, Iterable unsortedExpected) { - Iterable expected = Ordering.natural().sortedCopy(unsortedExpected); + Iterable expected = Ordering.natural().sortedCopy(unsortedExpected); - Iterable mergedIterator = Iterables.mergeSorted(iterables, Ordering.natural()); + Iterable mergedIterator = mergeSorted(iterables, Ordering.natural()); assertEquals(Lists.newLinkedList(expected), Lists.newLinkedList(mergedIterator)); } diff --git a/android/guava-tests/test/com/google/common/collect/IteratorsTest.java b/android/guava-tests/test/com/google/common/collect/IteratorsTest.java index e71c8bd51eea..1e3ec7b42a9b 100644 --- a/android/guava-tests/test/com/google/common/collect/IteratorsTest.java +++ b/android/guava-tests/test/com/google/common/collect/IteratorsTest.java @@ -16,19 +16,35 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.equalTo; import static com.google.common.collect.CollectPreconditions.checkRemove; import static com.google.common.collect.Iterators.advance; +import static com.google.common.collect.Iterators.all; +import static com.google.common.collect.Iterators.any; +import static com.google.common.collect.Iterators.elementsEqual; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Iterators.filter; +import static com.google.common.collect.Iterators.find; +import static com.google.common.collect.Iterators.frequency; import static com.google.common.collect.Iterators.get; import static com.google.common.collect.Iterators.getLast; +import static com.google.common.collect.Iterators.getOnlyElement; +import static com.google.common.collect.Iterators.singletonIterator; +import static com.google.common.collect.Iterators.tryFind; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -39,6 +55,7 @@ import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.ListFeature; +import com.google.common.primitives.Ints; import com.google.common.testing.NullPointerTester; import java.util.ArrayList; import java.util.Arrays; @@ -57,6 +74,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code Iterators}. @@ -64,9 +83,12 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class IteratorsTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(IteratorsTest.class.getSimpleName()); suite.addTest(testsForRemoveAllAndRetainAll()); @@ -76,18 +98,10 @@ public static Test suite() { @SuppressWarnings("DoNotCall") public void testEmptyIterator() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail("no exception thrown"); - } catch (NoSuchElementException expected) { - } - try { - iterator.remove(); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); } @SuppressWarnings("DoNotCall") @@ -97,55 +111,27 @@ public void testEmptyListIterator() { assertFalse(iterator.hasPrevious()); assertEquals(0, iterator.nextIndex()); assertEquals(-1, iterator.previousIndex()); - try { - iterator.next(); - fail("no exception thrown"); - } catch (NoSuchElementException expected) { - } - try { - iterator.previous(); - fail("no exception thrown"); - } catch (NoSuchElementException expected) { - } - try { - iterator.remove(); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } - try { - iterator.set("a"); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } - try { - iterator.add("a"); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(NoSuchElementException.class, () -> iterator.previous()); + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); + assertThrows(UnsupportedOperationException.class, () -> iterator.set("a")); + assertThrows(UnsupportedOperationException.class, () -> iterator.add("a")); } public void testEmptyModifiableIterator() { Iterator iterator = Iterators.emptyModifiableIterator(); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail("Expected NoSuchElementException"); - } catch (NoSuchElementException expected) { - } - try { - iterator.remove(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IllegalStateException.class, () -> iterator.remove()); } public void testSize0() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); assertEquals(0, Iterators.size(iterator)); } public void testSize1() { - Iterator iterator = Collections.singleton(0).iterator(); + Iterator iterator = singleton(0).iterator(); assertEquals(1, Iterators.size(iterator)); } @@ -157,7 +143,7 @@ public void testSize_partiallyConsumed() { } public void test_contains_nonnull_yes() { - Iterator set = asList("a", null, "b").iterator(); + Iterator<@Nullable String> set = Arrays.<@Nullable String>asList("a", null, "b").iterator(); assertTrue(Iterators.contains(set, "b")); } @@ -167,7 +153,7 @@ public void test_contains_nonnull_no() { } public void test_contains_null_yes() { - Iterator set = asList("a", null, "b").iterator(); + Iterator<@Nullable String> set = Arrays.<@Nullable String>asList("a", null, "b").iterator(); assertTrue(Iterators.contains(set, null)); } @@ -177,76 +163,60 @@ public void test_contains_null_no() { } public void testGetOnlyElement_noDefault_valid() { - Iterator iterator = Collections.singletonList("foo").iterator(); - assertEquals("foo", Iterators.getOnlyElement(iterator)); + Iterator iterator = singletonList("foo").iterator(); + assertEquals("foo", getOnlyElement(iterator)); } public void testGetOnlyElement_noDefault_empty() { - Iterator iterator = Iterators.emptyIterator(); - try { - Iterators.getOnlyElement(iterator); - fail(); - } catch (NoSuchElementException expected) { - } + Iterator iterator = emptyIterator(); + assertThrows(NoSuchElementException.class, () -> getOnlyElement(iterator)); } public void testGetOnlyElement_noDefault_moreThanOneLessThanFiveElements() { Iterator iterator = asList("one", "two").iterator(); - try { - Iterators.getOnlyElement(iterator); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("expected one element but was: "); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterator)); + assertThat(expected).hasMessageThat().isEqualTo("expected one element but was: "); } public void testGetOnlyElement_noDefault_fiveElements() { Iterator iterator = asList("one", "two", "three", "four", "five").iterator(); - try { - Iterators.getOnlyElement(iterator); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("expected one element but was: "); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterator)); + assertThat(expected) + .hasMessageThat() + .isEqualTo("expected one element but was: "); } public void testGetOnlyElement_noDefault_moreThanFiveElements() { Iterator iterator = asList("one", "two", "three", "four", "five", "six").iterator(); - try { - Iterators.getOnlyElement(iterator); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("expected one element but was: "); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterator)); + assertThat(expected) + .hasMessageThat() + .isEqualTo("expected one element but was: "); } public void testGetOnlyElement_withDefault_singleton() { - Iterator iterator = Collections.singletonList("foo").iterator(); - assertEquals("foo", Iterators.getOnlyElement(iterator, "bar")); + Iterator iterator = singletonList("foo").iterator(); + assertEquals("foo", getOnlyElement(iterator, "bar")); } public void testGetOnlyElement_withDefault_empty() { - Iterator iterator = Iterators.emptyIterator(); - assertEquals("bar", Iterators.getOnlyElement(iterator, "bar")); + Iterator iterator = emptyIterator(); + assertEquals("bar", getOnlyElement(iterator, "bar")); } public void testGetOnlyElement_withDefault_empty_null() { - Iterator iterator = Iterators.emptyIterator(); - assertNull(Iterators.getOnlyElement(iterator, null)); + Iterator iterator = emptyIterator(); + assertNull(Iterators.<@Nullable String>getOnlyElement(iterator, null)); } public void testGetOnlyElement_withDefault_two() { Iterator iterator = asList("foo", "bar").iterator(); - try { - Iterators.getOnlyElement(iterator, "x"); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo("expected one element but was: "); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> getOnlyElement(iterator, "x")); + assertThat(expected).hasMessageThat().isEqualTo("expected one element but was: "); } @GwtIncompatible // Iterators.toArray(Iterator, Class) @@ -258,7 +228,7 @@ public void testToArrayEmpty() { @GwtIncompatible // Iterators.toArray(Iterator, Class) public void testToArraySingleton() { - Iterator iterator = Collections.singletonList("a").iterator(); + Iterator iterator = singletonList("a").iterator(); String[] array = Iterators.toArray(iterator, String.class); assertTrue(Arrays.equals(new String[] {"a"}, array)); } @@ -273,23 +243,23 @@ public void testToArray() { public void testFilterSimple() { Iterator unfiltered = Lists.newArrayList("foo", "bar").iterator(); - Iterator filtered = Iterators.filter(unfiltered, Predicates.equalTo("foo")); - List expected = Collections.singletonList("foo"); + Iterator filtered = filter(unfiltered, equalTo("foo")); + List expected = singletonList("foo"); List actual = Lists.newArrayList(filtered); assertEquals(expected, actual); } public void testFilterNoMatch() { Iterator unfiltered = Lists.newArrayList("foo", "bar").iterator(); - Iterator filtered = Iterators.filter(unfiltered, Predicates.alwaysFalse()); - List expected = Collections.emptyList(); + Iterator filtered = filter(unfiltered, Predicates.alwaysFalse()); + List expected = emptyList(); List actual = Lists.newArrayList(filtered); assertEquals(expected, actual); } public void testFilterMatchAll() { Iterator unfiltered = Lists.newArrayList("foo", "bar").iterator(); - Iterator filtered = Iterators.filter(unfiltered, Predicates.alwaysTrue()); + Iterator filtered = filter(unfiltered, Predicates.alwaysTrue()); List expected = Lists.newArrayList("foo", "bar"); List actual = Lists.newArrayList(filtered); assertEquals(expected, actual); @@ -298,7 +268,7 @@ public void testFilterMatchAll() { public void testFilterNothing() { Iterator unfiltered = Collections.emptyList().iterator(); Iterator filtered = - Iterators.filter( + filter( unfiltered, new Predicate() { @Override @@ -307,7 +277,7 @@ public boolean apply(String s) { } }); - List expected = Collections.emptyList(); + List expected = emptyList(); List actual = Lists.newArrayList(filtered); assertEquals(expected, actual); } @@ -326,128 +296,124 @@ public boolean apply(Integer integer) { 5, UNMODIFIABLE, asList(2, 4), IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { - return Iterators.filter(list.iterator(), isEven); + return filter(list.iterator(), isEven); } }.test(); } public void testAny() { List list = Lists.newArrayList(); - Predicate predicate = Predicates.equalTo("pants"); + Predicate predicate = equalTo("pants"); - assertFalse(Iterators.any(list.iterator(), predicate)); + assertFalse(any(list.iterator(), predicate)); list.add("cool"); - assertFalse(Iterators.any(list.iterator(), predicate)); + assertFalse(any(list.iterator(), predicate)); list.add("pants"); - assertTrue(Iterators.any(list.iterator(), predicate)); + assertTrue(any(list.iterator(), predicate)); } public void testAll() { List list = Lists.newArrayList(); - Predicate predicate = Predicates.equalTo("cool"); + Predicate predicate = equalTo("cool"); - assertTrue(Iterators.all(list.iterator(), predicate)); + assertTrue(all(list.iterator(), predicate)); list.add("cool"); - assertTrue(Iterators.all(list.iterator(), predicate)); + assertTrue(all(list.iterator(), predicate)); list.add("pants"); - assertFalse(Iterators.all(list.iterator(), predicate)); + assertFalse(all(list.iterator(), predicate)); } public void testFind_firstElement() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("cool", Iterators.find(iterator, Predicates.equalTo("cool"))); + assertEquals("cool", find(iterator, equalTo("cool"))); assertEquals("pants", iterator.next()); } public void testFind_lastElement() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("pants", Iterators.find(iterator, Predicates.equalTo("pants"))); + assertEquals("pants", find(iterator, equalTo("pants"))); assertFalse(iterator.hasNext()); } public void testFind_notPresent() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - try { - Iterators.find(iterator, Predicates.alwaysFalse()); - fail(); - } catch (NoSuchElementException e) { - } + assertThrows(NoSuchElementException.class, () -> find(iterator, Predicates.alwaysFalse())); assertFalse(iterator.hasNext()); } public void testFind_matchAlways() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("cool", Iterators.find(iterator, Predicates.alwaysTrue())); + assertEquals("cool", find(iterator, Predicates.alwaysTrue())); } public void testFind_withDefault_first() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("cool", Iterators.find(iterator, Predicates.equalTo("cool"), "woot")); + assertEquals("cool", find(iterator, equalTo("cool"), "woot")); assertEquals("pants", iterator.next()); } public void testFind_withDefault_last() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("pants", Iterators.find(iterator, Predicates.equalTo("pants"), "woot")); + assertEquals("pants", find(iterator, equalTo("pants"), "woot")); assertFalse(iterator.hasNext()); } public void testFind_withDefault_notPresent() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("woot", Iterators.find(iterator, Predicates.alwaysFalse(), "woot")); + assertEquals("woot", find(iterator, Predicates.alwaysFalse(), "woot")); assertFalse(iterator.hasNext()); } public void testFind_withDefault_notPresent_nullReturn() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertNull(Iterators.find(iterator, Predicates.alwaysFalse(), null)); + assertNull(find(iterator, Predicates.alwaysFalse(), null)); assertFalse(iterator.hasNext()); } public void testFind_withDefault_matchAlways() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("cool", Iterators.find(iterator, Predicates.alwaysTrue(), "woot")); + assertEquals("cool", find(iterator, Predicates.alwaysTrue(), "woot")); assertEquals("pants", iterator.next()); } public void testTryFind_firstElement() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertThat(Iterators.tryFind(iterator, Predicates.equalTo("cool"))).hasValue("cool"); + assertThat(tryFind(iterator, equalTo("cool"))).hasValue("cool"); } public void testTryFind_lastElement() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertThat(Iterators.tryFind(iterator, Predicates.equalTo("pants"))).hasValue("pants"); + assertThat(tryFind(iterator, equalTo("pants"))).hasValue("pants"); } public void testTryFind_alwaysTrue() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertThat(Iterators.tryFind(iterator, Predicates.alwaysTrue())).hasValue("cool"); + assertThat(tryFind(iterator, Predicates.alwaysTrue())).hasValue("cool"); } public void testTryFind_alwaysFalse_orDefault() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertEquals("woot", Iterators.tryFind(iterator, Predicates.alwaysFalse()).or("woot")); + assertEquals("woot", tryFind(iterator, Predicates.alwaysFalse()).or("woot")); assertFalse(iterator.hasNext()); } public void testTryFind_alwaysFalse_isPresent() { Iterable list = Lists.newArrayList("cool", "pants"); Iterator iterator = list.iterator(); - assertThat(Iterators.tryFind(iterator, Predicates.alwaysFalse())).isAbsent(); + assertThat(tryFind(iterator, Predicates.alwaysFalse())).isAbsent(); assertFalse(iterator.hasNext()); } @@ -488,7 +454,7 @@ public Integer apply(String from) { } public void testPoorlyBehavedTransform() { - Iterator input = asList("1", null, "3").iterator(); + Iterator input = asList("1", "not a number", "3").iterator(); Iterator result = Iterators.transform( input, @@ -500,21 +466,17 @@ public Integer apply(String from) { }); result.next(); - try { - result.next(); - fail("Expected NFE"); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> result.next()); } public void testNullFriendlyTransform() { - Iterator input = asList(1, 2, null, 3).iterator(); + Iterator<@Nullable Integer> input = Arrays.<@Nullable Integer>asList(1, 2, null, 3).iterator(); Iterator result = Iterators.transform( input, - new Function() { + new Function<@Nullable Integer, String>() { @Override - public String apply(Integer from) { + public String apply(@Nullable Integer from) { return String.valueOf(from); } }); @@ -544,7 +506,7 @@ public void testCycleOfOneWithRemove() { assertTrue(cycle.hasNext()); assertEquals("a", cycle.next()); cycle.remove(); - assertEquals(Collections.emptyList(), iterable); + assertEquals(emptyList(), iterable); assertFalse(cycle.hasNext()); } @@ -568,46 +530,34 @@ public void testCycleOfTwoWithRemove() { assertTrue(cycle.hasNext()); assertEquals("a", cycle.next()); cycle.remove(); - assertEquals(Collections.singletonList("b"), iterable); + assertEquals(singletonList("b"), iterable); assertTrue(cycle.hasNext()); assertEquals("b", cycle.next()); assertTrue(cycle.hasNext()); assertEquals("b", cycle.next()); cycle.remove(); - assertEquals(Collections.emptyList(), iterable); + assertEquals(emptyList(), iterable); assertFalse(cycle.hasNext()); } public void testCycleRemoveWithoutNext() { Iterator cycle = Iterators.cycle("a", "b"); assertTrue(cycle.hasNext()); - try { - cycle.remove(); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> cycle.remove()); } public void testCycleRemoveSameElementTwice() { Iterator cycle = Iterators.cycle("a", "b"); cycle.next(); cycle.remove(); - try { - cycle.remove(); - fail("no exception thrown"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> cycle.remove()); } public void testCycleWhenRemoveIsNotSupported() { Iterable iterable = asList("a", "b"); Iterator cycle = Iterators.cycle(iterable); cycle.next(); - try { - cycle.remove(); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> cycle.remove()); } public void testCycleRemoveAfterHasNext() { @@ -617,7 +567,7 @@ public void testCycleRemoveAfterHasNext() { assertEquals("a", cycle.next()); assertTrue(cycle.hasNext()); cycle.remove(); - assertEquals(Collections.emptyList(), iterable); + assertEquals(emptyList(), iterable); assertFalse(cycle.hasNext()); } @@ -674,7 +624,7 @@ void checkConcurrentModification() { } public void testCycleRemoveAfterHasNextExtraPicky() { - PickyIterable iterable = new PickyIterable("a"); + PickyIterable iterable = new PickyIterable<>("a"); Iterator cycle = Iterators.cycle(iterable); assertTrue(cycle.hasNext()); assertEquals("a", cycle.next()); @@ -691,11 +641,7 @@ public void testCycleNoSuchElementException() { assertEquals("a", cycle.next()); cycle.remove(); assertFalse(cycle.hasNext()); - try { - cycle.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> cycle.next()); } @GwtIncompatible // unreasonably slow @@ -715,7 +661,6 @@ protected Iterator newTargetIterator() { @GwtIncompatible // slow (~5s) public void testConcatNoIteratorsYieldsEmpty() { new EmptyIteratorTester() { - @SuppressWarnings("unchecked") @Override protected Iterator newTargetIterator() { return Iterators.concat(); @@ -726,7 +671,6 @@ protected Iterator newTargetIterator() { @GwtIncompatible // slow (~5s) public void testConcatOneEmptyIteratorYieldsEmpty() { new EmptyIteratorTester() { - @SuppressWarnings("unchecked") @Override protected Iterator newTargetIterator() { return Iterators.concat(iterateOver()); @@ -747,7 +691,6 @@ protected Iterator newTargetIterator() { @GwtIncompatible // slow (~3s) public void testConcatSingletonYieldsSingleton() { new SingletonIteratorTester() { - @SuppressWarnings("unchecked") @Override protected Iterator newTargetIterator() { return Iterators.concat(iterateOver(1)); @@ -798,52 +741,43 @@ protected Iterator newTargetIterator() { } public void testConcatPartiallyAdvancedSecond() { - Iterator itr1 = - Iterators.concat(Iterators.singletonIterator("a"), Iterators.forArray("b", "c")); + Iterator itr1 = Iterators.concat(singletonIterator("a"), Iterators.forArray("b", "c")); assertEquals("a", itr1.next()); assertEquals("b", itr1.next()); - Iterator itr2 = Iterators.concat(Iterators.singletonIterator("d"), itr1); + Iterator itr2 = Iterators.concat(singletonIterator("d"), itr1); assertEquals("d", itr2.next()); assertEquals("c", itr2.next()); } public void testConcatPartiallyAdvancedFirst() { - Iterator itr1 = - Iterators.concat(Iterators.singletonIterator("a"), Iterators.forArray("b", "c")); + Iterator itr1 = Iterators.concat(singletonIterator("a"), Iterators.forArray("b", "c")); assertEquals("a", itr1.next()); assertEquals("b", itr1.next()); - Iterator itr2 = Iterators.concat(itr1, Iterators.singletonIterator("d")); + Iterator itr2 = Iterators.concat(itr1, singletonIterator("d")); assertEquals("c", itr2.next()); assertEquals("d", itr2.next()); } /** Illustrates the somewhat bizarre behavior when a null is passed in. */ public void testConcatContainingNull() { - @SuppressWarnings("unchecked") - Iterator> input = asList(iterateOver(1, 2), null, iterateOver(3)).iterator(); + Iterator> input = + (Iterator>) + Arrays.<@Nullable Iterator>asList(iterateOver(1, 2), null, iterateOver(3)) + .iterator(); Iterator result = Iterators.concat(input); assertEquals(1, (int) result.next()); assertEquals(2, (int) result.next()); - try { - result.hasNext(); - fail("no exception thrown"); - } catch (NullPointerException e) { - } - try { - result.next(); - fail("no exception thrown"); - } catch (NullPointerException e) { - } + assertThrows(NullPointerException.class, () -> result.hasNext()); + assertThrows(NullPointerException.class, () -> result.next()); // There is no way to get "through" to the 3. Buh-bye } - @SuppressWarnings("unchecked") public void testConcatVarArgsContainingNull() { - try { - Iterators.concat(iterateOver(1, 2), null, iterateOver(3), iterateOver(4), iterateOver(5)); - fail("no exception thrown"); - } catch (NullPointerException e) { - } + assertThrows( + NullPointerException.class, + () -> + Iterators.concat( + iterateOver(1, 2), null, iterateOver(3), iterateOver(4), iterateOver(5))); } public void testConcatNested_appendToEnd() { @@ -891,6 +825,7 @@ public void testAddAllToSet() { assertFalse(changed); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -918,8 +853,9 @@ protected DoubletonIteratorTester() { } } - private static Iterator iterateOver(final Integer... values) { - return newArrayList(values).iterator(); + private static Iterator iterateOver(int... values) { + // Note: Ints.asList's iterator does not support remove which we need for testing. + return new ArrayList<>(Ints.asList(values)).iterator(); } public void testElementsEqual() { @@ -928,65 +864,61 @@ public void testElementsEqual() { // Base case. a = Lists.newArrayList(); - b = Collections.emptySet(); - assertTrue(Iterators.elementsEqual(a.iterator(), b.iterator())); + b = emptySet(); + assertTrue(elementsEqual(a.iterator(), b.iterator())); // A few elements. a = asList(4, 8, 15, 16, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertTrue(Iterators.elementsEqual(a.iterator(), b.iterator())); + assertTrue(elementsEqual(a.iterator(), b.iterator())); // The same, but with nulls. - a = asList(4, 8, null, 16, 23, 42); - b = asList(4, 8, null, 16, 23, 42); - assertTrue(Iterators.elementsEqual(a.iterator(), b.iterator())); + a = Arrays.<@Nullable Integer>asList(4, 8, null, 16, 23, 42); + b = Arrays.<@Nullable Integer>asList(4, 8, null, 16, 23, 42); + assertTrue(elementsEqual(a.iterator(), b.iterator())); // Different Iterable types (still equal elements, though). a = ImmutableList.of(4, 8, 15, 16, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertTrue(Iterators.elementsEqual(a.iterator(), b.iterator())); + assertTrue(elementsEqual(a.iterator(), b.iterator())); // An element differs. a = asList(4, 8, 15, 12, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterators.elementsEqual(a.iterator(), b.iterator())); + assertFalse(elementsEqual(a.iterator(), b.iterator())); // null versus non-null. - a = asList(4, 8, 15, null, 23, 42); + a = Arrays.<@Nullable Integer>asList(4, 8, 15, null, 23, 42); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterators.elementsEqual(a.iterator(), b.iterator())); - assertFalse(Iterators.elementsEqual(b.iterator(), a.iterator())); + assertFalse(elementsEqual(a.iterator(), b.iterator())); + assertFalse(elementsEqual(b.iterator(), a.iterator())); // Different lengths. a = asList(4, 8, 15, 16, 23); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterators.elementsEqual(a.iterator(), b.iterator())); - assertFalse(Iterators.elementsEqual(b.iterator(), a.iterator())); + assertFalse(elementsEqual(a.iterator(), b.iterator())); + assertFalse(elementsEqual(b.iterator(), a.iterator())); // Different lengths, one is empty. - a = Collections.emptySet(); + a = emptySet(); b = asList(4, 8, 15, 16, 23, 42); - assertFalse(Iterators.elementsEqual(a.iterator(), b.iterator())); - assertFalse(Iterators.elementsEqual(b.iterator(), a.iterator())); + assertFalse(elementsEqual(a.iterator(), b.iterator())); + assertFalse(elementsEqual(b.iterator(), a.iterator())); } public void testPartition_badSize() { - Iterator source = Iterators.singletonIterator(1); - try { - Iterators.partition(source, 0); - fail(); - } catch (IllegalArgumentException expected) { - } + Iterator source = singletonIterator(1); + assertThrows(IllegalArgumentException.class, () -> Iterators.partition(source, 0)); } public void testPartition_empty() { - Iterator source = Iterators.emptyIterator(); + Iterator source = emptyIterator(); Iterator> partitions = Iterators.partition(source, 1); assertFalse(partitions.hasNext()); } public void testPartition_singleton1() { - Iterator source = Iterators.singletonIterator(1); + Iterator source = singletonIterator(1); Iterator> partitions = Iterators.partition(source, 1); assertTrue(partitions.hasNext()); assertTrue(partitions.hasNext()); @@ -995,7 +927,7 @@ public void testPartition_singleton1() { } public void testPartition_singleton2() { - Iterator source = Iterators.singletonIterator(1); + Iterator source = singletonIterator(1); Iterator> partitions = Iterators.partition(source, 2); assertTrue(partitions.hasNext()); assertTrue(partitions.hasNext()); @@ -1032,8 +964,8 @@ public void testPartition_view() { assertEquals(ImmutableList.of(3), first); } - @GwtIncompatible // ? - // TODO: Figure out why this is failing in GWT. + @J2ktIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in J2KT. + @GwtIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in GWT public void testPartitionRandomAccess() { Iterator source = asList(1, 2, 3).iterator(); Iterator> partitions = Iterators.partition(source, 2); @@ -1042,22 +974,18 @@ public void testPartitionRandomAccess() { } public void testPaddedPartition_badSize() { - Iterator source = Iterators.singletonIterator(1); - try { - Iterators.paddedPartition(source, 0); - fail(); - } catch (IllegalArgumentException expected) { - } + Iterator source = singletonIterator(1); + assertThrows(IllegalArgumentException.class, () -> Iterators.paddedPartition(source, 0)); } public void testPaddedPartition_empty() { - Iterator source = Iterators.emptyIterator(); + Iterator source = emptyIterator(); Iterator> partitions = Iterators.paddedPartition(source, 1); assertFalse(partitions.hasNext()); } public void testPaddedPartition_singleton1() { - Iterator source = Iterators.singletonIterator(1); + Iterator source = singletonIterator(1); Iterator> partitions = Iterators.paddedPartition(source, 1); assertTrue(partitions.hasNext()); assertTrue(partitions.hasNext()); @@ -1066,21 +994,21 @@ public void testPaddedPartition_singleton1() { } public void testPaddedPartition_singleton2() { - Iterator source = Iterators.singletonIterator(1); + Iterator source = singletonIterator(1); Iterator> partitions = Iterators.paddedPartition(source, 2); assertTrue(partitions.hasNext()); assertTrue(partitions.hasNext()); - assertEquals(asList(1, null), partitions.next()); + assertEquals(Arrays.<@Nullable Integer>asList(1, null), partitions.next()); assertFalse(partitions.hasNext()); } @GwtIncompatible // fairly slow (~50s) public void testPaddedPartition_general() { + ImmutableList> expectedElements = + ImmutableList.of( + asList(1, 2, 3), asList(4, 5, 6), Arrays.<@Nullable Integer>asList(7, null, null)); new IteratorTester>( - 5, - IteratorFeature.UNMODIFIABLE, - ImmutableList.of(asList(1, 2, 3), asList(4, 5, 6), asList(7, null, null)), - IteratorTester.KnownOrder.KNOWN_ORDER) { + 5, IteratorFeature.UNMODIFIABLE, expectedElements, IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator> newTargetIterator() { Iterator source = Iterators.forArray(1, 2, 3, 4, 5, 6, 7); @@ -1114,11 +1042,8 @@ public void testForArrayEmpty() { String[] array = new String[0]; Iterator iterator = Iterators.forArray(array); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); + assertThrows(IndexOutOfBoundsException.class, () -> Iterators.forArrayWithPosition(array, 1)); } @SuppressWarnings("DoNotCall") @@ -1128,50 +1053,27 @@ public void testForArrayTypical() { assertTrue(iterator.hasNext()); assertEquals("foo", iterator.next()); assertTrue(iterator.hasNext()); - try { - iterator.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); assertEquals("bar", iterator.next()); assertFalse(iterator.hasNext()); - try { - iterator.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> iterator.next()); } - public void testForArrayOffset() { - String[] array = {"foo", "bar", "cat", "dog"}; - Iterator iterator = Iterators.forArray(array, 1, 2, 0); + public void testForArrayWithPosition() { + String[] array = {"foo", "bar", "cat"}; + Iterator iterator = Iterators.forArrayWithPosition(array, 1); assertTrue(iterator.hasNext()); assertEquals("bar", iterator.next()); assertTrue(iterator.hasNext()); assertEquals("cat", iterator.next()); assertFalse(iterator.hasNext()); - try { - Iterators.forArray(array, 2, 3, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } } - public void testForArrayLength0() { + public void testForArrayLengthWithPositionBoundaryCases() { String[] array = {"foo", "bar"}; - assertFalse(Iterators.forArray(array, 0, 0, 0).hasNext()); - assertFalse(Iterators.forArray(array, 1, 0, 0).hasNext()); - assertFalse(Iterators.forArray(array, 2, 0, 0).hasNext()); - try { - Iterators.forArray(array, -1, 0, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - Iterators.forArray(array, 3, 0, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertFalse(Iterators.forArrayWithPosition(array, 2).hasNext()); + assertThrows(IndexOutOfBoundsException.class, () -> Iterators.forArrayWithPosition(array, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> Iterators.forArrayWithPosition(array, 3)); } @GwtIncompatible // unreasonably slow @@ -1185,27 +1087,17 @@ protected Iterator newTargetIterator() { }.test(); } - @GwtIncompatible // unreasonably slow - public void testForArrayWithOffsetUsingTester() { - new IteratorTester( - 6, UNMODIFIABLE, asList(1, 2, 3), IteratorTester.KnownOrder.KNOWN_ORDER) { - @Override - protected Iterator newTargetIterator() { - return Iterators.forArray(new Integer[] {0, 1, 2, 3, 4}, 1, 3, 0); - } - }.test(); - } + /* + * TODO(cpovirk): Test forArray with ListIteratorTester (not just IteratorTester), including with + * a start position other than 0. + */ public void testForEnumerationEmpty() { Enumeration enumer = enumerate(); Iterator iter = Iterators.forEnumeration(enumer); assertFalse(iter.hasNext()); - try { - iter.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> iter.next()); } @SuppressWarnings("DoNotCall") @@ -1216,17 +1108,9 @@ public void testForEnumerationSingleton() { assertTrue(iter.hasNext()); assertTrue(iter.hasNext()); assertEquals(1, (int) iter.next()); - try { - iter.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iter.remove()); assertFalse(iter.hasNext()); - try { - iter.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> iter.next()); } public void testForEnumerationTypical() { @@ -1243,15 +1127,11 @@ public void testForEnumerationTypical() { } public void testAsEnumerationEmpty() { - Iterator iter = Iterators.emptyIterator(); + Iterator iter = emptyIterator(); Enumeration enumer = Iterators.asEnumeration(iter); assertFalse(enumer.hasMoreElements()); - try { - enumer.nextElement(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> enumer.nextElement()); } public void testAsEnumerationSingleton() { @@ -1262,11 +1142,7 @@ public void testAsEnumerationSingleton() { assertTrue(enumer.hasMoreElements()); assertEquals(1, (int) enumer.nextElement()); assertFalse(enumer.hasMoreElements()); - try { - enumer.nextElement(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> enumer.nextElement()); } public void testAsEnumerationTypical() { @@ -1282,8 +1158,10 @@ public void testAsEnumerationTypical() { assertFalse(enumer.hasMoreElements()); } - private static Enumeration enumerate(Integer... ints) { - Vector vector = new Vector<>(asList(ints)); + // We're testing our asEnumeration method against a known-good implementation. + @SuppressWarnings("JdkObsolete") + private static Enumeration enumerate(int... ints) { + Vector vector = new Vector<>(Ints.asList(ints)); return vector.elements(); } @@ -1293,7 +1171,8 @@ public void testToString() { } public void testToStringWithNull() { - Iterator iterator = Lists.newArrayList("hello", null, "world").iterator(); + Iterator<@Nullable String> iterator = + Lists.<@Nullable String>newArrayList("hello", null, "world").iterator(); assertEquals("[hello, null, world]", Iterators.toString(iterator)); } @@ -1302,13 +1181,10 @@ public void testToStringEmptyIterator() { assertEquals("[]", Iterators.toString(iterator)); } + @SuppressWarnings("JUnitIncompatibleType") // Fails with j2kt. public void testLimit() { List list = newArrayList(); - try { - Iterators.limit(list.iterator(), -1); - fail("expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Iterators.limit(list.iterator(), -1)); assertFalse(Iterators.limit(list.iterator(), 0).hasNext()); assertFalse(Iterators.limit(list.iterator(), 1).hasNext()); @@ -1350,18 +1226,18 @@ protected Iterator newTargetIterator() { } public void testGetNext_withDefault_singleton() { - Iterator iterator = Collections.singletonList("foo").iterator(); + Iterator iterator = singletonList("foo").iterator(); assertEquals("foo", Iterators.getNext(iterator, "bar")); } public void testGetNext_withDefault_empty() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); assertEquals("bar", Iterators.getNext(iterator, "bar")); } public void testGetNext_withDefault_empty_null() { - Iterator iterator = Iterators.emptyIterator(); - assertNull(Iterators.getNext(iterator, null)); + Iterator iterator = emptyIterator(); + assertNull(Iterators.<@Nullable String>getNext(iterator, null)); } public void testGetNext_withDefault_two() { @@ -1378,26 +1254,22 @@ public void testGetLast_basic() { public void testGetLast_exception() { List list = newArrayList(); - try { - getLast(list.iterator()); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> getLast(list.iterator())); } public void testGetLast_withDefault_singleton() { - Iterator iterator = Collections.singletonList("foo").iterator(); + Iterator iterator = singletonList("foo").iterator(); assertEquals("foo", Iterators.getLast(iterator, "bar")); } public void testGetLast_withDefault_empty() { - Iterator iterator = Iterators.emptyIterator(); + Iterator iterator = emptyIterator(); assertEquals("bar", Iterators.getLast(iterator, "bar")); } public void testGetLast_withDefault_empty_null() { - Iterator iterator = Iterators.emptyIterator(); - assertNull(Iterators.getLast(iterator, null)); + Iterator iterator = emptyIterator(); + assertNull(Iterators.<@Nullable String>getLast(iterator, null)); } public void testGetLast_withDefault_two() { @@ -1419,11 +1291,7 @@ public void testGet_atSize() { list.add("a"); list.add("b"); Iterator iterator = list.iterator(); - try { - get(iterator, 2); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> get(iterator, 2)); assertFalse(iterator.hasNext()); } @@ -1432,33 +1300,21 @@ public void testGet_pastEnd() { list.add("a"); list.add("b"); Iterator iterator = list.iterator(); - try { - get(iterator, 5); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> get(iterator, 5)); assertFalse(iterator.hasNext()); } public void testGet_empty() { List list = newArrayList(); Iterator iterator = list.iterator(); - try { - get(iterator, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> get(iterator, 0)); assertFalse(iterator.hasNext()); } public void testGet_negativeIndex() { List list = newArrayList("a", "b", "c"); Iterator iterator = list.iterator(); - try { - get(iterator, -1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> get(iterator, -1)); } public void testGet_withDefault_basic() { @@ -1493,12 +1349,7 @@ public void testGet_withDefault_negativeIndex() { list.add("a"); list.add("b"); Iterator iterator = list.iterator(); - try { - get(iterator, -1, "c"); - fail(); - } catch (IndexOutOfBoundsException expected) { - // pass - } + assertThrows(IndexOutOfBoundsException.class, () -> get(iterator, -1, "c")); assertTrue(iterator.hasNext()); } @@ -1523,20 +1374,16 @@ public void testAdvance_pastEnd() { public void testAdvance_illegalArgument() { List list = newArrayList("a", "b", "c"); Iterator iterator = list.iterator(); - try { - advance(iterator, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> advance(iterator, -1)); } public void testFrequency() { - List list = newArrayList("a", null, "b", null, "a", null); - assertEquals(2, Iterators.frequency(list.iterator(), "a")); - assertEquals(1, Iterators.frequency(list.iterator(), "b")); - assertEquals(0, Iterators.frequency(list.iterator(), "c")); - assertEquals(0, Iterators.frequency(list.iterator(), 4.2)); - assertEquals(3, Iterators.frequency(list.iterator(), null)); + List<@Nullable String> list = newArrayList("a", null, "b", null, "a", null); + assertEquals(2, frequency(list.iterator(), "a")); + assertEquals(1, frequency(list.iterator(), "b")); + assertEquals(0, frequency(list.iterator(), "c")); + assertEquals(0, frequency(list.iterator(), 4.2)); + assertEquals(3, frequency(list.iterator(), null)); } @GwtIncompatible // slow (~4s) @@ -1545,7 +1392,7 @@ public void testSingletonIterator() { 3, UNMODIFIABLE, singleton(1), IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { - return Iterators.singletonIterator(1); + return singletonIterator(1); } }.test(); } @@ -1590,7 +1437,9 @@ public void testRetainAll() { assertEquals(newArrayList("b", "d"), list); } + @J2ktIncompatible @GwtIncompatible // ListTestSuiteBuilder + @AndroidIncompatible // test-suite builders private static Test testsForRemoveAllAndRetainAll() { return ListTestSuiteBuilder.using( new TestStringListGenerator() { @@ -1655,24 +1504,19 @@ public void testConsumingIterator_duelingIterators() { Iterator i2 = Iterators.consumingIterator(list.iterator()); i1.next(); - try { - i2.next(); - fail("Concurrent modification should throw an exception."); - } catch (ConcurrentModificationException cme) { - // Pass - } + assertThrows(ConcurrentModificationException.class, () -> i2.next()); } public void testIndexOf_consumedData() { Iterator iterator = Lists.newArrayList("manny", "mo", "jack").iterator(); - assertEquals(1, Iterators.indexOf(iterator, Predicates.equalTo("mo"))); + assertEquals(1, Iterators.indexOf(iterator, equalTo("mo"))); assertEquals("jack", iterator.next()); assertFalse(iterator.hasNext()); } public void testIndexOf_consumedDataWithDuplicates() { Iterator iterator = Lists.newArrayList("manny", "mo", "mo", "jack").iterator(); - assertEquals(1, Iterators.indexOf(iterator, Predicates.equalTo("mo"))); + assertEquals(1, Iterators.indexOf(iterator, equalTo("mo"))); assertEquals("mo", iterator.next()); assertEquals("jack", iterator.next()); assertFalse(iterator.hasNext()); @@ -1680,11 +1524,11 @@ public void testIndexOf_consumedDataWithDuplicates() { public void testIndexOf_consumedDataNoMatch() { Iterator iterator = Lists.newArrayList("manny", "mo", "mo", "jack").iterator(); - assertEquals(-1, Iterators.indexOf(iterator, Predicates.equalTo("bob"))); + assertEquals(-1, Iterators.indexOf(iterator, equalTo("bob"))); assertFalse(iterator.hasNext()); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableIteratorShortCircuit() { Iterator mod = Lists.newArrayList("a", "b", "c").iterator(); UnmodifiableIterator unmod = Iterators.unmodifiableIterator(mod); @@ -1693,7 +1537,7 @@ public void testUnmodifiableIteratorShortCircuit() { assertSame(unmod, Iterators.unmodifiableIterator((Iterator) unmod)); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testPeekingIteratorShortCircuit() { Iterator nonpeek = Lists.newArrayList("a", "b", "c").iterator(); PeekingIterator peek = Iterators.peekingIterator(nonpeek); diff --git a/android/guava-tests/test/com/google/common/collect/LegacyComparable.java b/android/guava-tests/test/com/google/common/collect/LegacyComparable.java index 8b0c9620cd68..941471b7a2f3 100644 --- a/android/guava-tests/test/com/google/common/collect/LegacyComparable.java +++ b/android/guava-tests/test/com/google/common/collect/LegacyComparable.java @@ -16,9 +16,14 @@ package com.google.common.collect; +import static java.util.Arrays.asList; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import java.util.Arrays; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A class that implements {@code Comparable} without generics, such as those found in libraries @@ -27,15 +32,16 @@ * * @author Kevin Bourrillion */ -@SuppressWarnings("ComparableType") +@SuppressWarnings({"ComparableType", "rawtypes"}) // https://github.com/google/guava/issues/989 @GwtCompatible +@NullMarked class LegacyComparable implements Comparable, Serializable { static final LegacyComparable X = new LegacyComparable("x"); static final LegacyComparable Y = new LegacyComparable("y"); static final LegacyComparable Z = new LegacyComparable("z"); - static final Iterable VALUES_FORWARD = Arrays.asList(X, Y, Z); - static final Iterable VALUES_BACKWARD = Arrays.asList(Z, Y, X); + static final Iterable VALUES_FORWARD = asList(X, Y, Z); + static final Iterable VALUES_BACKWARD = asList(Z, Y, X); private final String value; @@ -51,7 +57,7 @@ public int compareTo(Object object) { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof LegacyComparable) { LegacyComparable that = (LegacyComparable) object; return this.value.equals(that.value); @@ -64,5 +70,5 @@ public int hashCode() { return value.hashCode(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java b/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java index ce3ec9d9f1fa..51378a31b49e 100644 --- a/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java +++ b/android/guava-tests/test/com/google/common/collect/LenientSerializableTester.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import static com.google.common.collect.Iterables.elementsEqual; import static com.google.common.testing.SerializableTester.reserialize; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -26,6 +27,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; /** * Variant of {@link SerializableTester} that does not require the reserialized object's class to be @@ -38,6 +40,7 @@ * parameter for non-GWT, non-test files, and it didn't seem worth adding one for this unusual case. */ @GwtCompatible(emulated = true) +@NullUnmarked final class LenientSerializableTester { /* * TODO(cpovirk): move this to c.g.c.testing if we allow for c.g.c.annotations dependencies so @@ -65,7 +68,7 @@ static Multiset reserializeAndAssertLenient(Multiset original) { @GwtIncompatible // SerializableTester static Collection reserializeAndAssertElementsEqual(Collection original) { Collection copy = reserialize(original); - assertTrue(Iterables.elementsEqual(original, copy)); + assertTrue(elementsEqual(original, copy)); assertTrue(copy instanceof ImmutableCollection); return copy; } diff --git a/android/guava-tests/test/com/google/common/collect/LinkedHashMultimapTest.java b/android/guava-tests/test/com/google/common/collect/LinkedHashMultimapTest.java index 92d8e052a8d0..c14bad731170 100644 --- a/android/guava-tests/test/com/google/common/collect/LinkedHashMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/LinkedHashMultimapTest.java @@ -17,6 +17,9 @@ package com.google.common.collect; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Multimaps.synchronizedMultimap; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newLinkedHashSet; import static com.google.common.collect.testing.Helpers.mapEntry; @@ -26,6 +29,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -34,7 +38,6 @@ import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -43,6 +46,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@code LinkedHashMultimap}. @@ -50,9 +55,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class LinkedHashMultimapTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -114,8 +122,8 @@ public void testToString() { Multimap multimap = LinkedHashMultimap.create(); multimap.put("foo", 3); multimap.put("bar", 1); - multimap.putAll("foo", Arrays.asList(-1, 2, 4)); - multimap.putAll("bar", Arrays.asList(2, 3)); + multimap.putAll("foo", asList(-1, 2, 4)); + multimap.putAll("bar", asList(2, 3)); multimap.put("foo", 1); assertEquals("{foo=[3, -1, 2, 4, 1], bar=[1, 2, 3]}", multimap.toString()); } @@ -130,11 +138,13 @@ public void testOrderingUnmodifiable() { assertOrderingReadOnly(Multimaps.unmodifiableMultimap(multimap)); } + @J2ktIncompatible // Synchronized public void testOrderingSynchronized() { Multimap multimap = initializeMultimap5(); - assertOrderingReadOnly(Multimaps.synchronizedMultimap(multimap)); + assertOrderingReadOnly(synchronizedMultimap(multimap)); } + @J2ktIncompatible @GwtIncompatible // SeriazableTester public void testSerializationOrdering() { Multimap multimap = initializeMultimap5(); @@ -142,6 +152,7 @@ public void testSerializationOrdering() { assertOrderingReadOnly(copy); } + @J2ktIncompatible @GwtIncompatible // SeriazableTester public void testSerializationOrderingKeysAndEntries() { Multimap multimap = LinkedHashMultimap.create(); @@ -167,11 +178,11 @@ private void assertOrderingReadOnly(Multimap multimap) { assertThat(multimap.values()).containsExactly(5, 4, 3, 2, 1).inOrder(); Iterator> entryIterator = multimap.entries().iterator(); - assertEquals(Maps.immutableEntry("foo", 5), entryIterator.next()); - assertEquals(Maps.immutableEntry("bar", 4), entryIterator.next()); - assertEquals(Maps.immutableEntry("foo", 3), entryIterator.next()); - assertEquals(Maps.immutableEntry("cow", 2), entryIterator.next()); - assertEquals(Maps.immutableEntry("bar", 1), entryIterator.next()); + assertEquals(immutableEntry("foo", 5), entryIterator.next()); + assertEquals(immutableEntry("bar", 4), entryIterator.next()); + assertEquals(immutableEntry("foo", 3), entryIterator.next()); + assertEquals(immutableEntry("cow", 2), entryIterator.next()); + assertEquals(immutableEntry("bar", 1), entryIterator.next()); Iterator>> collectionIterator = multimap.asMap().entrySet().iterator(); @@ -202,7 +213,7 @@ public void testOrderingUpdates() { } public void testToStringNullExact() { - Multimap multimap = LinkedHashMultimap.create(); + Multimap<@Nullable String, @Nullable Integer> multimap = LinkedHashMultimap.create(); multimap.put("foo", 3); multimap.put("foo", -1); @@ -262,17 +273,9 @@ public void testCreateFromSizes() { } public void testCreateFromIllegalSizes() { - try { - LinkedHashMultimap.create(-20, 15); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LinkedHashMultimap.create(-20, 15)); - try { - LinkedHashMultimap.create(20, -15); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LinkedHashMultimap.create(20, -15)); } @GwtIncompatible // unreasonably slow @@ -282,7 +285,7 @@ public void testGetIteration() { MODIFIABLE, newLinkedHashSet(asList(2, 3, 4, 7, 8)), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -302,19 +305,18 @@ protected void verify(List elements) { @GwtIncompatible // unreasonably slow public void testEntriesIteration() { - @SuppressWarnings("unchecked") Set> set = Sets.newLinkedHashSet( asList( - Maps.immutableEntry("foo", 2), - Maps.immutableEntry("foo", 3), - Maps.immutableEntry("bar", 4), - Maps.immutableEntry("bar", 5), - Maps.immutableEntry("foo", 6))); + immutableEntry("foo", 2), + immutableEntry("foo", 3), + immutableEntry("bar", 4), + immutableEntry("bar", 5), + immutableEntry("foo", 6))); new IteratorTester>( 6, MODIFIABLE, set, IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator> newTargetIterator() { @@ -339,7 +341,7 @@ public void testKeysIteration() { MODIFIABLE, newArrayList("foo", "foo", "bar", "bar", "foo"), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -361,7 +363,7 @@ protected void verify(List elements) { public void testValuesIteration() { new IteratorTester( 6, MODIFIABLE, newArrayList(2, 3, 4, 5, 6), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -386,7 +388,7 @@ public void testKeySetIteration() { MODIFIABLE, newLinkedHashSet(asList("foo", "bar", "baz", "dog", "cat")), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -410,18 +412,17 @@ protected void verify(List elements) { @GwtIncompatible // unreasonably slow public void testAsSetIteration() { - @SuppressWarnings("unchecked") Set>> set = newLinkedHashSet( asList( - Maps.immutableEntry("foo", (Collection) Sets.newHashSet(2, 3, 6)), - Maps.immutableEntry("bar", (Collection) Sets.newHashSet(4, 5, 10, 11)), - Maps.immutableEntry("baz", (Collection) Sets.newHashSet(7, 8)), - Maps.immutableEntry("dog", (Collection) Sets.newHashSet(9)), - Maps.immutableEntry("cat", (Collection) Sets.newHashSet(12, 13, 14)))); + immutableEntry("foo", (Collection) newHashSet(2, 3, 6)), + immutableEntry("bar", (Collection) newHashSet(4, 5, 10, 11)), + immutableEntry("baz", (Collection) newHashSet(7, 8)), + immutableEntry("dog", (Collection) newHashSet(9)), + immutableEntry("cat", (Collection) newHashSet(12, 13, 14)))); new IteratorTester>>( 6, MODIFIABLE, set, IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator>> newTargetIterator() { diff --git a/android/guava-tests/test/com/google/common/collect/LinkedHashMultisetTest.java b/android/guava-tests/test/com/google/common/collect/LinkedHashMultisetTest.java index da7e86867d10..adfbfe656341 100644 --- a/android/guava-tests/test/com/google/common/collect/LinkedHashMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/LinkedHashMultisetTest.java @@ -21,16 +21,17 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.google.MultisetFeature; import com.google.common.collect.testing.google.MultisetTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringMultisetGenerator; -import java.util.Arrays; import java.util.List; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link LinkedHashMultiset}. @@ -38,9 +39,12 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class LinkedHashMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -59,6 +63,8 @@ public static Test suite() { return suite; } + @J2ktIncompatible + @AndroidIncompatible // test-suite builders private static TestStringMultisetGenerator linkedHashMultisetGenerator() { return new TestStringMultisetGenerator() { @Override @@ -101,7 +107,7 @@ public void testCreateWithSize() { } public void testCreateFromIterable() { - Multiset multiset = LinkedHashMultiset.create(Arrays.asList("foo", "bar", "foo")); + Multiset multiset = LinkedHashMultiset.create(asList("foo", "bar", "foo")); assertEquals(3, multiset.size()); assertEquals(2, multiset.count("foo")); assertEquals("[foo x 2, bar]", multiset.toString()); diff --git a/android/guava-tests/test/com/google/common/collect/LinkedListMultimapTest.java b/android/guava-tests/test/com/google/common/collect/LinkedListMultimapTest.java index b0d48f0f4d66..b047eb3e650b 100644 --- a/android/guava-tests/test/com/google/common/collect/LinkedListMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/LinkedListMultimapTest.java @@ -17,6 +17,8 @@ package com.google.common.collect; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newLinkedHashSet; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; @@ -24,9 +26,11 @@ import static com.google.common.collect.testing.IteratorFeature.SUPPORTS_SET; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.ListIteratorTester; import com.google.common.collect.testing.features.CollectionFeature; @@ -38,7 +42,6 @@ import com.google.common.testing.NullPointerTester; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -48,6 +51,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code LinkedListMultimap}. @@ -55,9 +60,12 @@ * @author Mike Bostock */ @GwtCompatible(emulated = true) +@NullMarked public class LinkedListMultimapTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -120,8 +128,8 @@ public void testReplaceValuesRandomAccess() { Multimap multimap = create(); multimap.put("foo", 1); multimap.put("foo", 3); - assertTrue(multimap.replaceValues("foo", Arrays.asList(2, 4)) instanceof RandomAccess); - assertTrue(multimap.replaceValues("bar", Arrays.asList(2, 4)) instanceof RandomAccess); + assertTrue(multimap.replaceValues("foo", asList(2, 4)) instanceof RandomAccess); + assertTrue(multimap.replaceValues("bar", asList(2, 4)) instanceof RandomAccess); } public void testCreateFromMultimap() { @@ -143,11 +151,7 @@ public void testCreateFromSize() { } public void testCreateFromIllegalSize() { - try { - LinkedListMultimap.create(-20); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LinkedListMultimap.create(-20)); } public void testLinkedGetAdd() { @@ -225,7 +229,7 @@ public void testLinkedClear() { assertEquals(asList(1, 2), foos); assertThat(values).containsExactly(1, 2, 3).inOrder(); map.clear(); - assertEquals(Collections.emptyList(), foos); + assertEquals(emptyList(), foos); assertThat(values).isEmpty(); assertEquals("[]", map.entries().toString()); assertEquals("{}", map.toString()); @@ -292,18 +296,15 @@ public void testLinkedAsMapEntries() { map.put("foo", 2); map.put("bar", 3); Iterator>> entries = map.asMap().entrySet().iterator(); - Entry> entry = entries.next(); - assertEquals("bar", entry.getKey()); - assertThat(entry.getValue()).containsExactly(1, 3).inOrder(); - try { - entry.setValue(Arrays.asList()); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + Entry> barEntry = entries.next(); + assertEquals("bar", barEntry.getKey()); + assertThat(barEntry.getValue()).containsExactly(1, 3).inOrder(); + assertThrows( + UnsupportedOperationException.class, () -> barEntry.setValue(Arrays.asList())); entries.remove(); // clear - entry = entries.next(); - assertEquals("foo", entry.getKey()); - assertThat(entry.getValue()).contains(2); + Entry> fooEntry = entries.next(); + assertEquals("foo", fooEntry.getKey()); + assertThat(fooEntry.getValue()).contains(2); assertFalse(entries.hasNext()); assertEquals("{foo=[2]}", map.toString()); } @@ -332,26 +333,23 @@ public void testEntriesAfterMultimapUpdate() { assertEquals(3, (int) entryb.getValue()); } - @SuppressWarnings("unchecked") @GwtIncompatible // unreasonably slow public void testEntriesIteration() { List> addItems = ImmutableList.of( - Maps.immutableEntry("foo", 99), - Maps.immutableEntry("foo", 88), - Maps.immutableEntry("bar", 77)); + immutableEntry("foo", 99), immutableEntry("foo", 88), immutableEntry("bar", 77)); for (final int startIndex : new int[] {0, 3, 5}) { List> list = Lists.newArrayList( - Maps.immutableEntry("foo", 2), - Maps.immutableEntry("foo", 3), - Maps.immutableEntry("bar", 4), - Maps.immutableEntry("bar", 5), - Maps.immutableEntry("foo", 6)); + immutableEntry("foo", 2), + immutableEntry("foo", 3), + immutableEntry("bar", 4), + immutableEntry("bar", 5), + immutableEntry("foo", 6)); new ListIteratorTester>( 3, addItems, ImmutableList.of(SUPPORTS_REMOVE), list, startIndex) { - private LinkedListMultimap multimap; + private @Nullable LinkedListMultimap multimap; @Override protected ListIterator> newTargetIterator() { @@ -377,7 +375,7 @@ public void testKeysIteration() { MODIFIABLE, newArrayList("foo", "foo", "bar", "bar", "foo"), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -406,13 +404,13 @@ public void testValuesIteration() { ImmutableList.of(SUPPORTS_REMOVE, SUPPORTS_SET), Lists.newArrayList(2, 3, 4, 5, 6), startIndex) { - private LinkedListMultimap multimap; + private @Nullable LinkedListMultimap multimap; @Override protected ListIterator newTargetIterator() { multimap = create(); multimap.put("bar", 2); - multimap.putAll("foo", Arrays.asList(3, 4)); + multimap.putAll("foo", asList(3, 4)); multimap.put("bar", 5); multimap.put("foo", 6); return multimap.values().listIterator(startIndex); @@ -433,7 +431,7 @@ public void testKeySetIteration() { MODIFIABLE, newLinkedHashSet(asList("foo", "bar", "baz", "dog", "cat")), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -455,21 +453,20 @@ protected void verify(List elements) { }.test(); } - @SuppressWarnings("unchecked") @GwtIncompatible // unreasonably slow public void testAsSetIteration() { Set>> set = Sets.newLinkedHashSet( asList( - Maps.immutableEntry("foo", (Collection) asList(2, 3, 6)), - Maps.immutableEntry("bar", (Collection) asList(4, 5, 10, 11)), - Maps.immutableEntry("baz", (Collection) asList(7, 8)), - Maps.immutableEntry("dog", (Collection) asList(9)), - Maps.immutableEntry("cat", (Collection) asList(12, 13, 14)))); + immutableEntry("foo", (Collection) asList(2, 3, 6)), + immutableEntry("bar", (Collection) asList(4, 5, 10, 11)), + immutableEntry("baz", (Collection) asList(7, 8)), + immutableEntry("dog", (Collection) asList(9)), + immutableEntry("cat", (Collection) asList(12, 13, 14)))); new IteratorTester>>( 6, MODIFIABLE, set, IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator>> newTargetIterator() { diff --git a/android/guava-tests/test/com/google/common/collect/ListsImplTest.java b/android/guava-tests/test/com/google/common/collect/ListsImplTest.java index 2d18f5a72e56..93bba1bc6b0d 100644 --- a/android/guava-tests/test/com/google/common/collect/ListsImplTest.java +++ b/android/guava-tests/test/com/google/common/collect/ListsImplTest.java @@ -18,13 +18,15 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; @@ -32,9 +34,12 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** Tests the package level *impl methods directly using various types of lists. */ @GwtCompatible(emulated = true) +@NullMarked public class ListsImplTest extends TestCase { /** Describes how a list is modifiable */ @@ -64,12 +69,13 @@ public String getName() { /** Creates a new list with the given contents. */ public abstract List createList(Class listType, Collection contents); - /** The modifiablity of this list example. */ + /** The modifiability of this list example. */ public Modifiability modifiability() { return modifiability; } } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { TestSuite suite = new TestSuite(); @@ -82,6 +88,7 @@ public static Test suite() { return suite; } + @J2ktIncompatible @GwtIncompatible // suite sub call private static TestSuite createExampleSuite(ListExample example) { TestSuite resultSuite = new TestSuite(ListsImplTest.class); @@ -92,18 +99,22 @@ private static TestSuite createExampleSuite(ListExample example) { return resultSuite; } - private ListExample example; + private @Nullable ListExample example; private ListExample getExample() { // because sometimes one version with a null example is created. return example == null ? new ImmutableListExample("test") : example; } + @J2ktIncompatible + @GwtIncompatible // not used under GWT, and super.getName() is not available under J2CL @Override public String getName() { return example == null ? super.getName() : buildTestName(); } + @J2ktIncompatible + @GwtIncompatible // not used under GWT, and super.getName() is not available under J2CL private String buildTestName() { return super.getName() + ":" + example.getName(); } @@ -136,8 +147,8 @@ public void testEqualsImpl() { assertThat(Lists.equalsImpl(base, copy)).isTrue(); assertThat(Lists.equalsImpl(base, otherType)).isTrue(); - List unEqualItems = - Arrays.asList(outOfOrder, diffValue, diffLength, empty, null, new Object()); + List<@Nullable Object> unEqualItems = + asList(outOfOrder, diffValue, diffLength, empty, null, new Object()); for (Object other : unEqualItems) { assertWithMessage("%s", other).that(Lists.equalsImpl(base, other)).isFalse(); } @@ -151,14 +162,14 @@ public void testAddAllImpl() { List> toAdd = ImmutableList.of( - (Iterable) Collections.singleton("A"), - Collections.emptyList(), + singleton("A"), + emptyList(), ImmutableList.of("A", "B", "C"), ImmutableList.of("D", "E")); List indexes = ImmutableList.of(0, 0, 1, 3); List> expected = ImmutableList.of( - Collections.singletonList("A"), + ImmutableList.of("A"), ImmutableList.of("A"), ImmutableList.of("A", "A", "B", "C"), ImmutableList.of("A", "A", "D", "E", "B", "C")); @@ -232,9 +243,8 @@ private void checkLastIndexOf(List toTest, int[] expected) { } @SafeVarargs - @SuppressWarnings("varargs") private final List createList(Class listType, T... contents) { - return getExample().createList(listType, Arrays.asList(contents)); + return getExample().createList(listType, asList(contents)); } private static final class ArrayListExample extends ListExample { @@ -256,6 +266,8 @@ protected LinkedListExample(String name) { } @Override + // We are testing our utilities on LinkedList. + @SuppressWarnings("JdkObsolete") public List createList(Class listType, Collection contents) { return new LinkedList<>(contents); } @@ -270,9 +282,8 @@ protected ArraysAsListExample(String name) { @Override public List createList(Class listType, Collection contents) { - @SuppressWarnings("unchecked") // safe by contract T[] array = Iterables.toArray(contents, listType); - return Arrays.asList(array); + return asList(array); } } @@ -288,6 +299,7 @@ public List createList(Class listType, Collection content } } + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArrayList private static final class CopyOnWriteListExample extends ListExample { diff --git a/android/guava-tests/test/com/google/common/collect/ListsTest.java b/android/guava-tests/test/com/google/common/collect/ListsTest.java index 33736d6d8116..69e0440801f2 100644 --- a/android/guava-tests/test/com/google/common/collect/ListsTest.java +++ b/android/guava-tests/test/com/google/common/collect/ListsTest.java @@ -17,13 +17,25 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.elementsEqual; +import static com.google.common.collect.Lists.cartesianProduct; +import static com.google.common.collect.Lists.charactersOf; +import static com.google.common.collect.Lists.computeArrayListCapacity; +import static com.google.common.collect.Lists.newArrayListWithExpectedSize; +import static com.google.common.collect.Lists.partition; +import static com.google.common.collect.Lists.transform; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static com.google.common.truth.Truth.assertThat; +import static java.lang.System.arraycopy; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.nCopies; import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.testing.IteratorTester; @@ -39,7 +51,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -50,6 +61,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@code Lists}. @@ -59,6 +71,7 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class ListsTest extends TestCase { private static final Collection SOME_COLLECTION = asList(0, 1, 1); @@ -78,7 +91,7 @@ public Iterator iterator() { return SOME_COLLECTION.iterator(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static final List SOME_LIST = Lists.newArrayList(1, 2, 3, 4); @@ -95,10 +108,12 @@ public String apply(Number n) { return String.valueOf(n); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ListsTest.class); @@ -109,7 +124,7 @@ public static Test suite() { @Override protected List create(String[] elements) { String[] rest = new String[elements.length - 1]; - System.arraycopy(elements, 1, rest, 0, elements.length - 1); + arraycopy(elements, 1, rest, 0, elements.length - 1); return Lists.asList(elements[0], rest); } }) @@ -127,7 +142,7 @@ protected List create(String[] elements) { @Override protected List create(String[] elements) { String[] rest = new String[elements.length - 2]; - System.arraycopy(elements, 2, rest, 0, elements.length - 2); + arraycopy(elements, 2, rest, 0, elements.length - 2); return Lists.asList(elements[0], elements[1], rest); } }) @@ -149,7 +164,7 @@ protected List create(String[] elements) { for (String element : elements) { fromList.add("q" + checkNotNull(element)); } - return Lists.transform(fromList, removeFirst); + return transform(fromList, removeFirst); } }) .named("Lists.transform, random access, no nulls") @@ -169,7 +184,7 @@ protected List create(String[] elements) { for (String element : elements) { fromList.add("q" + checkNotNull(element)); } - return Lists.transform(fromList, removeFirst); + return transform(fromList, removeFirst); } }) .named("Lists.transform, sequential access, no nulls") @@ -186,7 +201,7 @@ protected List create(String[] elements) { @Override protected List create(String[] elements) { List fromList = Lists.newArrayList(elements); - return Lists.transform(fromList, Functions.identity()); + return transform(fromList, Functions.identity()); } }) .named("Lists.transform, random access, nulls") @@ -203,7 +218,7 @@ protected List create(String[] elements) { @Override protected List create(String[] elements) { List fromList = Lists.newLinkedList(asList(elements)); - return Lists.transform(fromList, Functions.identity()); + return transform(fromList, Functions.identity()); } }) .named("Lists.transform, sequential access, nulls") @@ -305,7 +320,7 @@ protected List create(String[] elements) { public void testCharactersOfIsView() { StringBuilder builder = new StringBuilder("abc"); - List chars = Lists.charactersOf(builder); + List chars = charactersOf(builder); assertEquals(asList('a', 'b', 'c'), chars); builder.append("def"); assertEquals(asList('a', 'b', 'c', 'd', 'e', 'f'), chars); @@ -315,39 +330,31 @@ public void testCharactersOfIsView() { public void testNewArrayListEmpty() { ArrayList list = Lists.newArrayList(); - assertEquals(Collections.emptyList(), list); + assertEquals(emptyList(), list); } public void testNewArrayListWithCapacity() { ArrayList list = Lists.newArrayListWithCapacity(0); - assertEquals(Collections.emptyList(), list); + assertEquals(emptyList(), list); ArrayList bigger = Lists.newArrayListWithCapacity(256); - assertEquals(Collections.emptyList(), bigger); + assertEquals(emptyList(), bigger); } public void testNewArrayListWithCapacity_negative() { - try { - Lists.newArrayListWithCapacity(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Lists.newArrayListWithCapacity(-1)); } public void testNewArrayListWithExpectedSize() { - ArrayList list = Lists.newArrayListWithExpectedSize(0); - assertEquals(Collections.emptyList(), list); + ArrayList list = newArrayListWithExpectedSize(0); + assertEquals(emptyList(), list); - ArrayList bigger = Lists.newArrayListWithExpectedSize(256); - assertEquals(Collections.emptyList(), bigger); + ArrayList bigger = newArrayListWithExpectedSize(256); + assertEquals(emptyList(), bigger); } public void testNewArrayListWithExpectedSize_negative() { - try { - Lists.newArrayListWithExpectedSize(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> newArrayListWithExpectedSize(-1)); } public void testNewArrayListVarArgs() { @@ -356,11 +363,11 @@ public void testNewArrayListVarArgs() { } public void testComputeArrayListCapacity() { - assertEquals(5, Lists.computeArrayListCapacity(0)); - assertEquals(13, Lists.computeArrayListCapacity(8)); - assertEquals(89, Lists.computeArrayListCapacity(77)); - assertEquals(22000005, Lists.computeArrayListCapacity(20000000)); - assertEquals(Integer.MAX_VALUE, Lists.computeArrayListCapacity(Integer.MAX_VALUE - 1000)); + assertEquals(5, computeArrayListCapacity(0)); + assertEquals(13, computeArrayListCapacity(8)); + assertEquals(89, computeArrayListCapacity(77)); + assertEquals(22000005, computeArrayListCapacity(20000000)); + assertEquals(Integer.MAX_VALUE, computeArrayListCapacity(Integer.MAX_VALUE - 1000)); } public void testNewArrayListFromCollection() { @@ -380,7 +387,7 @@ public void testNewArrayListFromIterator() { public void testNewLinkedListEmpty() { LinkedList list = Lists.newLinkedList(); - assertEquals(Collections.emptyList(), list); + assertEquals(emptyList(), list); } public void testNewLinkedListFromCollection() { @@ -393,18 +400,21 @@ public void testNewLinkedListFromIterable() { assertEquals(SOME_COLLECTION, list); } + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArrayList public void testNewCOWALEmpty() { CopyOnWriteArrayList list = Lists.newCopyOnWriteArrayList(); - assertEquals(Collections.emptyList(), list); + assertEquals(emptyList(), list); } + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArrayList public void testNewCOWALFromIterable() { CopyOnWriteArrayList list = Lists.newCopyOnWriteArrayList(SOME_ITERABLE); assertEquals(SOME_COLLECTION, list); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -427,20 +437,13 @@ public void testArraysAsList() { assertEquals("FOO", otherWay.get(0)); // But it can't grow - try { - otherWay.add("nope"); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> otherWay.add("nope")); // And it can't shrink - try { - otherWay.remove(2); - fail("no exception thrown"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> otherWay.remove(2)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testAsList1() { List list = Lists.asList("foo", new String[] {"bar", "baz"}); @@ -499,6 +502,7 @@ protected Iterator newTargetIterator() { }.test(); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testAsList2Small() { List list = Lists.asList("foo", "bar", new String[0]); @@ -565,7 +569,7 @@ private static void assertReverseView(List fromList, List toLi toList.set(1, 8); assertEquals(asList(5, 7, 8, 3), fromList); toList.clear(); - assertEquals(Collections.emptyList(), fromList); + assertEquals(emptyList(), fromList); } @SafeVarargs @@ -573,28 +577,24 @@ private static List list(E... elements) { return ImmutableList.copyOf(elements); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary1x1() { - assertThat(Lists.cartesianProduct(list(1), list(2))).contains(list(1, 2)); + assertThat(cartesianProduct(list(1), list(2))).contains(list(1, 2)); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary1x2() { - assertThat(Lists.cartesianProduct(list(1), list(2, 3))) + assertThat(cartesianProduct(list(1), list(2, 3))) .containsExactly(list(1, 2), list(1, 3)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary2x2() { - assertThat(Lists.cartesianProduct(list(1, 2), list(3, 4))) + assertThat(cartesianProduct(list(1, 2), list(3, 4))) .containsExactly(list(1, 3), list(1, 4), list(2, 3), list(2, 4)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_2x2x2() { - assertThat(Lists.cartesianProduct(list(0, 1), list(0, 1), list(0, 1))) + assertThat(cartesianProduct(list(0, 1), list(0, 1), list(0, 1))) .containsExactly( list(0, 0, 0), list(0, 0, 1), @@ -607,9 +607,8 @@ public void testCartesianProduct_2x2x2() { .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_contains() { - List> actual = Lists.cartesianProduct(list(1, 2), list(3, 4)); + List> actual = cartesianProduct(list(1, 2), list(3, 4)); assertTrue(actual.contains(list(1, 3))); assertTrue(actual.contains(list(1, 4))); assertTrue(actual.contains(list(2, 3))); @@ -618,7 +617,7 @@ public void testCartesianProduct_contains() { } public void testCartesianProduct_indexOf() { - List> actual = Lists.cartesianProduct(list(1, 2), list(3, 4)); + List> actual = cartesianProduct(list(1, 2), list(3, 4)); assertEquals(0, actual.indexOf(list(1, 3))); assertEquals(1, actual.indexOf(list(1, 4))); assertEquals(2, actual.indexOf(list(2, 3))); @@ -630,7 +629,7 @@ public void testCartesianProduct_indexOf() { } public void testCartesianProduct_lastIndexOf() { - List> actual = Lists.cartesianProduct(list(1, 1), list(2, 3)); + List> actual = cartesianProduct(list(1, 1), list(2, 3)); assertThat(actual.lastIndexOf(list(1, 2))).isEqualTo(2); assertThat(actual.lastIndexOf(list(1, 3))).isEqualTo(3); assertThat(actual.lastIndexOf(list(1, 1))).isEqualTo(-1); @@ -639,7 +638,6 @@ public void testCartesianProduct_lastIndexOf() { assertThat(actual.lastIndexOf(list(1, 1, 1))).isEqualTo(-1); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_unrelatedTypes() { List x = list(1, 2); List y = list("3", "4"); @@ -654,35 +652,31 @@ public void testCartesianProduct_unrelatedTypes() { .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProductTooBig() { - List list = Collections.nCopies(10000, "foo"); - try { - Lists.cartesianProduct(list, list, list, list, list); - fail("Expected IAE"); - } catch (IllegalArgumentException expected) { - } + List list = nCopies(10000, "foo"); + assertThrows( + IllegalArgumentException.class, () -> cartesianProduct(list, list, list, list, list)); } public void testTransformHashCodeRandomAccess() { - List list = Lists.transform(SOME_LIST, SOME_FUNCTION); + List list = transform(SOME_LIST, SOME_FUNCTION); assertEquals(SOME_STRING_LIST.hashCode(), list.hashCode()); } public void testTransformHashCodeSequential() { - List list = Lists.transform(SOME_SEQUENTIAL_LIST, SOME_FUNCTION); + List list = transform(SOME_SEQUENTIAL_LIST, SOME_FUNCTION); assertEquals(SOME_STRING_LIST.hashCode(), list.hashCode()); } public void testTransformModifiableRandomAccess() { List fromList = Lists.newArrayList(SOME_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformModifiable(list); } public void testTransformModifiableSequential() { List fromList = Lists.newLinkedList(SOME_SEQUENTIAL_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformModifiable(list); } @@ -702,18 +696,18 @@ private static void assertTransformModifiable(List list) { } catch (UnsupportedOperationException expected) { } list.clear(); - assertEquals(Collections.emptyList(), list); + assertEquals(emptyList(), list); } public void testTransformViewRandomAccess() { List fromList = Lists.newArrayList(SOME_LIST); - List toList = Lists.transform(fromList, SOME_FUNCTION); + List toList = transform(fromList, SOME_FUNCTION); assertTransformView(fromList, toList); } public void testTransformViewSequential() { List fromList = Lists.newLinkedList(SOME_SEQUENTIAL_LIST); - List toList = Lists.transform(fromList, SOME_FUNCTION); + List toList = transform(fromList, SOME_FUNCTION); assertTransformView(fromList, toList); } @@ -734,46 +728,54 @@ private static void assertTransformView(List fromList, List toL toList.remove("5"); assertEquals(asList(3), fromList); toList.clear(); - assertEquals(Collections.emptyList(), fromList); + assertEquals(emptyList(), fromList); } public void testTransformRandomAccess() { - List list = Lists.transform(SOME_LIST, SOME_FUNCTION); + List list = transform(SOME_LIST, SOME_FUNCTION); assertTrue(list instanceof RandomAccess); } public void testTransformSequential() { - List list = Lists.transform(SOME_SEQUENTIAL_LIST, SOME_FUNCTION); + List list = transform(SOME_SEQUENTIAL_LIST, SOME_FUNCTION); assertFalse(list instanceof RandomAccess); } + public void testTransformRandomAccessIsNotEmpty() { + List transformedList = transform(SOME_LIST, SOME_FUNCTION); + assertFalse(transformedList.isEmpty()); + } + + public void testTransformSequentialIsNotEmpty() { + List transformedList = transform(SOME_SEQUENTIAL_LIST, SOME_FUNCTION); + assertFalse(transformedList.isEmpty()); + } + public void testTransformListIteratorRandomAccess() { List fromList = Lists.newArrayList(SOME_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformListIterator(list); } public void testTransformListIteratorSequential() { List fromList = Lists.newLinkedList(SOME_SEQUENTIAL_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformListIterator(list); } public void testTransformPreservesIOOBEsThrownByFunction() { - try { - Lists.transform( - ImmutableList.of("foo", "bar"), - new Function() { - @Override - public String apply(String input) { - throw new IndexOutOfBoundsException(); - } - }) - .toArray(); - fail(); - } catch (IndexOutOfBoundsException expected) { - // success - } + assertThrows( + IndexOutOfBoundsException.class, + () -> + transform( + ImmutableList.of("foo", "bar"), + new Function() { + @Override + public String apply(String input) { + throw new IndexOutOfBoundsException(); + } + }) + .toArray()); } private static void assertTransformListIterator(List list) { @@ -809,26 +811,24 @@ private static void assertTransformListIterator(List list) { try { iterator.add("1"); fail("transformed list iterator is addable"); - } catch (UnsupportedOperationException expected) { - } catch (IllegalStateException expected) { + } catch (UnsupportedOperationException | IllegalStateException expected) { } try { iterator.set("1"); fail("transformed list iterator is settable"); - } catch (UnsupportedOperationException expected) { - } catch (IllegalStateException expected) { + } catch (UnsupportedOperationException | IllegalStateException expected) { } } public void testTransformIteratorRandomAccess() { List fromList = Lists.newArrayList(SOME_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformIterator(list); } public void testTransformIteratorSequential() { List fromList = Lists.newLinkedList(SOME_SEQUENTIAL_LIST); - List list = Lists.transform(fromList, SOME_FUNCTION); + List list = transform(fromList, SOME_FUNCTION); assertTransformIterator(list); } @@ -840,9 +840,8 @@ public void testTransformIteratorSequential() { public void testTransformedSequentialIterationUsesBackingListIterationOnly() { List randomAccessList = Lists.newArrayList(SOME_SEQUENTIAL_LIST); List listIteratorOnlyList = new ListIterationOnlyList<>(randomAccessList); - List transform = Lists.transform(listIteratorOnlyList, SOME_FUNCTION); - assertTrue( - Iterables.elementsEqual(transform, Lists.transform(randomAccessList, SOME_FUNCTION))); + List transform = transform(listIteratorOnlyList, SOME_FUNCTION); + assertTrue(elementsEqual(transform, transform(randomAccessList, SOME_FUNCTION))); } private static class ListIterationOnlyList extends ForwardingList { @@ -890,55 +889,52 @@ private static void assertTransformIterator(List list) { } public void testPartition_badSize() { - List source = Collections.singletonList(1); - try { - Lists.partition(source, 0); - fail(); - } catch (IllegalArgumentException expected) { - } + List source = singletonList(1); + assertThrows(IllegalArgumentException.class, () -> partition(source, 0)); } public void testPartition_empty() { - List source = Collections.emptyList(); - List> partitions = Lists.partition(source, 1); + List source = emptyList(); + List> partitions = partition(source, 1); assertTrue(partitions.isEmpty()); assertEquals(0, partitions.size()); } public void testPartition_1_1() { - List source = Collections.singletonList(1); - List> partitions = Lists.partition(source, 1); + List source = singletonList(1); + List> partitions = partition(source, 1); assertEquals(1, partitions.size()); - assertEquals(Collections.singletonList(1), partitions.get(0)); + assertEquals(singletonList(1), partitions.get(0)); } public void testPartition_1_2() { - List source = Collections.singletonList(1); - List> partitions = Lists.partition(source, 2); + List source = singletonList(1); + List> partitions = partition(source, 2); assertEquals(1, partitions.size()); - assertEquals(Collections.singletonList(1), partitions.get(0)); + assertEquals(singletonList(1), partitions.get(0)); } public void testPartition_2_1() { List source = asList(1, 2); - List> partitions = Lists.partition(source, 1); + List> partitions = partition(source, 1); assertEquals(2, partitions.size()); - assertEquals(Collections.singletonList(1), partitions.get(0)); - assertEquals(Collections.singletonList(2), partitions.get(1)); + assertEquals(singletonList(1), partitions.get(0)); + assertEquals(singletonList(2), partitions.get(1)); } public void testPartition_3_2() { List source = asList(1, 2, 3); - List> partitions = Lists.partition(source, 2); + List> partitions = partition(source, 2); assertEquals(2, partitions.size()); assertEquals(asList(1, 2), partitions.get(0)); assertEquals(asList(3), partitions.get(1)); } + @J2ktIncompatible // Arrays.asList(...).subList() doesn't implement RandomAccess in J2KT. @GwtIncompatible // ArrayList.subList doesn't implement RandomAccess in GWT. public void testPartitionRandomAccessTrue() { List source = asList(1, 2, 3); - List> partitions = Lists.partition(source, 2); + List> partitions = partition(source, 2); assertTrue( "partition should be RandomAccess, but not: " + partitions.getClass(), @@ -955,7 +951,7 @@ public void testPartitionRandomAccessTrue() { public void testPartitionRandomAccessFalse() { List source = Lists.newLinkedList(asList(1, 2, 3)); - List> partitions = Lists.partition(source, 2); + List> partitions = partition(source, 2); assertFalse(partitions instanceof RandomAccess); assertFalse(partitions.get(0) instanceof RandomAccess); assertFalse(partitions.get(1) instanceof RandomAccess); @@ -965,7 +961,7 @@ public void testPartitionRandomAccessFalse() { public void testPartition_view() { List list = asList(1, 2, 3); - List> partitions = Lists.partition(list, 3); + List> partitions = partition(list, 3); // Changes before the partition is retrieved are reflected list.set(0, 3); @@ -989,12 +985,13 @@ public void testPartition_view() { public void testPartitionSize_1() { List list = asList(1, 2, 3); - assertEquals(1, Lists.partition(list, Integer.MAX_VALUE).size()); - assertEquals(1, Lists.partition(list, Integer.MAX_VALUE - 1).size()); + assertEquals(1, partition(list, Integer.MAX_VALUE).size()); + assertEquals(1, partition(list, Integer.MAX_VALUE - 1).size()); } @GwtIncompatible // cannot do such a big explicit copy + @J2ktIncompatible // too slow public void testPartitionSize_2() { - assertEquals(2, Lists.partition(Collections.nCopies(0x40000001, 1), 0x40000000).size()); + assertEquals(2, partition(nCopies(0x40000001, 1), 0x40000000).size()); } } diff --git a/android/guava-tests/test/com/google/common/collect/LockHeldAssertingSet.java b/android/guava-tests/test/com/google/common/collect/LockHeldAssertingSet.java new file mode 100644 index 000000000000..90ce704a6661 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/LockHeldAssertingSet.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static junit.framework.Assert.assertTrue; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Set; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; + +/** + * {@link Set} implementation that asserts that a given lock is held whenever one of its methods is + * called. + */ +@NullUnmarked +class LockHeldAssertingSet extends ForwardingSet implements Serializable { + final Set delegate; + final Object mutex; + + LockHeldAssertingSet(Set delegate, Object mutex) { + checkNotNull(mutex); + this.delegate = delegate; + this.mutex = mutex; + } + + @Override + protected Set delegate() { + return delegate; + } + + @Override + public String toString() { + assertTrue(Thread.holdsLock(mutex)); + return super.toString(); + } + + @Override + public boolean equals(@Nullable Object o) { + assertTrue(Thread.holdsLock(mutex)); + return super.equals(o); + } + + @Override + public int hashCode() { + assertTrue(Thread.holdsLock(mutex)); + return super.hashCode(); + } + + @Override + public boolean add(@Nullable E o) { + assertTrue(Thread.holdsLock(mutex)); + return super.add(o); + } + + @Override + public boolean addAll(Collection c) { + assertTrue(Thread.holdsLock(mutex)); + return super.addAll(c); + } + + @Override + public void clear() { + assertTrue(Thread.holdsLock(mutex)); + super.clear(); + } + + @Override + public boolean contains(@Nullable Object o) { + assertTrue(Thread.holdsLock(mutex)); + return super.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + assertTrue(Thread.holdsLock(mutex)); + return super.containsAll(c); + } + + @Override + public boolean isEmpty() { + assertTrue(Thread.holdsLock(mutex)); + return super.isEmpty(); + } + + /* + * We don't assert that the lock is held during calls to iterator(), stream(), and spliterator: + * `Synchronized` doesn't guarantee that it will hold the mutex for those calls because callers + * are responsible for taking the mutex themselves: + * https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/Collections.html#synchronizedCollection(java.util.Collection) + * + * Similarly, we avoid having those methods *implemented* in terms of *other* TestSet methods + * that will perform holdsLock assertions: + * + * - For iterator(), we can accomplish that by not overriding iterator() at all. That way, we + * inherit an implementation that forwards to the delegate collection, which performs no + * holdsLock assertions. + * + * - For stream() and spliterator(), we have to forward to the delegate ourselves because + * ForwardingSet does not forward `default` methods, as discussed in its Javadoc. + */ + + // Currently, we don't include stream() and spliterator() for our classes in the Android flavor. + + @Override + public boolean remove(@Nullable Object o) { + assertTrue(Thread.holdsLock(mutex)); + return super.remove(o); + } + + @Override + public boolean removeAll(Collection c) { + assertTrue(Thread.holdsLock(mutex)); + return super.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + assertTrue(Thread.holdsLock(mutex)); + return super.retainAll(c); + } + + @Override + public int size() { + assertTrue(Thread.holdsLock(mutex)); + return super.size(); + } + + @Override + public Object[] toArray() { + assertTrue(Thread.holdsLock(mutex)); + return super.toArray(); + } + + @Override + public T[] toArray(T[] a) { + assertTrue(Thread.holdsLock(mutex)); + return super.toArray(a); + } + + private static final long serialVersionUID = 0; +} diff --git a/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java b/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java index 47fc74c82e5e..7bc01a7dc2e0 100644 --- a/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapMakerInternalMapTest.java @@ -29,9 +29,13 @@ import java.lang.ref.Reference; import java.util.concurrent.atomic.AtomicReferenceArray; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author Charles Fry */ +/** + * @author Charles Fry + */ @SuppressWarnings("deprecation") // many tests of deprecated methods +@NullUnmarked public class MapMakerInternalMapTest extends TestCase { static final int SMALL_MAX_SIZE = DRAIN_THRESHOLD * 5; @@ -90,7 +94,7 @@ protected int doHash(Object t) { } public void testSetConcurrencyLevel() { - // round up to nearest power of two + // round up to the nearest power of two checkConcurrencyLevel(1, 1); checkConcurrencyLevel(2, 2); @@ -109,7 +113,7 @@ private static void checkConcurrencyLevel(int concurrencyLevel, int segmentCount } public void testSetInitialCapacity() { - // share capacity over each segment, then round up to nearest power of two + // share capacity over each segment, then round up to the nearest power of two checkInitialCapacity(1, 0, 1); checkInitialCapacity(1, 1, 1); @@ -152,42 +156,6 @@ private static void checkInitialCapacity( } } - public void testSetMaximumSize() { - // vary maximumSize wrt concurrencyLevel - - for (int maxSize = 1; maxSize < 8; maxSize++) { - checkMaximumSize(1, 8, maxSize); - checkMaximumSize(2, 8, maxSize); - checkMaximumSize(4, 8, maxSize); - checkMaximumSize(8, 8, maxSize); - } - - checkMaximumSize(1, 8, Integer.MAX_VALUE); - checkMaximumSize(2, 8, Integer.MAX_VALUE); - checkMaximumSize(4, 8, Integer.MAX_VALUE); - checkMaximumSize(8, 8, Integer.MAX_VALUE); - - // vary initial capacity wrt maximumSize - - for (int capacity = 0; capacity < 8; capacity++) { - checkMaximumSize(1, capacity, 4); - checkMaximumSize(2, capacity, 4); - checkMaximumSize(4, capacity, 4); - checkMaximumSize(8, capacity, 4); - } - } - - private static void checkMaximumSize(int concurrencyLevel, int initialCapacity, int maxSize) { - MapMakerInternalMap map = - makeMap( - createMapMaker().concurrencyLevel(concurrencyLevel).initialCapacity(initialCapacity)); - int totalCapacity = 0; - for (int i = 0; i < map.segments.length; i++) { - totalCapacity += map.segments[i].maxSegmentSize; - } - assertTrue("totalCapcity=" + totalCapacity + ", maxSize=" + maxSize, totalCapacity <= maxSize); - } - public void testSetWeakKeys() { MapMakerInternalMap map = makeMap(createMapMaker().weakKeys()); checkStrength(map, Strength.WEAK, Strength.STRONG); @@ -877,7 +845,6 @@ public void testDrainValueReferenceQueueOnWrite() { Object valueTwo = new Object(); map.put(keyOne, valueOne); - @SuppressWarnings("unchecked") WeakValueEntry entry = (WeakValueEntry) segment.getEntry(keyOne, hashOne); WeakValueReference valueReference = entry.getValueReference(); @@ -938,7 +905,6 @@ public void testDrainValueReferenceQueueOnRead() { Object keyTwo = new Object(); map.put(keyOne, valueOne); - @SuppressWarnings("unchecked") WeakValueEntry entry = (WeakValueEntry) segment.getEntry(keyOne, hashOne); WeakValueReference valueReference = entry.getValueReference(); diff --git a/android/guava-tests/test/com/google/common/collect/MapMakerTest.java b/android/guava-tests/test/com/google/common/collect/MapMakerTest.java index 4b54f0dd5350..70f27ca2f45a 100644 --- a/android/guava-tests/test/com/google/common/collect/MapMakerTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapMakerTest.java @@ -16,21 +16,28 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.testing.NullPointerTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author Charles Fry */ +/** + * @author Charles Fry + */ @GwtCompatible(emulated = true) +@J2ktIncompatible // MapMaker +@NullUnmarked public class MapMakerTest extends TestCase { - @GwtIncompatible // NullPointerTester public void testNullParameters() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -45,6 +52,7 @@ static final class DelayingIdentityLoader implements Function { this.delayLatch = delayLatch; } + @CanIgnoreReturnValue // Sure, why not? @Override public T apply(T key) { awaitUninterruptibly(delayLatch); @@ -57,31 +65,24 @@ public T apply(T key) { * anywhere else */ - /** Tests for the builder. */ - public static class MakerTest extends TestCase { - public void testInitialCapacity_negative() { - MapMaker maker = new MapMaker(); - try { - maker.initialCapacity(-1); - fail(); - } catch (IllegalArgumentException expected) { - } - } + public void testInitialCapacity_negative() { + MapMaker maker = new MapMaker(); + assertThrows(IllegalArgumentException.class, () -> maker.initialCapacity(-1)); + } - // TODO(cpovirk): enable when ready - public void xtestInitialCapacity_setTwice() { - MapMaker maker = new MapMaker().initialCapacity(16); - try { - // even to the same value is not allowed - maker.initialCapacity(16); - fail(); - } catch (IllegalArgumentException expected) { - } + // TODO(cpovirk): enable when ready (apparently after a change to our GWT emulation) + public void xtestInitialCapacity_setTwice() { + MapMaker maker = new MapMaker().initialCapacity(16); + try { + // even to the same value is not allowed + maker.initialCapacity(16); + fail(); + } catch (IllegalStateException expected) { } + } - public void testReturnsPlainConcurrentHashMapWhenPossible() { - Map map = new MapMaker().initialCapacity(5).makeMap(); - assertTrue(map instanceof ConcurrentHashMap); - } + public void testReturnsPlainConcurrentHashMapWhenPossible() { + Map map = new MapMaker().initialCapacity(5).makeMap(); + assertTrue(map instanceof ConcurrentHashMap); } } diff --git a/android/guava-tests/test/com/google/common/collect/MapsCollectionTest.java b/android/guava-tests/test/com/google/common/collect/MapsCollectionTest.java index 9aa8e66b70fb..9bf47c4396b0 100644 --- a/android/guava-tests/test/com/google/common/collect/MapsCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapsCollectionTest.java @@ -18,13 +18,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Maps.transformValues; import static com.google.common.collect.testing.Helpers.mapEntry; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.sort; -import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Maps.EntryTransformer; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.MapTestSuiteBuilder; import com.google.common.collect.testing.NavigableMapTestSuiteBuilder; import com.google.common.collect.testing.SafeTreeMap; @@ -39,26 +40,29 @@ import com.google.common.collect.testing.google.BiMapTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringBiMapGenerator; import com.google.common.io.BaseEncoding; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.NavigableSet; +import java.util.Objects; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Test suites for wrappers in {@code Maps}. * * @author Louis Wasserman */ +@NullUnmarked +@AndroidIncompatible // test-suite builders public class MapsCollectionTest extends TestCase { public static Test suite() { TestSuite suite = new TestSuite(); @@ -133,7 +137,7 @@ public Integer apply(String input) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -202,13 +206,13 @@ public Integer apply(String input) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override public Iterable> order( List> insertionOrder) { - Collections.sort( + sort( insertionOrder, new Comparator>() { @Override @@ -269,13 +273,13 @@ public Integer apply(String input) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override public Iterable> order( List> insertionOrder) { - Collections.sort( + sort( insertionOrder, new Comparator>() { @Override @@ -561,16 +565,16 @@ static void putEntries(Map map, Entry[] entries) static final Predicate FILTER_KEYS = new Predicate() { @Override - public boolean apply(@CheckForNull String string) { - return !"banana".equals(string) && !"eggplant".equals(string); + public boolean apply(@Nullable String string) { + return !Objects.equals(string, "banana") && !Objects.equals(string, "eggplant"); } }; static final Predicate FILTER_VALUES = new Predicate() { @Override - public boolean apply(@CheckForNull String string) { - return !"toast".equals(string) && !"spam".equals(string); + public boolean apply(@Nullable String string) { + return !Objects.equals(string, "toast") && !Objects.equals(string, "spam"); } }; @@ -578,8 +582,8 @@ public boolean apply(@CheckForNull String string) { new Predicate>() { @Override public boolean apply(Entry entry) { - return !Helpers.mapEntry("banana", "toast").equals(entry) - && !Helpers.mapEntry("eggplant", "spam").equals(entry); + return !mapEntry("banana", "toast").equals(entry) + && !mapEntry("eggplant", "spam").equals(entry); } }; @@ -587,7 +591,7 @@ public boolean apply(Entry entry) { new Predicate>() { @Override public boolean apply(Entry entry) { - return !Helpers.mapEntry("banana", "toast").equals(entry); + return !mapEntry("banana", "toast").equals(entry); } }; @@ -595,7 +599,7 @@ public boolean apply(Entry entry) { new Predicate>() { @Override public boolean apply(Entry entry) { - return !Helpers.mapEntry("eggplant", "spam").equals(entry); + return !mapEntry("eggplant", "spam").equals(entry); } }; @@ -631,14 +635,14 @@ protected SortedMap delegate() { } private static String encode(String str) { - return BaseEncoding.base64().encode(str.getBytes(Charsets.UTF_8)); + return BaseEncoding.base64().encode(str.getBytes(UTF_8)); } private static final Function DECODE_FUNCTION = new Function() { @Override public String apply(String input) { - return new String(BaseEncoding.base64().decode(input), Charsets.UTF_8); + return new String(BaseEncoding.base64().decode(input), UTF_8); } }; @@ -669,7 +673,7 @@ protected Map create(Entry[] entries) { for (Entry entry : entries) { map.put(entry.getKey(), encode(entry.getValue())); } - return Maps.transformValues(map, DECODE_FUNCTION); + return transformValues(map, DECODE_FUNCTION); } }) .named("Maps.transformValues[Map, Function]") @@ -716,7 +720,7 @@ protected SortedMap create(Entry[] entries) { for (Entry entry : entries) { map.put(entry.getKey(), encode(entry.getValue())); } - return Maps.transformValues(map, DECODE_FUNCTION); + return transformValues(map, DECODE_FUNCTION); } }) .named("Maps.transformValues[SortedMap, Function]") @@ -759,7 +763,7 @@ protected NavigableMap create(Entry[] entries) { for (Entry entry : entries) { map.put(entry.getKey(), encode(entry.getValue())); } - return Maps.transformValues(map, DECODE_FUNCTION); + return transformValues(map, DECODE_FUNCTION); } }) .named("Maps.transformValues[NavigableMap, Function]") diff --git a/android/guava-tests/test/com/google/common/collect/MapsSortedTransformValuesTest.java b/android/guava-tests/test/com/google/common/collect/MapsSortedTransformValuesTest.java index 98bbde501aa4..b6580f68c66a 100644 --- a/android/guava-tests/test/com/google/common/collect/MapsSortedTransformValuesTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapsSortedTransformValuesTest.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.transformValues; + import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link Maps#transformValues(SortedMap, Function)}. @@ -27,11 +30,11 @@ * @author Louis Wasserman */ @GwtCompatible -public class MapsSortedTransformValuesTest extends MapsTransformValuesTest { - +@NullMarked +public class MapsSortedTransformValuesTest extends AbstractMapsTransformValuesTest { @Override protected SortedMap makeEmptyMap() { - return Maps.transformValues(Maps.newTreeMap(), Functions.identity()); + return transformValues(Maps.newTreeMap(), Functions.identity()); } @Override @@ -40,6 +43,6 @@ protected SortedMap makePopulatedMap() { underlying.put("a", 1); underlying.put("b", 2); underlying.put("c", 3); - return Maps.transformValues(underlying, Functions.toStringFunction()); + return transformValues(underlying, Functions.toStringFunction()); } } diff --git a/android/guava-tests/test/com/google/common/collect/MapsTest.java b/android/guava-tests/test/com/google/common/collect/MapsTest.java index d0f98386346d..61ddae77aaf9 100644 --- a/android/guava-tests/test/com/google/common/collect/MapsTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapsTest.java @@ -16,25 +16,28 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.collect.Maps.transformEntries; import static com.google.common.collect.Maps.transformValues; import static com.google.common.collect.Maps.unmodifiableNavigableMap; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.util.Arrays.asList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.base.Equivalence; import com.google.common.base.Function; import com.google.common.base.Functions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; import com.google.common.collect.Maps.EntryTransformer; import com.google.common.collect.Maps.ValueDifferenceImpl; -import com.google.common.collect.SetsTest.Derived; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; @@ -63,6 +66,8 @@ import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code Maps}. @@ -72,13 +77,15 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked +@SuppressWarnings("JUnitIncompatibleType") // Many intentional violations here. public class MapsTest extends TestCase { private static final Comparator SOME_COMPARATOR = Collections.reverseOrder(); public void testHashMap() { HashMap map = Maps.newHashMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); } public void testHashMapWithInitialMap() { @@ -95,17 +102,12 @@ public void testHashMapGeneralizesTypes() { original.put("a", 1); original.put("b", 2); original.put("c", 3); - HashMap map = - Maps.newHashMap((Map) original); + HashMap map = Maps.newHashMap(original); assertEquals(original, map); } public void testCapacityForNegativeSizeFails() { - try { - Maps.capacity(-1); - fail("Negative expected size must result in IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Maps.capacity(-1)); } /** @@ -117,6 +119,7 @@ public void testCapacityForNegativeSizeFails() { * *

    This test may fail miserably on non-OpenJDK environments... */ + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // relies on assumptions about OpenJDK public void testNewHashMapWithExpectedSize_wontGrow() throws Exception { @@ -126,11 +129,15 @@ public void testNewHashMapWithExpectedSize_wontGrow() throws Exception { for (int size = 1; size < 200; size++) { assertWontGrow( - size, Maps.newHashMapWithExpectedSize(size), Maps.newHashMapWithExpectedSize(size)); + size, + new HashMap<>(), + Maps.newHashMapWithExpectedSize(size), + Maps.newHashMapWithExpectedSize(size)); } } /** Same test as above but for newLinkedHashMapWithExpectedSize */ + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // relies on assumptions about OpenJDK public void testNewLinkedHashMapWithExpectedSize_wontGrow() throws Exception { @@ -139,14 +146,20 @@ public void testNewLinkedHashMapWithExpectedSize_wontGrow() throws Exception { for (int size = 1; size < 200; size++) { assertWontGrow( size, + new LinkedHashMap<>(), Maps.newLinkedHashMapWithExpectedSize(size), Maps.newLinkedHashMapWithExpectedSize(size)); } } + @J2ktIncompatible @GwtIncompatible // reflection private static void assertWontGrow( - int size, HashMap map1, HashMap map2) throws Exception { + int size, + HashMap referenceMap, + HashMap map1, + HashMap map2) + throws Exception { // Only start measuring table size after the first element inserted, to // deal with empty-map optimization. map1.put(0, null); @@ -168,8 +181,19 @@ private static void assertWontGrow( assertWithMessage("table size after adding " + size + " elements") .that(bucketsOf(map1)) .isEqualTo(initialBuckets); + + // Ensure that referenceMap, which doesn't use WithExpectedSize, ends up with the same table + // size as the other two maps. If it ended up with a smaller size that would imply that we + // computed the wrong initial capacity. + for (int i = 0; i < size; i++) { + referenceMap.put(i, null); + } + assertWithMessage("table size after adding " + size + " elements") + .that(initialBuckets) + .isAtMost(bucketsOf(referenceMap)); } + @J2ktIncompatible @GwtIncompatible // reflection private static int bucketsOf(HashMap hashMap) throws Exception { Field tableField = HashMap.class.getDeclaredField("table"); @@ -198,10 +222,9 @@ public void testCapacityForLargeSizes() { public void testLinkedHashMap() { LinkedHashMap map = Maps.newLinkedHashMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); } - @SuppressWarnings("serial") public void testLinkedHashMapWithInitialMap() { Map map = new LinkedHashMap( @@ -249,23 +272,23 @@ public void testLinkedHashMapGeneralizesTypes() { @SuppressWarnings("IdentityHashMapBoxing") public void testIdentityHashMap() { IdentityHashMap map = Maps.newIdentityHashMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); } public void testConcurrentMap() { ConcurrentMap map = Maps.newConcurrentMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); } public void testTreeMap() { TreeMap map = Maps.newTreeMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); assertNull(map.comparator()); } public void testTreeMapDerived() { TreeMap map = Maps.newTreeMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); map.put(new Derived("foo"), 1); map.put(new Derived("bar"), 2); assertThat(map.keySet()).containsExactly(new Derived("bar"), new Derived("foo")).inOrder(); @@ -275,7 +298,7 @@ public void testTreeMapDerived() { public void testTreeMapNonGeneric() { TreeMap map = Maps.newTreeMap(); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); map.put(new LegacyComparable("foo"), 1); map.put(new LegacyComparable("bar"), 2); assertThat(map.keySet()) @@ -287,7 +310,7 @@ public void testTreeMapNonGeneric() { public void testTreeMapWithComparator() { TreeMap map = Maps.newTreeMap(SOME_COMPARATOR); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); assertSame(SOME_COMPARATOR, map.comparator()); } @@ -307,17 +330,15 @@ public enum SomeEnum { public void testEnumMap() { EnumMap map = Maps.newEnumMap(SomeEnum.class); - assertEquals(Collections.emptyMap(), map); + assertEquals(emptyMap(), map); map.put(SomeEnum.SOME_INSTANCE, 0); - assertEquals(Collections.singletonMap(SomeEnum.SOME_INSTANCE, 0), map); + assertEquals(singletonMap(SomeEnum.SOME_INSTANCE, 0), map); } public void testEnumMapNullClass() { - try { - Maps.newEnumMap((Class) null); - fail("no exception thrown"); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> Maps.newEnumMap((Class) null)); } public void testEnumMapWithInitialEnumMap() { @@ -343,15 +364,11 @@ public void testEnumMapWithInitialMap() { public void testEnumMapWithInitialEmptyMap() { Map original = Maps.newHashMap(); - try { - Maps.newEnumMap(original); - fail("Empty map must result in an IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Maps.newEnumMap(original)); } public void testToStringImplWithNullKeys() throws Exception { - Map hashmap = Maps.newHashMap(); + Map<@Nullable String, String> hashmap = Maps.newHashMap(); hashmap.put("foo", "bar"); hashmap.put(null, "baz"); @@ -359,20 +376,21 @@ public void testToStringImplWithNullKeys() throws Exception { } public void testToStringImplWithNullValues() throws Exception { - Map hashmap = Maps.newHashMap(); + Map hashmap = Maps.newHashMap(); hashmap.put("foo", "bar"); hashmap.put("baz", null); assertEquals(hashmap.toString(), Maps.toStringImpl(hashmap)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { new NullPointerTester().testAllPublicStaticMethods(Maps.class); } - private static final Map EMPTY = Collections.emptyMap(); - private static final Map SINGLETON = Collections.singletonMap(1, 2); + private static final Map EMPTY = emptyMap(); + private static final Map SINGLETON = singletonMap(1, 2); public void testMapDifferenceEmptyEmpty() { MapDifference diff = Maps.difference(EMPTY, EMPTY); @@ -553,14 +571,14 @@ public void testSortedMapDifferenceTypical() { SortedMapDifference diff1 = Maps.difference(left, right); assertFalse(diff1.areEqual()); assertThat(diff1.entriesOnlyOnLeft().entrySet()) - .containsExactly(Maps.immutableEntry(4, "d"), Maps.immutableEntry(2, "b")) + .containsExactly(immutableEntry(4, "d"), immutableEntry(2, "b")) .inOrder(); - assertThat(diff1.entriesOnlyOnRight().entrySet()).contains(Maps.immutableEntry(6, "z")); - assertThat(diff1.entriesInCommon().entrySet()).contains(Maps.immutableEntry(1, "a")); + assertThat(diff1.entriesOnlyOnRight().entrySet()).contains(immutableEntry(6, "z")); + assertThat(diff1.entriesInCommon().entrySet()).contains(immutableEntry(1, "a")); assertThat(diff1.entriesDiffering().entrySet()) .containsExactly( - Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g")), - Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f"))) + immutableEntry(5, ValueDifferenceImpl.create("e", "g")), + immutableEntry(3, ValueDifferenceImpl.create("c", "f"))) .inOrder(); assertEquals( "not equal: only on left={4=d, 2=b}: only on right={6=z}: " @@ -569,11 +587,11 @@ public void testSortedMapDifferenceTypical() { SortedMapDifference diff2 = Maps.difference(right, left); assertFalse(diff2.areEqual()); - assertThat(diff2.entriesOnlyOnLeft().entrySet()).contains(Maps.immutableEntry(6, "z")); + assertThat(diff2.entriesOnlyOnLeft().entrySet()).contains(immutableEntry(6, "z")); assertThat(diff2.entriesOnlyOnRight().entrySet()) - .containsExactly(Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")) + .containsExactly(immutableEntry(2, "b"), immutableEntry(4, "d")) .inOrder(); - assertThat(diff1.entriesInCommon().entrySet()).contains(Maps.immutableEntry(1, "a")); + assertThat(diff1.entriesInCommon().entrySet()).contains(immutableEntry(1, "a")); assertEquals( ImmutableMap.of( 3, ValueDifferenceImpl.create("f", "c"), @@ -595,30 +613,18 @@ public void testSortedMapDifferenceImmutable() { left.put(6, "z"); assertFalse(diff1.areEqual()); assertThat(diff1.entriesOnlyOnLeft().entrySet()) - .containsExactly(Maps.immutableEntry(2, "b"), Maps.immutableEntry(4, "d")) + .containsExactly(immutableEntry(2, "b"), immutableEntry(4, "d")) .inOrder(); - assertThat(diff1.entriesOnlyOnRight().entrySet()).contains(Maps.immutableEntry(6, "z")); - assertThat(diff1.entriesInCommon().entrySet()).contains(Maps.immutableEntry(1, "a")); + assertThat(diff1.entriesOnlyOnRight().entrySet()).contains(immutableEntry(6, "z")); + assertThat(diff1.entriesInCommon().entrySet()).contains(immutableEntry(1, "a")); assertThat(diff1.entriesDiffering().entrySet()) .containsExactly( - Maps.immutableEntry(3, ValueDifferenceImpl.create("c", "f")), - Maps.immutableEntry(5, ValueDifferenceImpl.create("e", "g"))) + immutableEntry(3, ValueDifferenceImpl.create("c", "f")), + immutableEntry(5, ValueDifferenceImpl.create("e", "g"))) .inOrder(); - try { - diff1.entriesInCommon().put(7, "x"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - diff1.entriesOnlyOnLeft().put(7, "x"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - diff1.entriesOnlyOnRight().put(7, "x"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> diff1.entriesInCommon().put(7, "x")); + assertThrows(UnsupportedOperationException.class, () -> diff1.entriesOnlyOnLeft().put(7, "x")); + assertThrows(UnsupportedOperationException.class, () -> diff1.entriesOnlyOnRight().put(7, "x")); } public void testSortedMapDifferenceEquals() { @@ -743,26 +749,11 @@ public void testAsMapSortedWritesThrough() { public void testAsMapSortedSubViewKeySetsDoNotSupportAdd() { SortedMap map = Maps.asMap(new NonNavigableSortedSet(), LENGTH_FUNCTION); - try { - map.subMap("a", "z").keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.tailMap("a").keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.headMap("r").keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.headMap("r").tailMap("m").keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.subMap("a", "z").keySet().add("a")); + assertThrows(UnsupportedOperationException.class, () -> map.tailMap("a").keySet().add("a")); + assertThrows(UnsupportedOperationException.class, () -> map.headMap("r").keySet().add("a")); + assertThrows( + UnsupportedOperationException.class, () -> map.headMap("r").tailMap("m").keySet().add("a")); } public void testAsMapSortedEmpty() { @@ -869,31 +860,17 @@ public void testAsMapNavigableWritesThrough() { @GwtIncompatible // NavigableMap public void testAsMapNavigableSubViewKeySetsDoNotSupportAdd() { NavigableMap map = Maps.asMap(Sets.newTreeSet(), LENGTH_FUNCTION); - try { - map.descendingKeySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.subMap("a", true, "z", false).keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.tailMap("a", true).keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.headMap("r", true).keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } - try { - map.headMap("r", false).tailMap("m", true).keySet().add("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.descendingKeySet().add("a")); + assertThrows( + UnsupportedOperationException.class, + () -> map.subMap("a", true, "z", false).keySet().add("a")); + assertThrows( + UnsupportedOperationException.class, () -> map.tailMap("a", true).keySet().add("a")); + assertThrows( + UnsupportedOperationException.class, () -> map.headMap("r", true).keySet().add("a")); + assertThrows( + UnsupportedOperationException.class, + () -> map.headMap("r", false).tailMap("m", true).keySet().add("a")); } @GwtIncompatible // NavigableMap @@ -933,21 +910,15 @@ public void testToMapWithDuplicateKeys() { } public void testToMapWithNullKeys() { - Iterable strings = Arrays.asList("one", null, "three"); - try { - Maps.toMap(strings, Functions.constant("foo")); - fail(); - } catch (NullPointerException expected) { - } + Iterable<@Nullable String> strings = asList("one", null, "three"); + assertThrows( + NullPointerException.class, + () -> Maps.toMap((Iterable) strings, Functions.constant("foo"))); } public void testToMapWithNullValues() { Iterable strings = ImmutableList.of("one", "two", "three"); - try { - Maps.toMap(strings, Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Maps.toMap(strings, Functions.constant(null))); } private static final ImmutableBiMap INT_TO_STRING_MAP = @@ -985,37 +956,31 @@ public void testUniqueIndexIterator() { /** Can't create the map if more than one value maps to the same key. */ public void testUniqueIndexDuplicates() { - try { - Map unused = - Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("Multimaps.index"); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> Maps.uniqueIndex(ImmutableSet.of("one", "uno"), Functions.constant(1))); + assertThat(expected).hasMessageThat().contains("Multimaps.index"); } /** Null values are not allowed. */ public void testUniqueIndexNullValue() { - List listWithNull = Lists.newArrayList((String) null); - try { - Maps.uniqueIndex(listWithNull, Functions.constant(1)); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable String> listWithNull = Lists.newArrayList((String) null); + assertThrows( + NullPointerException.class, + () -> Maps.uniqueIndex((List) listWithNull, Functions.constant(1))); } /** Null keys aren't allowed either. */ public void testUniqueIndexNullKey() { List oneStringList = Lists.newArrayList("foo"); - try { - Maps.uniqueIndex(oneStringList, Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> Maps.uniqueIndex(oneStringList, Functions.constant(null))); } + @J2ktIncompatible @GwtIncompatible // Maps.fromProperties - @SuppressWarnings("deprecation") // StringBufferInputStream public void testFromProperties() throws IOException { Properties testProp = new Properties(); @@ -1063,6 +1028,7 @@ public void testFromProperties() throws IOException { assertNotSame(System.getProperty("java.version"), result.get("java.version")); } + @J2ktIncompatible @GwtIncompatible // Maps.fromProperties @SuppressWarnings("serial") // never serialized public void testFromPropertiesNullKey() { @@ -1070,19 +1036,16 @@ public void testFromPropertiesNullKey() { new Properties() { @Override public Enumeration propertyNames() { - return Iterators.asEnumeration(Arrays.asList(null, "first", "second").iterator()); + return Iterators.asEnumeration(asList(null, "first", "second").iterator()); } }; properties.setProperty("first", "true"); properties.setProperty("second", "null"); - try { - Maps.fromProperties(properties); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Maps.fromProperties(properties)); } + @J2ktIncompatible @GwtIncompatible // Maps.fromProperties @SuppressWarnings("serial") // never serialized public void testFromPropertiesNonStringKeys() { @@ -1095,11 +1058,7 @@ public Enumeration propertyNames() { } }; - try { - Maps.fromProperties(properties); - fail(); - } catch (ClassCastException expected) { - } + assertThrows(ClassCastException.class, () -> Maps.fromProperties(properties)); } public void testAsConverter_nominal() throws Exception { @@ -1130,11 +1089,7 @@ public void testAsConverter_noMapping() throws Exception { "one", 1, "two", 2); Converter converter = Maps.asConverter(biMap); - try { - converter.convert("three"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> converter.convert("three")); } public void testAsConverter_nullConversions() throws Exception { @@ -1155,11 +1110,7 @@ public void testAsConverter_isAView() throws Exception { assertEquals((Integer) 1, converter.convert("one")); assertEquals((Integer) 2, converter.convert("two")); - try { - converter.convert("three"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> converter.convert("three")); biMap.put("three", 3); @@ -1169,15 +1120,13 @@ public void testAsConverter_isAView() throws Exception { } public void testAsConverter_withNullMapping() throws Exception { - BiMap biMap = HashBiMap.create(); + BiMap biMap = HashBiMap.create(); biMap.put("one", 1); biMap.put("two", 2); biMap.put("three", null); - try { - Maps.asConverter(biMap).convert("three"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Maps.asConverter((BiMap) biMap).convert("three")); } public void testAsConverter_toString() { @@ -1216,88 +1165,46 @@ public void testUnmodifiableBiMap() { assertEquals(true, unmod.inverse().get("four").equals(4)); /* UnsupportedOperationException on direct modifications. */ - try { - unmod.put(4, "four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - unmod.forcePut(4, "four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - unmod.putAll(Collections.singletonMap(4, "four")); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> unmod.put(4, "four")); + assertThrows(UnsupportedOperationException.class, () -> unmod.forcePut(4, "four")); + assertThrows(UnsupportedOperationException.class, () -> unmod.putAll(singletonMap(4, "four"))); /* UnsupportedOperationException on indirect modifications. */ BiMap inverse = unmod.inverse(); - try { - inverse.put("four", 4); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - inverse.forcePut("four", 4); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - inverse.putAll(Collections.singletonMap("four", 4)); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> inverse.put("four", 4)); + assertThrows(UnsupportedOperationException.class, () -> inverse.forcePut("four", 4)); + assertThrows( + UnsupportedOperationException.class, () -> inverse.putAll(singletonMap("four", 4))); Set values = unmod.values(); - try { - values.remove("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> values.remove("four")); Set> entries = unmod.entrySet(); Entry entry = entries.iterator().next(); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); @SuppressWarnings("unchecked") Entry entry2 = (Entry) entries.toArray()[0]; - try { - entry2.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entry2.setValue("four")); } public void testImmutableEntry() { - Entry e = Maps.immutableEntry("foo", 1); + Entry e = immutableEntry("foo", 1); assertEquals("foo", e.getKey()); assertEquals(1, (int) e.getValue()); - try { - e.setValue(2); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> e.setValue(2)); assertEquals("foo=1", e.toString()); assertEquals(101575, e.hashCode()); } public void testImmutableEntryNull() { - Entry e = Maps.immutableEntry((String) null, (Integer) null); + Entry<@Nullable String, @Nullable Integer> e = immutableEntry((String) null, (Integer) null); assertNull(e.getKey()); assertNull(e.getValue()); - try { - e.setValue(null); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> e.setValue(null)); assertEquals("null=null", e.toString()); assertEquals(0, e.hashCode()); } /** See {@link SynchronizedBiMapTest} for more tests. */ + @J2ktIncompatible // Synchronized public void testSynchronizedBiMap() { BiMap bimap = HashBiMap.create(); bimap.put("one", 1); @@ -1308,264 +1215,7 @@ public void testSynchronizedBiMap() { assertEquals(ImmutableSet.of(1, 2, 3), sync.inverse().keySet()); } - private static final Predicate NOT_LENGTH_3 = - new Predicate() { - @Override - public boolean apply(String input) { - return input == null || input.length() != 3; - } - }; - - private static final Predicate EVEN = - new Predicate() { - @Override - public boolean apply(Integer input) { - return input == null || input % 2 == 0; - } - }; - - private static final Predicate> CORRECT_LENGTH = - new Predicate>() { - @Override - public boolean apply(Entry input) { - return input.getKey().length() == input.getValue(); - } - }; - - private static final Function SQRT_FUNCTION = - new Function() { - @Override - public Double apply(Integer in) { - return Math.sqrt(in); - } - }; - - public static class FilteredMapTest extends TestCase { - Map createUnfiltered() { - return Maps.newHashMap(); - } - - public void testFilteredKeysIllegalPut() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); - filtered.put("a", 1); - filtered.put("b", 2); - assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); - - try { - filtered.put("yyy", 3); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - public void testFilteredKeysIllegalPutAll() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); - filtered.put("a", 1); - filtered.put("b", 2); - assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); - - try { - filtered.putAll(ImmutableMap.of("c", 3, "zzz", 4, "b", 5)); - fail(); - } catch (IllegalArgumentException expected) { - } - - assertEquals(ImmutableMap.of("a", 1, "b", 2), filtered); - } - - public void testFilteredKeysFilteredReflectsBackingChanges() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterKeys(unfiltered, NOT_LENGTH_3); - unfiltered.put("two", 2); - unfiltered.put("three", 3); - unfiltered.put("four", 4); - assertEquals(ImmutableMap.of("two", 2, "three", 3, "four", 4), unfiltered); - assertEquals(ImmutableMap.of("three", 3, "four", 4), filtered); - - unfiltered.remove("three"); - assertEquals(ImmutableMap.of("two", 2, "four", 4), unfiltered); - assertEquals(ImmutableMap.of("four", 4), filtered); - - unfiltered.clear(); - assertEquals(ImmutableMap.of(), unfiltered); - assertEquals(ImmutableMap.of(), filtered); - } - - public void testFilteredValuesIllegalPut() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterValues(unfiltered, EVEN); - filtered.put("a", 2); - unfiltered.put("b", 4); - unfiltered.put("c", 5); - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - - try { - filtered.put("yyy", 3); - fail(); - } catch (IllegalArgumentException expected) { - } - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - } - - public void testFilteredValuesIllegalPutAll() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterValues(unfiltered, EVEN); - filtered.put("a", 2); - unfiltered.put("b", 4); - unfiltered.put("c", 5); - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - - try { - filtered.putAll(ImmutableMap.of("c", 4, "zzz", 5, "b", 6)); - fail(); - } catch (IllegalArgumentException expected) { - } - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - } - - public void testFilteredValuesIllegalSetValue() { - Map unfiltered = createUnfiltered(); - Map filtered = Maps.filterValues(unfiltered, EVEN); - filtered.put("a", 2); - filtered.put("b", 4); - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - - Entry entry = filtered.entrySet().iterator().next(); - try { - entry.setValue(5); - fail(); - } catch (IllegalArgumentException expected) { - } - - assertEquals(ImmutableMap.of("a", 2, "b", 4), filtered); - } - - public void testFilteredValuesClear() { - Map unfiltered = createUnfiltered(); - unfiltered.put("one", 1); - unfiltered.put("two", 2); - unfiltered.put("three", 3); - unfiltered.put("four", 4); - Map filtered = Maps.filterValues(unfiltered, EVEN); - assertEquals(ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4), unfiltered); - assertEquals(ImmutableMap.of("two", 2, "four", 4), filtered); - - filtered.clear(); - assertEquals(ImmutableMap.of("one", 1, "three", 3), unfiltered); - assertTrue(filtered.isEmpty()); - } - - public void testFilteredEntriesIllegalPut() { - Map unfiltered = createUnfiltered(); - unfiltered.put("cat", 3); - unfiltered.put("dog", 2); - unfiltered.put("horse", 5); - Map filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); - assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered); - - filtered.put("chicken", 7); - assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); - - try { - filtered.put("cow", 7); - fail(); - } catch (IllegalArgumentException expected) { - } - assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); - } - - public void testFilteredEntriesIllegalPutAll() { - Map unfiltered = createUnfiltered(); - unfiltered.put("cat", 3); - unfiltered.put("dog", 2); - unfiltered.put("horse", 5); - Map filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); - assertEquals(ImmutableMap.of("cat", 3, "horse", 5), filtered); - - filtered.put("chicken", 7); - assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); - - try { - filtered.putAll(ImmutableMap.of("sheep", 5, "cow", 7)); - fail(); - } catch (IllegalArgumentException expected) { - } - assertEquals(ImmutableMap.of("cat", 3, "horse", 5, "chicken", 7), filtered); - } - - public void testFilteredEntriesObjectPredicate() { - Map unfiltered = createUnfiltered(); - unfiltered.put("cat", 3); - unfiltered.put("dog", 2); - unfiltered.put("horse", 5); - Predicate predicate = Predicates.alwaysFalse(); - Map filtered = Maps.filterEntries(unfiltered, predicate); - assertTrue(filtered.isEmpty()); - } - - public void testFilteredEntriesWildCardEntryPredicate() { - Map unfiltered = createUnfiltered(); - unfiltered.put("cat", 3); - unfiltered.put("dog", 2); - unfiltered.put("horse", 5); - Predicate> predicate = - new Predicate>() { - @Override - public boolean apply(Entry input) { - return "cat".equals(input.getKey()) || Integer.valueOf(2) == input.getValue(); - } - }; - Map filtered = Maps.filterEntries(unfiltered, predicate); - assertEquals(ImmutableMap.of("cat", 3, "dog", 2), filtered); - } - } - - public static class FilteredSortedMapTest extends FilteredMapTest { - @Override - SortedMap createUnfiltered() { - return Maps.newTreeMap(); - } - - public void testFirstAndLastKeyFilteredMap() { - SortedMap unfiltered = createUnfiltered(); - unfiltered.put("apple", 2); - unfiltered.put("banana", 6); - unfiltered.put("cat", 3); - unfiltered.put("dog", 5); - - SortedMap filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); - assertEquals("banana", filtered.firstKey()); - assertEquals("cat", filtered.lastKey()); - } - - public void testHeadSubTailMap_FilteredMap() { - SortedMap unfiltered = createUnfiltered(); - unfiltered.put("apple", 2); - unfiltered.put("banana", 6); - unfiltered.put("cat", 4); - unfiltered.put("dog", 3); - SortedMap filtered = Maps.filterEntries(unfiltered, CORRECT_LENGTH); - - assertEquals(ImmutableMap.of("banana", 6), filtered.headMap("dog")); - assertEquals(ImmutableMap.of(), filtered.headMap("banana")); - assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.headMap("emu")); - - assertEquals(ImmutableMap.of("banana", 6), filtered.subMap("banana", "dog")); - assertEquals(ImmutableMap.of("dog", 3), filtered.subMap("cat", "emu")); - - assertEquals(ImmutableMap.of("dog", 3), filtered.tailMap("cat")); - assertEquals(ImmutableMap.of("banana", 6, "dog", 3), filtered.tailMap("banana")); - } - } - - public static class FilteredBiMapTest extends FilteredMapTest { - @Override - BiMap createUnfiltered() { - return HashBiMap.create(); - } - } + private static final Function SQRT_FUNCTION = in -> Math.sqrt(in); public void testTransformValues() { Map map = ImmutableMap.of("a", 4, "b", 9); @@ -1741,92 +1391,62 @@ public void testUnmodifiableNavigableMap() { ensureNotDirectlyModifiable(unmod.tailMap(2, true)); Collection values = unmod.values(); - try { - values.add("4"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - values.remove("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - values.removeAll(Collections.singleton("four")); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - values.retainAll(Collections.singleton("four")); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - Iterator iterator = values.iterator(); - iterator.next(); - iterator.remove(); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> values.add("4")); + assertThrows(UnsupportedOperationException.class, () -> values.remove("four")); + assertThrows(UnsupportedOperationException.class, () -> values.removeAll(singleton("four"))); + assertThrows(UnsupportedOperationException.class, () -> values.retainAll(singleton("four"))); + assertThrows( + UnsupportedOperationException.class, + () -> { + Iterator iterator = values.iterator(); + iterator.next(); + iterator.remove(); + }); Set> entries = unmod.entrySet(); - try { - Iterator> iterator = entries.iterator(); - iterator.next(); - iterator.remove(); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + assertThrows( + UnsupportedOperationException.class, + () -> { + Iterator> iterator = entries.iterator(); + iterator.next(); + iterator.remove(); + }); + { + Entry entry = entries.iterator().next(); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - Entry entry = entries.iterator().next(); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.lowerEntry(1); + assertNull(entry); } - entry = unmod.lowerEntry(1); - assertNull(entry); - entry = unmod.floorEntry(2); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.floorEntry(2); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - entry = unmod.ceilingEntry(2); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.ceilingEntry(2); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - entry = unmod.lowerEntry(2); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.lowerEntry(2); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - entry = unmod.higherEntry(2); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.higherEntry(2); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - entry = unmod.firstEntry(); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.firstEntry(); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - entry = unmod.lastEntry(); - try { - entry.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + Entry entry = unmod.lastEntry(); + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } - @SuppressWarnings("unchecked") - Entry entry2 = (Entry) entries.toArray()[0]; - try { - entry2.setValue("four"); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { + { + @SuppressWarnings("unchecked") + Entry entry = (Entry) entries.toArray()[0]; + assertThrows(UnsupportedOperationException.class, () -> entry.setValue("four")); } } @@ -1838,7 +1458,7 @@ void ensureNotDirectlyModifiable(NavigableMap unmod) { } catch (UnsupportedOperationException expected) { } try { - unmod.putAll(Collections.singletonMap(4, "four")); + unmod.putAll(singletonMap(4, "four")); fail("UnsupportedOperationException expected"); } catch (UnsupportedOperationException expected) { } @@ -1934,11 +1554,7 @@ public void testSubMap_unnaturalOrdering() { .put(10, 0) .build(); - try { - Maps.subMap(map, Range.closed(4, 8)); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Maps.subMap(map, Range.closed(4, 8))); // These results are all incorrect, but there's no way (short of iterating over the result) // to verify that with an arbitrary ordering or comparator. diff --git a/android/guava-tests/test/com/google/common/collect/MapsTransformValuesTest.java b/android/guava-tests/test/com/google/common/collect/MapsTransformValuesTest.java index b4382dbcacd0..e1cafc86b964 100644 --- a/android/guava-tests/test/com/google/common/collect/MapsTransformValuesTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapsTransformValuesTest.java @@ -16,50 +16,25 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.transformValues; + import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Functions; -import com.google.common.collect.testing.MapInterfaceTest; -import java.util.Collection; -import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullMarked; /** - * Tests for {@link Maps#transformValues}. + * Tests for {@link Maps#transformValues(Map, Function)}. * * @author Isaac Shum */ @GwtCompatible -public class MapsTransformValuesTest extends MapInterfaceTest { - - /** - * Constructor that assigns {@code supportsIteratorRemove} the same value as {@code - * supportsRemove}. - */ - protected MapsTransformValuesTest( - boolean allowsNullKeys, - boolean allowsNullValues, - boolean supportsPut, - boolean supportsRemove, - boolean supportsClear) { - super( - allowsNullKeys, - allowsNullValues, - supportsPut, - supportsRemove, - supportsClear, - supportsRemove); - } - - public MapsTransformValuesTest() { - super(false, true, false, true, true); - } - +@NullMarked +public class MapsTransformValuesTest extends AbstractMapsTransformValuesTest { + @Override protected Map makeEmptyMap() { - return Maps.transformValues(Maps.newHashMap(), Functions.identity()); + return transformValues(Maps.newHashMap(), Functions.identity()); } @Override @@ -68,241 +43,6 @@ protected Map makePopulatedMap() { underlying.put("a", 1); underlying.put("b", 2); underlying.put("c", 3); - return Maps.transformValues(underlying, Functions.toStringFunction()); - } - - @Override - protected String getKeyNotInPopulatedMap() throws UnsupportedOperationException { - return "z"; - } - - @Override - protected String getValueNotInPopulatedMap() throws UnsupportedOperationException { - return "26"; - } - - /** Helper assertion comparing two maps */ - private void assertMapsEqual(Map expected, Map map) { - assertEquals(expected, map); - assertEquals(expected.hashCode(), map.hashCode()); - assertEquals(expected.entrySet(), map.entrySet()); - - // Assert that expectedValues > mapValues and that - // mapValues > expectedValues; i.e. that expectedValues == mapValues. - Collection expectedValues = expected.values(); - Collection mapValues = map.values(); - assertEquals(expectedValues.size(), mapValues.size()); - assertTrue(expectedValues.containsAll(mapValues)); - assertTrue(mapValues.containsAll(expectedValues)); - } - - public void testTransformEmptyMapEquality() { - Map map = - Maps.transformValues(ImmutableMap.of(), Functions.toStringFunction()); - assertMapsEqual(Maps.newHashMap(), map); - } - - public void testTransformSingletonMapEquality() { - Map map = - Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); - Map expected = ImmutableMap.of("a", "1"); - assertMapsEqual(expected, map); - assertEquals(expected.get("a"), map.get("a")); - } - - public void testTransformIdentityFunctionEquality() { - Map underlying = ImmutableMap.of("a", 1); - Map map = Maps.transformValues(underlying, Functions.identity()); - assertMapsEqual(underlying, map); - } - - public void testTransformPutEntryIsUnsupported() { - Map map = - Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); - try { - map.put("b", "2"); - fail(); - } catch (UnsupportedOperationException expected) { - } - - try { - map.putAll(ImmutableMap.of("b", "2")); - fail(); - } catch (UnsupportedOperationException expected) { - } - - try { - map.entrySet().iterator().next().setValue("one"); - fail(); - } catch (UnsupportedOperationException expected) { - } - } - - public void testTransformRemoveEntry() { - Map underlying = Maps.newHashMap(); - underlying.put("a", 1); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); - assertEquals("1", map.remove("a")); - assertNull(map.remove("b")); - } - - public void testTransformEqualityOfMapsWithNullValues() { - Map underlying = Maps.newHashMap(); - underlying.put("a", null); - underlying.put("b", ""); - - Map map = - Maps.transformValues( - underlying, - new Function() { - @Override - public Boolean apply(@CheckForNull String from) { - return from == null; - } - }); - Map expected = ImmutableMap.of("a", true, "b", false); - assertMapsEqual(expected, map); - assertEquals(expected.get("a"), map.get("a")); - assertEquals(expected.containsKey("a"), map.containsKey("a")); - assertEquals(expected.get("b"), map.get("b")); - assertEquals(expected.containsKey("b"), map.containsKey("b")); - assertEquals(expected.get("c"), map.get("c")); - assertEquals(expected.containsKey("c"), map.containsKey("c")); - } - - public void testTransformReflectsUnderlyingMap() { - Map underlying = Maps.newHashMap(); - underlying.put("a", 1); - underlying.put("b", 2); - underlying.put("c", 3); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); - assertEquals(underlying.size(), map.size()); - - underlying.put("d", 4); - assertEquals(underlying.size(), map.size()); - assertEquals("4", map.get("d")); - - underlying.remove("c"); - assertEquals(underlying.size(), map.size()); - assertFalse(map.containsKey("c")); - - underlying.clear(); - assertEquals(underlying.size(), map.size()); - } - - public void testTransformChangesAreReflectedInUnderlyingMap() { - Map underlying = Maps.newLinkedHashMap(); - underlying.put("a", 1); - underlying.put("b", 2); - underlying.put("c", 3); - underlying.put("d", 4); - underlying.put("e", 5); - underlying.put("f", 6); - underlying.put("g", 7); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); - - map.remove("a"); - assertFalse(underlying.containsKey("a")); - - Set keys = map.keySet(); - keys.remove("b"); - assertFalse(underlying.containsKey("b")); - - Iterator keyIterator = keys.iterator(); - keyIterator.next(); - keyIterator.remove(); - assertFalse(underlying.containsKey("c")); - - Collection values = map.values(); - values.remove("4"); - assertFalse(underlying.containsKey("d")); - - Iterator valueIterator = values.iterator(); - valueIterator.next(); - valueIterator.remove(); - assertFalse(underlying.containsKey("e")); - - Set> entries = map.entrySet(); - Entry firstEntry = entries.iterator().next(); - entries.remove(firstEntry); - assertFalse(underlying.containsKey("f")); - - Iterator> entryIterator = entries.iterator(); - entryIterator.next(); - entryIterator.remove(); - assertFalse(underlying.containsKey("g")); - - assertTrue(underlying.isEmpty()); - assertTrue(map.isEmpty()); - assertTrue(keys.isEmpty()); - assertTrue(values.isEmpty()); - assertTrue(entries.isEmpty()); - } - - public void testTransformEquals() { - Map underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2); - Map expected = Maps.transformValues(underlying, Functions.identity()); - - assertMapsEqual(expected, expected); - - Map equalToUnderlying = Maps.newTreeMap(); - equalToUnderlying.putAll(underlying); - Map map = - Maps.transformValues(equalToUnderlying, Functions.identity()); - assertMapsEqual(expected, map); - - map = - Maps.transformValues( - ImmutableMap.of("a", 1, "b", 2, "c", 3), - new Function() { - @Override - public Integer apply(Integer from) { - return from - 1; - } - }); - assertMapsEqual(expected, map); - } - - public void testTransformEntrySetContains() { - Map underlying = Maps.newHashMap(); - underlying.put("a", null); - underlying.put("b", true); - underlying.put(null, true); - - Map map = - Maps.transformValues( - underlying, - new Function() { - @Override - public Boolean apply(@CheckForNull Boolean from) { - return (from == null) ? true : null; - } - }); - - Set> entries = map.entrySet(); - assertTrue(entries.contains(Maps.immutableEntry("a", true))); - assertTrue(entries.contains(Maps.immutableEntry("b", (Boolean) null))); - assertTrue(entries.contains(Maps.immutableEntry((String) null, (Boolean) null))); - - assertFalse(entries.contains(Maps.immutableEntry("c", (Boolean) null))); - assertFalse(entries.contains(Maps.immutableEntry((String) null, true))); - } - - @Override - public void testKeySetRemoveAllNullFromEmpty() { - try { - super.testKeySetRemoveAllNullFromEmpty(); - } catch (RuntimeException tolerated) { - // GWT's HashMap.keySet().removeAll(null) doesn't throws NPE. - } - } - - @Override - public void testEntrySetRemoveAllNullFromEmpty() { - try { - super.testEntrySetRemoveAllNullFromEmpty(); - } catch (RuntimeException tolerated) { - // GWT's HashMap.entrySet().removeAll(null) doesn't throws NPE. - } + return transformValues(underlying, Functions.toStringFunction()); } } diff --git a/android/guava-tests/test/com/google/common/collect/MapsTransformValuesUnmodifiableIteratorTest.java b/android/guava-tests/test/com/google/common/collect/MapsTransformValuesUnmodifiableIteratorTest.java index 10739513f0c1..d53579f14f9f 100644 --- a/android/guava-tests/test/com/google/common/collect/MapsTransformValuesUnmodifiableIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/MapsTransformValuesUnmodifiableIteratorTest.java @@ -16,6 +16,10 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Maps.transformValues; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Functions; @@ -25,7 +29,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Maps#transformValues} when the backing map's views have iterators that don't @@ -34,6 +39,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class MapsTransformValuesUnmodifiableIteratorTest extends MapInterfaceTest { // TODO(jlevy): Move shared code of this class and MapsTransformValuesTest // to a superclass. @@ -133,7 +139,7 @@ public boolean retainAll(Collection c) { @Override protected Map makeEmptyMap() { Map underlying = Maps.newHashMap(); - return Maps.transformValues( + return transformValues( new UnmodifiableIteratorMap(underlying), Functions.toStringFunction()); } @@ -143,7 +149,7 @@ protected Map makePopulatedMap() { underlying.put("a", 1); underlying.put("b", 2); underlying.put("c", 3); - return Maps.transformValues( + return transformValues( new UnmodifiableIteratorMap(underlying), Functions.toStringFunction()); } @@ -174,13 +180,13 @@ private void assertMapsEqual(Map expected, Map map) { public void testTransformEmptyMapEquality() { Map map = - Maps.transformValues(ImmutableMap.of(), Functions.toStringFunction()); + transformValues(ImmutableMap.of(), Functions.toStringFunction()); assertMapsEqual(Maps.newHashMap(), map); } public void testTransformSingletonMapEquality() { Map map = - Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); + transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); Map expected = ImmutableMap.of("a", "1"); assertMapsEqual(expected, map); assertEquals(expected.get("a"), map.get("a")); @@ -188,51 +194,41 @@ public void testTransformSingletonMapEquality() { public void testTransformIdentityFunctionEquality() { Map underlying = ImmutableMap.of("a", 1); - Map map = Maps.transformValues(underlying, Functions.identity()); + Map map = transformValues(underlying, Functions.identity()); assertMapsEqual(underlying, map); } public void testTransformPutEntryIsUnsupported() { Map map = - Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); - try { - map.put("b", "2"); - fail(); - } catch (UnsupportedOperationException expected) { - } + transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction()); + assertThrows(UnsupportedOperationException.class, () -> map.put("b", "2")); - try { - map.putAll(ImmutableMap.of("b", "2")); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.putAll(ImmutableMap.of("b", "2"))); - try { - map.entrySet().iterator().next().setValue("one"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> map.entrySet().iterator().next().setValue("one")); } public void testTransformRemoveEntry() { Map underlying = Maps.newHashMap(); underlying.put("a", 1); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); + Map map = transformValues(underlying, Functions.toStringFunction()); assertEquals("1", map.remove("a")); assertNull(map.remove("b")); } public void testTransformEqualityOfMapsWithNullValues() { - Map underlying = Maps.newHashMap(); + Map underlying = Maps.newHashMap(); underlying.put("a", null); underlying.put("b", ""); - Map map = - Maps.transformValues( + Map<@Nullable String, Boolean> map = + transformValues( underlying, - new Function() { + new Function<@Nullable String, Boolean>() { @Override - public Boolean apply(@CheckForNull String from) { + public Boolean apply(@Nullable String from) { return from == null; } }); @@ -251,7 +247,7 @@ public void testTransformReflectsUnderlyingMap() { underlying.put("a", 1); underlying.put("b", 2); underlying.put("c", 3); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); + Map map = transformValues(underlying, Functions.toStringFunction()); assertEquals(underlying.size(), map.size()); underlying.put("d", 4); @@ -275,7 +271,7 @@ public void testTransformChangesAreReflectedInUnderlyingMap() { underlying.put("e", 5); underlying.put("f", 6); underlying.put("g", 7); - Map map = Maps.transformValues(underlying, Functions.toStringFunction()); + Map map = transformValues(underlying, Functions.toStringFunction()); map.remove("a"); assertFalse(underlying.containsKey("a")); @@ -317,18 +313,17 @@ public void testTransformChangesAreReflectedInUnderlyingMap() { public void testTransformEquals() { Map underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2); - Map expected = Maps.transformValues(underlying, Functions.identity()); + Map expected = transformValues(underlying, Functions.identity()); assertMapsEqual(expected, expected); Map equalToUnderlying = Maps.newTreeMap(); equalToUnderlying.putAll(underlying); - Map map = - Maps.transformValues(equalToUnderlying, Functions.identity()); + Map map = transformValues(equalToUnderlying, Functions.identity()); assertMapsEqual(expected, map); map = - Maps.transformValues( + transformValues( ImmutableMap.of("a", 1, "b", 2, "c", 3), new Function() { @Override @@ -340,28 +335,29 @@ public Integer apply(Integer from) { } public void testTransformEntrySetContains() { - Map underlying = Maps.newHashMap(); + Map<@Nullable String, @Nullable Boolean> underlying = Maps.newHashMap(); underlying.put("a", null); underlying.put("b", true); underlying.put(null, true); - Map map = - Maps.transformValues( + Map<@Nullable String, @Nullable Boolean> map = + transformValues( underlying, - new Function() { + new Function<@Nullable Boolean, @Nullable Boolean>() { @Override - public Boolean apply(@CheckForNull Boolean from) { + public @Nullable Boolean apply(@Nullable Boolean from) { return (from == null) ? true : null; } }); - Set> entries = map.entrySet(); - assertTrue(entries.contains(Maps.immutableEntry("a", true))); - assertTrue(entries.contains(Maps.immutableEntry("b", (Boolean) null))); - assertTrue(entries.contains(Maps.immutableEntry((String) null, (Boolean) null))); + Set> entries = map.entrySet(); + assertTrue(entries.contains(immutableEntry("a", true))); + assertTrue(entries.contains(Maps.immutableEntry("b", null))); + assertTrue( + entries.contains(Maps.<@Nullable String, @Nullable Boolean>immutableEntry(null, null))); - assertFalse(entries.contains(Maps.immutableEntry("c", (Boolean) null))); - assertFalse(entries.contains(Maps.immutableEntry((String) null, true))); + assertFalse(entries.contains(Maps.immutableEntry("c", null))); + assertFalse(entries.contains(Maps.<@Nullable String, Boolean>immutableEntry(null, true))); } @Override diff --git a/android/guava-tests/test/com/google/common/collect/MinMaxPriorityQueueTest.java b/android/guava-tests/test/com/google/common/collect/MinMaxPriorityQueueTest.java index 5471e3609d04..b5d4cf5d6331 100644 --- a/android/guava-tests/test/com/google/common/collect/MinMaxPriorityQueueTest.java +++ b/android/guava-tests/test/com/google/common/collect/MinMaxPriorityQueueTest.java @@ -19,10 +19,15 @@ import static com.google.common.base.Objects.equal; import static com.google.common.collect.Platform.reduceExponentIfGwt; import static com.google.common.collect.Platform.reduceIterationsIfGwt; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; +import static java.util.Collections.shuffle; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.IteratorFeature; import com.google.common.collect.testing.IteratorTester; import com.google.common.collect.testing.QueueTestSuiteBuilder; @@ -47,6 +52,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link MinMaxPriorityQueue}. @@ -55,10 +62,13 @@ * @author Sverre Sundsdal */ @GwtCompatible(emulated = true) +@NullMarked public class MinMaxPriorityQueueTest extends TestCase { - private static final Ordering SOME_COMPARATOR = Ordering.natural().reverse(); + private static final Ordering SOME_COMPARATOR = Ordering.natural().reverse(); + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(MinMaxPriorityQueueTest.class); @@ -67,7 +77,7 @@ public static Test suite() { new TestStringQueueGenerator() { @Override protected Queue create(String[] elements) { - return MinMaxPriorityQueue.create(Arrays.asList(elements)); + return MinMaxPriorityQueue.create(asList(elements)); } }) .named("MinMaxPriorityQueue") @@ -92,6 +102,9 @@ public void testCreation_comparator() { assertSame(SOME_COMPARATOR, queue.comparator()); } + // We use the rawtypeToWildcard "cast" to make the test work with J2KT in other tests. Leaving one + // test without that cast to verify that using the raw Comparable works outside J2KT. + @J2ktIncompatible // J2KT's translation of raw Comparable is not a supertype of Int translation public void testCreation_expectedSize() { MinMaxPriorityQueue queue = MinMaxPriorityQueue.expectedSize(8).create(); assertEquals(8, queue.capacity()); @@ -108,7 +121,8 @@ public void testCreation_expectedSize_comparator() { } public void testCreation_maximumSize() { - MinMaxPriorityQueue queue = MinMaxPriorityQueue.maximumSize(42).create(); + MinMaxPriorityQueue queue = + rawtypeToWildcard(MinMaxPriorityQueue.maximumSize(42)).create(); assertEquals(11, queue.capacity()); assertEquals(42, queue.maximumSize); checkNatural(queue); @@ -124,7 +138,7 @@ public void testCreation_comparator_maximumSize() { public void testCreation_expectedSize_maximumSize() { MinMaxPriorityQueue queue = - MinMaxPriorityQueue.expectedSize(8).maximumSize(42).create(); + rawtypeToWildcard(MinMaxPriorityQueue.expectedSize(8)).maximumSize(42).create(); assertEquals(8, queue.capacity()); assertEquals(42, queue.maximumSize); checkNatural(queue); @@ -150,7 +164,8 @@ public void testCreation_comparator_withContents() { } public void testCreation_expectedSize_withContents() { - MinMaxPriorityQueue queue = MinMaxPriorityQueue.expectedSize(8).create(NUMBERS); + MinMaxPriorityQueue queue = + rawtypeToWildcard(MinMaxPriorityQueue.expectedSize(8)).create(NUMBERS); assertEquals(6, queue.size()); assertEquals(8, queue.capacity()); checkUnbounded(queue); @@ -158,7 +173,8 @@ public void testCreation_expectedSize_withContents() { } public void testCreation_maximumSize_withContents() { - MinMaxPriorityQueue queue = MinMaxPriorityQueue.maximumSize(42).create(NUMBERS); + MinMaxPriorityQueue queue = + rawtypeToWildcard(MinMaxPriorityQueue.maximumSize(42)).create(NUMBERS); assertEquals(6, queue.size()); assertEquals(11, queue.capacity()); assertEquals(42, queue.maximumSize); @@ -194,7 +210,8 @@ public void testHeapIntact() { Random random = new Random(0); int heapSize = 99; int numberOfModifications = 100; - MinMaxPriorityQueue mmHeap = MinMaxPriorityQueue.expectedSize(heapSize).create(); + MinMaxPriorityQueue mmHeap = + rawtypeToWildcard(MinMaxPriorityQueue.expectedSize(heapSize)).create(); /* * this map would contain the same exact elements as the MinMaxHeap; the * value in the map is the number of occurrences of the key. @@ -276,7 +293,7 @@ public void testSmallMinHeap() { public void testRemove() { MinMaxPriorityQueue mmHeap = MinMaxPriorityQueue.create(); mmHeap.addAll(Lists.newArrayList(1, 2, 3, 4, 47, 1, 5, 3, 0)); - assertTrue("Heap is not intact initally", mmHeap.isIntact()); + assertTrue("Heap is not intact initially", mmHeap.isIntact()); assertEquals(9, mmHeap.size()); mmHeap.remove(5); assertEquals(8, mmHeap.size()); @@ -315,11 +332,7 @@ public void testIteratorPastEndException() { assertTrue("Iterator has reached end prematurely", it.hasNext()); it.next(); it.next(); - try { - it.next(); - fail("No exception thrown when iterating past end of heap"); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> it.next()); } public void testIteratorConcurrentModification() { @@ -330,11 +343,7 @@ public void testIteratorConcurrentModification() { it.next(); it.next(); mmHeap.remove(4); - try { - it.next(); - fail("No exception thrown when iterating a modified heap"); - } catch (ConcurrentModificationException expected) { - } + assertThrows(ConcurrentModificationException.class, () -> it.next()); } /** Tests a failure caused by fix to childless uncle issue. */ @@ -466,7 +475,8 @@ public void testIteratorInvalidatingIteratorRemove2() { } public void testRemoveFromStringHeap() { - MinMaxPriorityQueue mmHeap = MinMaxPriorityQueue.expectedSize(5).create(); + MinMaxPriorityQueue mmHeap = + rawtypeToWildcard(MinMaxPriorityQueue.expectedSize(5)).create(); Collections.addAll(mmHeap, "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric"); assertTrue("Heap is not intact initially", mmHeap.isIntact()); assertEquals("bar", mmHeap.peek()); @@ -483,7 +493,7 @@ public void testRemoveFromStringHeap() { public void testCreateWithOrdering() { MinMaxPriorityQueue mmHeap = - MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).create(); + MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).create(); Collections.addAll(mmHeap, "foo", "bar", "foobar", "barfoo", "larry", "sergey", "eric"); assertTrue("Heap is not intact initially", mmHeap.isIntact()); assertEquals("sergey", mmHeap.peek()); @@ -492,7 +502,9 @@ public void testCreateWithOrdering() { public void testCreateWithCapacityAndOrdering() { MinMaxPriorityQueue mmHeap = - MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()).expectedSize(5).create(); + MinMaxPriorityQueue.orderedBy(Ordering.natural().reverse()) + .expectedSize(5) + .create(); Collections.addAll(mmHeap, 1, 7, 2, 56, 2, 5, 23, 68, 0, 3); assertTrue("Heap is not intact initially", mmHeap.isIntact()); assertEquals(68, (int) mmHeap.peek()); @@ -507,7 +519,7 @@ private > void runIterator(final List values, int ste IteratorFeature.MODIFIABLE, Lists.newLinkedList(values), IteratorTester.KnownOrder.UNKNOWN_ORDER) { - private MinMaxPriorityQueue mmHeap; + private @Nullable MinMaxPriorityQueue mmHeap; @Override protected Iterator newTargetIterator() { @@ -517,7 +529,7 @@ protected Iterator newTargetIterator() { @Override protected void verify(List elements) { - assertEquals(Sets.newHashSet(elements), Sets.newHashSet(mmHeap.iterator())); + assertEquals(newHashSet(elements), newHashSet(mmHeap.iterator())); assertIntact(mmHeap); } }; @@ -542,7 +554,8 @@ public void testRemoveAt() { Random random = new Random(seed); int heapSize = 999; int numberOfModifications = reduceIterationsIfGwt(500); - MinMaxPriorityQueue mmHeap = MinMaxPriorityQueue.expectedSize(heapSize).create(); + MinMaxPriorityQueue mmHeap = + rawtypeToWildcard(MinMaxPriorityQueue.expectedSize(heapSize)).create(); for (int i = 0; i < heapSize; i++) { mmHeap.add(random.nextInt()); } @@ -741,9 +754,9 @@ public void testRandomRemoves() { Random random = new Random(0); for (int attempts = 0; attempts < reduceIterationsIfGwt(1000); attempts++) { ArrayList elements = createOrderedList(10); - Collections.shuffle(elements, random); + shuffle(elements, random); MinMaxPriorityQueue queue = MinMaxPriorityQueue.create(elements); - Collections.shuffle(elements, random); + shuffle(elements, random); for (Integer element : elements) { assertThat(queue.remove(element)).isTrue(); assertIntact(queue); @@ -864,28 +877,15 @@ public void testIsEvenLevel() { // since isEvenLevel adds 1, we need to do - 2. assertTrue(MinMaxPriorityQueue.isEvenLevel((1 << 31) - 2)); assertTrue(MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE - 1)); - try { - MinMaxPriorityQueue.isEvenLevel((1 << 31) - 1); - fail("Should overflow"); - } catch (IllegalStateException expected) { - } - try { - MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE); - fail("Should overflow"); - } catch (IllegalStateException expected) { - } - try { - MinMaxPriorityQueue.isEvenLevel(1 << 31); - fail("Should be negative"); - } catch (IllegalStateException expected) { - } - try { - MinMaxPriorityQueue.isEvenLevel(Integer.MIN_VALUE); - fail("Should be negative"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> MinMaxPriorityQueue.isEvenLevel((1 << 31) - 1)); + assertThrows( + IllegalStateException.class, () -> MinMaxPriorityQueue.isEvenLevel(Integer.MAX_VALUE)); + assertThrows(IllegalStateException.class, () -> MinMaxPriorityQueue.isEvenLevel(1 << 31)); + assertThrows( + IllegalStateException.class, () -> MinMaxPriorityQueue.isEvenLevel(Integer.MIN_VALUE)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -942,7 +942,8 @@ private static void assertIntactUsingStartedWith( } } - private static void assertEqualsUsingSeed(long seed, Object expected, Object actual) { + private static void assertEqualsUsingSeed( + long seed, @Nullable Object expected, @Nullable Object actual) { if (!equal(actual, expected)) { // fail(), but with the JUnit-supplied message. assertEquals("Using seed " + seed, expected, actual); @@ -950,10 +951,18 @@ private static void assertEqualsUsingSeed(long seed, Object expected, Object act } private static void assertEqualsUsingStartedWith( - Collection startedWith, Object expected, Object actual) { + Collection startedWith, @Nullable Object expected, @Nullable Object actual) { if (!equal(actual, expected)) { // fail(), but with the JUnit-supplied message. assertEquals("Started with " + startedWith, expected, actual); } } + + // J2kt cannot translate the Comparable rawtype in a usable way (it becomes Comparable + // but types are typically only Comparable to themselves). + @SuppressWarnings({"rawtypes", "unchecked"}) + private static MinMaxPriorityQueue.Builder> rawtypeToWildcard( + MinMaxPriorityQueue.Builder builder) { + return (MinMaxPriorityQueue.Builder) builder; + } } diff --git a/android/guava-tests/test/com/google/common/collect/MultimapBuilderTest.java b/android/guava-tests/test/com/google/common/collect/MultimapBuilderTest.java index 5528aba94c67..27b3de8d9bbd 100644 --- a/android/guava-tests/test/com/google/common/collect/MultimapBuilderTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultimapBuilderTest.java @@ -18,7 +18,9 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.MultimapBuilder.MultimapBuilderWithKeys; +import com.google.common.collect.MultimapBuilder.SortedSetMultimapBuilder; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; @@ -27,6 +29,8 @@ import java.util.SortedMap; import java.util.SortedSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link MultimapBuilder}. @@ -34,27 +38,32 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class MultimapBuilderTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // doesn't build without explicit type parameters on build() methods public void testGenerics() { - ListMultimap a = MultimapBuilder.hashKeys().arrayListValues().build(); - SortedSetMultimap b = MultimapBuilder.linkedHashKeys().treeSetValues().build(); - SetMultimap c = + ListMultimap unusedA = MultimapBuilder.hashKeys().arrayListValues().build(); + SortedSetMultimap unusedB = + MultimapBuilder.linkedHashKeys().treeSetValues().build(); + SetMultimap unusedC = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER).hashSetValues().build(); } public void testGenerics_gwtCompatible() { - ListMultimap a = + ListMultimap unusedA = MultimapBuilder.hashKeys().arrayListValues().build(); - SortedSetMultimap b = - MultimapBuilder.linkedHashKeys().treeSetValues().build(); - SetMultimap c = + SortedSetMultimap unusedB = + rawtypeToWildcard(MultimapBuilder.linkedHashKeys().treeSetValues()) + .build(); + SetMultimap unusedC = MultimapBuilder.treeKeys(String.CASE_INSENSITIVE_ORDER) .hashSetValues() .build(); } + @J2ktIncompatible @GwtIncompatible // doesn't build without explicit type parameters on build() methods public void testTreeKeys() { ListMultimap multimap = MultimapBuilder.treeKeys().arrayListValues().build(); @@ -64,11 +73,27 @@ public void testTreeKeys() { public void testTreeKeys_gwtCompatible() { ListMultimap multimap = - MultimapBuilder.treeKeys().arrayListValues().build(); + rawtypeToWildcard(MultimapBuilder.treeKeys()).arrayListValues().build(); assertTrue(multimap.keySet() instanceof SortedSet); assertTrue(multimap.asMap() instanceof SortedMap); } + // J2kt cannot translate the Comparable rawtype in a usable way (it becomes Comparable + // but types are typically only Comparable to themselves). + @SuppressWarnings({"rawtypes", "unchecked"}) + private static MultimapBuilderWithKeys> rawtypeToWildcard( + MultimapBuilderWithKeys treeKeys) { + return (MultimapBuilderWithKeys) treeKeys; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static + SortedSetMultimapBuilder> rawtypeToWildcard( + SortedSetMultimapBuilder setMultimapBuilder) { + return (SortedSetMultimapBuilder) setMultimapBuilder; + } + + @J2ktIncompatible @GwtIncompatible // serialization public void testSerialization() throws Exception { for (MultimapBuilderWithKeys builderWithKeys : @@ -93,6 +118,7 @@ public void testSerialization() throws Exception { } } + @J2ktIncompatible @GwtIncompatible // serialization private static void reserializeAndAssert(Object object) throws Exception { Object copy = reserialize(object); @@ -100,6 +126,7 @@ private static void reserializeAndAssert(Object object) throws Exception { assertEquals(object.getClass(), copy.getClass()); } + @J2ktIncompatible @GwtIncompatible // serialization private static Object reserialize(Object object) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); diff --git a/android/guava-tests/test/com/google/common/collect/MultimapsCollectionTest.java b/android/guava-tests/test/com/google/common/collect/MultimapsCollectionTest.java index e684c3b1f095..a881b53b35bc 100644 --- a/android/guava-tests/test/com/google/common/collect/MultimapsCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultimapsCollectionTest.java @@ -16,7 +16,13 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.collect.Maps.newHashMap; +import static com.google.common.collect.Multimaps.filterKeys; +import static com.google.common.collect.Multimaps.filterValues; +import static com.google.common.collect.Multimaps.synchronizedListMultimap; import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; @@ -30,7 +36,6 @@ import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Function; -import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.Maps.EntryTransformer; @@ -64,6 +69,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Run collection tests on wrappers from {@link Multimaps}. @@ -71,6 +77,8 @@ * @author Jared Levy */ @GwtIncompatible // suite // TODO(cpovirk): set up collect/gwt/suites version +@NullUnmarked +@AndroidIncompatible // test-suite builders public class MultimapsCollectionTest extends TestCase { private static final Feature[] FOR_MAP_FEATURES_ONE = { @@ -175,11 +183,11 @@ abstract static class TestEntriesGenerator @Override public SampleElements> samples() { return new SampleElements<>( - Maps.immutableEntry("bar", 1), - Maps.immutableEntry("bar", 2), - Maps.immutableEntry("foo", 3), - Maps.immutableEntry("bar", 3), - Maps.immutableEntry("cat", 2)); + immutableEntry("bar", 1), + immutableEntry("bar", 2), + immutableEntry("foo", 3), + immutableEntry("bar", 3), + immutableEntry("cat", 2)); } @Override @@ -215,22 +223,6 @@ public List> create(Object... elements) { } } - private static final Predicate> FILTER_GET_PREDICATE = - new Predicate>() { - @Override - public boolean apply(Entry entry) { - return !"badvalue".equals(entry.getValue()) && 55556 != entry.getKey(); - } - }; - - private static final Predicate> FILTER_KEYSET_PREDICATE = - new Predicate>() { - @Override - public boolean apply(Entry entry) { - return !"badkey".equals(entry.getKey()) && 55556 != entry.getValue(); - } - }; - public static Test suite() { TestSuite suite = new TestSuite(); @@ -243,8 +235,7 @@ public static Test suite() { @Override protected ListMultimap create(Entry[] entries) { ListMultimap multimap = - Multimaps.synchronizedListMultimap( - ArrayListMultimap.create()); + synchronizedListMultimap(ArrayListMultimap.create()); for (Entry entry : entries) { multimap.put(entry.getKey(), entry.getValue()); } @@ -373,7 +364,7 @@ public M create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -492,7 +483,7 @@ public SampleElements> samples() { @SuppressWarnings("unchecked") @Override public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override @@ -577,8 +568,7 @@ SetMultimap filter(SetMultimap multimap) { multimap.put("foo", 17); multimap.put("bar", 32); multimap.put("foo", 16); - return Multimaps.filterKeys( - multimap, Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar")))); + return filterKeys(multimap, not(Predicates.in(ImmutableSet.of("foo", "bar")))); } }) .named("Multimaps.filterKeys[SetMultimap, Predicate]") @@ -599,8 +589,7 @@ ListMultimap filter(ListMultimap multimap) { multimap.put("foo", 17); multimap.put("bar", 32); multimap.put("foo", 16); - return Multimaps.filterKeys( - multimap, Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar")))); + return filterKeys(multimap, not(Predicates.in(ImmutableSet.of("foo", "bar")))); } }) .named("Multimaps.filterKeys[ListMultimap, Predicate]") @@ -620,10 +609,8 @@ ListMultimap filter(ListMultimap multimap) { multimap.put("foo", 17); multimap.put("bar", 32); multimap.put("foo", 16); - multimap = - Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("foo"))); - return Multimaps.filterKeys( - multimap, Predicates.not(Predicates.equalTo("bar"))); + multimap = filterKeys(multimap, not(equalTo("foo"))); + return filterKeys(multimap, not(equalTo("bar"))); } }) .named("Multimaps.filterKeys[Multimaps.filterKeys[ListMultimap], Predicate]") @@ -643,8 +630,8 @@ SetMultimap filter(SetMultimap multimap) { multimap.put("one", 314); multimap.put("two", 159); multimap.put("one", 265); - return Multimaps.filterValues( - multimap, Predicates.not(Predicates.in(ImmutableSet.of(314, 159, 265)))); + return filterValues( + multimap, not(Predicates.in(ImmutableSet.of(314, 159, 265)))); } }) .named("Multimaps.filterValues[SetMultimap, Predicate]") @@ -664,7 +651,7 @@ SetMultimap filter(SetMultimap multimap) { ImmutableSetMultimap.of("foo", 314, "one", 159, "two", 265, "bar", 358); multimap.putAll(badEntries); return Multimaps.filterEntries( - multimap, Predicates.not(Predicates.in(badEntries.entries()))); + multimap, not(Predicates.in(badEntries.entries()))); } }) .named("Multimaps.filterEntries[SetMultimap, Predicate]") @@ -684,10 +671,9 @@ SetMultimap filter(SetMultimap multimap) { ImmutableSetMultimap.of("foo", 314, "one", 159, "two", 265, "bar", 358); multimap.putAll(badEntries); multimap = - Multimaps.filterKeys( - multimap, Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar")))); + filterKeys(multimap, not(Predicates.in(ImmutableSet.of("foo", "bar")))); return Multimaps.filterEntries( - multimap, Predicates.not(Predicates.in(badEntries.entries()))); + multimap, not(Predicates.in(badEntries.entries()))); } }) .named("Multimaps.filterEntries[Multimaps.filterKeys[SetMultimap]]") @@ -709,10 +695,8 @@ SetMultimap filter(SetMultimap multimap) { multimap = Multimaps.filterEntries( multimap, - Predicates.not( - Predicates.in(ImmutableMap.of("one", 159, "two", 265).entrySet()))); - return Multimaps.filterKeys( - multimap, Predicates.not(Predicates.in(ImmutableSet.of("foo", "bar")))); + not(Predicates.in(ImmutableMap.of("one", 159, "two", 265).entrySet()))); + return filterKeys(multimap, not(Predicates.in(ImmutableSet.of("foo", "bar")))); } }) .named("Multimaps.filterKeys[Multimaps.filterEntries[SetMultimap]]") @@ -731,10 +715,8 @@ SetMultimap filter(SetMultimap multimap) { ImmutableSetMultimap badEntries = ImmutableSetMultimap.of("foo", 314, "bar", 358); multimap.putAll(badEntries); - multimap = - Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("foo"))); - multimap = - Multimaps.filterKeys(multimap, Predicates.not(Predicates.equalTo("bar"))); + multimap = filterKeys(multimap, not(equalTo("foo"))); + multimap = filterKeys(multimap, not(equalTo("bar"))); return multimap; } }) diff --git a/android/guava-tests/test/com/google/common/collect/MultimapsFilterEntriesAsMapTest.java b/android/guava-tests/test/com/google/common/collect/MultimapsFilterEntriesAsMapTest.java index 314a12774e8d..528fe4724557 100644 --- a/android/guava-tests/test/com/google/common/collect/MultimapsFilterEntriesAsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultimapsFilterEntriesAsMapTest.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import org.jspecify.annotations.NullUnmarked; /** * Tests for Multimaps.filterEntries().asMap(). @@ -28,12 +30,13 @@ * @author Jared Levy */ @GwtIncompatible(value = "untested") +@NullUnmarked public class MultimapsFilterEntriesAsMapTest extends AbstractMultimapAsMapImplementsMapTest { private static final Predicate> PREDICATE = new Predicate>() { @Override public boolean apply(Entry entry) { - return !"badkey".equals(entry.getKey()) && 55556 != entry.getValue(); + return !Objects.equals(entry.getKey(), "badkey") && entry.getValue() != 55556; } }; diff --git a/android/guava-tests/test/com/google/common/collect/MultimapsTest.java b/android/guava-tests/test/com/google/common/collect/MultimapsTest.java index 02ea574bbc50..efd47462f70c 100644 --- a/android/guava-tests/test/com/google/common/collect/MultimapsTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultimapsTest.java @@ -18,14 +18,24 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Multimaps.filterKeys; +import static com.google.common.collect.Multimaps.synchronizedListMultimap; +import static com.google.common.collect.Multimaps.synchronizedMultimap; +import static com.google.common.collect.Multimaps.synchronizedSetMultimap; +import static com.google.common.collect.Multimaps.synchronizedSortedSetMultimap; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.testing.Helpers.nefariousMapEntry; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Predicates; @@ -55,8 +65,9 @@ import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeSet; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code Multimaps}. @@ -64,12 +75,13 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class MultimapsTest extends TestCase { private static final Comparator INT_COMPARATOR = Ordering.natural().reverse().nullsFirst(); - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableListMultimapShortCircuit() { ListMultimap mod = ArrayListMultimap.create(); ListMultimap unmod = Multimaps.unmodifiableListMultimap(mod); @@ -82,7 +94,7 @@ public void testUnmodifiableListMultimapShortCircuit() { immutable, Multimaps.unmodifiableListMultimap((ListMultimap) immutable)); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableSetMultimapShortCircuit() { SetMultimap mod = HashMultimap.create(); SetMultimap unmod = Multimaps.unmodifiableSetMultimap(mod); @@ -95,7 +107,7 @@ public void testUnmodifiableSetMultimapShortCircuit() { immutable, Multimaps.unmodifiableSetMultimap((SetMultimap) immutable)); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableMultimapShortCircuit() { Multimap mod = HashMultimap.create(); Multimap unmod = Multimaps.unmodifiableMultimap(mod); @@ -108,9 +120,11 @@ public void testUnmodifiableMultimapShortCircuit() { @GwtIncompatible // slow (~10s) public void testUnmodifiableArrayListMultimap() { - checkUnmodifiableMultimap(ArrayListMultimap.create(), true); + checkUnmodifiableMultimap( + ArrayListMultimap.<@Nullable String, @Nullable Integer>create(), true); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableArrayListMultimap() { Multimap unmodifiable = @@ -138,9 +152,10 @@ public void testUnmodifiableLinkedListMultimapRandomAccess() { @GwtIncompatible // slow (~10s) public void testUnmodifiableHashMultimap() { - checkUnmodifiableMultimap(HashMultimap.create(), false); + checkUnmodifiableMultimap(HashMultimap.<@Nullable String, @Nullable Integer>create(), false); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableHashMultimap() { Multimap unmodifiable = @@ -153,6 +168,7 @@ public void testUnmodifiableTreeMultimap() { checkUnmodifiableMultimap(TreeMultimap.create(), false, "null", 42); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableTreeMultimap() { Multimap unmodifiable = @@ -161,16 +177,19 @@ public void testSerializingUnmodifiableTreeMultimap() { } @GwtIncompatible // slow (~10s) + @J2ktIncompatible // Synchronized public void testUnmodifiableSynchronizedArrayListMultimap() { checkUnmodifiableMultimap( - Multimaps.synchronizedListMultimap(ArrayListMultimap.create()), true); + synchronizedListMultimap(ArrayListMultimap.<@Nullable String, @Nullable Integer>create()), + true); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableSynchronizedArrayListMultimap() { Multimap unmodifiable = prepareUnmodifiableTests( - Multimaps.synchronizedListMultimap(ArrayListMultimap.create()), + synchronizedListMultimap(ArrayListMultimap.create()), true, null, null); @@ -178,36 +197,37 @@ public void testSerializingUnmodifiableSynchronizedArrayListMultimap() { } @GwtIncompatible // slow (~10s) + @J2ktIncompatible // Synchronized public void testUnmodifiableSynchronizedHashMultimap() { checkUnmodifiableMultimap( - Multimaps.synchronizedSetMultimap(HashMultimap.create()), false); + synchronizedSetMultimap(HashMultimap.<@Nullable String, @Nullable Integer>create()), false); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableSynchronizedHashMultimap() { Multimap unmodifiable = prepareUnmodifiableTests( - Multimaps.synchronizedSetMultimap(HashMultimap.create()), - false, - null, - null); + synchronizedSetMultimap(HashMultimap.create()), false, null, null); SerializableTester.reserializeAndAssert(unmodifiable); } @GwtIncompatible // slow (~10s) + @J2ktIncompatible // Synchronized public void testUnmodifiableSynchronizedTreeMultimap() { TreeMultimap delegate = TreeMultimap.create(Ordering.natural(), INT_COMPARATOR); - SortedSetMultimap multimap = Multimaps.synchronizedSortedSetMultimap(delegate); + SortedSetMultimap multimap = synchronizedSortedSetMultimap(delegate); checkUnmodifiableMultimap(multimap, false, "null", 42); assertSame(INT_COMPARATOR, multimap.valueComparator()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerializingUnmodifiableSynchronizedTreeMultimap() { TreeMultimap delegate = TreeMultimap.create(Ordering.natural(), INT_COMPARATOR); - SortedSetMultimap multimap = Multimaps.synchronizedSortedSetMultimap(delegate); + SortedSetMultimap multimap = synchronizedSortedSetMultimap(delegate); Multimap unmodifiable = prepareUnmodifiableTests(multimap, false, "null", 42); SerializableTester.reserializeAndAssert(unmodifiable); assertSame(INT_COMPARATOR, multimap.valueComparator()); @@ -227,25 +247,13 @@ public void testUnmodifiableMultimapEntries() { Multimap mod = HashMultimap.create(); Multimap unmod = Multimaps.unmodifiableMultimap(mod); mod.put("foo", 1); - Entry entry = unmod.entries().iterator().next(); - try { - entry.setValue(2); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - entry = (Entry) unmod.entries().toArray()[0]; - try { - entry.setValue(2); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + Entry fromIterator = unmod.entries().iterator().next(); + assertThrows(UnsupportedOperationException.class, () -> fromIterator.setValue(2)); + Entry fromToArray = (Entry) unmod.entries().toArray()[0]; + assertThrows(UnsupportedOperationException.class, () -> fromToArray.setValue(2)); Entry[] array = (Entry[]) new Entry[2]; assertSame(array, unmod.entries().toArray(array)); - try { - array[0].setValue(2); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> array[0].setValue(2)); assertFalse(unmod.entries().contains(nefariousMapEntry("pwnd", 2))); assertFalse(unmod.keys().contains("pwnd")); } @@ -255,7 +263,7 @@ public void testUnmodifiableMultimapEntries() { * multimap must support null keys and values. */ private static void checkUnmodifiableMultimap( - Multimap multimap, boolean permitsDuplicates) { + Multimap<@Nullable String, @Nullable Integer> multimap, boolean permitsDuplicates) { checkUnmodifiableMultimap(multimap, permitsDuplicates, null, null); } @@ -265,10 +273,10 @@ private static void checkUnmodifiableMultimap( * involving nulls. */ private static void checkUnmodifiableMultimap( - Multimap multimap, + Multimap<@Nullable String, @Nullable Integer> multimap, boolean permitsDuplicates, - @CheckForNull String nullKey, - @CheckForNull Integer nullValue) { + @Nullable String nullKey, + @Nullable Integer nullValue) { Multimap unmodifiable = prepareUnmodifiableTests(multimap, permitsDuplicates, nullKey, nullValue); @@ -294,11 +302,11 @@ private static void checkUnmodifiableMultimap( } /** Prepares the multimap for unmodifiable tests, returning an unmodifiable view of the map. */ - private static Multimap prepareUnmodifiableTests( - Multimap multimap, + private static Multimap<@Nullable String, @Nullable Integer> prepareUnmodifiableTests( + Multimap<@Nullable String, @Nullable Integer> multimap, boolean permitsDuplicates, - @CheckForNull String nullKey, - @CheckForNull Integer nullValue) { + @Nullable String nullKey, + @Nullable Integer nullValue) { multimap.clear(); multimap.put("foo", 1); multimap.put("foo", 2); @@ -316,21 +324,26 @@ private static Multimap prepareUnmodifiableTests( assertEquals(8, multimap.size()); } - Multimap unmodifiable; + Multimap<@Nullable String, @Nullable Integer> unmodifiable; if (multimap instanceof SortedSetMultimap) { unmodifiable = - Multimaps.unmodifiableSortedSetMultimap((SortedSetMultimap) multimap); + Multimaps.unmodifiableSortedSetMultimap( + (SortedSetMultimap<@Nullable String, @Nullable Integer>) multimap); } else if (multimap instanceof SetMultimap) { - unmodifiable = Multimaps.unmodifiableSetMultimap((SetMultimap) multimap); + unmodifiable = + Multimaps.unmodifiableSetMultimap( + (SetMultimap<@Nullable String, @Nullable Integer>) multimap); } else if (multimap instanceof ListMultimap) { - unmodifiable = Multimaps.unmodifiableListMultimap((ListMultimap) multimap); + unmodifiable = + Multimaps.unmodifiableListMultimap( + (ListMultimap<@Nullable String, @Nullable Integer>) multimap); } else { unmodifiable = Multimaps.unmodifiableMultimap(multimap); } return unmodifiable; } - private static void assertUnmodifiableIterableInTandem( + private static void assertUnmodifiableIterableInTandem( Iterable unmodifiable, Iterable modifiable) { UnmodifiableCollectionTests.assertIteratorIsUnmodifiable(unmodifiable.iterator()); UnmodifiableCollectionTests.assertIteratorsInOrder( @@ -404,28 +417,15 @@ public void testForMap() { assertTrue(multimapView.containsKey("foo")); assertTrue(multimapView.containsValue(1)); assertTrue(multimapView.containsEntry("bar", 2)); - assertEquals(Collections.singleton(1), multimapView.get("foo")); - assertEquals(Collections.singleton(2), multimapView.get("bar")); - try { - multimapView.put("baz", 3); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - multimapView.putAll("baz", Collections.singleton(3)); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - multimapView.putAll(multimap); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - multimapView.replaceValues("foo", Collections.emptySet()); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertEquals(singleton(1), multimapView.get("foo")); + assertEquals(singleton(2), multimapView.get("bar")); + assertThrows(UnsupportedOperationException.class, () -> multimapView.put("baz", 3)); + assertThrows( + UnsupportedOperationException.class, () -> multimapView.putAll("baz", singleton(3))); + assertThrows(UnsupportedOperationException.class, () -> multimapView.putAll(multimap)); + assertThrows( + UnsupportedOperationException.class, + () -> multimapView.replaceValues("foo", Collections.emptySet())); multimapView.remove("bar", 2); assertFalse(multimapView.containsKey("bar")); assertFalse(map.containsKey("bar")); @@ -435,7 +435,7 @@ public void testForMap() { assertThat(multimapView.values()).contains(1); assertThat(multimapView.entries()).contains(Maps.immutableEntry("foo", 1)); assertThat(multimapView.asMap().entrySet()) - .contains(Maps.immutableEntry("foo", (Collection) Collections.singleton(1))); + .contains(Maps.immutableEntry("foo", (Collection) singleton(1))); multimapView.clear(); assertFalse(multimapView.containsKey("foo")); assertFalse(map.containsKey("foo")); @@ -448,6 +448,7 @@ public void testForMap() { assertEquals(multimapView, ArrayListMultimap.create()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testForMapSerialization() { Map map = Maps.newHashMap(); @@ -464,10 +465,10 @@ public void testForMapRemoveAll() { map.put("cow", 3); Multimap multimap = Multimaps.forMap(map); assertEquals(3, multimap.size()); - assertEquals(Collections.emptySet(), multimap.removeAll("dog")); + assertEquals(emptySet(), multimap.removeAll("dog")); assertEquals(3, multimap.size()); assertTrue(multimap.containsKey("bar")); - assertEquals(Collections.singleton(2), multimap.removeAll("bar")); + assertEquals(singleton(2), multimap.removeAll("bar")); assertEquals(2, multimap.size()); assertFalse(multimap.containsKey("bar")); } @@ -477,7 +478,7 @@ public void testForMapAsMap() { map.put("foo", 1); map.put("bar", 2); Map> asMap = Multimaps.forMap(map).asMap(); - assertEquals(Collections.singleton(1), asMap.get("foo")); + assertEquals(singleton(1), asMap.get("foo")); assertNull(asMap.get("cow")); assertTrue(asMap.containsKey("foo")); assertFalse(asMap.containsKey("cow")); @@ -485,15 +486,15 @@ public void testForMapAsMap() { Set>> entries = asMap.entrySet(); assertFalse(entries.contains((Object) 4.5)); assertFalse(entries.remove((Object) 4.5)); - assertFalse(entries.contains(Maps.immutableEntry("foo", Collections.singletonList(1)))); - assertFalse(entries.remove(Maps.immutableEntry("foo", Collections.singletonList(1)))); + assertFalse(entries.contains(Maps.immutableEntry("foo", singletonList(1)))); + assertFalse(entries.remove(Maps.immutableEntry("foo", singletonList(1)))); assertFalse(entries.contains(Maps.immutableEntry("foo", Sets.newLinkedHashSet(asList(1, 2))))); assertFalse(entries.remove(Maps.immutableEntry("foo", Sets.newLinkedHashSet(asList(1, 2))))); - assertFalse(entries.contains(Maps.immutableEntry("foo", Collections.singleton(2)))); - assertFalse(entries.remove(Maps.immutableEntry("foo", Collections.singleton(2)))); + assertFalse(entries.contains(Maps.immutableEntry("foo", singleton(2)))); + assertFalse(entries.remove(Maps.immutableEntry("foo", singleton(2)))); assertTrue(map.containsKey("foo")); - assertTrue(entries.contains(Maps.immutableEntry("foo", Collections.singleton(1)))); - assertTrue(entries.remove(Maps.immutableEntry("foo", Collections.singleton(1)))); + assertTrue(entries.contains(Maps.immutableEntry("foo", singleton(1)))); + assertTrue(entries.remove(Maps.immutableEntry("foo", singleton(1)))); assertFalse(map.containsKey("foo")); } @@ -501,7 +502,7 @@ public void testForMapGetIteration() { IteratorTester tester = new IteratorTester( 4, MODIFIABLE, newHashSet(1), IteratorTester.KnownOrder.KNOWN_ORDER) { - private Multimap multimap; + private @Nullable Multimap multimap; @Override protected Iterator newTargetIterator() { @@ -542,11 +543,16 @@ public E get() { private static class QueueSupplier extends CountingSupplier> { @Override + /* + * We need a Queue that implements equals() for the equality tests we perform after + * reserializing the multimap. + */ + @SuppressWarnings("JdkObsolete") public Queue getImpl() { return new LinkedList<>(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } public void testNewMultimapWithCollectionRejectingNegativeElements() { @@ -577,24 +583,16 @@ public boolean addAll(Collection collection) { Map> map = Maps.newEnumMap(Color.class); Multimap multimap = Multimaps.newMultimap(map, factory); - try { - multimap.put(Color.BLUE, -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> multimap.put(Color.BLUE, -1)); multimap.put(Color.RED, 1); multimap.put(Color.BLUE, 2); - try { - multimap.put(Color.GREEN, -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> multimap.put(Color.GREEN, -1)); assertThat(multimap.entries()) .containsExactly(Maps.immutableEntry(Color.RED, 1), Maps.immutableEntry(Color.BLUE, 2)); } public void testNewMultimap() { - // The ubiquitous EnumArrayBlockingQueueMultimap + // The ubiquitous EnumLinkedListMultimap CountingSupplier> factory = new QueueSupplier(); Map> map = Maps.newEnumMap(Color.class); @@ -631,6 +629,7 @@ public void testNewMultimapValueCollectionMatchesList() { assertTrue(multimap.get(Color.BLUE) instanceof List); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNewMultimapSerialization() { CountingSupplier> factory = new QueueSupplier(); @@ -647,7 +646,7 @@ public LinkedList getImpl() { return new LinkedList<>(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } public void testNewListMultimap() { @@ -666,6 +665,7 @@ public void testNewListMultimap() { assertTrue(multimap.asMap() instanceof SortedMap); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNewListMultimapSerialization() { CountingSupplier> factory = new ListSupplier(); @@ -682,7 +682,7 @@ public Set getImpl() { return new HashSet<>(4); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } public void testNewSetMultimap() { @@ -697,6 +697,7 @@ public void testNewSetMultimap() { assertEquals(Sets.newHashSet(4, 3, 1), multimap.get(Color.BLUE)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNewSetMultimapSerialization() { CountingSupplier> factory = new SetSupplier(); @@ -713,7 +714,7 @@ public TreeSet getImpl() { return Sets.newTreeSet(INT_COMPARATOR); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } public void testNewSortedSetMultimap() { @@ -730,6 +731,7 @@ public void testNewSortedSetMultimap() { assertEquals(INT_COMPARATOR, multimap.valueComparator()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNewSortedSetMultimapSerialization() { CountingSupplier> factory = new SortedSetSupplier(); @@ -796,21 +798,16 @@ public Integer apply(String input) { } public void testIndex_nullValue() { - List values = Arrays.asList(1, null); - try { - Multimaps.index(values, Functions.identity()); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Integer> values = Arrays.asList(1, null); + assertThrows( + NullPointerException.class, + () -> Multimaps.index((List) values, Functions.identity())); } public void testIndex_nullKey() { List values = Arrays.asList(1, 2); - try { - Multimaps.index(values, Functions.constant(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> Multimaps.index(values, Functions.constant(null))); } @GwtIncompatible(value = "untested") @@ -909,10 +906,18 @@ public String transformEntry(String key, Integer value) { assertEquals("{a=[a1, a4, a4], b=[b6]}", transformed.toString()); } - public void testSynchronizedMultimapSampleCodeCompilation() { + @J2ktIncompatible // Synchronized + public void testSynchronizedMultimapSampleCodeCompilation() { + // Extra indirection for J2KT, to avoid error: not enough information to infer type variable K + this.<@Nullable Object, @Nullable Object>genericTestSynchronizedMultimapSampleCodeCompilation(); + } + + @J2ktIncompatible // Synchronized + private + void genericTestSynchronizedMultimapSampleCodeCompilation() { K key = null; - Multimap multimap = Multimaps.synchronizedMultimap(HashMultimap.create()); + Multimap multimap = synchronizedMultimap(HashMultimap.create()); Collection values = multimap.get(key); // Needn't be in synchronized block synchronized (multimap) { // Synchronizing on multimap, not values! Iterator i = values.iterator(); // Must be in synchronized block @@ -922,7 +927,7 @@ public void testSynchronizedMultimapSampleCodeCompilation() { } } - private static void foo(Object o) {} + private static void foo(Object unused) {} public void testFilteredKeysSetMultimapReplaceValues() { SetMultimap multimap = LinkedHashMultimap.create(); @@ -932,15 +937,12 @@ public void testFilteredKeysSetMultimapReplaceValues() { multimap.put("bar", 4); SetMultimap filtered = - Multimaps.filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); + filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); assertEquals(ImmutableSet.of(), filtered.replaceValues("baz", ImmutableSet.of())); - try { - filtered.replaceValues("baz", ImmutableSet.of(5)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> filtered.replaceValues("baz", ImmutableSet.of(5))); } public void testFilteredKeysSetMultimapGetBadValue() { @@ -951,19 +953,11 @@ public void testFilteredKeysSetMultimapGetBadValue() { multimap.put("bar", 4); SetMultimap filtered = - Multimaps.filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); + filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); Set bazSet = filtered.get("baz"); assertThat(bazSet).isEmpty(); - try { - bazSet.add(5); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - bazSet.addAll(ImmutableSet.of(6, 7)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> bazSet.add(5)); + assertThrows(IllegalArgumentException.class, () -> bazSet.addAll(ImmutableSet.of(6, 7))); } public void testFilteredKeysListMultimapGetBadValue() { @@ -974,31 +968,16 @@ public void testFilteredKeysListMultimapGetBadValue() { multimap.put("bar", 4); ListMultimap filtered = - Multimaps.filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); + filterKeys(multimap, Predicates.in(ImmutableSet.of("foo", "bar"))); List bazList = filtered.get("baz"); assertThat(bazList).isEmpty(); - try { - bazList.add(5); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - bazList.add(0, 6); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - bazList.addAll(ImmutableList.of(7, 8)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - bazList.addAll(0, ImmutableList.of(9, 10)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> bazList.add(5)); + assertThrows(IllegalArgumentException.class, () -> bazList.add(0, 6)); + assertThrows(IllegalArgumentException.class, () -> bazList.addAll(ImmutableList.of(7, 8))); + assertThrows(IllegalArgumentException.class, () -> bazList.addAll(0, ImmutableList.of(9, 10))); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { new NullPointerTester().testAllPublicStaticMethods(Multimaps.class); diff --git a/android/guava-tests/test/com/google/common/collect/MultimapsTransformValuesAsMapTest.java b/android/guava-tests/test/com/google/common/collect/MultimapsTransformValuesAsMapTest.java index 491a632f9e6e..d68c9829ec44 100644 --- a/android/guava-tests/test/com/google/common/collect/MultimapsTransformValuesAsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultimapsTransformValuesAsMapTest.java @@ -20,6 +20,7 @@ import com.google.common.base.Functions; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Tests for Multimaps.transformValues().asMap(). @@ -27,6 +28,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class MultimapsTransformValuesAsMapTest extends AbstractMultimapAsMapImplementsMapTest { public MultimapsTransformValuesAsMapTest() { diff --git a/android/guava-tests/test/com/google/common/collect/MultisetsCollectionTest.java b/android/guava-tests/test/com/google/common/collect/MultisetsCollectionTest.java index f143ebd10831..9583f28abb58 100644 --- a/android/guava-tests/test/com/google/common/collect/MultisetsCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultisetsCollectionTest.java @@ -16,7 +16,14 @@ package com.google.common.collect; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Multisets.difference; +import static com.google.common.collect.Multisets.intersection; +import static com.google.common.collect.Multisets.sum; +import static com.google.common.collect.Multisets.union; +import static com.google.common.collect.Multisets.unmodifiableMultiset; import static java.util.Arrays.asList; +import static java.util.Collections.sort; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Objects; @@ -33,6 +40,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Collection tests on wrappers from {@link Multisets}. @@ -40,6 +48,8 @@ * @author Jared Levy */ @GwtIncompatible // suite // TODO(cpovirk): set up collect/gwt/suites version +@NullUnmarked +@AndroidIncompatible // test-suite builders public class MultisetsCollectionTest extends TestCase { public static Test suite() { TestSuite suite = new TestSuite(); @@ -50,7 +60,7 @@ public static Test suite() { CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, CollectionFeature.SERIALIZABLE, - CollectionFeature.ALLOWS_NULL_QUERIES) + CollectionFeature.ALLOWS_NULL_VALUES) .named("Multisets.unmodifiableMultiset[LinkedHashMultiset]") .createTestSuite()); @@ -111,7 +121,7 @@ private static TestStringMultisetGenerator unmodifiableMultisetGenerator() { return new TestStringMultisetGenerator() { @Override protected Multiset create(String[] elements) { - return Multisets.unmodifiableMultiset(LinkedHashMultiset.create(asList(elements))); + return unmodifiableMultiset(LinkedHashMultiset.create(asList(elements))); } @Override @@ -139,7 +149,7 @@ protected Multiset create(String[] elements) { @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }; @@ -163,7 +173,7 @@ protected Multiset create(String[] elements) { multiset2.add(elements[i]); } } - return Multisets.union(multiset1, multiset2); + return union(multiset1, multiset2); } }; } @@ -193,7 +203,7 @@ protected Multiset create(String[] elements) { multiset2.add(elements[1], 2); } } - return Multisets.intersection(multiset1, multiset2); + return intersection(multiset1, multiset2); } }; } @@ -212,7 +222,7 @@ protected Multiset create(String[] elements) { multiset2.add(elements[i]); } } - return Multisets.sum(multiset1, multiset2); + return sum(multiset1, multiset2); } }; } @@ -233,7 +243,7 @@ protected Multiset create(String[] elements) { multiset1.add(elements[i], i + 2); multiset2.add(elements[i], i + 1); } - return Multisets.difference(multiset1, multiset2); + return difference(multiset1, multiset2); } }; } @@ -241,8 +251,7 @@ protected Multiset create(String[] elements) { private static final ImmutableMultiset ELEMENTS_TO_FILTER_OUT = ImmutableMultiset.of("foobar", "bazfoo", "foobar", "foobar"); - private static final Predicate PREDICATE = - Predicates.not(Predicates.in(ELEMENTS_TO_FILTER_OUT)); + private static final Predicate PREDICATE = not(Predicates.in(ELEMENTS_TO_FILTER_OUT)); private static TestStringMultisetGenerator filteredGenerator() { return new TestStringMultisetGenerator() { diff --git a/android/guava-tests/test/com/google/common/collect/MultisetsImmutableEntryTest.java b/android/guava-tests/test/com/google/common/collect/MultisetsImmutableEntryTest.java index bef27b9b20e5..63e01282cfe1 100644 --- a/android/guava-tests/test/com/google/common/collect/MultisetsImmutableEntryTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultisetsImmutableEntryTest.java @@ -16,10 +16,14 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static java.util.Collections.nCopies; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multiset.Entry; -import java.util.Collections; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Multisets#immutableEntry}. @@ -27,15 +31,16 @@ * @author Mike Bostock */ @GwtCompatible +@NullMarked public class MultisetsImmutableEntryTest extends TestCase { - private static final String NE = null; + private static final @Nullable String NE = null; - private static Entry entry(final E element, final int count) { + private static Entry entry(final E element, final int count) { return Multisets.immutableEntry(element, count); } - private static Entry control(E element, int count) { - return HashMultiset.create(Collections.nCopies(count, element)).entrySet().iterator().next(); + private static Entry control(E element, int count) { + return HashMultiset.create(nCopies(count, element)).entrySet().iterator().next(); } public void testToString() { @@ -75,10 +80,6 @@ public void testHashCodeNull() { } public void testNegativeCount() { - try { - entry("foo", -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> entry("foo", -1)); } } diff --git a/android/guava-tests/test/com/google/common/collect/MultisetsTest.java b/android/guava-tests/test/com/google/common/collect/MultisetsTest.java index 32e4408f99c9..5ee36d44662b 100644 --- a/android/guava-tests/test/com/google/common/collect/MultisetsTest.java +++ b/android/guava-tests/test/com/google/common/collect/MultisetsTest.java @@ -16,16 +16,23 @@ package com.google.common.collect; +import static com.google.common.collect.Multisets.difference; +import static com.google.common.collect.Multisets.intersection; +import static com.google.common.collect.Multisets.sum; +import static com.google.common.collect.Multisets.union; +import static com.google.common.collect.Multisets.unmodifiableMultiset; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.DerivedComparable; import com.google.common.testing.NullPointerTester; -import java.util.Arrays; import java.util.Collections; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link Multisets}. @@ -35,6 +42,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class MultisetsTest extends TestCase { /* See MultisetsImmutableEntryTest for immutableEntry() tests. */ @@ -78,95 +86,95 @@ public void testNewTreeMultisetComparator() { public void testRetainOccurrencesEmpty() { Multiset multiset = HashMultiset.create(); - Multiset toRetain = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset toRetain = HashMultiset.create(asList("a", "b", "a")); assertFalse(Multisets.retainOccurrences(multiset, toRetain)); assertThat(multiset).isEmpty(); } public void testRemoveOccurrencesIterableEmpty() { Multiset multiset = HashMultiset.create(); - Iterable toRemove = Arrays.asList("a", "b", "a"); + Iterable toRemove = asList("a", "b", "a"); assertFalse(Multisets.removeOccurrences(multiset, toRemove)); assertTrue(multiset.isEmpty()); } public void testRemoveOccurrencesMultisetEmpty() { Multiset multiset = HashMultiset.create(); - Multiset toRemove = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset toRemove = HashMultiset.create(asList("a", "b", "a")); assertFalse(Multisets.removeOccurrences(multiset, toRemove)); assertTrue(multiset.isEmpty()); } public void testUnion() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "b", "c")); - assertThat(Multisets.union(ms1, ms2)).containsExactly("a", "a", "b", "b", "c"); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("a", "b", "b", "c")); + assertThat(union(ms1, ms2)).containsExactly("a", "a", "b", "b", "c"); } public void testUnionEqualMultisets() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "a")); - assertEquals(ms1, Multisets.union(ms1, ms2)); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("a", "b", "a")); + assertEquals(ms1, union(ms1, ms2)); } public void testUnionEmptyNonempty() { Multiset ms1 = HashMultiset.create(); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "a")); - assertEquals(ms2, Multisets.union(ms1, ms2)); + Multiset ms2 = HashMultiset.create(asList("a", "b", "a")); + assertEquals(ms2, union(ms1, ms2)); } public void testUnionNonemptyEmpty() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); Multiset ms2 = HashMultiset.create(); - assertEquals(ms1, Multisets.union(ms1, ms2)); + assertEquals(ms1, union(ms1, ms2)); } public void testIntersectEmptyNonempty() { Multiset ms1 = HashMultiset.create(); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "a")); - assertThat(Multisets.intersection(ms1, ms2)).isEmpty(); + Multiset ms2 = HashMultiset.create(asList("a", "b", "a")); + assertThat(intersection(ms1, ms2)).isEmpty(); } public void testIntersectNonemptyEmpty() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); Multiset ms2 = HashMultiset.create(); - assertThat(Multisets.intersection(ms1, ms2)).isEmpty(); + assertThat(intersection(ms1, ms2)).isEmpty(); } public void testSum() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("b", "c")); - assertThat(Multisets.sum(ms1, ms2)).containsExactly("a", "a", "b", "b", "c"); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("b", "c")); + assertThat(sum(ms1, ms2)).containsExactly("a", "a", "b", "b", "c"); } public void testSumEmptyNonempty() { Multiset ms1 = HashMultiset.create(); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "a")); - assertThat(Multisets.sum(ms1, ms2)).containsExactly("a", "b", "a"); + Multiset ms2 = HashMultiset.create(asList("a", "b", "a")); + assertThat(sum(ms1, ms2)).containsExactly("a", "b", "a"); } public void testSumNonemptyEmpty() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); Multiset ms2 = HashMultiset.create(); - assertThat(Multisets.sum(ms1, ms2)).containsExactly("a", "b", "a"); + assertThat(sum(ms1, ms2)).containsExactly("a", "b", "a"); } public void testDifferenceWithNoRemovedElements() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("a")); - assertThat(Multisets.difference(ms1, ms2)).containsExactly("a", "b"); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("a")); + assertThat(difference(ms1, ms2)).containsExactly("a", "b"); } public void testDifferenceWithRemovedElement() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("b")); - assertThat(Multisets.difference(ms1, ms2)).containsExactly("a", "a"); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("b")); + assertThat(difference(ms1, ms2)).containsExactly("a", "a"); } public void testDifferenceWithMoreElementsInSecondMultiset() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "b", "b")); - Multiset diff = Multisets.difference(ms1, ms2); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); + Multiset ms2 = HashMultiset.create(asList("a", "b", "b", "b")); + Multiset diff = difference(ms1, ms2); assertThat(diff).contains("a"); assertEquals(0, diff.count("b")); assertEquals(1, diff.count("a")); @@ -176,88 +184,88 @@ public void testDifferenceWithMoreElementsInSecondMultiset() { public void testDifferenceEmptyNonempty() { Multiset ms1 = HashMultiset.create(); - Multiset ms2 = HashMultiset.create(Arrays.asList("a", "b", "a")); - assertEquals(ms1, Multisets.difference(ms1, ms2)); + Multiset ms2 = HashMultiset.create(asList("a", "b", "a")); + assertEquals(ms1, difference(ms1, ms2)); } public void testDifferenceNonemptyEmpty() { - Multiset ms1 = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset ms1 = HashMultiset.create(asList("a", "b", "a")); Multiset ms2 = HashMultiset.create(); - assertEquals(ms1, Multisets.difference(ms1, ms2)); + assertEquals(ms1, difference(ms1, ms2)); } public void testContainsOccurrencesEmpty() { - Multiset superMultiset = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset superMultiset = HashMultiset.create(asList("a", "b", "a")); Multiset subMultiset = HashMultiset.create(); assertTrue(Multisets.containsOccurrences(superMultiset, subMultiset)); assertFalse(Multisets.containsOccurrences(subMultiset, superMultiset)); } public void testContainsOccurrences() { - Multiset superMultiset = HashMultiset.create(Arrays.asList("a", "b", "a")); - Multiset subMultiset = HashMultiset.create(Arrays.asList("a", "b")); + Multiset superMultiset = HashMultiset.create(asList("a", "b", "a")); + Multiset subMultiset = HashMultiset.create(asList("a", "b")); assertTrue(Multisets.containsOccurrences(superMultiset, subMultiset)); assertFalse(Multisets.containsOccurrences(subMultiset, superMultiset)); - Multiset diffMultiset = HashMultiset.create(Arrays.asList("a", "b", "c")); + Multiset diffMultiset = HashMultiset.create(asList("a", "b", "c")); assertFalse(Multisets.containsOccurrences(superMultiset, diffMultiset)); assertTrue(Multisets.containsOccurrences(diffMultiset, subMultiset)); } public void testRetainEmptyOccurrences() { - Multiset multiset = HashMultiset.create(Arrays.asList("a", "b", "a")); + Multiset multiset = HashMultiset.create(asList("a", "b", "a")); Multiset toRetain = HashMultiset.create(); assertTrue(Multisets.retainOccurrences(multiset, toRetain)); assertTrue(multiset.isEmpty()); } public void testRetainOccurrences() { - Multiset multiset = TreeMultiset.create(Arrays.asList("a", "b", "a", "c")); - Multiset toRetain = HashMultiset.create(Arrays.asList("a", "b", "b")); + Multiset multiset = TreeMultiset.create(asList("a", "b", "a", "c")); + Multiset toRetain = HashMultiset.create(asList("a", "b", "b")); assertTrue(Multisets.retainOccurrences(multiset, toRetain)); assertThat(multiset).containsExactly("a", "b").inOrder(); } public void testRemoveEmptyOccurrencesMultiset() { - Multiset multiset = TreeMultiset.create(Arrays.asList("a", "b", "a")); + Multiset multiset = TreeMultiset.create(asList("a", "b", "a")); Multiset toRemove = HashMultiset.create(); assertFalse(Multisets.removeOccurrences(multiset, toRemove)); assertThat(multiset).containsExactly("a", "a", "b").inOrder(); } public void testRemoveOccurrencesMultiset() { - Multiset multiset = TreeMultiset.create(Arrays.asList("a", "b", "a", "c")); - Multiset toRemove = HashMultiset.create(Arrays.asList("a", "b", "b")); + Multiset multiset = TreeMultiset.create(asList("a", "b", "a", "c")); + Multiset toRemove = HashMultiset.create(asList("a", "b", "b")); assertTrue(Multisets.removeOccurrences(multiset, toRemove)); assertThat(multiset).containsExactly("a", "c").inOrder(); } public void testRemoveEmptyOccurrencesIterable() { - Multiset multiset = TreeMultiset.create(Arrays.asList("a", "b", "a")); + Multiset multiset = TreeMultiset.create(asList("a", "b", "a")); Iterable toRemove = ImmutableList.of(); assertFalse(Multisets.removeOccurrences(multiset, toRemove)); assertThat(multiset).containsExactly("a", "a", "b").inOrder(); } public void testRemoveOccurrencesMultisetIterable() { - Multiset multiset = TreeMultiset.create(Arrays.asList("a", "b", "a", "c")); - List toRemove = Arrays.asList("a", "b", "b"); + Multiset multiset = TreeMultiset.create(asList("a", "b", "a", "c")); + List toRemove = asList("a", "b", "b"); assertTrue(Multisets.removeOccurrences(multiset, toRemove)); assertThat(multiset).containsExactly("a", "c").inOrder(); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testUnmodifiableMultisetShortCircuit() { Multiset mod = HashMultiset.create(); - Multiset unmod = Multisets.unmodifiableMultiset(mod); + Multiset unmod = unmodifiableMultiset(mod); assertNotSame(mod, unmod); - assertSame(unmod, Multisets.unmodifiableMultiset(unmod)); + assertSame(unmod, unmodifiableMultiset(unmod)); ImmutableMultiset immutable = ImmutableMultiset.of("a", "a", "b", "a"); - assertSame(immutable, Multisets.unmodifiableMultiset(immutable)); - assertSame(immutable, Multisets.unmodifiableMultiset((Multiset) immutable)); + assertSame(immutable, unmodifiableMultiset(immutable)); + assertSame(immutable, unmodifiableMultiset((Multiset) immutable)); } public void testHighestCountFirst() { - Multiset multiset = HashMultiset.create(Arrays.asList("a", "a", "a", "b", "c", "c")); + Multiset multiset = HashMultiset.create(asList("a", "a", "a", "b", "c", "c")); ImmutableMultiset sortedMultiset = Multisets.copyHighestCountFirst(multiset); assertThat(sortedMultiset.entrySet()) @@ -272,6 +280,7 @@ public void testHighestCountFirst() { assertThat(Multisets.copyHighestCountFirst(ImmutableMultiset.of())).isEmpty(); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { new NullPointerTester().testAllPublicStaticMethods(Multisets.class); diff --git a/android/guava-tests/test/com/google/common/collect/MutableClassToInstanceMapTest.java b/android/guava-tests/test/com/google/common/collect/MutableClassToInstanceMapTest.java index 156b62d9875c..d01a45128a80 100644 --- a/android/guava-tests/test/com/google/common/collect/MutableClassToInstanceMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/MutableClassToInstanceMapTest.java @@ -16,6 +16,8 @@ package com.google.common.collect; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.ImmutableClassToInstanceMapTest.Impl; import com.google.common.collect.ImmutableClassToInstanceMapTest.TestClassToInstanceMapGenerator; import com.google.common.collect.testing.MapTestSuiteBuilder; @@ -27,13 +29,16 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Unit test of {@link MutableClassToInstanceMap}. * * @author Kevin Bourrillion */ +@NullUnmarked public class MutableClassToInstanceMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(MutableClassToInstanceMapTest.class); @@ -44,7 +49,7 @@ public static Test suite() { // Other tests will verify what real, warning-free usage looks like // but here we have to do some serious fudging @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public Map create(Object... elements) { MutableClassToInstanceMap map = MutableClassToInstanceMap.create(); for (Object object : elements) { @@ -76,18 +81,13 @@ protected void setUp() throws Exception { } public void testConstraint() { - - /** + /* * We'll give ourselves a pass on testing all the possible ways of breaking the constraint, * because we know that newClassMap() is implemented using ConstrainedMap which is itself * well-tested. A purist would object to this, but what can I say, we're dirty cheaters. */ map.put(Integer.class, new Integer(5)); - try { - map.put(Double.class, new Long(42)); - fail(); - } catch (ClassCastException expected) { - } + assertThrows(ClassCastException.class, () -> map.put(Double.class, new Long(42))); // Won't compile: map.put(String.class, "x"); } @@ -104,11 +104,7 @@ public void testPutAndGetInstance() { } public void testNull() { - try { - map.put(null, new Integer(1)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> map.put(null, new Integer(1))); map.putInstance(Integer.class, null); assertNull(map.get(Integer.class)); assertNull(map.getInstance(Integer.class)); diff --git a/android/guava-tests/test/com/google/common/collect/NewCustomTableTest.java b/android/guava-tests/test/com/google/common/collect/NewCustomTableTest.java index 6ad3aa212a1f..947dda86b406 100644 --- a/android/guava-tests/test/com/google/common/collect/NewCustomTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/NewCustomTableTest.java @@ -16,12 +16,15 @@ package com.google.common.collect; +import static com.google.common.collect.Tables.newCustomTable; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Supplier; import java.util.Map; import java.util.TreeMap; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link Tables#newCustomTable}. @@ -29,10 +32,11 @@ * @author Jared Levy */ @GwtCompatible -public class NewCustomTableTest extends AbstractTableTest { +@NullMarked +public class NewCustomTableTest extends AbstractTableTest { @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { Supplier> factory = new Supplier>() { @Override @@ -41,7 +45,7 @@ public TreeMap get() { } }; Map> backingMap = Maps.newLinkedHashMap(); - Table table = Tables.newCustomTable(backingMap, factory); + Table table = newCustomTable(backingMap, factory); populate(table, data); return table; } diff --git a/android/guava-tests/test/com/google/common/collect/ObjectArraysTest.java b/android/guava-tests/test/com/google/common/collect/ObjectArraysTest.java index 32d043f74543..52d78e5f55b8 100644 --- a/android/guava-tests/test/com/google/common/collect/ObjectArraysTest.java +++ b/android/guava-tests/test/com/google/common/collect/ObjectArraysTest.java @@ -20,11 +20,13 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; -import java.io.Serializable; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code ObjectArrays}. @@ -32,8 +34,10 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class ObjectArraysTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -41,42 +45,43 @@ public void testNullPointerExceptions() { } @GwtIncompatible // ObjectArrays.newArray(Class, int) - public void testNewArray_fromClass_Empty() { + public void testNewArray_fromClass_empty() { String[] empty = ObjectArrays.newArray(String.class, 0); assertEquals(String[].class, empty.getClass()); assertThat(empty).isEmpty(); } @GwtIncompatible // ObjectArrays.newArray(Class, int) - public void testNewArray_fromClass_Nonempty() { + public void testNewArray_fromClass_nonempty() { String[] array = ObjectArrays.newArray(String.class, 2); assertEquals(String[].class, array.getClass()); assertThat(array).hasLength(2); assertNull(array[0]); } + @J2ktIncompatible // Array::class literal not available in Kotlin KMP @GwtIncompatible // ObjectArrays.newArray(Class, int) - public void testNewArray_fromClass_OfArray() { + public void testNewArray_fromClass_ofArray() { String[][] array = ObjectArrays.newArray(String[].class, 1); assertEquals(String[][].class, array.getClass()); assertThat(array).hasLength(1); assertNull(array[0]); } - public void testNewArray_fromArray_Empty() { + public void testNewArray_fromArray_empty() { String[] in = new String[0]; String[] empty = ObjectArrays.newArray(in, 0); assertThat(empty).isEmpty(); } - public void testNewArray_fromArray_Nonempty() { + public void testNewArray_fromArray_nonempty() { String[] array = ObjectArrays.newArray(new String[0], 2); assertEquals(String[].class, array.getClass()); assertThat(array).hasLength(2); assertNull(array[0]); } - public void testNewArray_fromArray_OfArray() { + public void testNewArray_fromArray_ofArray() { String[][] array = ObjectArrays.newArray(new String[0][0], 1); assertEquals(String[][].class, array.getClass()); assertThat(array).hasLength(1); @@ -114,14 +119,14 @@ public void testConcatBasic() { @GwtIncompatible // ObjectArrays.concat(Object[], Object[], Class) public void testConcatWithMoreGeneralType() { - Serializable[] result = ObjectArrays.concat(new String[0], new String[0], Serializable.class); - assertEquals(Serializable[].class, result.getClass()); + CharSequence[] result = ObjectArrays.concat(new String[0], new String[0], CharSequence.class); + assertEquals(CharSequence[].class, result.getClass()); } public void testToArrayImpl1() { doTestToArrayImpl1(Lists.newArrayList()); doTestToArrayImpl1(Lists.newArrayList(1)); - doTestToArrayImpl1(Lists.newArrayList(1, null, 3)); + doTestToArrayImpl1(Lists.<@Nullable Integer>newArrayList(1, null, 3)); } private void doTestToArrayImpl1(List list) { @@ -139,9 +144,9 @@ public void testToArrayImpl2() { doTestToArrayImpl2(Lists.newArrayList(1), new Integer[1], true); doTestToArrayImpl2(Lists.newArrayList(1), new Integer[] {2, 3}, true); - doTestToArrayImpl2(Lists.newArrayList(1, null, 3), new Integer[0], false); - doTestToArrayImpl2(Lists.newArrayList(1, null, 3), new Integer[2], false); - doTestToArrayImpl2(Lists.newArrayList(1, null, 3), new Integer[3], true); + doTestToArrayImpl2(Lists.<@Nullable Integer>newArrayList(1, null, 3), new Integer[0], false); + doTestToArrayImpl2(Lists.<@Nullable Integer>newArrayList(1, null, 3), new Integer[2], false); + doTestToArrayImpl2(Lists.<@Nullable Integer>newArrayList(1, null, 3), new Integer[3], true); } private void doTestToArrayImpl2(List list, Integer[] array1, boolean expectModify) { diff --git a/android/guava-tests/test/com/google/common/collect/OrderingTest.java b/android/guava-tests/test/com/google/common/collect/OrderingTest.java index 7c4acc943159..0f3925532c96 100644 --- a/android/guava-tests/test/com/google/common/collect/OrderingTest.java +++ b/android/guava-tests/test/com/google/common/collect/OrderingTest.java @@ -17,20 +17,28 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterators.singletonIterator; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.testing.Helpers.testComparator; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.testing.SerializableTester.reserializeAndAssert; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static java.util.Arrays.sort; +import static java.util.Collections.emptyList; +import static java.util.Collections.singleton; +import static java.util.Collections.sort; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Ordering.ArbitraryOrdering; import com.google.common.collect.Ordering.IncomparableValueException; import com.google.common.collect.testing.Helpers; -import com.google.common.primitives.Ints; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import java.util.Arrays; @@ -40,8 +48,10 @@ import java.util.List; import java.util.Random; import java.util.RandomAccess; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@code Ordering}. @@ -49,13 +59,14 @@ * @author Jesse Wilson */ @GwtCompatible(emulated = true) +@NullMarked public class OrderingTest extends TestCase { // TODO(cpovirk): some of these are inexplicably slow (20-30s) under GWT private final Ordering numberOrdering = new NumberOrdering(); public void testAllEqual() { - Ordering comparator = Ordering.allEqual(); + Ordering<@Nullable Object> comparator = Ordering.allEqual(); assertSame(comparator, comparator.reverse()); assertEquals(0, comparator.compare(null, null)); @@ -72,26 +83,31 @@ public void testAllEqual() { // From https://github.com/google/guava/issues/1342 public void testComplicatedOrderingExample() { Integer nullInt = (Integer) null; - Ordering> example = - Ordering.natural().nullsFirst().reverse().lexicographical().reverse().nullsLast(); - List list1 = Lists.newArrayList(); - List list2 = Lists.newArrayList(1); - List list3 = Lists.newArrayList(1, 1); - List list4 = Lists.newArrayList(1, 2); - List list5 = Lists.newArrayList(1, null, 2); - List list6 = Lists.newArrayList(2); - List list7 = Lists.newArrayList(nullInt); - List list8 = Lists.newArrayList(nullInt, nullInt); - List> list = + Ordering<@Nullable Iterable<@Nullable Integer>> example = + Ordering.natural() + .nullsFirst() + .reverse() + .lexicographical() + .reverse() + .>nullsLast(); + List<@Nullable Integer> list1 = Lists.newArrayList(); + List<@Nullable Integer> list2 = Lists.newArrayList(1); + List<@Nullable Integer> list3 = Lists.newArrayList(1, 1); + List<@Nullable Integer> list4 = Lists.newArrayList(1, 2); + List<@Nullable Integer> list5 = Lists.newArrayList(1, null, 2); + List<@Nullable Integer> list6 = Lists.newArrayList(2); + List<@Nullable Integer> list7 = Lists.newArrayList(nullInt); + List<@Nullable Integer> list8 = Lists.newArrayList(nullInt, nullInt); + List<@Nullable List<@Nullable Integer>> list = Lists.newArrayList(list1, list2, list3, list4, list5, list6, list7, list8, null); - List> sorted = example.sortedCopy(list); + List<@Nullable List<@Nullable Integer>> sorted = example.sortedCopy(list); // [[null, null], [null], [1, null, 2], [1, 1], [1, 2], [1], [2], [], null] assertThat(sorted) .containsExactly( - Lists.newArrayList(nullInt, nullInt), - Lists.newArrayList(nullInt), - Lists.newArrayList(1, null, 2), + Lists.<@Nullable Integer>newArrayList(nullInt, nullInt), + Lists.<@Nullable Integer>newArrayList(nullInt), + Lists.<@Nullable Integer>newArrayList(1, null, 2), Lists.newArrayList(1, 1), Lists.newArrayList(1, 2), Lists.newArrayList(1), @@ -103,22 +119,10 @@ public void testComplicatedOrderingExample() { public void testNatural() { Ordering comparator = Ordering.natural(); - Helpers.testComparator(comparator, Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE); - try { - comparator.compare(1, null); - fail(); - } catch (NullPointerException expected) { - } - try { - comparator.compare(null, 2); - fail(); - } catch (NullPointerException expected) { - } - try { - comparator.compare(null, null); - fail(); - } catch (NullPointerException expected) { - } + testComparator(comparator, Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE); + assertThrows(NullPointerException.class, () -> comparator.compare(1, null)); + assertThrows(NullPointerException.class, () -> comparator.compare(null, 2)); + assertThrows(NullPointerException.class, () -> comparator.compare(null, null)); assertSame(comparator, reserialize(comparator)); assertEquals("Ordering.natural()", comparator.toString()); } @@ -129,7 +133,7 @@ public void testFrom() { assertTrue(caseInsensitiveOrdering.compare("a", "B") < 0); assertTrue(caseInsensitiveOrdering.compare("B", "a") > 0); - @SuppressWarnings("deprecation") // test of deprecated method + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method Ordering orderingFromOrdering = Ordering.from(Ordering.natural()); new EqualsTester() .addEqualityGroup(caseInsensitiveOrdering, Ordering.from(String.CASE_INSENSITIVE_ORDER)) @@ -139,39 +143,40 @@ public void testFrom() { public void testExplicit_none() { Comparator c = Ordering.explicit(Collections.emptyList()); - try { - c.compare(0, 0); - fail(); - } catch (IncomparableValueException expected) { - assertEquals(0, expected.value); - } + IncomparableValueException expected = + assertThrows(IncomparableValueException.class, () -> c.compare(0, 0)); + assertEquals(0, expected.value); reserializeAndAssert(c); } public void testExplicit_one() { Comparator c = Ordering.explicit(0); assertEquals(0, c.compare(0, 0)); - try { - c.compare(0, 1); - fail(); - } catch (IncomparableValueException expected) { - assertEquals(1, expected.value); - } + IncomparableValueException expected = + assertThrows(IncomparableValueException.class, () -> c.compare(0, 1)); + assertEquals(1, expected.value); reserializeAndAssert(c); assertEquals("Ordering.explicit([0])", c.toString()); } + public void testExplicitMax_b297601553() { + Ordering c = Ordering.explicit(1, 2, 3); + + // TODO(b/297601553): this should probably throw CCE since 0 isn't explicitly listed + assertEquals(0, (int) c.max(asList(0))); + IncomparableValueException expected = + assertThrows(IncomparableValueException.class, () -> c.max(asList(0, 1))); + assertEquals(0, expected.value); + } + public void testExplicit_two() { Comparator c = Ordering.explicit(42, 5); assertEquals(0, c.compare(5, 5)); assertTrue(c.compare(5, 42) > 0); assertTrue(c.compare(42, 5) < 0); - try { - c.compare(5, 666); - fail(); - } catch (IncomparableValueException expected) { - assertEquals(666, expected.value); - } + IncomparableValueException expected = + assertThrows(IncomparableValueException.class, () -> c.compare(5, 666)); + assertEquals(666, expected.value); new EqualsTester() .addEqualityGroup(c, Ordering.explicit(42, 5)) .addEqualityGroup(Ordering.explicit(5, 42)) @@ -182,22 +187,20 @@ public void testExplicit_two() { public void testExplicit_sortingExample() { Comparator c = Ordering.explicit(2, 8, 6, 1, 7, 5, 3, 4, 0, 9); - List list = Arrays.asList(0, 3, 5, 6, 7, 8, 9); - Collections.sort(list, c); + List list = asList(0, 3, 5, 6, 7, 8, 9); + sort(list, c); assertThat(list).containsExactly(8, 6, 7, 5, 3, 0, 9).inOrder(); reserializeAndAssert(c); } + @SuppressWarnings("DistinctVarargsChecker") // test of buggy call public void testExplicit_withDuplicates() { - try { - Ordering.explicit(1, 2, 3, 4, 2); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ordering.explicit(1, 2, 3, 4, 2)); } // A more limited test than the one that follows, but this one uses the // actual public API. + @J2ktIncompatible // Ordering.arbitrary public void testArbitrary_withoutCollisions() { List list = Lists.newArrayList(); for (int i = 0; i < 50; i++) { @@ -205,15 +208,16 @@ public void testArbitrary_withoutCollisions() { } Ordering arbitrary = Ordering.arbitrary(); - Collections.sort(list, arbitrary); + sort(list, arbitrary); // Now we don't care what order it's put the list in, only that // comparing any pair of elements gives the answer we expect. - Helpers.testComparator(arbitrary, list); + testComparator(arbitrary, list); assertEquals("Ordering.arbitrary()", arbitrary.toString()); } + @J2ktIncompatible // ArbitraryOrdering public void testArbitrary_withCollisions() { List list = Lists.newArrayList(); for (int i = 0; i < 50; i++) { @@ -231,16 +235,16 @@ int identityHashCode(Object object) { // Don't let the elements be in such a predictable order list = shuffledCopy(list, new Random(1)); - Collections.sort(list, arbitrary); + sort(list, arbitrary); // Now we don't care what order it's put the list in, only that // comparing any pair of elements gives the answer we expect. - Helpers.testComparator(arbitrary, list); + testComparator(arbitrary, list); } public void testUsingToString() { Ordering ordering = Ordering.usingToString(); - Helpers.testComparator(ordering, 1, 12, 124, 2); + testComparator(ordering, 1, 12, 124, 2); assertEquals("Ordering.usingToString()", ordering.toString()); assertSame(ordering, reserialize(ordering)); } @@ -268,7 +272,7 @@ public Character apply(String string) { } private static Ordering byCharAt(int index) { - return Ordering.natural().onResultOf(CharAtFunction.values()[index]); + return Ordering.natural().onResultOf(CharAtFunction.values()[index]); } public void testCompound_static() { @@ -276,7 +280,7 @@ public void testCompound_static() { Ordering.compound( ImmutableList.of( byCharAt(0), byCharAt(1), byCharAt(2), byCharAt(3), byCharAt(4), byCharAt(5))); - Helpers.testComparator( + testComparator( comparator, ImmutableList.of( "applesauce", @@ -292,7 +296,7 @@ public void testCompound_static() { public void testCompound_instance() { Comparator comparator = byCharAt(1).compound(byCharAt(0)); - Helpers.testComparator( + testComparator( comparator, ImmutableList.of("red", "yellow", "violet", "blue", "indigo", "green", "orange")); } @@ -303,42 +307,42 @@ public void testCompound_instance_generics() { Ordering integers = Ordering.explicit(1); // Like by like equals like - Ordering a = numbers.compound(numbers); + Ordering unusedA = numbers.compound(numbers); // The compound takes the more specific type of the two, regardless of order - Ordering b = numbers.compound(objects); - Ordering c = objects.compound(numbers); + Ordering unusedB = numbers.compound(objects); + Ordering unusedC = objects.compound(numbers); - Ordering d = numbers.compound(integers); - Ordering e = integers.compound(numbers); + Ordering unusedD = numbers.compound(integers); + Ordering unusedE = integers.compound(numbers); // This works with three levels too (IDEA falsely reports errors as noted // below. Both javac and eclipse handle these cases correctly.) - Ordering f = numbers.compound(objects).compound(objects); // bad IDEA - Ordering g = objects.compound(numbers).compound(objects); - Ordering h = objects.compound(objects).compound(numbers); + Ordering unusedF = numbers.compound(objects).compound(objects); // bad IDEA + Ordering unusedG = objects.compound(numbers).compound(objects); + Ordering unusedH = objects.compound(objects).compound(numbers); - Ordering i = numbers.compound(objects.compound(objects)); - Ordering j = objects.compound(numbers.compound(objects)); // bad IDEA - Ordering k = objects.compound(objects.compound(numbers)); + Ordering unusedI = numbers.compound(objects.compound(objects)); + Ordering unusedJ = objects.compound(numbers.compound(objects)); // bad IDEA + Ordering unusedK = objects.compound(objects.compound(numbers)); // You can also arbitrarily assign a more restricted type - not an intended // feature, exactly, but unavoidable (I think) and harmless - Ordering l = objects.compound(numbers); + Ordering unusedL = objects.compound(numbers); // This correctly doesn't work: - // Ordering m = numbers.compound(objects); + // Ordering unusedM = numbers.compound(objects); // Sadly, the following works in javac 1.6, but at least it fails for // eclipse, and is *correctly* highlighted red in IDEA. - // Ordering n = objects.compound(numbers); + // Ordering unusedN = objects.compound(numbers); } public void testReverse() { Ordering reverseOrder = numberOrdering.reverse(); - Helpers.testComparator(reverseOrder, Integer.MAX_VALUE, 1, 0, -1, Integer.MIN_VALUE); + testComparator(reverseOrder, Integer.MAX_VALUE, 1, 0, -1, Integer.MIN_VALUE); new EqualsTester() .addEqualityGroup(reverseOrder, numberOrdering.reverse()) @@ -354,7 +358,7 @@ public void testReverseOfReverseSameAsForward() { } private enum StringLengthFunction implements Function { - StringLength; + STRING_LENGTH; @Override public Integer apply(String string) { @@ -362,42 +366,41 @@ public Integer apply(String string) { } } - private static final Ordering DECREASING_INTEGER = Ordering.natural().reverse(); + private static final Ordering DECREASING_INTEGER = Ordering.natural().reverse(); public void testOnResultOf_natural() { Comparator comparator = - Ordering.natural().onResultOf(StringLengthFunction.StringLength); + Ordering.natural().onResultOf(StringLengthFunction.STRING_LENGTH); assertTrue(comparator.compare("to", "be") == 0); assertTrue(comparator.compare("or", "not") < 0); assertTrue(comparator.compare("that", "to") > 0); new EqualsTester() .addEqualityGroup( - comparator, Ordering.natural().onResultOf(StringLengthFunction.StringLength)) + comparator, Ordering.natural().onResultOf(StringLengthFunction.STRING_LENGTH)) .addEqualityGroup(DECREASING_INTEGER) .testEquals(); reserializeAndAssert(comparator); - assertEquals("Ordering.natural().onResultOf(StringLength)", comparator.toString()); + assertEquals("Ordering.natural().onResultOf(STRING_LENGTH)", comparator.toString()); } public void testOnResultOf_chained() { Comparator comparator = - DECREASING_INTEGER.onResultOf(StringLengthFunction.StringLength); + DECREASING_INTEGER.onResultOf(StringLengthFunction.STRING_LENGTH); assertTrue(comparator.compare("to", "be") == 0); assertTrue(comparator.compare("not", "or") < 0); assertTrue(comparator.compare("to", "that") > 0); new EqualsTester() .addEqualityGroup( - comparator, DECREASING_INTEGER.onResultOf(StringLengthFunction.StringLength)) + comparator, DECREASING_INTEGER.onResultOf(StringLengthFunction.STRING_LENGTH)) .addEqualityGroup(DECREASING_INTEGER.onResultOf(Functions.constant(1))) .addEqualityGroup(Ordering.natural()) .testEquals(); reserializeAndAssert(comparator); - assertEquals("Ordering.natural().reverse().onResultOf(StringLength)", comparator.toString()); + assertEquals("Ordering.natural().reverse().onResultOf(STRING_LENGTH)", comparator.toString()); } - @SuppressWarnings("unchecked") // dang varargs public void testLexicographical() { Ordering ordering = Ordering.natural(); Ordering> lexy = ordering.lexicographical(); @@ -408,7 +411,7 @@ public void testLexicographical() { ImmutableList ab = ImmutableList.of("a", "b"); ImmutableList b = ImmutableList.of("b"); - Helpers.testComparator(lexy, empty, a, aa, ab, b); + testComparator(lexy, empty, a, aa, ab, b); new EqualsTester() .addEqualityGroup(lexy, ordering.lexicographical()) @@ -418,8 +421,8 @@ public void testLexicographical() { } public void testNullsFirst() { - Ordering ordering = Ordering.natural().nullsFirst(); - Helpers.testComparator(ordering, null, Integer.MIN_VALUE, 0, 1); + Ordering<@Nullable Integer> ordering = Ordering.natural().nullsFirst(); + Helpers.<@Nullable Integer>testComparator(ordering, null, Integer.MIN_VALUE, 0, 1); new EqualsTester() .addEqualityGroup(ordering, Ordering.natural().nullsFirst()) @@ -429,8 +432,8 @@ public void testNullsFirst() { } public void testNullsLast() { - Ordering ordering = Ordering.natural().nullsLast(); - Helpers.testComparator(ordering, 0, 1, Integer.MAX_VALUE, null); + Ordering<@Nullable Integer> ordering = Ordering.natural().nullsLast(); + Helpers.<@Nullable Integer>testComparator(ordering, 0, 1, Integer.MAX_VALUE, null); new EqualsTester() .addEqualityGroup(ordering, Ordering.natural().nullsLast()) @@ -439,35 +442,37 @@ public void testNullsLast() { .testEquals(); } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testBinarySearch() { List ints = Lists.newArrayList(0, 2, 3, 5, 7, 9); assertEquals(4, numberOrdering.binarySearch(ints, 7)); } public void testSortedCopy() { - List unsortedInts = Collections.unmodifiableList(Arrays.asList(5, 0, 3, null, 0, 9)); - List sortedInts = numberOrdering.nullsLast().sortedCopy(unsortedInts); - assertEquals(Arrays.asList(0, 0, 3, 5, 9, null), sortedInts); + List<@Nullable Integer> unsortedInts = + unmodifiableList(Arrays.<@Nullable Integer>asList(5, 0, 3, null, 0, 9)); + List<@Nullable Integer> sortedInts = numberOrdering.nullsLast().sortedCopy(unsortedInts); + assertEquals(Arrays.<@Nullable Integer>asList(0, 0, 3, 5, 9, null), sortedInts); - assertEquals( - Collections.emptyList(), numberOrdering.sortedCopy(Collections.emptyList())); + assertEquals(emptyList(), numberOrdering.sortedCopy(Collections.emptyList())); } public void testImmutableSortedCopy() { ImmutableList unsortedInts = ImmutableList.of(5, 3, 0, 9, 3); ImmutableList sortedInts = numberOrdering.immutableSortedCopy(unsortedInts); - assertEquals(Arrays.asList(0, 3, 3, 5, 9), sortedInts); + assertEquals(asList(0, 3, 3, 5, 9), sortedInts); assertEquals( Collections.emptyList(), numberOrdering.immutableSortedCopy(Collections.emptyList())); - List listWithNull = Arrays.asList(5, 3, null, 9); - try { - Ordering.natural().nullsFirst().immutableSortedCopy(listWithNull); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Integer> listWithNull = asList(5, 3, null, 9); + assertThrows( + NullPointerException.class, + () -> + Ordering.natural() + .nullsFirst() + .immutableSortedCopy((List) listWithNull)); } public void testIsOrdered() { @@ -476,7 +481,7 @@ public void testIsOrdered() { assertTrue(numberOrdering.isOrdered(asList(0, 3, 5, 9))); assertTrue(numberOrdering.isOrdered(asList(0, 0, 3, 3))); assertTrue(numberOrdering.isOrdered(asList(0, 3))); - assertTrue(numberOrdering.isOrdered(Collections.singleton(1))); + assertTrue(numberOrdering.isOrdered(singleton(1))); assertTrue(numberOrdering.isOrdered(Collections.emptyList())); } @@ -486,7 +491,7 @@ public void testIsStrictlyOrdered() { assertTrue(numberOrdering.isStrictlyOrdered(asList(0, 3, 5, 9))); assertFalse(numberOrdering.isStrictlyOrdered(asList(0, 0, 3, 3))); assertTrue(numberOrdering.isStrictlyOrdered(asList(0, 3))); - assertTrue(numberOrdering.isStrictlyOrdered(Collections.singleton(1))); + assertTrue(numberOrdering.isStrictlyOrdered(singleton(1))); assertTrue(numberOrdering.isStrictlyOrdered(Collections.emptyList())); } @@ -519,37 +524,32 @@ public void testLeastOfIterator_empty_1() { } public void testLeastOfIterable_simple_negativeOne() { - try { - numberOrdering.leastOf(Arrays.asList(3, 4, 5, -1), -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> numberOrdering.leastOf(asList(3, 4, 5, -1), -1)); } public void testLeastOfIterator_simple_negativeOne() { - try { - numberOrdering.leastOf(Iterators.forArray(3, 4, 5, -1), -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> numberOrdering.leastOf(Iterators.forArray(3, 4, 5, -1), -1)); } public void testLeastOfIterable_singleton_0() { - List result = numberOrdering.leastOf(Arrays.asList(3), 0); + List result = numberOrdering.leastOf(asList(3), 0); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(), result); } public void testLeastOfIterator_singleton_0() { - List result = numberOrdering.leastOf(Iterators.singletonIterator(3), 0); + List result = numberOrdering.leastOf(singletonIterator(3), 0); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(), result); } public void testLeastOfIterable_simple_0() { - List result = numberOrdering.leastOf(Arrays.asList(3, 4, 5, -1), 0); + List result = numberOrdering.leastOf(asList(3, 4, 5, -1), 0); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(), result); @@ -563,7 +563,7 @@ public void testLeastOfIterator_simple_0() { } public void testLeastOfIterable_simple_1() { - List result = numberOrdering.leastOf(Arrays.asList(3, 4, 5, -1), 1); + List result = numberOrdering.leastOf(asList(3, 4, 5, -1), 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(-1), result); @@ -577,23 +577,24 @@ public void testLeastOfIterator_simple_1() { } public void testLeastOfIterable_simple_nMinusOne_withNullElement() { - List list = Arrays.asList(3, null, 5, -1); - List result = Ordering.natural().nullsLast().leastOf(list, list.size() - 1); + List<@Nullable Integer> list = asList(3, null, 5, -1); + List<@Nullable Integer> result = + Ordering.natural().nullsLast().leastOf(list, list.size() - 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(-1, 3, 5), result); } public void testLeastOfIterator_simple_nMinusOne_withNullElement() { - Iterator itr = Iterators.forArray(3, null, 5, -1); - List result = Ordering.natural().nullsLast().leastOf(itr, 3); + Iterator<@Nullable Integer> itr = Iterators.forArray(3, null, 5, -1); + List<@Nullable Integer> result = Ordering.natural().nullsLast().leastOf(itr, 3); assertTrue(result instanceof RandomAccess); assertListImmutable(result); assertEquals(ImmutableList.of(-1, 3, 5), result); } public void testLeastOfIterable_simple_nMinusOne() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list, list.size() - 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -601,7 +602,7 @@ public void testLeastOfIterable_simple_nMinusOne() { } public void testLeastOfIterator_simple_nMinusOne() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list.iterator(), list.size() - 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -609,7 +610,7 @@ public void testLeastOfIterator_simple_nMinusOne() { } public void testLeastOfIterable_simple_n() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list, list.size()); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -617,7 +618,7 @@ public void testLeastOfIterable_simple_n() { } public void testLeastOfIterator_simple_n() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list.iterator(), list.size()); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -625,23 +626,25 @@ public void testLeastOfIterator_simple_n() { } public void testLeastOfIterable_simple_n_withNullElement() { - List list = Arrays.asList(3, 4, 5, null, -1); - List result = Ordering.natural().nullsLast().leastOf(list, list.size()); + List<@Nullable Integer> list = asList(3, 4, 5, null, -1); + List<@Nullable Integer> result = + Ordering.natural().nullsLast().leastOf(list, list.size()); assertTrue(result instanceof RandomAccess); assertListImmutable(result); - assertEquals(Arrays.asList(-1, 3, 4, 5, null), result); + assertEquals(Arrays.<@Nullable Integer>asList(-1, 3, 4, 5, null), result); } public void testLeastOfIterator_simple_n_withNullElement() { - List list = Arrays.asList(3, 4, 5, null, -1); - List result = Ordering.natural().nullsLast().leastOf(list.iterator(), list.size()); + List<@Nullable Integer> list = asList(3, 4, 5, null, -1); + List<@Nullable Integer> result = + Ordering.natural().nullsLast().leastOf(list.iterator(), list.size()); assertTrue(result instanceof RandomAccess); assertListImmutable(result); - assertEquals(Arrays.asList(-1, 3, 4, 5, null), result); + assertEquals(Arrays.<@Nullable Integer>asList(-1, 3, 4, 5, null), result); } public void testLeastOfIterable_simple_nPlusOne() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list, list.size() + 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -649,7 +652,7 @@ public void testLeastOfIterable_simple_nPlusOne() { } public void testLeastOfIterator_simple_nPlusOne() { - List list = Arrays.asList(3, 4, 5, -1); + List list = asList(3, 4, 5, -1); List result = numberOrdering.leastOf(list.iterator(), list.size() + 1); assertTrue(result instanceof RandomAccess); assertListImmutable(result); @@ -663,7 +666,7 @@ public void testLeastOfIterable_ties() { assertNotSame(foo, bar); assertEquals(foo, bar); - List list = Arrays.asList(3, foo, bar, -1); + List list = asList(3, foo, bar, -1); List result = numberOrdering.leastOf(list, list.size()); assertEquals(ImmutableList.of(-1, 3, foo, bar), result); } @@ -675,7 +678,7 @@ public void testLeastOfIterator_ties() { assertNotSame(foo, bar); assertEquals(foo, bar); - List list = Arrays.asList(3, foo, bar, -1); + List list = asList(3, foo, bar, -1); List result = numberOrdering.leastOf(list.iterator(), list.size()); assertEquals(ImmutableList.of(-1, 3, foo, bar), result); } @@ -707,15 +710,16 @@ private static void runLeastOfComparison(int iterations, int elements, int seeds } public void testLeastOfIterableLargeK() { - List list = Arrays.asList(4, 2, 3, 5, 1); - assertEquals(Arrays.asList(1, 2, 3, 4, 5), Ordering.natural().leastOf(list, Integer.MAX_VALUE)); + List list = asList(4, 2, 3, 5, 1); + assertEquals( + asList(1, 2, 3, 4, 5), Ordering.natural().leastOf(list, Integer.MAX_VALUE)); } public void testLeastOfIteratorLargeK() { - List list = Arrays.asList(4, 2, 3, 5, 1); + List list = asList(4, 2, 3, 5, 1); assertEquals( - Arrays.asList(1, 2, 3, 4, 5), - Ordering.natural().leastOf(list.iterator(), Integer.MAX_VALUE)); + asList(1, 2, 3, 4, 5), + Ordering.natural().leastOf(list.iterator(), Integer.MAX_VALUE)); } public void testGreatestOfIterable_simple() { @@ -724,8 +728,8 @@ public void testGreatestOfIterable_simple() { * test would be enough. It doesn't... but we'll cheat and act like it does * anyway. There's a comment there to remind us to fix this if we change it. */ - List list = Arrays.asList(3, 1, 3, 2, 4, 2, 4, 3); - assertEquals(Arrays.asList(4, 4, 3, 3), numberOrdering.greatestOf(list, 4)); + List list = asList(3, 1, 3, 2, 4, 2, 4, 3); + assertEquals(asList(4, 4, 3, 3), numberOrdering.greatestOf(list, 4)); } public void testGreatestOfIterator_simple() { @@ -734,8 +738,8 @@ public void testGreatestOfIterator_simple() { * test would be enough. It doesn't... but we'll cheat and act like it does * anyway. There's a comment there to remind us to fix this if we change it. */ - List list = Arrays.asList(3, 1, 3, 2, 4, 2, 4, 3); - assertEquals(Arrays.asList(4, 4, 3, 3), numberOrdering.greatestOf(list.iterator(), 4)); + List list = asList(3, 1, 3, 2, 4, 2, 4, 3); + assertEquals(asList(4, 4, 3, 3), numberOrdering.greatestOf(list.iterator(), 4)); } private static void assertListImmutable(List result) { @@ -833,11 +837,11 @@ public int hashCode() { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return other instanceof NumberOrdering; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /* @@ -868,13 +872,14 @@ public void testCombinationsExhaustively_startingFromFromComparator() { testExhaustively(Ordering.from(String.CASE_INSENSITIVE_ORDER), "A", "b", "C", "d"); } + @J2ktIncompatible // Ordering.arbitrary @GwtIncompatible // too slow public void testCombinationsExhaustively_startingFromArbitrary() { Ordering arbitrary = Ordering.arbitrary(); Object[] array = {1, "foo", new Object()}; // There's no way to tell what the order should be except empirically - Arrays.sort(array, arbitrary); + sort(array, arbitrary); testExhaustively(arbitrary, array); } @@ -887,18 +892,18 @@ private static void testExhaustively( checkArgument( strictlyOrderedElements.length >= 3, "strictlyOrderedElements " + "requires at least 3 elements"); - List list = Arrays.asList(strictlyOrderedElements); + List list = asList(strictlyOrderedElements); // for use calling Collection.toArray later T[] emptyArray = Platform.newArray(strictlyOrderedElements, 0); // shoot me, but I didn't want to deal with wildcards through the whole test @SuppressWarnings("unchecked") - Scenario starter = new Scenario<>((Ordering) ordering, list, emptyArray); + Scenario starter = new Scenario<>((Ordering) ordering, list, emptyArray); verifyScenario(starter, 0); } - private static void verifyScenario(Scenario scenario, int level) { + private static void verifyScenario(Scenario scenario, int level) { scenario.testCompareTo(); scenario.testIsOrdered(); scenario.testMinAndMax(); @@ -916,7 +921,7 @@ private static void verifyScenario(Scenario scenario, int level) { * An aggregation of an ordering with a list (of size > 1) that should prove to be in strictly * increasing order according to that ordering. */ - private static class Scenario { + private static class Scenario { final Ordering ordering; final List strictlyOrderedList; final T[] emptyArray; @@ -928,7 +933,7 @@ private static class Scenario { } void testCompareTo() { - Helpers.testComparator(ordering, strictlyOrderedList); + testComparator(ordering, strictlyOrderedList); } void testIsOrdered() { @@ -936,7 +941,7 @@ void testIsOrdered() { assertTrue(ordering.isStrictlyOrdered(strictlyOrderedList)); } - @SuppressWarnings("unchecked") // generic arrays and unchecked cast + // generic arrays and unchecked cast void testMinAndMax() { List shuffledList = Lists.newArrayList(strictlyOrderedList); shuffledList = shuffledCopy(shuffledList, new Random(5)); @@ -962,6 +967,7 @@ void testMinAndMax() { assertEquals(max, ordering.max(max, min)); } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method void testBinarySearch() { for (int i = 0; i < strictlyOrderedList.size(); i++) { assertEquals(i, ordering.binarySearch(strictlyOrderedList, strictlyOrderedList.get(i))); @@ -978,7 +984,8 @@ void testSortedCopy() { assertEquals(strictlyOrderedList, ordering.sortedCopy(shuffledList)); if (!strictlyOrderedList.contains(null)) { - assertEquals(strictlyOrderedList, ordering.immutableSortedCopy(shuffledList)); + List<@NonNull T> nonNullShuffledList = (List<@NonNull T>) shuffledList; + assertEquals(strictlyOrderedList, ordering.immutableSortedCopy(nonNullShuffledList)); } } } @@ -991,7 +998,7 @@ void testSortedCopy() { private enum OrderingMutation { REVERSE { @Override - Scenario mutate(Scenario scenario) { + Scenario mutate(Scenario scenario) { List newList = Lists.newArrayList(scenario.strictlyOrderedList); Collections.reverse(newList); return new Scenario(scenario.ordering.reverse(), newList, scenario.emptyArray); @@ -999,8 +1006,7 @@ Scenario mutate(Scenario scenario) { }, NULLS_FIRST { @Override - Scenario mutate(Scenario scenario) { - @SuppressWarnings("unchecked") + Scenario mutate(Scenario scenario) { List newList = Lists.newArrayList((T) null); for (T t : scenario.strictlyOrderedList) { if (t != null) { @@ -1012,7 +1018,7 @@ Scenario mutate(Scenario scenario) { }, NULLS_LAST { @Override - Scenario mutate(Scenario scenario) { + Scenario mutate(Scenario scenario) { List newList = Lists.newArrayList(); for (T t : scenario.strictlyOrderedList) { if (t != null) { @@ -1025,12 +1031,12 @@ Scenario mutate(Scenario scenario) { }, ON_RESULT_OF { @Override - Scenario mutate(final Scenario scenario) { + Scenario mutate(final Scenario scenario) { Ordering ordering = scenario.ordering.onResultOf( new Function() { @Override - public T apply(@CheckForNull Integer from) { + public T apply(Integer from) { return scenario.strictlyOrderedList.get(from); } }); @@ -1042,9 +1048,9 @@ public T apply(@CheckForNull Integer from) { } }, COMPOUND_THIS_WITH_NATURAL { - @SuppressWarnings("unchecked") // raw array + @SuppressWarnings("unchecked") // generic arrays @Override - Scenario mutate(Scenario scenario) { + Scenario mutate(Scenario scenario) { List> composites = Lists.newArrayList(); for (T t : scenario.strictlyOrderedList) { composites.add(new Composite(t, 1)); @@ -1055,13 +1061,14 @@ Scenario mutate(Scenario scenario) { .ordering .onResultOf(Composite.getValueFunction()) .compound(Ordering.natural()); - return new Scenario>(ordering, composites, new Composite[0]); + return new Scenario>( + ordering, composites, (Composite[]) new Composite[0]); } }, COMPOUND_NATURAL_WITH_THIS { - @SuppressWarnings("unchecked") // raw array + @SuppressWarnings("unchecked") // generic arrays @Override - Scenario mutate(Scenario scenario) { + Scenario mutate(Scenario scenario) { List> composites = Lists.newArrayList(); for (T t : scenario.strictlyOrderedList) { composites.add(new Composite(t, 1)); @@ -1070,37 +1077,38 @@ Scenario mutate(Scenario scenario) { composites.add(new Composite(t, 2)); } Ordering> ordering = - Ordering.natural() + Ordering.>natural() .compound(scenario.ordering.onResultOf(Composite.getValueFunction())); - return new Scenario>(ordering, composites, new Composite[0]); + return new Scenario>( + ordering, composites, (Composite[]) new Composite[0]); } }, LEXICOGRAPHICAL { - @SuppressWarnings("unchecked") // dang varargs + @SuppressWarnings("unchecked") // generic arrays @Override - Scenario mutate(Scenario scenario) { + Scenario mutate(Scenario scenario) { List> words = Lists.newArrayList(); words.add(Collections.emptyList()); for (T t : scenario.strictlyOrderedList) { - words.add(Arrays.asList(t)); + words.add(asList(t)); for (T s : scenario.strictlyOrderedList) { - words.add(Arrays.asList(t, s)); + words.add(asList(t, s)); } } return new Scenario>( - scenario.ordering.lexicographical(), words, new Iterable[0]); + scenario.ordering.lexicographical(), words, (Iterable[]) new Iterable[0]); } }, ; - abstract Scenario mutate(Scenario scenario); + abstract Scenario mutate(Scenario scenario); } /** * A dummy object we create so that we can have something meaningful to have a compound ordering * over. */ - private static class Composite implements Comparable> { + private static class Composite implements Comparable> { final T value; final int rank; @@ -1113,10 +1121,10 @@ private static class Composite implements Comparable> { // order of 't'. @Override public int compareTo(Composite that) { - return Ints.compare(rank, that.rank); + return Integer.compare(rank, that.rank); } - static Function, T> getValueFunction() { + static Function, T> getValueFunction() { return new Function, T>() { @Override public T apply(Composite from) { @@ -1126,6 +1134,7 @@ public T apply(Composite from) { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); @@ -1135,7 +1144,7 @@ public void testNullPointerExceptions() { tester.testAllPublicInstanceMethods(Ordering.usingToString().nullsFirst()); } - private static List shuffledCopy(List in, Random random) { + private static List shuffledCopy(List in, Random random) { List mutable = newArrayList(in); List out = newArrayList(); while (!mutable.isEmpty()) { diff --git a/android/guava-tests/test/com/google/common/collect/PackageSanityTests.java b/android/guava-tests/test/com/google/common/collect/PackageSanityTests.java index c847140d573e..52ebabb173f1 100644 --- a/android/guava-tests/test/com/google/common/collect/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/collect/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.collect; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Covers basic sanity checks for the entire package. @@ -24,6 +25,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { publicApiOnly(); // Many package-private classes are tested through the public API. diff --git a/android/guava-tests/test/com/google/common/collect/PeekingIteratorTest.java b/android/guava-tests/test/com/google/common/collect/PeekingIteratorTest.java index 0f79985bcf39..b725ad4fc71f 100644 --- a/android/guava-tests/test/com/google/common/collect/PeekingIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/PeekingIteratorTest.java @@ -17,19 +17,23 @@ package com.google.common.collect; import static com.google.common.collect.Iterators.peekingIterator; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE; import static com.google.common.collect.testing.IteratorFeature.UNMODIFIABLE; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.IteratorTester; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link PeekingIterator}. @@ -38,6 +42,7 @@ */ @SuppressWarnings("serial") // No serialization is used in this test @GwtCompatible(emulated = true) +@NullMarked public class PeekingIteratorTest extends TestCase { /** @@ -47,11 +52,11 @@ public class PeekingIteratorTest extends TestCase { * *

    This IteratorTester makes copies of the master so that it can later verify that {@link * PeekingIterator#remove()} removes the same elements as the reference's iterator {@code - * #remove()}. + * remove()}. */ - private static class PeekingIteratorTester extends IteratorTester { + private static class PeekingIteratorTester extends IteratorTester { private Iterable master; - private List targetList; + private @Nullable List targetList; public PeekingIteratorTester(Collection master) { super(master.size() + 3, MODIFIABLE, master, IteratorTester.KnownOrder.KNOWN_ORDER); @@ -73,7 +78,7 @@ protected void verify(List elements) { } } - private void actsLikeIteratorHelper(final List list) { + private void actsLikeIteratorHelper(final List list) { // Check with modifiable copies of the list new PeekingIteratorTester(list).test(); @@ -82,18 +87,18 @@ private void actsLikeIteratorHelper(final List list) { list.size() * 2 + 2, UNMODIFIABLE, list, IteratorTester.KnownOrder.KNOWN_ORDER) { @Override protected Iterator newTargetIterator() { - Iterator iterator = Collections.unmodifiableList(list).iterator(); + Iterator iterator = unmodifiableList(list).iterator(); return Iterators.peekingIterator(iterator); } }.test(); } public void testPeekingIteratorBehavesLikeIteratorOnEmptyIterable() { - actsLikeIteratorHelper(Collections.emptyList()); + actsLikeIteratorHelper(emptyList()); } public void testPeekingIteratorBehavesLikeIteratorOnSingletonIterable() { - actsLikeIteratorHelper(Collections.singletonList(new Object())); + actsLikeIteratorHelper(singletonList(new Object())); } // TODO(cpovirk): instead of skipping, use a smaller number of steps @@ -104,20 +109,15 @@ public void testPeekingIteratorBehavesLikeIteratorOnThreeElementIterable() { @GwtIncompatible // works but takes 5 minutes to run public void testPeekingIteratorAcceptsNullElements() { - actsLikeIteratorHelper(Lists.newArrayList(null, "A", null)); + actsLikeIteratorHelper(Lists.<@Nullable String>newArrayList(null, "A", null)); } public void testPeekOnEmptyList() { - List list = Collections.emptyList(); + List list = emptyList(); Iterator iterator = list.iterator(); PeekingIterator peekingIterator = Iterators.peekingIterator(iterator); - try { - peekingIterator.peek(); - fail("Should throw NoSuchElementException if nothing to peek()"); - } catch (NoSuchElementException e) { - /* expected */ - } + assertThrows(NoSuchElementException.class, () -> peekingIterator.peek()); } public void testPeekDoesntChangeIteration() { @@ -143,24 +143,9 @@ public void testPeekDoesntChangeIteration() { assertEquals( "next() should still return last element after peeking", "C", peekingIterator.next()); - try { - peekingIterator.peek(); - fail("Should throw exception if no next to peek()"); - } catch (NoSuchElementException e) { - /* expected */ - } - try { - peekingIterator.peek(); - fail("Should continue to throw exception if no next to peek()"); - } catch (NoSuchElementException e) { - /* expected */ - } - try { - peekingIterator.next(); - fail("next() should still throw exception after the end of iteration"); - } catch (NoSuchElementException e) { - /* expected */ - } + assertThrows(NoSuchElementException.class, () -> peekingIterator.peek()); + assertThrows(NoSuchElementException.class, () -> peekingIterator.peek()); + assertThrows(NoSuchElementException.class, () -> peekingIterator.next()); } public void testCantRemoveAfterPeek() { @@ -172,12 +157,7 @@ public void testCantRemoveAfterPeek() { assertEquals("B", peekingIterator.peek()); /* Should complain on attempt to remove() after peek(). */ - try { - peekingIterator.remove(); - fail("remove() should throw IllegalStateException after a peek()"); - } catch (IllegalStateException e) { - /* expected */ - } + assertThrows(IllegalStateException.class, () -> peekingIterator.remove()); assertEquals( "After remove() throws exception, peek should still be ok", "B", peekingIterator.peek()); diff --git a/android/guava-tests/test/com/google/common/collect/QueuesTest.java b/android/guava-tests/test/com/google/common/collect/QueuesTest.java index 819700e4c167..277e9eceb1f3 100644 --- a/android/guava-tests/test/com/google/common/collect/QueuesTest.java +++ b/android/guava-tests/test/com/google/common/collect/QueuesTest.java @@ -24,6 +24,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Stopwatch; import java.util.Collection; @@ -40,13 +41,15 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Queues}. * * @author Dimitris Andreou */ - +@NullUnmarked public class QueuesTest extends TestCase { /* * All the following tests relate to BlockingQueue methods in Queues. @@ -122,11 +125,11 @@ private void testMultipleProducers(BlockingQueue q) throws InterruptedEx public void testDrainTimesOut() throws Exception { for (BlockingQueue q : blockingQueues()) { - testDrainTimesOut(q); + checkDrainTimesOut(q); } } - private void testDrainTimesOut(BlockingQueue q) throws Exception { + private void checkDrainTimesOut(BlockingQueue q) throws Exception { for (boolean interruptibly : new boolean[] {true, false}) { assertEquals(0, Queues.drain(q, ImmutableList.of(), 1, 10, MILLISECONDS)); @@ -154,11 +157,11 @@ private void testDrainTimesOut(BlockingQueue q) throws Exception { public void testZeroElements() throws Exception { for (BlockingQueue q : blockingQueues()) { - testZeroElements(q); + checkZeroElements(q); } } - private void testZeroElements(BlockingQueue q) throws InterruptedException { + private void checkZeroElements(BlockingQueue q) throws InterruptedException { for (boolean interruptibly : new boolean[] {true, false}) { // asking to drain zero elements assertEquals(0, drain(q, ImmutableList.of(), 0, 10, MILLISECONDS, interruptibly)); @@ -167,21 +170,21 @@ private void testZeroElements(BlockingQueue q) throws InterruptedExcepti public void testEmpty() throws Exception { for (BlockingQueue q : blockingQueues()) { - testEmpty(q); + checkEmpty(q); } } - private void testEmpty(BlockingQueue q) { + private void checkEmpty(BlockingQueue q) { assertDrained(q); } public void testNegativeMaxElements() throws Exception { for (BlockingQueue q : blockingQueues()) { - testNegativeMaxElements(q); + checkNegativeMaxElements(q); } } - private void testNegativeMaxElements(BlockingQueue q) throws InterruptedException { + private void checkNegativeMaxElements(BlockingQueue q) throws InterruptedException { @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = threadPool.submit(new Producer(q, 1)); @@ -196,11 +199,11 @@ private void testNegativeMaxElements(BlockingQueue q) throws Interrupted public void testDrain_throws() throws Exception { for (BlockingQueue q : blockingQueues()) { - testDrain_throws(q); + checkDrainThrows(q); } } - private void testDrain_throws(BlockingQueue q) { + private void checkDrainThrows(BlockingQueue q) { @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = threadPool.submit(new Interrupter(currentThread())); try { @@ -212,17 +215,18 @@ private void testDrain_throws(BlockingQueue q) { public void testDrainUninterruptibly_doesNotThrow() throws Exception { for (BlockingQueue q : blockingQueues()) { - testDrainUninterruptibly_doesNotThrow(q); + testDrainUninterruptiblyDoesNotThrow(q); } } - private void testDrainUninterruptibly_doesNotThrow(final BlockingQueue q) { + private void testDrainUninterruptiblyDoesNotThrow(final BlockingQueue q) { final Thread mainThread = currentThread(); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = threadPool.submit( - new Callable() { - public Void call() throws InterruptedException { + new Callable<@Nullable Void>() { + @Override + public @Nullable Void call() throws InterruptedException { new Producer(q, 50).call(); new Interrupter(mainThread).run(); new Producer(q, 50).call(); @@ -238,23 +242,13 @@ public Void call() throws InterruptedException { } public void testNewLinkedBlockingDequeCapacity() { - try { - Queues.newLinkedBlockingDeque(0); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // any capacity less than 1 should throw IllegalArgumentException - } + assertThrows(IllegalArgumentException.class, () -> Queues.newLinkedBlockingDeque(0)); assertEquals(1, Queues.newLinkedBlockingDeque(1).remainingCapacity()); assertEquals(11, Queues.newLinkedBlockingDeque(11).remainingCapacity()); } public void testNewLinkedBlockingQueueCapacity() { - try { - Queues.newLinkedBlockingQueue(0); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // any capacity less than 1 should throw IllegalArgumentException - } + assertThrows(IllegalArgumentException.class, () -> Queues.newLinkedBlockingQueue(0)); assertEquals(1, Queues.newLinkedBlockingQueue(1).remainingCapacity()); assertEquals(11, Queues.newLinkedBlockingQueue(11).remainingCapacity()); } @@ -287,6 +281,7 @@ private void assertInterruptibleDrained(BlockingQueue q) { } // same as above; uninterruptible version + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. private void assertUninterruptibleDrained(BlockingQueue q) { assertEquals(0, Queues.drainUninterruptibly(q, ImmutableList.of(), 0, 10, MILLISECONDS)); @@ -303,7 +298,7 @@ private void assertUninterruptibleDrained(BlockingQueue q) { } } - private static class Producer implements Callable { + private static class Producer implements Callable<@Nullable Void> { final BlockingQueue q; final int elements; final CountDownLatch beganProducing = new CountDownLatch(1); @@ -315,7 +310,7 @@ private static class Producer implements Callable { } @Override - public Void call() throws InterruptedException { + public @Nullable Void call() throws InterruptedException { try { beganProducing.countDown(); for (int i = 0; i < elements; i++) { diff --git a/android/guava-tests/test/com/google/common/collect/RangeNonGwtTest.java b/android/guava-tests/test/com/google/common/collect/RangeNonGwtTest.java index 666624a305f1..e669fc41cf7f 100644 --- a/android/guava-tests/test/com/google/common/collect/RangeNonGwtTest.java +++ b/android/guava-tests/test/com/google/common/collect/RangeNonGwtTest.java @@ -18,6 +18,7 @@ import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test cases for {@link Range} which cannot run as GWT tests. @@ -25,6 +26,7 @@ * @author Gregory Kick * @see RangeTest */ +@NullUnmarked public class RangeNonGwtTest extends TestCase { public void testNullPointers() { diff --git a/android/guava-tests/test/com/google/common/collect/RangeTest.java b/android/guava-tests/test/com/google/common/collect/RangeTest.java index e8578bfd199b..b0db5296a8cd 100644 --- a/android/guava-tests/test/com/google/common/collect/RangeTest.java +++ b/android/guava-tests/test/com/google/common/collect/RangeTest.java @@ -19,6 +19,8 @@ import static com.google.common.collect.BoundType.CLOSED; import static com.google.common.collect.BoundType.OPEN; import static com.google.common.collect.DiscreteDomain.integers; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.testing.Helpers.testCompareToAndEquals; import static com.google.common.testing.SerializableTester.reserializeAndAssert; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; @@ -26,13 +28,13 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Predicate; -import com.google.common.collect.testing.Helpers; import com.google.common.testing.EqualsTester; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Range}. @@ -40,6 +42,7 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class RangeTest extends TestCase { public void testOpen() { Range range = Range.open(4, 8); @@ -56,16 +59,8 @@ public void testOpen() { } public void testOpen_invalid() { - try { - Range.open(4, 3); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - Range.open(3, 3); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.open(4, 3)); + assertThrows(IllegalArgumentException.class, () -> Range.open(3, 3)); } public void testClosed() { @@ -83,11 +78,7 @@ public void testClosed() { } public void testClosed_invalid() { - try { - Range.closed(4, 3); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.closed(4, 3)); } public void testOpenClosed() { @@ -289,9 +280,10 @@ public void testOrderingCuts() { Cut e = Range.greaterThan(1).lowerBound; Cut f = Range.greaterThan(1).upperBound; - Helpers.testCompareToAndEquals(ImmutableList.of(a, b, c, d, e, f)); + testCompareToAndEquals(ImmutableList.of(a, b, c, d, e, f)); } + @SuppressWarnings("DistinctVarargsChecker") public void testContainsAll() { Range range = Range.closed(3, 5); assertTrue(range.containsAll(asList(3, 3, 4, 5))); @@ -346,43 +338,35 @@ public void testIntersection_empty() { Range range = Range.closedOpen(3, 3); assertEquals(range, range.intersection(range)); - try { - range.intersection(Range.open(3, 5)); - fail(); - } catch (IllegalArgumentException expected) { - // TODO(kevinb): convert the rest of this file to Truth someday - assertThat(expected).hasMessageThat().contains("connected"); - } - try { - range.intersection(Range.closed(0, 2)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().contains("connected"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.open(3, 5))); + assertThat(expected).hasMessageThat().contains("connected"); + expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.closed(0, 2))); + assertThat(expected).hasMessageThat().contains("connected"); } public void testIntersection_deFactoEmpty() { - Range range = Range.open(3, 4); - assertEquals(range, range.intersection(range)); + { + Range range = Range.open(3, 4); + assertEquals(range, range.intersection(range)); - assertEquals(Range.openClosed(3, 3), range.intersection(Range.atMost(3))); - assertEquals(Range.closedOpen(4, 4), range.intersection(Range.atLeast(4))); + assertEquals(Range.openClosed(3, 3), range.intersection(Range.atMost(3))); + assertEquals(Range.closedOpen(4, 4), range.intersection(Range.atLeast(4))); - try { - range.intersection(Range.lessThan(3)); - fail(); - } catch (IllegalArgumentException expected) { + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.lessThan(3))); assertThat(expected).hasMessageThat().contains("connected"); - } - try { - range.intersection(Range.greaterThan(4)); - fail(); - } catch (IllegalArgumentException expected) { + expected = + assertThrows( + IllegalArgumentException.class, () -> range.intersection(Range.greaterThan(4))); assertThat(expected).hasMessageThat().contains("connected"); } - range = Range.closed(3, 4); - assertEquals(Range.openClosed(4, 4), range.intersection(Range.greaterThan(4))); + { + Range range = Range.closed(3, 4); + assertEquals(Range.openClosed(4, 4), range.intersection(Range.greaterThan(4))); + } } public void testIntersection_singleton() { @@ -397,30 +381,21 @@ public void testIntersection_singleton() { assertEquals(Range.closedOpen(3, 3), range.intersection(Range.lessThan(3))); assertEquals(Range.openClosed(3, 3), range.intersection(Range.greaterThan(3))); - try { - range.intersection(Range.atLeast(4)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().contains("connected"); - } - try { - range.intersection(Range.atMost(2)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().contains("connected"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.atLeast(4))); + assertThat(expected).hasMessageThat().contains("connected"); + expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.atMost(2))); + assertThat(expected).hasMessageThat().contains("connected"); } public void testIntersection_general() { Range range = Range.closed(4, 8); // separate below - try { - range.intersection(Range.closed(0, 2)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().contains("connected"); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> range.intersection(Range.closed(0, 2))); + assertThat(expected).hasMessageThat().contains("connected"); // adjacent below assertEquals(Range.closedOpen(4, 4), range.intersection(Range.closedOpen(2, 4))); @@ -456,58 +431,28 @@ public void testIntersection_general() { assertEquals(Range.openClosed(8, 8), range.intersection(Range.openClosed(8, 10))); // separate above - try { - range.intersection(Range.closed(10, 12)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().contains("connected"); - } + expected = + assertThrows( + IllegalArgumentException.class, () -> range.intersection(Range.closed(10, 12))); + assertThat(expected).hasMessageThat().contains("connected"); } public void testGap_overlapping() { Range range = Range.closedOpen(3, 5); - try { - range.gap(Range.closed(4, 6)); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - range.gap(Range.closed(2, 4)); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - range.gap(Range.closed(2, 3)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> range.gap(Range.closed(4, 6))); + assertThrows(IllegalArgumentException.class, () -> range.gap(Range.closed(2, 4))); + assertThrows(IllegalArgumentException.class, () -> range.gap(Range.closed(2, 3))); } public void testGap_invalidRangesWithInfinity() { - try { - Range.atLeast(1).gap(Range.atLeast(2)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.atLeast(1).gap(Range.atLeast(2))); - try { - Range.atLeast(2).gap(Range.atLeast(1)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.atLeast(2).gap(Range.atLeast(1))); - try { - Range.atMost(1).gap(Range.atMost(2)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.atMost(1).gap(Range.atMost(2))); - try { - Range.atMost(2).gap(Range.atMost(1)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Range.atMost(2).gap(Range.atMost(1))); } public void testGap_connectedAdjacentYieldsEmpty() { @@ -611,10 +556,10 @@ public void testEquals() { @GwtIncompatible // TODO(b/148207871): Restore once Eclipse compiler no longer flakes for this. public void testLegacyComparable() { - Range range = Range.closed(LegacyComparable.X, LegacyComparable.Y); + Range unused = Range.closed(LegacyComparable.X, LegacyComparable.Y); } - static final DiscreteDomain UNBOUNDED_DOMAIN = + private static final DiscreteDomain UNBOUNDED_DOMAIN = new DiscreteDomain() { @Override public Integer next(Integer value) { @@ -660,32 +605,20 @@ public void testCanonical_unboundedDomain() { } public void testEncloseAll() { - assertEquals(Range.closed(0, 0), Range.encloseAll(Arrays.asList(0))); - assertEquals(Range.closed(-3, 5), Range.encloseAll(Arrays.asList(5, -3))); - assertEquals(Range.closed(-3, 5), Range.encloseAll(Arrays.asList(1, 2, 2, 2, 5, -3, 0, -1))); + assertEquals(Range.closed(0, 0), Range.encloseAll(asList(0))); + assertEquals(Range.closed(-3, 5), Range.encloseAll(asList(5, -3))); + assertEquals(Range.closed(-3, 5), Range.encloseAll(asList(1, 2, 2, 2, 5, -3, 0, -1))); } public void testEncloseAll_empty() { - try { - Range.encloseAll(ImmutableSet.of()); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> Range.encloseAll(ImmutableSet.of())); } public void testEncloseAll_nullValue() { - List nullFirst = Lists.newArrayList(null, 0); - try { - Range.encloseAll(nullFirst); - fail(); - } catch (NullPointerException expected) { - } - List nullNotFirst = Lists.newArrayList(0, null); - try { - Range.encloseAll(nullNotFirst); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Integer> nullFirst = Lists.newArrayList(null, 0); + assertThrows(NullPointerException.class, () -> Range.encloseAll((List) nullFirst)); + List<@Nullable Integer> nullNotFirst = Lists.newArrayList(0, null); + assertThrows(NullPointerException.class, () -> Range.encloseAll((List) nullNotFirst)); } public void testEquivalentFactories() { diff --git a/android/guava-tests/test/com/google/common/collect/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/collect/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..93f5a5c94358 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ReflectionFreeAssertThrows.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.Ordering.IncomparableValueException; +import com.google.common.collect.TestExceptions.SomeCheckedException; +import com.google.common.collect.TestExceptions.SomeError; +import com.google.common.collect.TestExceptions.SomeOtherCheckedException; +import com.google.common.collect.TestExceptions.SomeUncheckedException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IncomparableValueException.class, e -> e instanceof IncomparableValueException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(SomeCheckedException.class, e -> e instanceof SomeCheckedException) + .put(SomeError.class, e -> e instanceof SomeError) + .put(SomeOtherCheckedException.class, e -> e instanceof SomeOtherCheckedException) + .put(SomeUncheckedException.class, e -> e instanceof SomeUncheckedException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/collect/RegularImmutableAsListTest.java b/android/guava-tests/test/com/google/common/collect/RegularImmutableAsListTest.java index b6c2358c590a..9c1a09c1a052 100644 --- a/android/guava-tests/test/com/google/common/collect/RegularImmutableAsListTest.java +++ b/android/guava-tests/test/com/google/common/collect/RegularImmutableAsListTest.java @@ -16,6 +16,8 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link RegularImmutableAsList}. @@ -23,6 +25,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class RegularImmutableAsListTest extends TestCase { /** * RegularImmutableAsList should assume its input is null-free without checking, because it only @@ -30,7 +33,8 @@ public class RegularImmutableAsListTest extends TestCase { */ public void testDoesntCheckForNull() { ImmutableSet set = ImmutableSet.of(1, 2, 3); - new RegularImmutableAsList(set, new Object[] {null, null, null}); + ImmutableList unused = + new RegularImmutableAsList(set, new @Nullable Object[] {null, null, null}); // shouldn't throw! } } diff --git a/android/guava-tests/test/com/google/common/collect/RegularImmutableMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/RegularImmutableMapMapInterfaceTest.java new file mode 100644 index 000000000000..f888c7677904 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/RegularImmutableMapMapInterfaceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class RegularImmutableMapMapInterfaceTest + extends AbstractImmutableMapMapInterfaceTest { + @Override + protected Map makeEmptyMap() { + return ImmutableMap.of(); + } + + @Override + protected Map makePopulatedMap() { + return ImmutableMap.of("one", 1, "two", 2, "three", 3); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/RegularImmutableMapWithUnhashableValuesMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/RegularImmutableMapWithUnhashableValuesMapInterfaceTest.java new file mode 100644 index 000000000000..641c99d8f42f --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/RegularImmutableMapWithUnhashableValuesMapInterfaceTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.testing.SampleElements.Unhashables; +import com.google.common.collect.testing.UnhashableObject; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. +@NullUnmarked +public class RegularImmutableMapWithUnhashableValuesMapInterfaceTest + extends AbstractImmutableMapMapInterfaceTest { + @Override + protected Map makeEmptyMap() { + return ImmutableMap.of(); + } + + @Override + protected Map makePopulatedMap() { + Unhashables unhashables = new Unhashables(); + return ImmutableMap.of(0, unhashables.e0(), 1, unhashables.e1(), 2, unhashables.e2()); + } + + @Override + protected Integer getKeyNotInPopulatedMap() { + return 3; + } + + @Override + protected UnhashableObject getValueNotInPopulatedMap() { + return new Unhashables().e3(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/RegularImmutableSortedMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/RegularImmutableSortedMapMapInterfaceTest.java new file mode 100644 index 000000000000..ca09158f053e --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/RegularImmutableSortedMapMapInterfaceTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class RegularImmutableSortedMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makeEmptyMap() { + return ImmutableSortedMap.of(); + } + + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("one", 1, "two", 2, "three", 3); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/RegularImmutableTableTest.java b/android/guava-tests/test/com/google/common/collect/RegularImmutableTableTest.java index 82d7b39b9ff5..86d67d5ad1e2 100644 --- a/android/guava-tests/test/com/google/common/collect/RegularImmutableTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/RegularImmutableTableTest.java @@ -16,19 +16,22 @@ package com.google.common.collect; +import static com.google.common.collect.Tables.immutableCell; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Table.Cell; +import org.jspecify.annotations.NullMarked; -/** @author Gregory Kick */ +/** + * @author Gregory Kick + */ @GwtCompatible +@NullMarked public class RegularImmutableTableTest extends AbstractImmutableTableTest { private static final ImmutableSet> CELLS = ImmutableSet.of( - Tables.immutableCell('a', 1, "foo"), - Tables.immutableCell('b', 1, "bar"), - Tables.immutableCell('a', 2, "baz")); + immutableCell('a', 1, "foo"), immutableCell('b', 1, "bar"), immutableCell('a', 2, "baz")); private static final ImmutableSet ROW_SPACE = ImmutableSet.of('a', 'b'); @@ -83,9 +86,9 @@ public void testForCells() { assertTrue( RegularImmutableTable.forCells( ImmutableSet.of( - Tables.immutableCell('a', 1, "blah"), - Tables.immutableCell('b', 2, "blah"), - Tables.immutableCell('c', 3, "blah"))) + immutableCell('a', 1, "blah"), + immutableCell('b', 2, "blah"), + immutableCell('c', 3, "blah"))) instanceof SparseImmutableTable); } diff --git a/android/guava-tests/test/com/google/common/collect/ReserializedImmutableMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ReserializedImmutableMapMapInterfaceTest.java new file mode 100644 index 000000000000..a09be5094498 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ReserializedImmutableMapMapInterfaceTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.testing.SerializableTester; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // SerializableTester +@NullUnmarked +public class ReserializedImmutableMapMapInterfaceTest + extends AbstractImmutableMapMapInterfaceTest { + @Override + protected Map makePopulatedMap() { + return SerializableTester.reserialize(ImmutableMap.of("one", 1, "two", 2, "three", 3)); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/ReserializedImmutableSortedMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/ReserializedImmutableSortedMapMapInterfaceTest.java new file mode 100644 index 000000000000..01e24ffb9e7c --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/ReserializedImmutableSortedMapMapInterfaceTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.testing.SerializableTester; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // SerializableTester +@NullUnmarked +public class ReserializedImmutableSortedMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return SerializableTester.reserialize(ImmutableSortedMap.of("one", 1, "two", 2, "three", 3)); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SetOperationsTest.java b/android/guava-tests/test/com/google/common/collect/SetOperationsTest.java index 7a1ec3a6590c..ec6daacba426 100644 --- a/android/guava-tests/test/com/google/common/collect/SetOperationsTest.java +++ b/android/guava-tests/test/com/google/common/collect/SetOperationsTest.java @@ -17,10 +17,12 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.SetTestSuiteBuilder; import com.google.common.collect.testing.TestStringSetGenerator; import com.google.common.collect.testing.features.CollectionFeature; @@ -30,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@link Sets#union}, {@link Sets#intersection} and {@link Sets#difference}. @@ -37,8 +40,11 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class SetOperationsTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); @@ -61,7 +67,7 @@ protected Set create(String[] elements) { @Override protected Set create(String[] elements) { checkArgument(elements.length == 1); - return Sets.union(Sets.newHashSet(elements), Sets.newHashSet(elements)); + return Sets.union(Sets.newHashSet(elements), newHashSet(elements)); } }) .named("singleton U itself") @@ -73,7 +79,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.union(Sets.newHashSet(), Sets.newHashSet(elements)); + return Sets.union(Sets.newHashSet(), newHashSet(elements)); } }) .named("empty U set") @@ -86,7 +92,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.union(Sets.newHashSet(elements), Sets.newHashSet()); + return Sets.union(newHashSet(elements), Sets.newHashSet()); } }) .named("set U empty") @@ -117,7 +123,7 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { checkArgument(elements.length == 3); return Sets.union( - Sets.newHashSet(elements[0]), Sets.newHashSet(elements[1], elements[2])); + newHashSet(elements[0]), newHashSet(elements[1], elements[2])); } }) .named("union of disjoint") @@ -131,7 +137,7 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { return Sets.union( Sets.newHashSet(elements[0], elements[1]), - Sets.newHashSet(elements[1], elements[2])); + newHashSet(elements[1], elements[2])); } }) .named("venn") @@ -156,8 +162,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.intersection( - Sets.newHashSet(), Sets.newHashSet((String) null)); + return Sets.intersection(Sets.newHashSet(), newHashSet((String) null)); } }) .named("empty & singleton") @@ -170,7 +175,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.intersection(Sets.newHashSet("a", "b"), Sets.newHashSet("c", "d")); + return Sets.intersection(newHashSet("a", "b"), newHashSet("c", "d")); } }) .named("intersection of disjoint") @@ -183,7 +188,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.intersection(Sets.newHashSet(elements), Sets.newHashSet(elements)); + return Sets.intersection(newHashSet(elements), newHashSet(elements)); } }) .named("set & itself") @@ -197,8 +202,7 @@ protected Set create(String[] elements) { @Override protected Set create(String[] elements) { return Sets.intersection( - Sets.newHashSet("a", elements[0], "b"), - Sets.newHashSet("c", elements[0], "d")); + newHashSet("a", elements[0], "b"), newHashSet("c", elements[0], "d")); } }) .named("intersection with overlap of one") @@ -223,7 +227,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.difference(Sets.newHashSet("a"), Sets.newHashSet("a")); + return Sets.difference(newHashSet("a"), newHashSet("a")); } }) .named("singleton - itself") @@ -236,8 +240,8 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - Set set = Sets.newHashSet("b", "c"); - Set other = Sets.newHashSet("a", "b", "c", "d"); + Set set = newHashSet("b", "c"); + Set other = newHashSet("a", "b", "c", "d"); return Sets.difference(set, other); } }) @@ -251,8 +255,8 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - Set set = Sets.newHashSet(elements); - Set other = Sets.newHashSet("wz", "xq"); + Set set = newHashSet(elements); + Set other = newHashSet("wz", "xq"); set.addAll(other); other.add("pq"); return Sets.difference(set, other); @@ -270,7 +274,7 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.difference(Sets.newHashSet(elements), Sets.newHashSet()); + return Sets.difference(newHashSet(elements), newHashSet()); } }) .named("set - empty") @@ -284,99 +288,97 @@ protected Set create(String[] elements) { @Override protected Set create(String[] elements) { return Sets.difference( - Sets.newHashSet(elements), Sets.newHashSet("xx", "xq")); + Sets.newHashSet(elements), newHashSet("xx", "xq")); } }) .named("set - disjoint") .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_VALUES) .createTestSuite()); - suite.addTestSuite(MoreTests.class); + suite.addTestSuite(SetOperationsTest.class); return suite; } - public static class MoreTests extends TestCase { - Set friends; - Set enemies; - - @Override - public void setUp() { - friends = Sets.newHashSet("Tom", "Joe", "Dave"); - enemies = Sets.newHashSet("Dick", "Harry", "Tom"); - } - - public void testUnion() { - Set all = Sets.union(friends, enemies); - assertEquals(5, all.size()); - - ImmutableSet immut = Sets.union(friends, enemies).immutableCopy(); - HashSet mut = Sets.union(friends, enemies).copyInto(new HashSet()); - - enemies.add("Buck"); - assertEquals(6, all.size()); - assertEquals(5, immut.size()); - assertEquals(5, mut.size()); - } - - public void testIntersection() { - Set friends = Sets.newHashSet("Tom", "Joe", "Dave"); - Set enemies = Sets.newHashSet("Dick", "Harry", "Tom"); - - Set frenemies = Sets.intersection(friends, enemies); - assertEquals(1, frenemies.size()); - - ImmutableSet immut = Sets.intersection(friends, enemies).immutableCopy(); - HashSet mut = Sets.intersection(friends, enemies).copyInto(new HashSet()); - - enemies.add("Joe"); - assertEquals(2, frenemies.size()); - assertEquals(1, immut.size()); - assertEquals(1, mut.size()); - } - - public void testDifference() { - Set friends = Sets.newHashSet("Tom", "Joe", "Dave"); - Set enemies = Sets.newHashSet("Dick", "Harry", "Tom"); - - Set goodFriends = Sets.difference(friends, enemies); - assertEquals(2, goodFriends.size()); - - ImmutableSet immut = Sets.difference(friends, enemies).immutableCopy(); - HashSet mut = Sets.difference(friends, enemies).copyInto(new HashSet()); - - enemies.add("Dave"); - assertEquals(1, goodFriends.size()); - assertEquals(2, immut.size()); - assertEquals(2, mut.size()); - } - - public void testSymmetricDifference() { - Set friends = Sets.newHashSet("Tom", "Joe", "Dave"); - Set enemies = Sets.newHashSet("Dick", "Harry", "Tom"); - - Set symmetricDifferenceFriendsFirst = Sets.symmetricDifference(friends, enemies); - assertEquals(4, symmetricDifferenceFriendsFirst.size()); - - Set symmetricDifferenceEnemiesFirst = Sets.symmetricDifference(enemies, friends); - assertEquals(4, symmetricDifferenceEnemiesFirst.size()); - - assertEquals(symmetricDifferenceFriendsFirst, symmetricDifferenceEnemiesFirst); - - ImmutableSet immut = Sets.symmetricDifference(friends, enemies).immutableCopy(); - HashSet mut = - Sets.symmetricDifference(friends, enemies).copyInto(new HashSet()); - - enemies.add("Dave"); - assertEquals(3, symmetricDifferenceFriendsFirst.size()); - assertEquals(4, immut.size()); - assertEquals(4, mut.size()); - - immut = Sets.symmetricDifference(enemies, friends).immutableCopy(); - mut = Sets.symmetricDifference(enemies, friends).copyInto(new HashSet()); - friends.add("Harry"); - assertEquals(2, symmetricDifferenceEnemiesFirst.size()); - assertEquals(3, immut.size()); - assertEquals(3, mut.size()); - } + Set friends; + Set enemies; + + @Override + public void setUp() { + friends = newHashSet("Tom", "Joe", "Dave"); + enemies = newHashSet("Dick", "Harry", "Tom"); + } + + public void testUnion() { + Set all = Sets.union(friends, enemies); + assertEquals(5, all.size()); + + ImmutableSet immut = Sets.union(friends, enemies).immutableCopy(); + HashSet mut = Sets.union(friends, enemies).copyInto(new HashSet()); + + enemies.add("Buck"); + assertEquals(6, all.size()); + assertEquals(5, immut.size()); + assertEquals(5, mut.size()); + } + + public void testIntersection() { + Set friends = newHashSet("Tom", "Joe", "Dave"); + Set enemies = newHashSet("Dick", "Harry", "Tom"); + + Set frenemies = Sets.intersection(friends, enemies); + assertEquals(1, frenemies.size()); + + ImmutableSet immut = Sets.intersection(friends, enemies).immutableCopy(); + HashSet mut = Sets.intersection(friends, enemies).copyInto(new HashSet()); + + enemies.add("Joe"); + assertEquals(2, frenemies.size()); + assertEquals(1, immut.size()); + assertEquals(1, mut.size()); + } + + public void testDifference() { + Set friends = newHashSet("Tom", "Joe", "Dave"); + Set enemies = newHashSet("Dick", "Harry", "Tom"); + + Set goodFriends = Sets.difference(friends, enemies); + assertEquals(2, goodFriends.size()); + + ImmutableSet immut = Sets.difference(friends, enemies).immutableCopy(); + HashSet mut = Sets.difference(friends, enemies).copyInto(new HashSet()); + + enemies.add("Dave"); + assertEquals(1, goodFriends.size()); + assertEquals(2, immut.size()); + assertEquals(2, mut.size()); + } + + public void testSymmetricDifference() { + Set friends = newHashSet("Tom", "Joe", "Dave"); + Set enemies = newHashSet("Dick", "Harry", "Tom"); + + Set symmetricDifferenceFriendsFirst = Sets.symmetricDifference(friends, enemies); + assertEquals(4, symmetricDifferenceFriendsFirst.size()); + + Set symmetricDifferenceEnemiesFirst = Sets.symmetricDifference(enemies, friends); + assertEquals(4, symmetricDifferenceEnemiesFirst.size()); + + assertEquals(symmetricDifferenceFriendsFirst, symmetricDifferenceEnemiesFirst); + + ImmutableSet immut = Sets.symmetricDifference(friends, enemies).immutableCopy(); + HashSet mut = + Sets.symmetricDifference(friends, enemies).copyInto(new HashSet()); + + enemies.add("Dave"); + assertEquals(3, symmetricDifferenceFriendsFirst.size()); + assertEquals(4, immut.size()); + assertEquals(4, mut.size()); + + immut = Sets.symmetricDifference(enemies, friends).immutableCopy(); + mut = Sets.symmetricDifference(enemies, friends).copyInto(new HashSet()); + friends.add("Harry"); + assertEquals(2, symmetricDifferenceEnemiesFirst.size()); + assertEquals(3, immut.size()); + assertEquals(3, mut.size()); } } diff --git a/android/guava-tests/test/com/google/common/collect/SetsFilterHashSetTest.java b/android/guava-tests/test/com/google/common/collect/SetsFilterHashSetTest.java new file mode 100644 index 000000000000..6a74bf9726ef --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SetsFilterHashSetTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Sets.newHashSet; + +import com.google.common.base.Predicate; +import com.google.common.collect.FilteredCollectionsTestUtil.AbstractFilteredSetTest; +import java.util.Set; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked +public final class SetsFilterHashSetTest extends AbstractFilteredSetTest> { + @Override + Set createUnfiltered(Iterable contents) { + return newHashSet(contents); + } + + @Override + Set filter(Set elements, Predicate predicate) { + return Sets.filter(elements, predicate); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SetsFilterNavigableSetTest.java b/android/guava-tests/test/com/google/common/collect/SetsFilterNavigableSetTest.java new file mode 100644 index 000000000000..2090e78098c6 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SetsFilterNavigableSetTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.base.Predicate; +import com.google.common.collect.FilteredCollectionsTestUtil.AbstractFilteredNavigableSetTest; +import java.util.NavigableSet; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked +public final class SetsFilterNavigableSetTest extends AbstractFilteredNavigableSetTest { + @Override + NavigableSet createUnfiltered(Iterable contents) { + return Sets.newTreeSet(contents); + } + + @Override + NavigableSet filter( + NavigableSet elements, Predicate predicate) { + return Sets.filter(elements, predicate); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SetsFilterSortedSetTest.java b/android/guava-tests/test/com/google/common/collect/SetsFilterSortedSetTest.java new file mode 100644 index 000000000000..8d6b85ab517f --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SetsFilterSortedSetTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.base.Predicate; +import com.google.common.collect.FilteredCollectionsTestUtil.AbstractFilteredSortedSetTest; +import java.util.SortedSet; +import java.util.TreeSet; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked +public final class SetsFilterSortedSetTest + extends AbstractFilteredSortedSetTest> { + @Override + SortedSet createUnfiltered(Iterable contents) { + final TreeSet result = Sets.newTreeSet(contents); + // we have to make the result not Navigable + return new ForwardingSortedSet() { + @Override + protected SortedSet delegate() { + return result; + } + }; + } + + @Override + SortedSet filter(SortedSet elements, Predicate predicate) { + return Sets.filter(elements, predicate); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SetsTest.java b/android/guava-tests/test/com/google/common/collect/SetsTest.java index d7799cec0c8a..5c2d58a11320 100644 --- a/android/guava-tests/test/com/google/common/collect/SetsTest.java +++ b/android/guava-tests/test/com/google/common/collect/SetsTest.java @@ -17,6 +17,8 @@ package com.google.common.collect; import static com.google.common.collect.Iterables.unmodifiableIterable; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Sets.cartesianProduct; import static com.google.common.collect.Sets.newEnumSet; import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newLinkedHashSet; @@ -27,11 +29,14 @@ import static com.google.common.truth.Truth.assertWithMessage; import static java.io.ObjectStreamConstants.TC_REFERENCE; import static java.io.ObjectStreamConstants.baseWireHandle; +import static java.lang.System.arraycopy; +import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Predicate; import com.google.common.collect.testing.AnEnum; import com.google.common.collect.testing.IteratorTester; @@ -52,7 +57,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.Serializable; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; @@ -73,10 +77,11 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.CopyOnWriteArraySet; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@code Sets}. @@ -85,6 +90,7 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class SetsTest extends TestCase { private static final IteratorTester.KnownOrder KNOWN_ORDER = @@ -92,7 +98,7 @@ public class SetsTest extends TestCase { private static final Collection EMPTY_COLLECTION = Arrays.asList(); - private static final Collection SOME_COLLECTION = Arrays.asList(0, 1, 1); + private static final Collection SOME_COLLECTION = asList(0, 1, 1); private static final Iterable SOME_ITERABLE = new Iterable() { @@ -102,11 +108,13 @@ public Iterator iterator() { } }; - private static final List LONGER_LIST = Arrays.asList(8, 6, 7, 5, 3, 0, 9); + private static final List LONGER_LIST = asList(8, 6, 7, 5, 3, 0, 9); private static final Comparator SOME_COMPARATOR = Collections.reverseOrder(); + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SetsTest.class); @@ -116,7 +124,7 @@ public static Test suite() { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return Sets.newConcurrentHashSet(Arrays.asList(elements)); + return Sets.newConcurrentHashSet(asList(elements)); } }) .named("Sets.newConcurrentHashSet") @@ -132,12 +140,12 @@ protected Set create(String[] elements) { // Remove last element, if size > 1 Set set1 = (size > 1) - ? Sets.newHashSet(Arrays.asList(elements).subList(0, size - 1)) - : Sets.newHashSet(elements); + ? newHashSet(asList(elements).subList(0, size - 1)) + : newHashSet(elements); // Remove first element, if size > 0 Set set2 = (size > 0) - ? Sets.newHashSet(Arrays.asList(elements).subList(1, size)) + ? newHashSet(asList(elements).subList(1, size)) : Sets.newHashSet(); return Sets.union(set1, set2); } @@ -151,9 +159,9 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - Set set1 = Sets.newHashSet(elements); + Set set1 = newHashSet(elements); set1.add(samples().e3()); - Set set2 = Sets.newHashSet(elements); + Set set2 = newHashSet(elements); set2.add(samples().e4()); return Sets.intersection(set1, set2); } @@ -167,9 +175,9 @@ protected Set create(String[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - Set set1 = Sets.newHashSet(elements); + Set set1 = newHashSet(elements); set1.add(samples().e3()); - Set set2 = Sets.newHashSet(samples().e3()); + Set set2 = newHashSet(samples().e3()); return Sets.difference(set1, set2); } }) @@ -183,7 +191,7 @@ protected Set create(String[] elements) { @Override protected Set create(AnEnum[] elements) { AnEnum[] otherElements = new AnEnum[elements.length - 1]; - System.arraycopy(elements, 1, otherElements, 0, otherElements.length); + arraycopy(elements, 1, otherElements, 0, otherElements.length); return Sets.immutableEnumSet(elements[0], otherElements); } }) @@ -197,8 +205,8 @@ protected Set create(AnEnum[] elements) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - SafeTreeSet set = new SafeTreeSet<>(Arrays.asList(elements)); - return Sets.unmodifiableNavigableSet(set); + SafeTreeSet set = new SafeTreeSet<>(asList(elements)); + return unmodifiableNavigableSet(set); } @Override @@ -218,7 +226,9 @@ public List order(List insertionOrder) { return suite; } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilter() { return SetTestSuiteBuilder.using( new TestStringSetGenerator() { @@ -241,7 +251,9 @@ public Set create(String[] elements) { .createTestSuite(); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterNoNulls() { TestSuite suite = new TestSuite(); suite.addTest( @@ -292,7 +304,9 @@ public List order(List insertionOrder) { return suite; } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders private static Test testsForFilterFiltered() { return SetTestSuiteBuilder.using( new TestStringSetGenerator() { @@ -330,18 +344,11 @@ public void testImmutableEnumSet() { Set units = Sets.immutableEnumSet(SomeEnum.D, SomeEnum.B); assertThat(units).containsExactly(SomeEnum.B, SomeEnum.D).inOrder(); - try { - units.remove(SomeEnum.B); - fail("ImmutableEnumSet should throw an exception on remove()"); - } catch (UnsupportedOperationException expected) { - } - try { - units.add(SomeEnum.C); - fail("ImmutableEnumSet should throw an exception on add()"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> units.remove(SomeEnum.B)); + assertThrows(UnsupportedOperationException.class, () -> units.add(SomeEnum.C)); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testImmutableEnumSet_serialized() { Set units = Sets.immutableEnumSet(SomeEnum.D, SomeEnum.B); @@ -363,6 +370,7 @@ public void testImmutableEnumSet_fromIterable() { assertThat(two).containsExactly(SomeEnum.B, SomeEnum.D).inOrder(); } + @J2ktIncompatible @GwtIncompatible // java serialization not supported in GWT. public void testImmutableEnumSet_deserializationMakesDefensiveCopy() throws Exception { ImmutableSet original = Sets.immutableEnumSet(SomeEnum.A, SomeEnum.B); @@ -378,6 +386,7 @@ public void testImmutableEnumSet_deserializationMakesDefensiveCopy() throws Exce assertTrue(deserialized.contains(SomeEnum.A)); } + @J2ktIncompatible @GwtIncompatible // java serialization not supported in GWT. private static byte[] serializeWithBackReference(Object original, int handleOffset) throws IOException { @@ -396,7 +405,7 @@ private static byte[] serializeWithBackReference(Object original, int handleOffs private static byte[] prepended(byte b, byte[] array) { byte[] out = new byte[array.length + 1]; out[0] = b; - System.arraycopy(array, 0, out, 1, array.length); + arraycopy(array, 0, out, 1, array.length); return out; } @@ -426,22 +435,22 @@ public void testNewEnumSet_iterable() { } public void testNewHashSetEmpty() { - HashSet set = Sets.newHashSet(); + HashSet set = newHashSet(); verifySetContents(set, EMPTY_COLLECTION); } public void testNewHashSetVarArgs() { - HashSet set = Sets.newHashSet(0, 1, 1); - verifySetContents(set, Arrays.asList(0, 1)); + HashSet set = newHashSet(0, 1, 1); + verifySetContents(set, asList(0, 1)); } public void testNewHashSetFromCollection() { - HashSet set = Sets.newHashSet(SOME_COLLECTION); + HashSet set = newHashSet(SOME_COLLECTION); verifySetContents(set, SOME_COLLECTION); } public void testNewHashSetFromIterable() { - HashSet set = Sets.newHashSet(SOME_ITERABLE); + HashSet set = newHashSet(SOME_ITERABLE); verifySetContents(set, SOME_ITERABLE); } @@ -456,7 +465,7 @@ public void testNewHashSetWithExpectedSizeLarge() { } public void testNewHashSetFromIterator() { - HashSet set = Sets.newHashSet(SOME_COLLECTION.iterator()); + HashSet set = newHashSet(SOME_COLLECTION.iterator()); verifySetContents(set, SOME_COLLECTION); } @@ -536,14 +545,14 @@ public void testNewTreeSetFromIterable() { } public void testNewTreeSetFromIterableDerived() { - Iterable iterable = Arrays.asList(new Derived("foo"), new Derived("bar")); + Iterable iterable = asList(new Derived("foo"), new Derived("bar")); TreeSet set = Sets.newTreeSet(iterable); assertThat(set).containsExactly(new Derived("bar"), new Derived("foo")).inOrder(); } public void testNewTreeSetFromIterableNonGeneric() { Iterable iterable = - Arrays.asList(new LegacyComparable("foo"), new LegacyComparable("bar")); + asList(new LegacyComparable("foo"), new LegacyComparable("bar")); TreeSet set = Sets.newTreeSet(iterable); assertThat(set) .containsExactly(new LegacyComparable("bar"), new LegacyComparable("foo")) @@ -566,69 +575,84 @@ public void testNewIdentityHashSet() { assertEquals(2, set.size()); } + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArraySet public void testNewCOWASEmpty() { CopyOnWriteArraySet set = Sets.newCopyOnWriteArraySet(); verifySetContents(set, EMPTY_COLLECTION); } + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArraySet public void testNewCOWASFromIterable() { CopyOnWriteArraySet set = Sets.newCopyOnWriteArraySet(SOME_ITERABLE); verifySetContents(set, SOME_COLLECTION); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfEnumSet() { Set units = EnumSet.of(SomeEnum.B, SomeEnum.D); EnumSet otherUnits = Sets.complementOf(units); verifySetContents(otherUnits, EnumSet.of(SomeEnum.A, SomeEnum.C)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfEnumSetWithType() { Set units = EnumSet.of(SomeEnum.B, SomeEnum.D); EnumSet otherUnits = Sets.complementOf(units, SomeEnum.class); verifySetContents(otherUnits, EnumSet.of(SomeEnum.A, SomeEnum.C)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfRegularSet() { - Set units = Sets.newHashSet(SomeEnum.B, SomeEnum.D); + Set units = newHashSet(SomeEnum.B, SomeEnum.D); EnumSet otherUnits = Sets.complementOf(units); verifySetContents(otherUnits, EnumSet.of(SomeEnum.A, SomeEnum.C)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfRegularSetWithType() { - Set units = Sets.newHashSet(SomeEnum.B, SomeEnum.D); + Set units = newHashSet(SomeEnum.B, SomeEnum.D); EnumSet otherUnits = Sets.complementOf(units, SomeEnum.class); verifySetContents(otherUnits, EnumSet.of(SomeEnum.A, SomeEnum.C)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfEmptySet() { - Set noUnits = Collections.emptySet(); + Set noUnits = emptySet(); EnumSet allUnits = Sets.complementOf(noUnits, SomeEnum.class); verifySetContents(EnumSet.allOf(SomeEnum.class), allUnits); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfFullSet() { - Set allUnits = Sets.newHashSet(SomeEnum.values()); + Set allUnits = newHashSet(SomeEnum.values()); EnumSet noUnits = Sets.complementOf(allUnits, SomeEnum.class); verifySetContents(noUnits, EnumSet.noneOf(SomeEnum.class)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfEmptyEnumSetWithoutType() { Set noUnits = EnumSet.noneOf(SomeEnum.class); EnumSet allUnits = Sets.complementOf(noUnits); verifySetContents(allUnits, EnumSet.allOf(SomeEnum.class)); } + @J2ktIncompatible + @GwtIncompatible // complementOf public void testComplementOfEmptySetWithoutTypeDoesntWork() { - Set set = Collections.emptySet(); - try { - Sets.complementOf(set); - fail(); - } catch (IllegalArgumentException expected) { - } + Set set = emptySet(); + assertThrows(IllegalArgumentException.class, () -> Sets.complementOf(set)); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointerExceptions() { new NullPointerTester() @@ -638,60 +662,52 @@ public void testNullPointerExceptions() { } public void testNewSetFromMap() { + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method Set set = Sets.newSetFromMap(new HashMap()); set.addAll(SOME_COLLECTION); verifySetContents(set, SOME_COLLECTION); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testNewSetFromMapSerialization() { + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method Set set = Sets.newSetFromMap(new LinkedHashMap()); set.addAll(SOME_COLLECTION); Set copy = SerializableTester.reserializeAndAssert(set); assertThat(copy).containsExactly(0, 1).inOrder(); } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testNewSetFromMapIllegal() { Map map = new LinkedHashMap<>(); map.put(2, true); - try { - Sets.newSetFromMap(map); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Sets.newSetFromMap(map)); } - // TODO: the overwhelming number of suppressions below suggests that maybe - // it's not worth having a varargs form of this method at all... - /** The 0-ary cartesian product is a single empty list. */ - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_zeroary() { - assertThat(Sets.cartesianProduct()).containsExactly(list()); + assertThat(cartesianProduct()).containsExactly(list()); } /** A unary cartesian product is one list of size 1 for each element in the input set. */ - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_unary() { - assertThat(Sets.cartesianProduct(set(1, 2))).containsExactly(list(1), list(2)); + assertThat(cartesianProduct(set(1, 2))).containsExactly(list(1), list(2)); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary0x0() { Set mt = emptySet(); - assertEmpty(Sets.cartesianProduct(mt, mt)); + assertEmpty(cartesianProduct(mt, mt)); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary0x1() { Set mt = emptySet(); - assertEmpty(Sets.cartesianProduct(mt, set(1))); + assertEmpty(cartesianProduct(mt, set(1))); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary1x0() { Set mt = emptySet(); - assertEmpty(Sets.cartesianProduct(set(1), mt)); + assertEmpty(cartesianProduct(set(1), mt)); } private static void assertEmpty(Set> set) { @@ -700,28 +716,24 @@ private static void assertEmpty(Set> set) { assertFalse(set.iterator().hasNext()); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary1x1() { - assertThat(Sets.cartesianProduct(set(1), set(2))).contains(list(1, 2)); + assertThat(cartesianProduct(set(1), set(2))).contains(list(1, 2)); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary1x2() { - assertThat(Sets.cartesianProduct(set(1), set(2, 3))) + assertThat(cartesianProduct(set(1), set(2, 3))) .containsExactly(list(1, 2), list(1, 3)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_binary2x2() { - assertThat(Sets.cartesianProduct(set(1, 2), set(3, 4))) + assertThat(cartesianProduct(set(1, 2), set(3, 4))) .containsExactly(list(1, 3), list(1, 4), list(2, 3), list(2, 4)) .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_2x2x2() { - assertThat(Sets.cartesianProduct(set(0, 1), set(0, 1), set(0, 1))) + assertThat(cartesianProduct(set(0, 1), set(0, 1), set(0, 1))) .containsExactly( list(0, 0, 0), list(0, 0, 1), @@ -734,9 +746,8 @@ public void testCartesianProduct_2x2x2() { .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_contains() { - Set> actual = Sets.cartesianProduct(set(1, 2), set(3, 4)); + Set> actual = cartesianProduct(set(1, 2), set(3, 4)); assertTrue(actual.contains(list(1, 3))); assertTrue(actual.contains(list(1, 4))); assertTrue(actual.contains(list(2, 3))); @@ -744,7 +755,21 @@ public void testCartesianProduct_contains() { assertFalse(actual.contains(list(3, 1))); } - @SuppressWarnings("unchecked") // varargs! + public void testCartesianProduct_equals() { + Set> cartesian = cartesianProduct(set(1, 2), set(3, 4)); + ImmutableSet> equivalent = + ImmutableSet.of(ImmutableList.of(1, 3), ImmutableList.of(1, 4), list(2, 3), list(2, 4)); + ImmutableSet> different1 = + ImmutableSet.of(ImmutableList.of(0, 3), ImmutableList.of(1, 4), list(2, 3), list(2, 4)); + ImmutableSet> different2 = + ImmutableSet.of(ImmutableList.of(1, 3), ImmutableList.of(1, 4), list(2, 3)); + new EqualsTester() + .addEqualityGroup(cartesian, equivalent) + .addEqualityGroup(different1) + .addEqualityGroup(different2) + .testEquals(); + } + public void testCartesianProduct_unrelatedTypes() { Set x = set(1, 2); Set y = set("3", "4"); @@ -759,40 +784,34 @@ public void testCartesianProduct_unrelatedTypes() { .inOrder(); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProductTooBig() { Set set = ContiguousSet.create(Range.closed(0, 10000), DiscreteDomain.integers()); - try { - Sets.cartesianProduct(set, set, set, set, set); - fail("Expected IAE"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> cartesianProduct(set, set, set, set, set)); } - @SuppressWarnings("unchecked") // varargs! public void testCartesianProduct_hashCode() { // Run through the same cartesian products we tested above - Set> degenerate = Sets.cartesianProduct(); + Set> degenerate = cartesianProduct(); checkHashCode(degenerate); - checkHashCode(Sets.cartesianProduct(set(1, 2))); + checkHashCode(cartesianProduct(set(1, 2))); int num = Integer.MAX_VALUE / 3 * 2; // tickle overflow-related problems - checkHashCode(Sets.cartesianProduct(set(1, 2, num))); + checkHashCode(cartesianProduct(set(1, 2, num))); Set mt = emptySet(); - checkHashCode(Sets.cartesianProduct(mt, mt)); - checkHashCode(Sets.cartesianProduct(mt, set(num))); - checkHashCode(Sets.cartesianProduct(set(num), mt)); - checkHashCode(Sets.cartesianProduct(set(num), set(1))); - checkHashCode(Sets.cartesianProduct(set(1), set(2, num))); - checkHashCode(Sets.cartesianProduct(set(1, num), set(2, num - 1))); - checkHashCode(Sets.cartesianProduct(set(1, num), set(2, num - 1), set(3, num + 1))); + checkHashCode(cartesianProduct(mt, mt)); + checkHashCode(cartesianProduct(mt, set(num))); + checkHashCode(cartesianProduct(set(num), mt)); + checkHashCode(cartesianProduct(set(num), set(1))); + checkHashCode(cartesianProduct(set(1), set(2, num))); + checkHashCode(cartesianProduct(set(1, num), set(2, num - 1))); + checkHashCode(cartesianProduct(set(1, num), set(2, num - 1), set(3, num + 1))); // a bigger one checkHashCode( - Sets.cartesianProduct(set(1, num, num + 1), set(2), set(3, num + 2), set(4, 5, 6, 7, 8))); + cartesianProduct(set(1, num, num + 1), set(2), set(3, num + 2), set(4, 5, 6, 7, 8))); } public void testPowerSetEmpty() { @@ -833,7 +852,7 @@ public void testPowerSetContents() { assertTrue(powerSet.contains(subset)); } assertFalse(powerSet.contains(ImmutableSet.of(1, 2, 4))); - assertFalse(powerSet.contains(singleton(null))); + assertFalse(powerSet.contains(Collections.<@Nullable Integer>singleton(null))); assertFalse(powerSet.contains(null)); assertFalse(powerSet.contains((Object) "notASet")); } @@ -852,11 +871,7 @@ public void testPowerSetIteration_manual() { assertEquals(ImmutableSet.of(3, 2), i.next()); assertEquals(ImmutableSet.of(3, 2, 1), i.next()); assertFalse(i.hasNext()); - try { - i.next(); - fail(); - } catch (NoSuchElementException expected) { - } + assertThrows(NoSuchElementException.class, () -> i.next()); } @GwtIncompatible // too slow for GWT @@ -908,27 +923,24 @@ public void testPowerSetSize() { } public void testPowerSetCreationErrors() { - try { - Set> unused = - powerSet( - newHashSet( - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5')); - fail(); - } catch (IllegalArgumentException expected) { - } - - try { - Set> unused = powerSet(ContiguousSet.closed(0, Integer.MAX_VALUE / 2)); - fail(); - } catch (IllegalArgumentException expected) { - } - - try { - powerSet(singleton(null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + Set> unused = + powerSet( + newHashSet( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', + '5')); + }); + + assertThrows( + IllegalArgumentException.class, + () -> { + Set> unused = powerSet(ContiguousSet.closed(0, Integer.MAX_VALUE / 2)); + }); + + assertThrows(NullPointerException.class, () -> powerSet(singleton(null))); } public void testPowerSetEqualsAndHashCode_verifyAgainstHashSet() { @@ -1010,7 +1022,8 @@ public int hashCode() { }; } - private static void assertPowerSetHashCode(int expected, Set elements) { + // TODO b/327389044 - `Set elements` should be enough but J2KT needs the + private static void assertPowerSetHashCode(int expected, Set elements) { assertEquals(expected, powerSet(elements).hashCode()); } @@ -1019,7 +1032,7 @@ private static void assertPowerSetSize(int i, Object... elements) { } private static void checkHashCode(Set set) { - assertEquals(Sets.newHashSet(set).hashCode(), set.hashCode()); + assertEquals(newHashSet(set).hashCode(), set.hashCode()); } public void testCombinations() { @@ -1077,7 +1090,7 @@ private static void verifyLinkedHashSetContents( * same as the given comparator. */ private static void verifySortedSetContents( - SortedSet set, Iterable iterable, @CheckForNull Comparator comparator) { + SortedSet set, Iterable iterable, @Nullable Comparator comparator) { assertSame(comparator, set.comparator()); verifySetContents(set, iterable); } @@ -1099,47 +1112,6 @@ private static void verifySetContents(Set set, Iterable contents) { assertEquals(expected, set); } - /** Simple base class to verify that we handle generics correctly. */ - static class Base implements Comparable, Serializable { - private final String s; - - public Base(String s) { - this.s = s; - } - - @Override - public int hashCode() { // delegate to 's' - return s.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (other == null) { - return false; - } else if (other instanceof Base) { - return s.equals(((Base) other).s); - } else { - return false; - } - } - - @Override - public int compareTo(Base o) { - return s.compareTo(o.s); - } - - private static final long serialVersionUID = 0; - } - - /** Simple derived class to verify that we handle generics correctly. */ - static class Derived extends Base { - public Derived(String s) { - super(s); - } - - private static final long serialVersionUID = 0; - } - @GwtIncompatible // NavigableSet public void testUnmodifiableNavigableSet() { TreeSet mod = Sets.newTreeSet(); @@ -1165,21 +1137,9 @@ public void testUnmodifiableNavigableSet() { /* UnsupportedOperationException on indirect modifications. */ NavigableSet reverse = unmod.descendingSet(); - try { - reverse.add(4); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - reverse.addAll(Collections.singleton(4)); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } - try { - reverse.remove(4); - fail("UnsupportedOperationException expected"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> reverse.add(4)); + assertThrows(UnsupportedOperationException.class, () -> reverse.addAll(singleton(4))); + assertThrows(UnsupportedOperationException.class, () -> reverse.remove(4)); } void ensureNotDirectlyModifiable(SortedSet unmod) { @@ -1194,7 +1154,7 @@ void ensureNotDirectlyModifiable(SortedSet unmod) { } catch (UnsupportedOperationException expected) { } try { - unmod.addAll(Collections.singleton(4)); + unmod.addAll(singleton(4)); fail("UnsupportedOperationException expected"); } catch (UnsupportedOperationException expected) { } @@ -1220,7 +1180,7 @@ void ensureNotDirectlyModifiable(NavigableSet unmod) { } catch (UnsupportedOperationException expected) { } try { - unmod.addAll(Collections.singleton(4)); + unmod.addAll(singleton(4)); fail("UnsupportedOperationException expected"); } catch (UnsupportedOperationException expected) { } @@ -1318,11 +1278,7 @@ public void testSubSet_unnaturalOrdering() { ImmutableSortedSet set = ImmutableSortedSet.reverseOrder().add(2, 4, 6, 8, 10).build(); - try { - Sets.subSet(set, Range.closed(4, 8)); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Sets.subSet(set, Range.closed(4, 8))); // These results are all incorrect, but there's no way (short of iterating over the result) // to verify that with an arbitrary ordering or comparator. diff --git a/android/guava-tests/test/com/google/common/collect/SimpleAbstractMultisetTest.java b/android/guava-tests/test/com/google/common/collect/SimpleAbstractMultisetTest.java index 02ab44b9f5c3..634cd96dca05 100644 --- a/android/guava-tests/test/com/google/common/collect/SimpleAbstractMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/SimpleAbstractMultisetTest.java @@ -15,9 +15,11 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -28,10 +30,11 @@ import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link AbstractMultiset}. @@ -41,8 +44,11 @@ */ @SuppressWarnings("serial") // No serialization is used in this test @GwtCompatible(emulated = true) +@NullMarked public class SimpleAbstractMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SimpleAbstractMultisetTest.class); @@ -65,6 +71,7 @@ protected Multiset create(String[] elements) { return suite; } + @SuppressWarnings("ModifiedButNotUsed") public void testFastAddAllMultiset() { final AtomicInteger addCalls = new AtomicInteger(); Multiset multiset = @@ -84,15 +91,12 @@ public int add(String element, int occurrences) { public void testRemoveUnsupported() { Multiset multiset = new NoRemoveMultiset<>(); multiset.add("a"); - try { - multiset.remove("a"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> multiset.remove("a")); assertTrue(multiset.contains("a")); } - private static class NoRemoveMultiset extends AbstractMultiset implements Serializable { + private static class NoRemoveMultiset extends AbstractMultiset + implements Serializable { final Map backingMap = Maps.newHashMap(); @Override @@ -106,7 +110,7 @@ public void clear() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { for (Entry entry : entrySet()) { if (Objects.equal(entry.getElement(), element)) { return entry.getCount(); @@ -116,12 +120,9 @@ public int count(@CheckForNull Object element) { } @Override - public int add(@CheckForNull E element, int occurrences) { + public int add(E element, int occurrences) { checkArgument(occurrences >= 0); - Integer frequency = backingMap.get(element); - if (frequency == null) { - frequency = 0; - } + Integer frequency = backingMap.getOrDefault(element, 0); if (occurrences == 0) { return frequency; } diff --git a/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapMapInterfaceTest.java new file mode 100644 index 000000000000..45be28eb17b9 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class SingletonImmutableMapMapInterfaceTest + extends AbstractImmutableMapMapInterfaceTest { + @Override + protected Map makePopulatedMap() { + return ImmutableMap.of("one", 1); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapWithUnhashableValueMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapWithUnhashableValueMapInterfaceTest.java new file mode 100644 index 000000000000..0891fa2952c2 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SingletonImmutableMapWithUnhashableValueMapInterfaceTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.testing.SampleElements.Unhashables; +import com.google.common.collect.testing.UnhashableObject; +import java.util.Map; +import org.jspecify.annotations.NullUnmarked; + +@GwtIncompatible // GWT's ImmutableMap emulation is backed by java.util.HashMap. +@NullUnmarked +public class SingletonImmutableMapWithUnhashableValueMapInterfaceTest + extends RegularImmutableMapWithUnhashableValuesMapInterfaceTest { + @Override + protected Map makePopulatedMap() { + Unhashables unhashables = new Unhashables(); + return ImmutableMap.of(0, unhashables.e0()); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SingletonImmutableSortedMapMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/SingletonImmutableSortedMapMapInterfaceTest.java new file mode 100644 index 000000000000..ae4f7d8962fe --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/SingletonImmutableSortedMapMapInterfaceTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class SingletonImmutableSortedMapMapInterfaceTest + extends AbstractImmutableSortedMapMapInterfaceTest { + @Override + protected SortedMap makePopulatedMap() { + return ImmutableSortedMap.of("one", 1); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "minus one"; + } + + @Override + protected Integer getValueNotInPopulatedMap() { + return -1; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/SingletonImmutableTableTest.java b/android/guava-tests/test/com/google/common/collect/SingletonImmutableTableTest.java index 16c5ecbbf959..2dded744ce1d 100644 --- a/android/guava-tests/test/com/google/common/collect/SingletonImmutableTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/SingletonImmutableTableTest.java @@ -16,12 +16,14 @@ package com.google.common.collect; +import static com.google.common.collect.Tables.immutableCell; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Objects; import com.google.common.testing.EqualsTester; +import org.jspecify.annotations.NullMarked; /** * Tests {@link SingletonImmutableTable}. @@ -29,6 +31,7 @@ * @author Gregory Kick */ @GwtCompatible(emulated = true) +@NullMarked public class SingletonImmutableTableTest extends AbstractImmutableTableTest { private final ImmutableTable testTable = new SingletonImmutableTable<>('a', 1, "blah"); @@ -38,7 +41,7 @@ public void testHashCode() { } public void testCellSet() { - assertEquals(ImmutableSet.of(Tables.immutableCell('a', 1, "blah")), testTable.cellSet()); + assertEquals(ImmutableSet.of(immutableCell('a', 1, "blah")), testTable.cellSet()); } public void testColumn() { diff --git a/android/guava-tests/test/com/google/common/collect/SortedIterablesTest.java b/android/guava-tests/test/com/google/common/collect/SortedIterablesTest.java index 543199d2e1ad..9b1d04bba477 100644 --- a/android/guava-tests/test/com/google/common/collect/SortedIterablesTest.java +++ b/android/guava-tests/test/com/google/common/collect/SortedIterablesTest.java @@ -15,8 +15,8 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.util.SortedSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@code SortedIterables}. @@ -24,13 +24,11 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class SortedIterablesTest extends TestCase { public void testSameComparator() { assertTrue(SortedIterables.hasSameComparator(Ordering.natural(), Sets.newTreeSet())); - // Before JDK6 (including under GWT), the TreeMap keySet is a plain Set. - if (Maps.newTreeMap().keySet() instanceof SortedSet) { - assertTrue(SortedIterables.hasSameComparator(Ordering.natural(), Maps.newTreeMap().keySet())); - } + assertTrue(SortedIterables.hasSameComparator(Ordering.natural(), Maps.newTreeMap().keySet())); assertTrue( SortedIterables.hasSameComparator( Ordering.natural().reverse(), Sets.newTreeSet(Ordering.natural().reverse()))); diff --git a/android/guava-tests/test/com/google/common/collect/SortedListsTest.java b/android/guava-tests/test/com/google/common/collect/SortedListsTest.java index a6cddcb723cc..797f9dc6251d 100644 --- a/android/guava-tests/test/com/google/common/collect/SortedListsTest.java +++ b/android/guava-tests/test/com/google/common/collect/SortedListsTest.java @@ -16,11 +16,13 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.SortedLists.KeyAbsentBehavior; import com.google.common.collect.SortedLists.KeyPresentBehavior; import com.google.common.testing.NullPointerTester; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for SortedLists. @@ -28,6 +30,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class SortedListsTest extends TestCase { private static final ImmutableList LIST_WITH_DUPS = ImmutableList.of(1, 1, 2, 4, 4, 4, 8); @@ -71,8 +74,6 @@ void assertModelAgrees( return; } break; - default: - throw new AssertionError(); } // key is not present int nextHigherIndex = list.size(); @@ -89,9 +90,8 @@ void assertModelAgrees( case INVERTED_INSERTION_INDEX: assertEquals(-1 - nextHigherIndex, answer); return; - default: - throw new AssertionError(); } + throw new AssertionError(); } public void testWithoutDups() { @@ -124,6 +124,7 @@ public void testWithDups() { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(SortedLists.class); diff --git a/android/guava-tests/test/com/google/common/collect/SpecialRandom.java b/android/guava-tests/test/com/google/common/collect/SpecialRandom.java index 5996e5bfe26c..571a061f74ae 100644 --- a/android/guava-tests/test/com/google/common/collect/SpecialRandom.java +++ b/android/guava-tests/test/com/google/common/collect/SpecialRandom.java @@ -17,6 +17,7 @@ package com.google.common.collect; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Utility class for being able to seed a {@link Random} value with a passed in seed from a @@ -26,6 +27,7 @@ * * @author Nicholaus Shupe */ +@NullUnmarked public final class SpecialRandom extends Random { public static SpecialRandom valueOf(String s) { return (s.length() == 0) ? new SpecialRandom() : new SpecialRandom(Long.parseLong(s)); diff --git a/android/guava-tests/test/com/google/common/collect/SubMapMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/SubMapMultimapAsMapImplementsMapTest.java index 1cb7abceb4cd..f97c6e792ecc 100644 --- a/android/guava-tests/test/com/google/common/collect/SubMapMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/SubMapMultimapAsMapImplementsMapTest.java @@ -16,11 +16,13 @@ package com.google.common.collect; +import static java.util.Collections.singleton; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; -import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@code TreeMultimap.asMap().subMap()} with {@link MapInterfaceTest}. @@ -28,6 +30,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class SubMapMultimapAsMapImplementsMapTest extends AbstractMultimapAsMapImplementsMapTest { public SubMapMultimapAsMapImplementsMapTest() { @@ -66,7 +69,7 @@ protected String getKeyNotInPopulatedMap() { @Override protected Collection getValueNotInPopulatedMap() { - return Collections.singleton(-2); + return singleton(-2); } @Override diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedBiMapTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedBiMapTest.java index 1e422b2b483e..c599d10c5618 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedBiMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedBiMapTest.java @@ -29,14 +29,18 @@ import java.util.Map.Entry; import java.util.Set; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code Synchronized#biMap}. * * @author Mike Bostock */ +@NullUnmarked public class SynchronizedBiMapTest extends SynchronizedMapTest { + @AndroidIncompatible // test-suite builders public static TestSuite suite() { TestSuite suite = new TestSuite(SynchronizedBiMapTest.class); suite.addTest( @@ -75,10 +79,10 @@ protected BiMap create() { return outer; } + @AndroidIncompatible // test-suite builders public static final class SynchronizedHashBiMapGenerator extends TestStringBiMapGenerator { @Override protected BiMap create(Entry[] entries) { - Object mutex = new Object(); BiMap result = HashBiMap.create(); for (Entry entry : entries) { checkArgument(!result.containsKey(entry.getKey())); @@ -88,6 +92,7 @@ protected BiMap create(Entry[] entries) { } } + @AndroidIncompatible // test-suite builders public static final class SynchTestingBiMapGenerator extends TestStringBiMapGenerator { @Override protected BiMap create(Entry[] entries) { @@ -111,7 +116,7 @@ public TestBiMap(BiMap delegate, Object mutex) { } @Override - public V forcePut(K key, V value) { + public @Nullable V forcePut(K key, V value) { assertTrue(Thread.holdsLock(mutex)); return delegate.forcePut(key, value); } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java index 1df6aa1bffd1..6f0f7a734cec 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedDequeTest.java @@ -21,12 +21,15 @@ import java.util.Deque; import java.util.Iterator; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Synchronized#deque} and {@link Queues#synchronizedDeque}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class SynchronizedDequeTest extends TestCase { protected Deque create() { @@ -38,7 +41,7 @@ protected Deque create() { private static final class TestDeque implements Deque { private final Deque delegate = Lists.newLinkedList(); - public final Object mutex = new Integer(1); // something Serializable + public final Object mutex = new Object[0]; // something Serializable @Override public boolean offer(E o) { @@ -47,7 +50,7 @@ public boolean offer(E o) { } @Override - public E poll() { + public @Nullable E poll() { assertTrue(Thread.holdsLock(mutex)); return delegate.poll(); } @@ -65,7 +68,7 @@ public boolean remove(Object object) { } @Override - public E peek() { + public @Nullable E peek() { assertTrue(Thread.holdsLock(mutex)); return delegate.peek(); } @@ -186,13 +189,13 @@ public E removeLast() { } @Override - public E pollFirst() { + public @Nullable E pollFirst() { assertTrue(Thread.holdsLock(mutex)); return delegate.pollFirst(); } @Override - public E pollLast() { + public @Nullable E pollLast() { assertTrue(Thread.holdsLock(mutex)); return delegate.pollLast(); } @@ -210,13 +213,13 @@ public E getLast() { } @Override - public E peekFirst() { + public @Nullable E peekFirst() { assertTrue(Thread.holdsLock(mutex)); return delegate.peekFirst(); } @Override - public E peekLast() { + public @Nullable E peekLast() { assertTrue(Thread.holdsLock(mutex)); return delegate.peekLast(); } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java index b07802a7b08a..b1ab77b6ff46 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedMapTest.java @@ -28,14 +28,17 @@ import java.util.Map.Entry; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code Synchronized#map}. * * @author Mike Bostock */ +@NullUnmarked public class SynchronizedMapTest extends TestCase { - public final Object mutex = new Integer(1); // something Serializable + public final Object mutex = new Object[0]; // something Serializable protected Map create() { TestMap inner = new TestMap<>(new HashMap(), mutex); @@ -71,7 +74,7 @@ public boolean isEmpty() { } @Override - public V remove(Object object) { + public @Nullable V remove(Object object) { assertTrue(Thread.holdsLock(mutex)); return super.remove(object); } @@ -95,13 +98,13 @@ public boolean containsValue(Object value) { } @Override - public V get(Object key) { + public @Nullable V get(Object key) { assertTrue(Thread.holdsLock(mutex)); return super.get(key); } @Override - public V put(K key, V value) { + public @Nullable V put(K key, V value) { assertTrue(Thread.holdsLock(mutex)); return super.put(key, value); } @@ -131,7 +134,7 @@ public Set> entrySet() { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { assertTrue(Thread.holdsLock(mutex)); return super.equals(obj); } @@ -216,15 +219,15 @@ public void testEntrySet() { } public void testEquals() { - create().equals(new HashMap()); + boolean unused = create().equals(new HashMap()); } public void testHashCode() { - create().hashCode(); + int unused = create().hashCode(); } public void testToString() { - create().toString(); + String unused = create().toString(); } public void testSerialization() { diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedMultimapTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedMultimapTest.java index 3c4f1375a0ab..f5f322d202cf 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedMultimapTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedMultimapTest.java @@ -16,7 +16,10 @@ package com.google.common.collect; +import static com.google.common.collect.Multimaps.synchronizedListMultimap; +import static com.google.common.collect.Multimaps.synchronizedSortedSetMultimap; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -24,24 +27,26 @@ import com.google.common.collect.testing.google.SetMultimapTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; import java.io.Serializable; -import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.RandomAccess; import java.util.Set; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code Synchronized#multimap}. * * @author Mike Bostock */ +@NullUnmarked public class SynchronizedMultimapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SynchronizedMultimapTest.class); @@ -75,7 +80,7 @@ protected SetMultimap create(Entry[] entries) { private static final class TestMultimap extends ForwardingSetMultimap implements Serializable { final SetMultimap delegate = HashMultimap.create(); - public final Object mutex = new Integer(1); // something Serializable + public final Object mutex = new Object[0]; // something Serializable @Override protected SetMultimap delegate() { @@ -89,7 +94,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { assertTrue(Thread.holdsLock(mutex)); return super.equals(o); } @@ -113,27 +118,27 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { assertTrue(Thread.holdsLock(mutex)); return super.containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { assertTrue(Thread.holdsLock(mutex)); return super.containsValue(value); } @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { assertTrue(Thread.holdsLock(mutex)); return super.containsEntry(key, value); } @Override - public Set get(@CheckForNull K key) { + public Set get(@Nullable K key) { assertTrue(Thread.holdsLock(mutex)); - /* TODO: verify that the Collection is also synchronized? */ + /* TODO: verify that the Set is also synchronized? */ return super.get(key); } @@ -144,7 +149,7 @@ public boolean put(K key, V value) { } @Override - public boolean putAll(@CheckForNull K key, Iterable values) { + public boolean putAll(@Nullable K key, Iterable values) { assertTrue(Thread.holdsLock(mutex)); return super.putAll(key, values); } @@ -156,19 +161,19 @@ public boolean putAll(Multimap map) { } @Override - public Set replaceValues(@CheckForNull K key, Iterable values) { + public Set replaceValues(@Nullable K key, Iterable values) { assertTrue(Thread.holdsLock(mutex)); return super.replaceValues(key, values); } @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { assertTrue(Thread.holdsLock(mutex)); return super.remove(key, value); } @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { assertTrue(Thread.holdsLock(mutex)); return super.removeAll(key); } @@ -189,7 +194,7 @@ public Set keySet() { @Override public Multiset keys() { assertTrue(Thread.holdsLock(mutex)); - /* TODO: verify that the Set is also synchronized? */ + /* TODO: verify that the Multiset is also synchronized? */ return super.keys(); } @@ -203,7 +208,7 @@ public Collection values() { @Override public Set> entries() { assertTrue(Thread.holdsLock(mutex)); - /* TODO: verify that the Collection is also synchronized? */ + /* TODO: verify that the Set is also synchronized? */ return super.entries(); } @@ -219,27 +224,23 @@ public Map> asMap() { public void testSynchronizedListMultimap() { ListMultimap multimap = - Multimaps.synchronizedListMultimap(ArrayListMultimap.create()); - multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); - multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); + synchronizedListMultimap(ArrayListMultimap.create()); + multimap.putAll("foo", asList(3, -1, 2, 4, 1)); + multimap.putAll("bar", asList(1, 2, 3, 1)); assertThat(multimap.removeAll("foo")).containsExactly(3, -1, 2, 4, 1).inOrder(); assertFalse(multimap.containsKey("foo")); - assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) - .containsExactly(1, 2, 3, 1) - .inOrder(); + assertThat(multimap.replaceValues("bar", asList(6, 5))).containsExactly(1, 2, 3, 1).inOrder(); assertThat(multimap.get("bar")).containsExactly(6, 5).inOrder(); } public void testSynchronizedSortedSetMultimap() { SortedSetMultimap multimap = - Multimaps.synchronizedSortedSetMultimap(TreeMultimap.create()); - multimap.putAll("foo", Arrays.asList(3, -1, 2, 4, 1)); - multimap.putAll("bar", Arrays.asList(1, 2, 3, 1)); + synchronizedSortedSetMultimap(TreeMultimap.create()); + multimap.putAll("foo", asList(3, -1, 2, 4, 1)); + multimap.putAll("bar", asList(1, 2, 3, 1)); assertThat(multimap.removeAll("foo")).containsExactly(-1, 1, 2, 3, 4).inOrder(); assertFalse(multimap.containsKey("foo")); - assertThat(multimap.replaceValues("bar", Arrays.asList(6, 5))) - .containsExactly(1, 2, 3) - .inOrder(); + assertThat(multimap.replaceValues("bar", asList(6, 5))).containsExactly(1, 2, 3).inOrder(); assertThat(multimap.get("bar")).containsExactly(5, 6).inOrder(); } @@ -247,7 +248,7 @@ public void testSynchronizedArrayListMultimapRandomAccess() { ListMultimap delegate = ArrayListMultimap.create(); delegate.put("foo", 1); delegate.put("foo", 3); - ListMultimap multimap = Multimaps.synchronizedListMultimap(delegate); + ListMultimap multimap = synchronizedListMultimap(delegate); assertTrue(multimap.get("foo") instanceof RandomAccess); assertTrue(multimap.get("bar") instanceof RandomAccess); } @@ -256,7 +257,7 @@ public void testSynchronizedLinkedListMultimapRandomAccess() { ListMultimap delegate = LinkedListMultimap.create(); delegate.put("foo", 1); delegate.put("foo", 3); - ListMultimap multimap = Multimaps.synchronizedListMultimap(delegate); + ListMultimap multimap = synchronizedListMultimap(delegate); assertFalse(multimap.get("foo") instanceof RandomAccess); assertFalse(multimap.get("bar") instanceof RandomAccess); } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableMapTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableMapTest.java index 41b597aac10f..4e1f5b336e17 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableMapTest.java @@ -33,12 +33,15 @@ import java.util.NavigableSet; import java.util.SortedMap; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Maps#synchronizedNavigableMap(NavigableMap)}. * * @author Louis Wasserman */ +@NullUnmarked public class SynchronizedNavigableMapTest extends SynchronizedMapTest { @Override protected NavigableMap create() { @@ -65,7 +68,7 @@ protected Entry delegate() { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { assertTrue(Thread.holdsLock(mutex)); return super.equals(object); } @@ -110,13 +113,13 @@ protected NavigableMap delegate() { } @Override - public Entry ceilingEntry(K key) { + public @Nullable Entry ceilingEntry(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().ceilingEntry(key); } @Override - public K ceilingKey(K key) { + public @Nullable K ceilingKey(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().ceilingKey(key); } @@ -134,19 +137,19 @@ public NavigableMap descendingMap() { } @Override - public Entry firstEntry() { + public @Nullable Entry firstEntry() { assertTrue(Thread.holdsLock(mutex)); return delegate().firstEntry(); } @Override - public Entry floorEntry(K key) { + public @Nullable Entry floorEntry(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().floorEntry(key); } @Override - public K floorKey(K key) { + public @Nullable K floorKey(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().floorKey(key); } @@ -163,31 +166,31 @@ public SortedMap headMap(K toKey) { } @Override - public Entry higherEntry(K key) { + public @Nullable Entry higherEntry(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().higherEntry(key); } @Override - public K higherKey(K key) { + public @Nullable K higherKey(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().higherKey(key); } @Override - public Entry lastEntry() { + public @Nullable Entry lastEntry() { assertTrue(Thread.holdsLock(mutex)); return delegate().lastEntry(); } @Override - public Entry lowerEntry(K key) { + public @Nullable Entry lowerEntry(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().lowerEntry(key); } @Override - public K lowerKey(K key) { + public @Nullable K lowerKey(K key) { assertTrue(Thread.holdsLock(mutex)); return delegate().lowerKey(key); } @@ -199,13 +202,13 @@ public NavigableSet navigableKeySet() { } @Override - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { assertTrue(Thread.holdsLock(mutex)); return delegate().pollFirstEntry(); } @Override - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { assertTrue(Thread.holdsLock(mutex)); return delegate().pollLastEntry(); } @@ -254,13 +257,14 @@ public K lastKey() { private static final long serialVersionUID = 0; } + @AndroidIncompatible // test-suite builders public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SynchronizedNavigableMapTest.class); suite.addTest( NavigableMapTestSuiteBuilder.using( new TestStringSortedMapGenerator() { - private final Object mutex = new Integer(1); + private final Object mutex = new Object[0]; // something Serializable @Override protected SortedMap create(Entry[] entries) { @@ -329,14 +333,14 @@ public void testFloorKey() { create().floorKey("a"); } - public void testHeadMap_K() { + public void testHeadMap_k() { NavigableMap map = create(); SortedMap headMap = map.headMap("a"); assertTrue(headMap instanceof SynchronizedSortedMap); assertSame(mutex, ((SynchronizedSortedMap) headMap).mutex); } - public void testHeadMap_K_B() { + public void testHeadMap_k_b() { NavigableMap map = create(); NavigableMap headMap = map.headMap("a", true); assertTrue(headMap instanceof SynchronizedNavigableMap); @@ -384,28 +388,28 @@ public void testPollLastEntry() { create().pollLastEntry(); } - public void testSubMap_K_K() { + public void testSubMap_k_k() { NavigableMap map = create(); SortedMap subMap = map.subMap("a", "b"); assertTrue(subMap instanceof SynchronizedSortedMap); assertSame(mutex, ((SynchronizedSortedMap) subMap).mutex); } - public void testSubMap_K_B_K_B() { + public void testSubMap_k_b_k_b() { NavigableMap map = create(); NavigableMap subMap = map.subMap("a", true, "b", false); assertTrue(subMap instanceof SynchronizedNavigableMap); assertSame(mutex, ((SynchronizedNavigableMap) subMap).mutex); } - public void testTailMap_K() { + public void testTailMap_k() { NavigableMap map = create(); SortedMap subMap = map.tailMap("a"); assertTrue(subMap instanceof SynchronizedSortedMap); assertSame(mutex, ((SynchronizedSortedMap) subMap).mutex); } - public void testTailMap_K_B() { + public void testTailMap_k_b() { NavigableMap map = create(); NavigableMap subMap = map.tailMap("a", true); assertTrue(subMap instanceof SynchronizedNavigableMap); diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableSetTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableSetTest.java index 8638b0d1769d..287599ff6243 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedNavigableSetTest.java @@ -32,26 +32,29 @@ import java.util.TreeSet; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Sets#synchronizedNavigableSet(NavigableSet)}. * * @author Louis Wasserman */ +@NullUnmarked public class SynchronizedNavigableSetTest extends TestCase { - private static final Object MUTEX = new Integer(1); // something Serializable + private static final Object MUTEX = new Object[0]; // something Serializable - @SuppressWarnings("unchecked") - protected NavigableSet create() { - TestSet inner = - new TestSet<>(new TreeSet((Comparator) Ordering.natural().nullsFirst()), MUTEX); + protected > NavigableSet create() { + LockHeldAssertingNavigableSet inner = + new LockHeldAssertingNavigableSet<>(new TreeSet<>(Ordering.natural().nullsFirst()), MUTEX); NavigableSet outer = Synchronized.navigableSet(inner, MUTEX); return outer; } - static class TestSet extends SynchronizedSetTest.TestSet implements NavigableSet { + static class LockHeldAssertingNavigableSet extends LockHeldAssertingSet + implements NavigableSet { - TestSet(NavigableSet delegate, Object mutex) { + LockHeldAssertingNavigableSet(NavigableSet delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -61,7 +64,7 @@ protected NavigableSet delegate() { } @Override - public E ceiling(E e) { + public @Nullable E ceiling(E e) { assertTrue(Thread.holdsLock(mutex)); return delegate().ceiling(e); } @@ -78,7 +81,7 @@ public NavigableSet descendingSet() { } @Override - public E floor(E e) { + public @Nullable E floor(E e) { assertTrue(Thread.holdsLock(mutex)); return delegate().floor(e); } @@ -95,24 +98,24 @@ public SortedSet headSet(E toElement) { } @Override - public E higher(E e) { + public @Nullable E higher(E e) { assertTrue(Thread.holdsLock(mutex)); return delegate().higher(e); } @Override - public E lower(E e) { + public @Nullable E lower(E e) { return delegate().lower(e); } @Override - public E pollFirst() { + public @Nullable E pollFirst() { assertTrue(Thread.holdsLock(mutex)); return delegate().pollFirst(); } @Override - public E pollLast() { + public @Nullable E pollLast() { assertTrue(Thread.holdsLock(mutex)); return delegate().pollLast(); } @@ -161,6 +164,7 @@ public E last() { private static final long serialVersionUID = 0; } + @AndroidIncompatible // test-suite builders public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SynchronizedNavigableSetTest.class); @@ -172,7 +176,8 @@ public static TestSuite suite() { protected NavigableSet create(String[] elements) { NavigableSet innermost = new SafeTreeSet<>(); Collections.addAll(innermost, elements); - TestSet inner = new TestSet<>(innermost, MUTEX); + LockHeldAssertingNavigableSet inner = + new LockHeldAssertingNavigableSet<>(innermost, MUTEX); NavigableSet outer = Synchronized.navigableSet(inner, MUTEX); return outer; } @@ -194,50 +199,50 @@ public List order(List insertionOrder) { } public void testDescendingSet() { - NavigableSet map = create(); - NavigableSet descendingSet = map.descendingSet(); + NavigableSet set = create(); + NavigableSet descendingSet = set.descendingSet(); assertTrue(descendingSet instanceof SynchronizedNavigableSet); assertSame(MUTEX, ((SynchronizedNavigableSet) descendingSet).mutex); } - public void testHeadSet_E() { - NavigableSet map = create(); - SortedSet headSet = map.headSet("a"); + public void testHeadSet_e() { + NavigableSet set = create(); + SortedSet headSet = set.headSet("a"); assertTrue(headSet instanceof SynchronizedSortedSet); assertSame(MUTEX, ((SynchronizedSortedSet) headSet).mutex); } - public void testHeadSet_E_B() { - NavigableSet map = create(); - NavigableSet headSet = map.headSet("a", true); + public void testHeadSet_e_b() { + NavigableSet set = create(); + NavigableSet headSet = set.headSet("a", true); assertTrue(headSet instanceof SynchronizedNavigableSet); assertSame(MUTEX, ((SynchronizedNavigableSet) headSet).mutex); } - public void testSubSet_E_E() { - NavigableSet map = create(); - SortedSet subSet = map.subSet("a", "b"); + public void testSubSet_e_e() { + NavigableSet set = create(); + SortedSet subSet = set.subSet("a", "b"); assertTrue(subSet instanceof SynchronizedSortedSet); assertSame(MUTEX, ((SynchronizedSortedSet) subSet).mutex); } - public void testSubSet_E_B_E_B() { - NavigableSet map = create(); - NavigableSet subSet = map.subSet("a", false, "b", true); + public void testSubSet_e_b_e_b() { + NavigableSet set = create(); + NavigableSet subSet = set.subSet("a", false, "b", true); assertTrue(subSet instanceof SynchronizedNavigableSet); assertSame(MUTEX, ((SynchronizedNavigableSet) subSet).mutex); } - public void testTailSet_E() { - NavigableSet map = create(); - SortedSet tailSet = map.tailSet("a"); + public void testTailSet_e() { + NavigableSet set = create(); + SortedSet tailSet = set.tailSet("a"); assertTrue(tailSet instanceof SynchronizedSortedSet); assertSame(MUTEX, ((SynchronizedSortedSet) tailSet).mutex); } - public void testTailSet_E_B() { - NavigableSet map = create(); - NavigableSet tailSet = map.tailSet("a", true); + public void testTailSet_e_b() { + NavigableSet set = create(); + NavigableSet tailSet = set.tailSet("a", true); assertTrue(tailSet instanceof SynchronizedNavigableSet); assertSame(MUTEX, ((SynchronizedNavigableSet) tailSet).mutex); } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java index f7b04fe2e864..4f236aa7b84d 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedQueueTest.java @@ -21,12 +21,15 @@ import java.util.Iterator; import java.util.Queue; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Synchronized#queue} and {@link Queues#synchronizedQueue}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class SynchronizedQueueTest extends TestCase { protected Queue create() { @@ -38,7 +41,7 @@ protected Queue create() { private static final class TestQueue implements Queue { private final Queue delegate = Lists.newLinkedList(); - public final Object mutex = new Integer(1); // something Serializable + public final Object mutex = new Object[0]; // something Serializable @Override public boolean offer(E o) { @@ -47,7 +50,7 @@ public boolean offer(E o) { } @Override - public E poll() { + public @Nullable E poll() { assertTrue(Thread.holdsLock(mutex)); return delegate.poll(); } @@ -65,7 +68,7 @@ public boolean remove(Object object) { } @Override - public E peek() { + public @Nullable E peek() { assertTrue(Thread.holdsLock(mutex)); return delegate.peek(); } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedSetTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedSetTest.java index 5ffcf5d54bf7..323480fe3f62 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedSetTest.java @@ -16,36 +16,36 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.testing.SetTestSuiteBuilder; import com.google.common.collect.testing.TestStringSetGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; -import java.io.Serializable; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import javax.annotation.CheckForNull; import junit.framework.Test; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code Synchronized#set}. * * @author Mike Bostock */ +@NullUnmarked +@AndroidIncompatible // test-suite builders public class SynchronizedSetTest extends TestCase { - public static final Object MUTEX = new Integer(1); // something Serializable + public static final Object MUTEX = new Object[0]; // something Serializable public static Test suite() { return SetTestSuiteBuilder.using( new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - TestSet inner = new TestSet<>(new HashSet(), MUTEX); + LockHeldAssertingSet inner = + new LockHeldAssertingSet<>(new HashSet(), MUTEX); Set outer = Synchronized.set(inner, inner.mutex); Collections.addAll(outer, elements); return outer; @@ -59,114 +59,4 @@ protected Set create(String[] elements) { CollectionFeature.SERIALIZABLE) .createTestSuite(); } - - static class TestSet extends ForwardingSet implements Serializable { - final Set delegate; - public final Object mutex; - - public TestSet(Set delegate, Object mutex) { - checkNotNull(mutex); - this.delegate = delegate; - this.mutex = mutex; - } - - @Override - protected Set delegate() { - return delegate; - } - - @Override - public String toString() { - assertTrue(Thread.holdsLock(mutex)); - return super.toString(); - } - - @Override - public boolean equals(@CheckForNull Object o) { - assertTrue(Thread.holdsLock(mutex)); - return super.equals(o); - } - - @Override - public int hashCode() { - assertTrue(Thread.holdsLock(mutex)); - return super.hashCode(); - } - - @Override - public boolean add(@CheckForNull E o) { - assertTrue(Thread.holdsLock(mutex)); - return super.add(o); - } - - @Override - public boolean addAll(Collection c) { - assertTrue(Thread.holdsLock(mutex)); - return super.addAll(c); - } - - @Override - public void clear() { - assertTrue(Thread.holdsLock(mutex)); - super.clear(); - } - - @Override - public boolean contains(@CheckForNull Object o) { - assertTrue(Thread.holdsLock(mutex)); - return super.contains(o); - } - - @Override - public boolean containsAll(Collection c) { - assertTrue(Thread.holdsLock(mutex)); - return super.containsAll(c); - } - - @Override - public boolean isEmpty() { - assertTrue(Thread.holdsLock(mutex)); - return super.isEmpty(); - } - - /* Don't test iterator(); it may or may not hold the mutex. */ - - @Override - public boolean remove(@CheckForNull Object o) { - assertTrue(Thread.holdsLock(mutex)); - return super.remove(o); - } - - @Override - public boolean removeAll(Collection c) { - assertTrue(Thread.holdsLock(mutex)); - return super.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - assertTrue(Thread.holdsLock(mutex)); - return super.retainAll(c); - } - - @Override - public int size() { - assertTrue(Thread.holdsLock(mutex)); - return super.size(); - } - - @Override - public Object[] toArray() { - assertTrue(Thread.holdsLock(mutex)); - return super.toArray(); - } - - @Override - public T[] toArray(T[] a) { - assertTrue(Thread.holdsLock(mutex)); - return super.toArray(a); - } - - private static final long serialVersionUID = 0; - } } diff --git a/android/guava-tests/test/com/google/common/collect/SynchronizedTableTest.java b/android/guava-tests/test/com/google/common/collect/SynchronizedTableTest.java index e1b45443660c..a80315812249 100644 --- a/android/guava-tests/test/com/google/common/collect/SynchronizedTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/SynchronizedTableTest.java @@ -20,12 +20,14 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; -public class SynchronizedTableTest extends AbstractTableTest { +@NullUnmarked +public class SynchronizedTableTest extends AbstractTableTest { private static final class TestTable implements Table, Serializable { final Table delegate = HashBasedTable.create(); - public final Object mutex = new Integer(1); // something Serializable + public final Object mutex = new Object[0]; // something Serializable @Override public String toString() { @@ -34,7 +36,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { assertTrue(Thread.holdsLock(mutex)); return delegate.equals(o); } @@ -58,7 +60,7 @@ public boolean isEmpty() { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { assertTrue(Thread.holdsLock(mutex)); return delegate.containsValue(value); } @@ -119,13 +121,13 @@ public boolean containsRow(Object rowKey) { } @Override - public V get(Object rowKey, Object columnKey) { + public @Nullable V get(Object rowKey, Object columnKey) { assertTrue(Thread.holdsLock(mutex)); return delegate.get(rowKey, columnKey); } @Override - public V put(R rowKey, C columnKey, V value) { + public @Nullable V put(R rowKey, C columnKey, V value) { assertTrue(Thread.holdsLock(mutex)); return delegate.put(rowKey, columnKey, value); } @@ -137,7 +139,7 @@ public void putAll(Table table) { } @Override - public V remove(Object rowKey, Object columnKey) { + public @Nullable V remove(Object rowKey, Object columnKey) { assertTrue(Thread.holdsLock(mutex)); return delegate.remove(rowKey, columnKey); } @@ -164,7 +166,7 @@ public Map> rowMap() { } @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { TestTable table = new TestTable<>(); Table synced = Synchronized.table(table, table.mutex); populate(synced, data); diff --git a/android/guava-tests/test/com/google/common/collect/TableCollectionTest.java b/android/guava-tests/test/com/google/common/collect/TableCollectionTest.java index 7c944441a0ec..effc828b8d50 100644 --- a/android/guava-tests/test/com/google/common/collect/TableCollectionTest.java +++ b/android/guava-tests/test/com/google/common/collect/TableCollectionTest.java @@ -17,9 +17,17 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Tables.immutableCell; +import static com.google.common.collect.Tables.transformValues; +import static com.google.common.collect.Tables.transpose; +import static com.google.common.collect.Tables.unmodifiableRowSortedTable; +import static com.google.common.collect.Tables.unmodifiableTable; +import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Table.Cell; @@ -35,17 +43,16 @@ import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.Feature; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedMap; import java.util.SortedSet; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Collection tests for {@link Table} implementations. @@ -54,20 +61,25 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class TableCollectionTest extends TestCase { + @J2ktIncompatible private static final Feature[] COLLECTION_FEATURES = { CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES }; + @J2ktIncompatible private static final Feature[] COLLECTION_FEATURES_ORDER = { CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, CollectionFeature.ALLOWS_NULL_QUERIES }; + @J2ktIncompatible private static final Feature[] COLLECTION_FEATURES_REMOVE = { CollectionSize.ANY, CollectionFeature.SUPPORTS_REMOVE, CollectionFeature.ALLOWS_NULL_QUERIES }; + @J2ktIncompatible private static final Feature[] COLLECTION_FEATURES_REMOVE_ORDER = { CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, @@ -75,38 +87,11 @@ public class TableCollectionTest extends TestCase { CollectionFeature.ALLOWS_NULL_QUERIES }; + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); - suite.addTestSuite(ArrayRowTests.class); - suite.addTestSuite(HashRowTests.class); - suite.addTestSuite(TreeRowTests.class); - suite.addTestSuite(TransposeRowTests.class); - suite.addTestSuite(TransformValueRowTests.class); - suite.addTestSuite(UnmodifiableHashRowTests.class); - suite.addTestSuite(UnmodifiableTreeRowTests.class); - suite.addTestSuite(ArrayColumnTests.class); - suite.addTestSuite(HashColumnTests.class); - suite.addTestSuite(TreeColumnTests.class); - suite.addTestSuite(TransposeColumnTests.class); - suite.addTestSuite(TransformValueColumnTests.class); - suite.addTestSuite(UnmodifiableHashColumnTests.class); - suite.addTestSuite(UnmodifiableTreeColumnTests.class); - suite.addTestSuite(ArrayRowMapTests.class); - suite.addTestSuite(HashRowMapTests.class); - suite.addTestSuite(TreeRowMapTests.class); - suite.addTestSuite(TreeRowMapHeadMapTests.class); - suite.addTestSuite(TreeRowMapTailMapTests.class); - suite.addTestSuite(TreeRowMapSubMapTests.class); - suite.addTestSuite(TransformValueRowMapTests.class); - suite.addTestSuite(UnmodifiableHashRowMapTests.class); - suite.addTestSuite(UnmodifiableTreeRowMapTests.class); - suite.addTestSuite(ArrayColumnMapTests.class); - suite.addTestSuite(HashColumnMapTests.class); - suite.addTestSuite(TreeColumnMapTests.class); - suite.addTestSuite(TransformValueColumnMapTests.class); - suite.addTestSuite(UnmodifiableHashColumnMapTests.class); - suite.addTestSuite(UnmodifiableTreeColumnMapTests.class); // Not testing rowKeySet() or columnKeySet() of Table.transformValues() // since the transformation doesn't affect the row and column key sets. @@ -158,7 +143,7 @@ protected SortedSet create(String[] elements) { @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -174,7 +159,7 @@ public List order(List insertionOrder) { protected Set create(String[] elements) { Table table = HashBasedTable.create(); populateForRowKeySet(table, elements); - return Tables.unmodifiableTable(table).rowKeySet(); + return unmodifiableTable(table).rowKeySet(); } }) .named("unmodifiableTable[HashBasedTable].rowKeySet") @@ -188,12 +173,12 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { RowSortedTable table = TreeBasedTable.create(); populateForRowKeySet(table, elements); - return Tables.unmodifiableRowSortedTable(table).rowKeySet(); + return unmodifiableRowSortedTable(table).rowKeySet(); } @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -247,7 +232,7 @@ protected Set create(String[] elements) { @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -262,7 +247,7 @@ public List order(List insertionOrder) { protected Set create(String[] elements) { Table table = HashBasedTable.create(); populateForColumnKeySet(table, elements); - return Tables.unmodifiableTable(table).columnKeySet(); + return unmodifiableTable(table).columnKeySet(); } }) .named("unmodifiableTable[HashBasedTable].columnKeySet") @@ -276,12 +261,12 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { RowSortedTable table = TreeBasedTable.create(); populateForColumnKeySet(table, elements); - return Tables.unmodifiableRowSortedTable(table).columnKeySet(); + return unmodifiableRowSortedTable(table).columnKeySet(); } @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -363,7 +348,7 @@ protected Collection create(String[] elements) { for (int i = 0; i < elements.length; i++) { table.put(i, 'a', "x" + checkNotNull(elements[i])); } - return Tables.transformValues(table, removeFirstCharacter).values(); + return transformValues(table, removeFirstCharacter).values(); } }) .named("TransformValues.values") @@ -380,7 +365,7 @@ protected Collection create(String[] elements) { table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); - return Tables.unmodifiableTable(table).values(); + return unmodifiableTable(table).values(); } }) .named("unmodifiableTable[HashBasedTable].values") @@ -396,7 +381,7 @@ protected Collection create(String[] elements) { table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); - return Tables.unmodifiableRowSortedTable(table).values(); + return unmodifiableRowSortedTable(table).values(); } }) .named("unmodifiableTable[TreeBasedTable].values") @@ -409,11 +394,11 @@ protected Collection create(String[] elements) { @Override public SampleElements> samples() { return new SampleElements<>( - Tables.immutableCell("bar", 1, 'a'), - Tables.immutableCell("bar", 2, 'b'), - Tables.immutableCell("bar", 3, (Character) null), - Tables.immutableCell("bar", 4, 'b'), - Tables.immutableCell("bar", 5, 'b')); + immutableCell("bar", 1, 'a'), + immutableCell("bar", 2, 'b'), + immutableCell("bar", 3, (Character) null), + immutableCell("bar", 4, 'b'), + immutableCell("bar", 5, 'b')); } @Override @@ -486,7 +471,7 @@ Table createTable() { @Override Table createTable() { Table original = TreeBasedTable.create(); - return Tables.transpose(original); + return transpose(original); } }) .named("TransposedTable.cellSet") @@ -513,7 +498,7 @@ public Set> create(Object... elements) { (Cell) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } - return Tables.transformValues(table, Functions.identity()).cellSet(); + return transformValues(table, Functions.identity()).cellSet(); } }) .named("TransformValues.cellSet") @@ -528,8 +513,7 @@ public Set> create(Object... elements) { new TestCellSetGenerator() { @Override Table createTable() { - return Tables.unmodifiableTable( - HashBasedTable.create()); + return unmodifiableTable(HashBasedTable.create()); } @Override @@ -541,7 +525,7 @@ public Set> create(Object... elements) { (Cell) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } - return Tables.unmodifiableTable(table).cellSet(); + return unmodifiableTable(table).cellSet(); } }) .named("unmodifiableTable[HashBasedTable].cellSet") @@ -553,7 +537,7 @@ public Set> create(Object... elements) { new TestCellSetGenerator() { @Override RowSortedTable createTable() { - return Tables.unmodifiableRowSortedTable( + return unmodifiableRowSortedTable( TreeBasedTable.create()); } @@ -566,7 +550,7 @@ public Set> create(Object... elements) { (Cell) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } - return Tables.unmodifiableRowSortedTable(table).cellSet(); + return unmodifiableRowSortedTable(table).cellSet(); } }) .named("unmodifiableRowSortedTable[TreeBasedTable].cellSet") @@ -620,7 +604,7 @@ protected Set create(String[] elements) { @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -635,9 +619,7 @@ public List order(List insertionOrder) { protected Set create(String[] elements) { Table table = HashBasedTable.create(); populateForRowKeySet(table, elements); - return Tables.transformValues(table, Functions.toStringFunction()) - .column(1) - .keySet(); + return transformValues(table, Functions.toStringFunction()).column(1).keySet(); } }) .named("TransformValues.column.keySet") @@ -651,7 +633,7 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { Table table = HashBasedTable.create(); populateForRowKeySet(table, elements); - return Tables.unmodifiableTable(table).column(1).keySet(); + return unmodifiableTable(table).column(1).keySet(); } }) .named("unmodifiableTable[HashBasedTable].column.keySet") @@ -665,12 +647,12 @@ protected Set create(String[] elements) { protected Set create(String[] elements) { RowSortedTable table = TreeBasedTable.create(); populateForRowKeySet(table, elements); - return Tables.unmodifiableRowSortedTable(table).column(1).keySet(); + return unmodifiableRowSortedTable(table).column(1).keySet(); } @Override public List order(List insertionOrder) { - Collections.sort(insertionOrder); + sort(insertionOrder); return insertionOrder; } }) @@ -704,16 +686,17 @@ private static void populateForValues( } } + @J2ktIncompatible private abstract static class TestCellSetGenerator implements TestSetGenerator> { @Override public SampleElements> samples() { return new SampleElements<>( - Tables.immutableCell("bar", 1, 'a'), - Tables.immutableCell("bar", 2, 'b'), - Tables.immutableCell("foo", 3, 'c'), - Tables.immutableCell("bar", 1, 'b'), - Tables.immutableCell("cat", 2, 'b')); + immutableCell("bar", 1, 'a'), + immutableCell("bar", 2, 'b'), + immutableCell("foo", 3, 'c'), + immutableCell("bar", 1, 'b'), + immutableCell("cat", 2, 'b')); } @Override @@ -770,7 +753,7 @@ protected Integer getValueNotInPopulatedMap() { } } - private abstract static class RowTests extends MapTests { + abstract static class RowTests extends MapTests { RowTests( boolean allowsNullValues, boolean supportsPut, @@ -798,138 +781,15 @@ protected Map makePopulatedMap() { } } - @GwtIncompatible // TODO(hhchan): ArrayTable - public static class ArrayRowTests extends RowTests { - public ArrayRowTests() { - super(true, true, false, false, false); - } - - @Override - protected String getKeyNotInPopulatedMap() { - throw new UnsupportedOperationException(); - } - - @Override - protected Map makeEmptyMap() { - throw new UnsupportedOperationException(); - } - - @Override - protected Table makeTable() { - return ArrayTable.create( - Arrays.asList('a', 'b', 'c'), Arrays.asList("one", "two", "three", "four")); - } - } - - public static class HashRowTests extends RowTests { - public HashRowTests() { - super(false, true, true, true, true); - } - - @Override - Table makeTable() { - return HashBasedTable.create(); - } - } - - public static class TreeRowTests extends RowTests { - public TreeRowTests() { - super(false, true, true, true, true); - } - - @Override - Table makeTable() { - return TreeBasedTable.create(); - } - } - - public static class TransposeRowTests extends RowTests { - public TransposeRowTests() { - super(false, true, true, true, false); - } - - @Override - Table makeTable() { - Table original = TreeBasedTable.create(); - return Tables.transpose(original); - } - } - - private static final Function DIVIDE_BY_2 = - new Function() { + static final Function<@Nullable Integer, @Nullable Integer> DIVIDE_BY_2 = + new Function<@Nullable Integer, @Nullable Integer>() { @Override - public Integer apply(Integer input) { + public @Nullable Integer apply(@Nullable Integer input) { return (input == null) ? null : input / 2; } }; - public static class TransformValueRowTests extends RowTests { - public TransformValueRowTests() { - super(false, false, true, true, true); - } - - @Override - Table makeTable() { - Table table = HashBasedTable.create(); - return Tables.transformValues(table, DIVIDE_BY_2); - } - - @Override - protected Map makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put('a', "one", 2); - table.put('a', "two", 4); - table.put('a', "three", 6); - table.put('b', "four", 8); - return Tables.transformValues(table, DIVIDE_BY_2).row('a'); - } - } - - public static class UnmodifiableHashRowTests extends RowTests { - public UnmodifiableHashRowTests() { - super(false, false, false, false, false); - } - - @Override - Table makeTable() { - Table table = HashBasedTable.create(); - return Tables.unmodifiableTable(table); - } - - @Override - protected Map makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put('a', "one", 1); - table.put('a', "two", 2); - table.put('a', "three", 3); - table.put('b', "four", 4); - return Tables.unmodifiableTable(table).row('a'); - } - } - - public static class UnmodifiableTreeRowTests extends RowTests { - public UnmodifiableTreeRowTests() { - super(false, false, false, false, false); - } - - @Override - Table makeTable() { - RowSortedTable table = TreeBasedTable.create(); - return Tables.unmodifiableRowSortedTable(table); - } - - @Override - protected Map makePopulatedMap() { - RowSortedTable table = TreeBasedTable.create(); - table.put('a', "one", 1); - table.put('a', "two", 2); - table.put('a', "three", 3); - table.put('b', "four", 4); - return Tables.unmodifiableRowSortedTable(table).row('a'); - } - } - - private abstract static class ColumnTests extends MapTests { + abstract static class ColumnTests extends MapTests { ColumnTests( boolean allowsNullValues, boolean supportsPut, @@ -957,129 +817,6 @@ protected Map makePopulatedMap() { } } - @GwtIncompatible // TODO(hhchan): ArrayTable - public static class ArrayColumnTests extends ColumnTests { - public ArrayColumnTests() { - super(true, true, false, false, false); - } - - @Override - protected String getKeyNotInPopulatedMap() { - throw new UnsupportedOperationException(); - } - - @Override - protected Map makeEmptyMap() { - throw new UnsupportedOperationException(); - } - - @Override - Table makeTable() { - return ArrayTable.create( - Arrays.asList("one", "two", "three", "four"), Arrays.asList('a', 'b', 'c')); - } - } - - public static class HashColumnTests extends ColumnTests { - public HashColumnTests() { - super(false, true, true, true, false); - } - - @Override - Table makeTable() { - return HashBasedTable.create(); - } - } - - public static class TreeColumnTests extends ColumnTests { - public TreeColumnTests() { - super(false, true, true, true, false); - } - - @Override - Table makeTable() { - return TreeBasedTable.create(); - } - } - - public static class TransposeColumnTests extends ColumnTests { - public TransposeColumnTests() { - super(false, true, true, true, true); - } - - @Override - Table makeTable() { - Table original = TreeBasedTable.create(); - return Tables.transpose(original); - } - } - - public static class TransformValueColumnTests extends ColumnTests { - public TransformValueColumnTests() { - super(false, false, true, true, false); - } - - @Override - Table makeTable() { - Table table = HashBasedTable.create(); - return Tables.transformValues(table, DIVIDE_BY_2); - } - - @Override - protected Map makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put("one", 'a', 1); - table.put("two", 'a', 2); - table.put("three", 'a', 3); - table.put("four", 'b', 4); - return Tables.transformValues(table, DIVIDE_BY_2).column('a'); - } - } - - public static class UnmodifiableHashColumnTests extends ColumnTests { - public UnmodifiableHashColumnTests() { - super(false, false, false, false, false); - } - - @Override - Table makeTable() { - Table table = HashBasedTable.create(); - return Tables.unmodifiableTable(table); - } - - @Override - protected Map makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put("one", 'a', 1); - table.put("two", 'a', 2); - table.put("three", 'a', 3); - table.put("four", 'b', 4); - return Tables.unmodifiableTable(table).column('a'); - } - } - - public static class UnmodifiableTreeColumnTests extends ColumnTests { - public UnmodifiableTreeColumnTests() { - super(false, false, false, false, false); - } - - @Override - Table makeTable() { - RowSortedTable table = TreeBasedTable.create(); - return Tables.unmodifiableRowSortedTable(table); - } - - @Override - protected Map makePopulatedMap() { - RowSortedTable table = TreeBasedTable.create(); - table.put("one", 'a', 1); - table.put("two", 'a', 2); - table.put("three", 'a', 3); - table.put("four", 'b', 4); - return Tables.unmodifiableRowSortedTable(table).column('a'); - } - } - private abstract static class MapMapTests extends MapInterfaceTest> { @@ -1110,13 +847,12 @@ protected Map getValueNotInPopulatedMap() { @Override public void testRemove() { final Map> map; - final String keyToRemove; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { return; } - keyToRemove = map.keySet().iterator().next(); + final String keyToRemove = map.keySet().iterator().next(); if (supportsRemove) { int initialSize = map.size(); map.get(keyToRemove); @@ -1126,17 +862,13 @@ public void testRemove() { assertFalse(map.containsKey(keyToRemove)); assertEquals(initialSize - 1, map.size()); } else { - try { - map.remove(keyToRemove); - fail("Expected UnsupportedOperationException."); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> map.remove(keyToRemove)); } assertInvariants(map); } } - private abstract static class RowMapTests extends MapMapTests { + abstract static class RowMapTests extends MapMapTests { RowMapTests( boolean allowsNullValues, boolean supportsRemove, @@ -1166,208 +898,15 @@ protected Map> makeEmptyMap() { } } - @GwtIncompatible // TODO(hhchan): ArrayTable - public static class ArrayRowMapTests extends RowMapTests { - public ArrayRowMapTests() { - super(true, false, false, false); - } - - @Override - Table makeTable() { - return ArrayTable.create(Arrays.asList("foo", "bar", "dog"), Arrays.asList(1, 2, 3)); - } - - @Override - protected Map> makeEmptyMap() { - throw new UnsupportedOperationException(); - } - } - - public static class HashRowMapTests extends RowMapTests { - public HashRowMapTests() { - super(false, true, true, true); - } - - @Override - Table makeTable() { - return HashBasedTable.create(); - } - } - - public static class TreeRowMapTests extends RowMapTests { - public TreeRowMapTests() { - super(false, true, true, true); - } - - @Override - Table makeTable() { - return TreeBasedTable.create(); - } - } - - public static class TreeRowMapHeadMapTests extends RowMapTests { - public TreeRowMapHeadMapTests() { - super(false, true, true, true); - } - - @Override - TreeBasedTable makeTable() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("z", 1, 'a'); - return table; - } - - @Override - protected Map> makePopulatedMap() { - TreeBasedTable table = makeTable(); - populateTable(table); - return table.rowMap().headMap("x"); - } - - @Override - protected Map> makeEmptyMap() { - return makeTable().rowMap().headMap("x"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "z"; - } - } - - public static class TreeRowMapTailMapTests extends RowMapTests { - public TreeRowMapTailMapTests() { - super(false, true, true, true); - } - - @Override - TreeBasedTable makeTable() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("a", 1, 'a'); - return table; - } - - @Override - protected Map> makePopulatedMap() { - TreeBasedTable table = makeTable(); - populateTable(table); - return table.rowMap().tailMap("b"); - } - - @Override - protected Map> makeEmptyMap() { - return makeTable().rowMap().tailMap("b"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "a"; - } - } - - public static class TreeRowMapSubMapTests extends RowMapTests { - public TreeRowMapSubMapTests() { - super(false, true, true, true); - } - - @Override - TreeBasedTable makeTable() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("a", 1, 'a'); - table.put("z", 1, 'a'); - return table; - } - - @Override - protected Map> makePopulatedMap() { - TreeBasedTable table = makeTable(); - populateTable(table); - return table.rowMap().subMap("b", "x"); - } - - @Override - protected Map> makeEmptyMap() { - return makeTable().rowMap().subMap("b", "x"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "z"; - } - } - - private static final Function FIRST_CHARACTER = - new Function() { + static final Function<@Nullable String, @Nullable Character> FIRST_CHARACTER = + new Function<@Nullable String, @Nullable Character>() { @Override - public Character apply(String input) { + public @Nullable Character apply(@Nullable String input) { return input == null ? null : input.charAt(0); } }; - public static class TransformValueRowMapTests extends RowMapTests { - public TransformValueRowMapTests() { - super(false, true, true, true); - } - - @Override - Table makeTable() { - Table original = HashBasedTable.create(); - return Tables.transformValues(original, FIRST_CHARACTER); - } - - @Override - protected Map> makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put("foo", 1, "apple"); - table.put("bar", 1, "banana"); - table.put("foo", 3, "cat"); - return Tables.transformValues(table, FIRST_CHARACTER).rowMap(); - } - } - - public static class UnmodifiableHashRowMapTests extends RowMapTests { - public UnmodifiableHashRowMapTests() { - super(false, false, false, false); - } - - @Override - Table makeTable() { - Table original = HashBasedTable.create(); - return Tables.unmodifiableTable(original); - } - - @Override - protected Map> makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put("foo", 1, 'a'); - table.put("bar", 1, 'b'); - table.put("foo", 3, 'c'); - return Tables.unmodifiableTable(table).rowMap(); - } - } - - public static class UnmodifiableTreeRowMapTests extends RowMapTests { - public UnmodifiableTreeRowMapTests() { - super(false, false, false, false); - } - - @Override - RowSortedTable makeTable() { - RowSortedTable original = TreeBasedTable.create(); - return Tables.unmodifiableRowSortedTable(original); - } - - @Override - protected SortedMap> makePopulatedMap() { - RowSortedTable table = TreeBasedTable.create(); - table.put("foo", 1, 'a'); - table.put("bar", 1, 'b'); - table.put("foo", 3, 'c'); - return Tables.unmodifiableRowSortedTable(table).rowMap(); - } - } - - private abstract static class ColumnMapTests extends MapMapTests { + abstract static class ColumnMapTests extends MapMapTests { ColumnMapTests( boolean allowsNullValues, boolean supportsRemove, @@ -1392,106 +931,4 @@ protected Map> makeEmptyMap() { return makeTable().columnMap(); } } - - @GwtIncompatible // TODO(hhchan): ArrayTable - public static class ArrayColumnMapTests extends ColumnMapTests { - public ArrayColumnMapTests() { - super(true, false, false, false); - } - - @Override - Table makeTable() { - return ArrayTable.create(Arrays.asList(1, 2, 3), Arrays.asList("foo", "bar", "dog")); - } - - @Override - protected Map> makeEmptyMap() { - throw new UnsupportedOperationException(); - } - } - - public static class HashColumnMapTests extends ColumnMapTests { - public HashColumnMapTests() { - super(false, true, true, false); - } - - @Override - Table makeTable() { - return HashBasedTable.create(); - } - } - - public static class TreeColumnMapTests extends ColumnMapTests { - public TreeColumnMapTests() { - super(false, true, true, false); - } - - @Override - Table makeTable() { - return TreeBasedTable.create(); - } - } - - public static class TransformValueColumnMapTests extends ColumnMapTests { - public TransformValueColumnMapTests() { - super(false, true, true, false); - } - - @Override - Table makeTable() { - Table original = HashBasedTable.create(); - return Tables.transformValues(original, FIRST_CHARACTER); - } - - @Override - protected Map> makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put(1, "foo", "apple"); - table.put(1, "bar", "banana"); - table.put(3, "foo", "cat"); - return Tables.transformValues(table, FIRST_CHARACTER).columnMap(); - } - } - - public static class UnmodifiableHashColumnMapTests extends ColumnMapTests { - public UnmodifiableHashColumnMapTests() { - super(false, false, false, false); - } - - @Override - Table makeTable() { - Table original = HashBasedTable.create(); - return Tables.unmodifiableTable(original); - } - - @Override - protected Map> makePopulatedMap() { - Table table = HashBasedTable.create(); - table.put(1, "foo", 'a'); - table.put(1, "bar", 'b'); - table.put(3, "foo", 'c'); - return Tables.unmodifiableTable(table).columnMap(); - } - } - - public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests { - public UnmodifiableTreeColumnMapTests() { - super(false, false, false, false); - } - - @Override - Table makeTable() { - RowSortedTable original = TreeBasedTable.create(); - return Tables.unmodifiableRowSortedTable(original); - } - - @Override - protected Map> makePopulatedMap() { - RowSortedTable table = TreeBasedTable.create(); - table.put(1, "foo", 'a'); - table.put(1, "bar", 'b'); - table.put(3, "foo", 'c'); - return Tables.unmodifiableRowSortedTable(table).columnMap(); - } - } } diff --git a/android/guava-tests/test/com/google/common/collect/TablesTest.java b/android/guava-tests/test/com/google/common/collect/TablesTest.java index 1cce12f27fb8..fbbdb5149228 100644 --- a/android/guava-tests/test/com/google/common/collect/TablesTest.java +++ b/android/guava-tests/test/com/google/common/collect/TablesTest.java @@ -16,12 +16,16 @@ package com.google.common.collect; +import static com.google.common.collect.Tables.immutableCell; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.Table.Cell; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Tables}. @@ -29,43 +33,54 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class TablesTest extends TestCase { @GwtIncompatible // SerializableTester public void testImmutableEntrySerialization() { - Cell entry = Tables.immutableCell("foo", 1, 'a'); + Cell entry = immutableCell("foo", 1, 'a'); SerializableTester.reserializeAndAssert(entry); } public void testImmutableEntryToString() { - Cell entry = Tables.immutableCell("foo", 1, 'a'); + Cell entry = immutableCell("foo", 1, 'a'); assertEquals("(foo,1)=a", entry.toString()); - Cell nullEntry = Tables.immutableCell(null, null, null); + Cell<@Nullable String, @Nullable Integer, @Nullable Character> nullEntry = + immutableCell(null, null, null); assertEquals("(null,null)=null", nullEntry.toString()); } public void testEntryEquals() { - Cell entry = Tables.immutableCell("foo", 1, 'a'); + Cell entry = immutableCell("foo", 1, 'a'); new EqualsTester() - .addEqualityGroup(entry, Tables.immutableCell("foo", 1, 'a')) - .addEqualityGroup(Tables.immutableCell("bar", 1, 'a')) - .addEqualityGroup(Tables.immutableCell("foo", 2, 'a')) - .addEqualityGroup(Tables.immutableCell("foo", 1, 'b')) - .addEqualityGroup(Tables.immutableCell(null, null, null)) + .addEqualityGroup(entry, immutableCell("foo", 1, 'a')) + .addEqualityGroup(immutableCell("bar", 1, 'a')) + .addEqualityGroup(immutableCell("foo", 2, 'a')) + .addEqualityGroup(immutableCell("foo", 1, 'b')) + .addEqualityGroup( + Tables.<@Nullable Object, @Nullable Object, @Nullable Object>immutableCell( + null, null, null)) .testEquals(); } public void testEntryEqualsNull() { - Cell entry = Tables.immutableCell(null, null, null); + Cell<@Nullable String, @Nullable Integer, @Nullable Character> entry = + immutableCell(null, null, null); new EqualsTester() - .addEqualityGroup(entry, Tables.immutableCell(null, null, null)) - .addEqualityGroup(Tables.immutableCell("bar", null, null)) - .addEqualityGroup(Tables.immutableCell(null, 2, null)) - .addEqualityGroup(Tables.immutableCell(null, null, 'b')) - .addEqualityGroup(Tables.immutableCell("foo", 1, 'a')) + .addEqualityGroup( + entry, + Tables.<@Nullable Object, @Nullable Object, @Nullable Object>immutableCell( + null, null, null)) + .addEqualityGroup( + Tables.immutableCell("bar", null, null)) + .addEqualityGroup( + Tables.<@Nullable Object, Integer, @Nullable Object>immutableCell(null, 2, null)) + .addEqualityGroup( + Tables.<@Nullable Object, @Nullable Object, Character>immutableCell(null, null, 'b')) + .addEqualityGroup(immutableCell("foo", 1, 'a')) .testEquals(); } } diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnMapTest.java new file mode 100644 index 000000000000..e5887875d742 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnMapTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.TableCollectionTest.FIRST_CHARACTER; +import static com.google.common.collect.Tables.transformValues; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransformValuesColumnMapTest extends ColumnMapTests { + public TablesTransformValuesColumnMapTest() { + super(false, true, true, false); + } + + @Override + Table makeTable() { + Table original = HashBasedTable.create(); + return transformValues(original, FIRST_CHARACTER); + } + + @Override + protected Map> makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put(1, "foo", "apple"); + table.put(1, "bar", "banana"); + table.put(3, "foo", "cat"); + return transformValues(table, FIRST_CHARACTER).columnMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnTest.java new file mode 100644 index 000000000000..c1a140092acc --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesColumnTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.TableCollectionTest.DIVIDE_BY_2; +import static com.google.common.collect.Tables.transformValues; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransformValuesColumnTest extends ColumnTests { + public TablesTransformValuesColumnTest() { + super(false, false, true, true, false); + } + + @Override + Table makeTable() { + Table table = HashBasedTable.create(); + return transformValues(table, DIVIDE_BY_2); + } + + @Override + protected Map makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put("one", 'a', 1); + table.put("two", 'a', 2); + table.put("three", 'a', 3); + table.put("four", 'b', 4); + return transformValues(table, DIVIDE_BY_2).column('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowMapTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowMapTest.java new file mode 100644 index 000000000000..0a3918b39ef6 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowMapTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.transformValues; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransformValuesRowMapTest extends RowMapTests { + public TablesTransformValuesRowMapTest() { + super(false, true, true, true); + } + + @Override + Table makeTable() { + Table original = HashBasedTable.create(); + return transformValues(original, TableCollectionTest.FIRST_CHARACTER); + } + + @Override + protected Map> makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put("foo", 1, "apple"); + table.put("bar", 1, "banana"); + table.put("foo", 3, "cat"); + return transformValues(table, TableCollectionTest.FIRST_CHARACTER).rowMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowTest.java new file mode 100644 index 000000000000..de34b485640d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesRowTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.transformValues; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransformValuesRowTest extends RowTests { + public TablesTransformValuesRowTest() { + super(false, false, true, true, true); + } + + @Override + Table makeTable() { + Table table = HashBasedTable.create(); + return transformValues(table, TableCollectionTest.DIVIDE_BY_2); + } + + @Override + protected Map makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put('a', "one", 2); + table.put('a', "two", 4); + table.put('a', "three", 6); + table.put('b', "four", 8); + return transformValues(table, TableCollectionTest.DIVIDE_BY_2).row('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesTest.java index 6730b3f519d4..35d32ad17daf 100644 --- a/android/guava-tests/test/com/google/common/collect/TablesTransformValuesTest.java +++ b/android/guava-tests/test/com/google/common/collect/TablesTransformValuesTest.java @@ -17,10 +17,15 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.collect.Tables.transformValues; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link Tables#transformValues}. @@ -28,28 +33,30 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) -public class TablesTransformValuesTest extends AbstractTableTest { +@NullMarked +public class TablesTransformValuesTest extends AbstractTableTest { - private static final Function FIRST_CHARACTER = - new Function() { + private static final Function<@Nullable String, @Nullable Character> FIRST_CHARACTER = + new Function<@Nullable String, @Nullable Character>() { @Override - public Character apply(String input) { + public @Nullable Character apply(@Nullable String input) { return input == null ? null : input.charAt(0); } }; @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { Table table = HashBasedTable.create(); checkArgument(data.length % 3 == 0); for (int i = 0; i < data.length; i += 3) { String value = (data[i + 2] == null) ? null : (data[i + 2] + "transformed"); table.put((String) data[i], (Integer) data[i + 1], value); } - return Tables.transformValues(table, FIRST_CHARACTER); + return transformValues(table, FIRST_CHARACTER); } // Null support depends on the underlying table and function. + @J2ktIncompatible @GwtIncompatible // NullPointerTester @Override public void testNullPointerInstance() {} @@ -57,11 +64,7 @@ public void testNullPointerInstance() {} // put() and putAll() aren't supported. @Override public void testPut() { - try { - table.put("foo", 1, 'a'); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> table.put("foo", 1, 'a')); assertSize(0); } @@ -72,11 +75,7 @@ public void testPutAllTable() { other.put("foo", 1, 'd'); other.put("bar", 2, 'e'); other.put("cat", 2, 'f'); - try { - table.putAll(other); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> table.putAll(other)); assertEquals((Character) 'a', table.get("foo", 1)); assertEquals((Character) 'b', table.get("bar", 1)); assertEquals((Character) 'c', table.get("foo", 3)); diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransposeColumnTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransposeColumnTest.java new file mode 100644 index 000000000000..f8e67f990829 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransposeColumnTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.transpose; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransposeColumnTest extends ColumnTests { + public TablesTransposeColumnTest() { + super(false, true, true, true, true); + } + + @Override + Table makeTable() { + Table original = TreeBasedTable.create(); + return transpose(original); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TablesTransposeRowTest.java b/android/guava-tests/test/com/google/common/collect/TablesTransposeRowTest.java new file mode 100644 index 000000000000..524ed112d70a --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TablesTransposeRowTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.transpose; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TablesTransposeRowTest extends RowTests { + public TablesTransposeRowTest() { + super(false, true, true, true, false); + } + + @Override + Table makeTable() { + Table original = TreeBasedTable.create(); + return transpose(original); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TestExceptions.java b/android/guava-tests/test/com/google/common/collect/TestExceptions.java new file mode 100644 index 000000000000..eb0025b49260 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TestExceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullUnmarked; + +/** Exception classes for use in tests. */ +@GwtCompatible +@NullUnmarked +final class TestExceptions { + static class SomeError extends Error {} + + static class SomeCheckedException extends Exception {} + + static class SomeOtherCheckedException extends Exception {} + + static class YetAnotherCheckedException extends Exception {} + + static class SomeUncheckedException extends RuntimeException {} + + static class SomeChainingException extends RuntimeException { + public SomeChainingException(Throwable cause) { + super(cause); + } + } + + private TestExceptions() {} +} diff --git a/android/guava-tests/test/com/google/common/collect/TopKSelectorTest.java b/android/guava-tests/test/com/google/common/collect/TopKSelectorTest.java index 2cebdc338b08..5f28508d8928 100644 --- a/android/guava-tests/test/com/google/common/collect/TopKSelectorTest.java +++ b/android/guava-tests/test/com/google/common/collect/TopKSelectorTest.java @@ -16,16 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.nCopies; import com.google.common.annotations.GwtCompatible; import com.google.common.math.IntMath; import com.google.common.primitives.Ints; import java.math.RoundingMode; -import java.util.Collections; import java.util.Comparator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code TopKSelector}. @@ -33,29 +35,15 @@ * @author Louis Wasserman */ @GwtCompatible +@NullUnmarked public class TopKSelectorTest extends TestCase { public void testNegativeK() { - try { - TopKSelector.least(-1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - TopKSelector.greatest(-1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - TopKSelector.least(-1, Ordering.natural()); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - TopKSelector.greatest(-1, Ordering.natural()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> TopKSelector.least(-1)); + assertThrows(IllegalArgumentException.class, () -> TopKSelector.greatest(-1)); + assertThrows(IllegalArgumentException.class, () -> TopKSelector.least(-1, Ordering.natural())); + assertThrows( + IllegalArgumentException.class, () -> TopKSelector.greatest(-1, Ordering.natural())); } public void testZeroK() { @@ -118,7 +106,7 @@ public int compare(Integer o1, Integer o2) { for (int i = 1; i < n; i++) { top.offer(0); } - assertThat(top.topK()).containsExactlyElementsIn(Collections.nCopies(k, 0)); + assertThat(top.topK()).containsExactlyElementsIn(nCopies(k, 0)); assertThat(compareCalls[0]).isAtMost(10L * n * IntMath.log2(k, RoundingMode.CEILING)); } diff --git a/android/guava-tests/test/com/google/common/collect/TransposedTableTest.java b/android/guava-tests/test/com/google/common/collect/TransposedTableTest.java index 7233cf175374..49a4910161cc 100644 --- a/android/guava-tests/test/com/google/common/collect/TransposedTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/TransposedTableTest.java @@ -16,7 +16,11 @@ package com.google.common.collect; +import static com.google.common.collect.Tables.transpose; + import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link Tables#transpose}. @@ -24,12 +28,13 @@ * @author Jared Levy */ @GwtCompatible -public class TransposedTableTest extends AbstractTableTest { +@NullMarked +public class TransposedTableTest extends AbstractTableTest { @Override - protected Table create(Object... data) { + protected Table create(@Nullable Object... data) { Table original = HashBasedTable.create(); - Table table = Tables.transpose(original); + Table table = transpose(original); table.clear(); populate(table, data); return table; @@ -37,26 +42,26 @@ protected Table create(Object... data) { public void testTransposeTransposed() { Table original = HashBasedTable.create(); - assertSame(original, Tables.transpose(Tables.transpose(original))); + assertSame(original, transpose(transpose(original))); } public void testPutOriginalModifiesTranspose() { Table original = HashBasedTable.create(); - Table transpose = Tables.transpose(original); + Table transpose = transpose(original); original.put(1, "foo", 'a'); assertEquals((Character) 'a', transpose.get("foo", 1)); } public void testPutTransposeModifiesOriginal() { Table original = HashBasedTable.create(); - Table transpose = Tables.transpose(original); + Table transpose = transpose(original); transpose.put("foo", 1, 'a'); assertEquals((Character) 'a', original.get(1, "foo")); } public void testTransposedViews() { Table original = HashBasedTable.create(); - Table transpose = Tables.transpose(original); + Table transpose = transpose(original); original.put(1, "foo", 'a'); assertSame(original.columnKeySet(), transpose.rowKeySet()); assertSame(original.rowKeySet(), transpose.columnKeySet()); diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnMapTest.java new file mode 100644 index 000000000000..f27017d6cfa2 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnMapTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableColumnMapTest extends ColumnMapTests { + public TreeBasedTableColumnMapTest() { + super(false, true, true, false); + } + + @Override + Table makeTable() { + return TreeBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnTest.java new file mode 100644 index 000000000000..b4e6008e38cd --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableColumnTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableColumnTest extends ColumnTests { + public TreeBasedTableColumnTest() { + super(false, true, true, true, false); + } + + @Override + Table makeTable() { + return TreeBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapHeadMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapHeadMapTest.java new file mode 100644 index 000000000000..59ba288793de --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapHeadMapTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableRowMapHeadMapTest extends RowMapTests { + public TreeBasedTableRowMapHeadMapTest() { + super(false, true, true, true); + } + + @Override + TreeBasedTable makeTable() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("z", 1, 'a'); + return table; + } + + @Override + protected Map> makePopulatedMap() { + TreeBasedTable table = makeTable(); + populateTable(table); + return table.rowMap().headMap("x"); + } + + @Override + protected Map> makeEmptyMap() { + return makeTable().rowMap().headMap("x"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "z"; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapInterfaceTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapInterfaceTest.java new file mode 100644 index 000000000000..70ed7faca268 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapInterfaceTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.testing.SortedMapInterfaceTest; +import java.util.SortedMap; +import org.jspecify.annotations.NullUnmarked; + +@GwtCompatible +@NullUnmarked +public class TreeBasedTableRowMapInterfaceTest extends SortedMapInterfaceTest { + public TreeBasedTableRowMapInterfaceTest() { + super(false, false, true, true, true); + } + + @Override + protected SortedMap makeEmptyMap() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("a", "b", "c"); + table.put("c", "b", "a"); + table.put("a", "a", "d"); + return table.row("b"); + } + + @Override + protected SortedMap makePopulatedMap() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("a", "b", "c"); + table.put("c", "b", "a"); + table.put("b", "b", "x"); + table.put("b", "c", "y"); + table.put("b", "x", "n"); + table.put("a", "a", "d"); + return table.row("b"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "q"; + } + + @Override + protected String getValueNotInPopulatedMap() { + return "p"; + } + + public void testClearSubMapOfRowMap() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("a", "b", "c"); + table.put("c", "b", "a"); + table.put("b", "b", "x"); + table.put("b", "c", "y"); + table.put("b", "x", "n"); + table.put("a", "a", "d"); + table.row("b").subMap("c", "x").clear(); + assertEquals(table.row("b"), ImmutableMap.of("b", "x", "x", "n")); + table.row("b").subMap("b", "y").clear(); + assertEquals(table.row("b"), ImmutableMap.of()); + assertFalse(table.backingMap.containsKey("b")); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapSubMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapSubMapTest.java new file mode 100644 index 000000000000..d9c5ba1e457a --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapSubMapTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableRowMapSubMapTest extends RowMapTests { + public TreeBasedTableRowMapSubMapTest() { + super(false, true, true, true); + } + + @Override + TreeBasedTable makeTable() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("a", 1, 'a'); + table.put("z", 1, 'a'); + return table; + } + + @Override + protected Map> makePopulatedMap() { + TreeBasedTable table = makeTable(); + populateTable(table); + return table.rowMap().subMap("b", "x"); + } + + @Override + protected Map> makeEmptyMap() { + return makeTable().rowMap().subMap("b", "x"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "z"; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTailMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTailMapTest.java new file mode 100644 index 000000000000..cf25c9d5972d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTailMapTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableRowMapTailMapTest extends RowMapTests { + public TreeBasedTableRowMapTailMapTest() { + super(false, true, true, true); + } + + @Override + TreeBasedTable makeTable() { + TreeBasedTable table = TreeBasedTable.create(); + table.put("a", 1, 'a'); + return table; + } + + @Override + protected Map> makePopulatedMap() { + TreeBasedTable table = makeTable(); + populateTable(table); + return table.rowMap().tailMap("b"); + } + + @Override + protected Map> makeEmptyMap() { + return makeTable().rowMap().tailMap("b"); + } + + @Override + protected String getKeyNotInPopulatedMap() { + return "a"; + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTest.java new file mode 100644 index 000000000000..e9c891fa24ef --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowMapTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableRowMapTest extends RowMapTests { + public TreeBasedTableRowMapTest() { + super(false, true, true, true); + } + + @Override + Table makeTable() { + return TreeBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowTest.java new file mode 100644 index 000000000000..990732ee3c67 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableRowTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class TreeBasedTableRowTest extends RowTests { + public TreeBasedTableRowTest() { + super(false, true, true, true, true); + } + + @Override + Table makeTable() { + return TreeBasedTable.create(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/TreeBasedTableTest.java b/android/guava-tests/test/com/google/common/collect/TreeBasedTableTest.java index 084e6492fb72..8aacaf350694 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeBasedTableTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeBasedTableTest.java @@ -16,11 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static java.util.Collections.singleton; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.testing.SortedMapInterfaceTest; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.SortedMapTestSuiteBuilder; import com.google.common.collect.testing.TestStringSortedMapGenerator; import com.google.common.collect.testing.features.CollectionFeature; @@ -35,6 +37,8 @@ import java.util.SortedMap; import junit.framework.Test; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Test cases for {@link TreeBasedTable}. @@ -43,12 +47,14 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -public class TreeBasedTableTest extends AbstractTableTest { +@NullMarked +public class TreeBasedTableTest extends AbstractTableTest { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(TreeBasedTableTest.class); - suite.addTestSuite(TreeRowTest.class); suite.addTest( SortedMapTestSuiteBuilder.using( new TestStringSortedMapGenerator() { @@ -73,58 +79,6 @@ protected SortedMap create(Entry[] entries) { return suite; } - public static class TreeRowTest extends SortedMapInterfaceTest { - public TreeRowTest() { - super(false, false, true, true, true); - } - - @Override - protected SortedMap makeEmptyMap() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("a", "b", "c"); - table.put("c", "b", "a"); - table.put("a", "a", "d"); - return table.row("b"); - } - - @Override - protected SortedMap makePopulatedMap() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("a", "b", "c"); - table.put("c", "b", "a"); - table.put("b", "b", "x"); - table.put("b", "c", "y"); - table.put("b", "x", "n"); - table.put("a", "a", "d"); - return table.row("b"); - } - - @Override - protected String getKeyNotInPopulatedMap() { - return "q"; - } - - @Override - protected String getValueNotInPopulatedMap() { - return "p"; - } - - public void testClearSubMapOfRowMap() { - TreeBasedTable table = TreeBasedTable.create(); - table.put("a", "b", "c"); - table.put("c", "b", "a"); - table.put("b", "b", "x"); - table.put("b", "c", "y"); - table.put("b", "x", "n"); - table.put("a", "a", "d"); - table.row("b").subMap("c", "x").clear(); - assertEquals(table.row("b"), ImmutableMap.of("b", "x", "x", "n")); - table.row("b").subMap("b", "y").clear(); - assertEquals(table.row("b"), ImmutableMap.of()); - assertFalse(table.backingMap.containsKey("b")); - } - } - private TreeBasedTable sortedTable; protected TreeBasedTable create( @@ -141,7 +95,7 @@ protected TreeBasedTable create( } @Override - protected TreeBasedTable create(Object... data) { + protected TreeBasedTable create(@Nullable Object... data) { TreeBasedTable table = TreeBasedTable.create(); table.put("foo", 4, 'a'); table.put("cat", 1, 'b'); @@ -173,6 +127,7 @@ public void testCreateCopy() { assertEquals(original, table); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { table = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); @@ -200,6 +155,7 @@ public void testValuesToString_ordered() { assertEquals("[b, a, c]", table.values().toString()); } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testRowComparator() { sortedTable = TreeBasedTable.create(); assertSame(Ordering.natural(), sortedTable.rowComparator()); @@ -247,25 +203,25 @@ public void testRowKeySetLast() { public void testRowKeySetHeadSet() { sortedTable = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Set set = sortedTable.rowKeySet().headSet("cat"); - assertEquals(Collections.singleton("bar"), set); + assertEquals(singleton("bar"), set); set.clear(); assertTrue(set.isEmpty()); - assertEquals(Collections.singleton("foo"), sortedTable.rowKeySet()); + assertEquals(singleton("foo"), sortedTable.rowKeySet()); } public void testRowKeySetTailSet() { sortedTable = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c'); Set set = sortedTable.rowKeySet().tailSet("cat"); - assertEquals(Collections.singleton("foo"), set); + assertEquals(singleton("foo"), set); set.clear(); assertTrue(set.isEmpty()); - assertEquals(Collections.singleton("bar"), sortedTable.rowKeySet()); + assertEquals(singleton("bar"), sortedTable.rowKeySet()); } public void testRowKeySetSubSet() { sortedTable = create("foo", 1, 'a', "bar", 1, 'b', "foo", 3, 'c', "dog", 2, 'd'); Set set = sortedTable.rowKeySet().subSet("cat", "egg"); - assertEquals(Collections.singleton("dog"), set); + assertEquals(singleton("dog"), set); set.clear(); assertTrue(set.isEmpty()); assertEquals(ImmutableSet.of("bar", "foo"), sortedTable.rowKeySet()); @@ -296,7 +252,7 @@ public void testRowKeyMapHeadMap() { assertEquals(ImmutableMap.of(1, 'b'), map.get("bar")); map.clear(); assertTrue(map.isEmpty()); - assertEquals(Collections.singleton("foo"), sortedTable.rowKeySet()); + assertEquals(singleton("foo"), sortedTable.rowKeySet()); } public void testRowKeyMapTailMap() { @@ -306,7 +262,7 @@ public void testRowKeyMapTailMap() { assertEquals(ImmutableMap.of(1, 'a', 3, 'c'), map.get("foo")); map.clear(); assertTrue(map.isEmpty()); - assertEquals(Collections.singleton("bar"), sortedTable.rowKeySet()); + assertEquals(singleton("bar"), sortedTable.rowKeySet()); } public void testRowKeyMapSubMap() { @@ -335,7 +291,7 @@ public void testColumnKeySet_isSortedWithRealComparator() { table = create( String.CASE_INSENSITIVE_ORDER, - Ordering.natural().reverse(), + Ordering.natural().reverse(), "a", 2, 'X', @@ -400,13 +356,13 @@ public void testRowEntrySetContains() { 20, 'X', "d", 15, 'X', "d", 20, 'X', "d", 1, 'X', "e", 5, 'X'); SortedMap row = sortedTable.row("c"); Set> entrySet = row.entrySet(); - assertTrue(entrySet.contains(Maps.immutableEntry(10, 'X'))); - assertTrue(entrySet.contains(Maps.immutableEntry(20, 'X'))); - assertFalse(entrySet.contains(Maps.immutableEntry(15, 'X'))); + assertTrue(entrySet.contains(immutableEntry(10, 'X'))); + assertTrue(entrySet.contains(immutableEntry(20, 'X'))); + assertFalse(entrySet.contains(immutableEntry(15, 'X'))); entrySet = row.tailMap(15).entrySet(); - assertFalse(entrySet.contains(Maps.immutableEntry(10, 'X'))); - assertTrue(entrySet.contains(Maps.immutableEntry(20, 'X'))); - assertFalse(entrySet.contains(Maps.immutableEntry(15, 'X'))); + assertFalse(entrySet.contains(immutableEntry(10, 'X'))); + assertTrue(entrySet.contains(immutableEntry(20, 'X'))); + assertFalse(entrySet.contains(immutableEntry(15, 'X'))); } public void testRowEntrySetRemove() { @@ -417,13 +373,13 @@ public void testRowEntrySetRemove() { 20, 'X', "d", 15, 'X', "d", 20, 'X', "d", 1, 'X', "e", 5, 'X'); SortedMap row = sortedTable.row("c"); Set> entrySet = row.tailMap(15).entrySet(); - assertFalse(entrySet.remove(Maps.immutableEntry(10, 'X'))); - assertTrue(entrySet.remove(Maps.immutableEntry(20, 'X'))); - assertFalse(entrySet.remove(Maps.immutableEntry(15, 'X'))); + assertFalse(entrySet.remove(immutableEntry(10, 'X'))); + assertTrue(entrySet.remove(immutableEntry(20, 'X'))); + assertFalse(entrySet.remove(immutableEntry(15, 'X'))); entrySet = row.entrySet(); - assertTrue(entrySet.remove(Maps.immutableEntry(10, 'X'))); - assertFalse(entrySet.remove(Maps.immutableEntry(20, 'X'))); - assertFalse(entrySet.remove(Maps.immutableEntry(15, 'X'))); + assertTrue(entrySet.remove(immutableEntry(10, 'X'))); + assertFalse(entrySet.remove(immutableEntry(20, 'X'))); + assertFalse(entrySet.remove(immutableEntry(15, 'X'))); } public void testRowSize() { diff --git a/android/guava-tests/test/com/google/common/collect/TreeMultimapExplicitTest.java b/android/guava-tests/test/com/google/common/collect/TreeMultimapExplicitTest.java index 92a3d83a7d8c..7622c411f9f3 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeMultimapExplicitTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeMultimapExplicitTest.java @@ -16,18 +16,22 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Sets.newHashSet; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.SerializableTester; -import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Map.Entry; import java.util.SortedSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@code TreeMultimap} with explicit comparators. @@ -35,17 +39,18 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class TreeMultimapExplicitTest extends TestCase { /** * Compare strings lengths, and if the lengths are equal compare the strings. A {@code null} is * less than any non-null value. */ - private enum StringLength implements Comparator { + private enum StringLength implements Comparator<@Nullable String> { COMPARATOR; @Override - public int compare(String first, String second) { + public int compare(@Nullable String first, @Nullable String second) { if (first == second) { return 0; } else if (first == null) { @@ -61,16 +66,16 @@ public int compare(String first, String second) { } /** Decreasing integer values. A {@code null} comes before any non-null value. */ - private static final Comparator DECREASING_INT_COMPARATOR = - Ordering.natural().reverse().nullsFirst(); + private static final Comparator<@Nullable Integer> DECREASING_INT_COMPARATOR = + Ordering.natural().reverse().nullsFirst(); private SetMultimap create() { return TreeMultimap.create(StringLength.COMPARATOR, DECREASING_INT_COMPARATOR); } /** Create and populate a {@code TreeMultimap} with explicit comparators. */ - private TreeMultimap createPopulate() { - TreeMultimap multimap = + private TreeMultimap<@Nullable String, @Nullable Integer> createPopulate() { + TreeMultimap<@Nullable String, @Nullable Integer> multimap = TreeMultimap.create(StringLength.COMPARATOR, DECREASING_INT_COMPARATOR); multimap.put("google", 2); multimap.put("google", 6); @@ -106,32 +111,32 @@ public void testToString() { Multimap multimap = create(); multimap.put("foo", 3); multimap.put("bar", 1); - multimap.putAll("foo", Arrays.asList(-1, 2, 4)); - multimap.putAll("bar", Arrays.asList(2, 3)); + multimap.putAll("foo", asList(-1, 2, 4)); + multimap.putAll("bar", asList(2, 3)); multimap.put("foo", 1); assertEquals("{bar=[3, 2, 1], foo=[4, 3, 2, 1, -1]}", multimap.toString()); } public void testGetComparator() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertEquals(StringLength.COMPARATOR, multimap.keyComparator()); assertEquals(DECREASING_INT_COMPARATOR, multimap.valueComparator()); } public void testOrderedGet() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertThat(multimap.get(null)).containsExactly(7, 3, 1).inOrder(); assertThat(multimap.get("google")).containsExactly(6, 2).inOrder(); assertThat(multimap.get("tree")).containsExactly(null, 0).inOrder(); } public void testOrderedKeySet() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertThat(multimap.keySet()).containsExactly(null, "tree", "google").inOrder(); } public void testOrderedAsMapEntries() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); Iterator>> iterator = multimap.asMap().entrySet().iterator(); Entry> entry = iterator.next(); assertEquals(null, entry.getKey()); @@ -145,26 +150,26 @@ public void testOrderedAsMapEntries() { } public void testOrderedEntries() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertThat(multimap.entries()) .containsExactly( - Maps.immutableEntry((String) null, 7), - Maps.immutableEntry((String) null, 3), - Maps.immutableEntry((String) null, 1), - Maps.immutableEntry("tree", (Integer) null), - Maps.immutableEntry("tree", 0), - Maps.immutableEntry("google", 6), - Maps.immutableEntry("google", 2)) + Maps.<@Nullable String, Integer>immutableEntry(null, 7), + Maps.<@Nullable String, Integer>immutableEntry(null, 3), + Maps.<@Nullable String, Integer>immutableEntry(null, 1), + Maps.immutableEntry("tree", null), + immutableEntry("tree", 0), + immutableEntry("google", 6), + immutableEntry("google", 2)) .inOrder(); } public void testOrderedValues() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertThat(multimap.values()).containsExactly(7, 3, 1, null, 0, 6, 2).inOrder(); } public void testComparator() { - TreeMultimap multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); assertEquals(DECREASING_INT_COMPARATOR, multimap.get("foo").comparator()); assertEquals(DECREASING_INT_COMPARATOR, multimap.get("missing").comparator()); } @@ -173,8 +178,8 @@ public void testMultimapComparators() { Multimap multimap = create(); multimap.put("foo", 3); multimap.put("bar", 1); - multimap.putAll("foo", Arrays.asList(-1, 2, 4)); - multimap.putAll("bar", Arrays.asList(2, 3)); + multimap.putAll("foo", asList(-1, 2, 4)); + multimap.putAll("bar", asList(2, 3)); multimap.put("foo", 1); TreeMultimap copy = TreeMultimap.create(StringLength.COMPARATOR, DECREASING_INT_COMPARATOR); @@ -185,21 +190,22 @@ public void testMultimapComparators() { } public void testSortedKeySet() { - TreeMultimap multimap = createPopulate(); - SortedSet keySet = multimap.keySet(); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); + SortedSet<@Nullable String> keySet = multimap.keySet(); assertEquals(null, keySet.first()); assertEquals("google", keySet.last()); assertEquals(StringLength.COMPARATOR, keySet.comparator()); - assertEquals(Sets.newHashSet(null, "tree"), keySet.headSet("yahoo")); - assertEquals(Sets.newHashSet("google"), keySet.tailSet("yahoo")); - assertEquals(Sets.newHashSet("tree"), keySet.subSet("ask", "yahoo")); + assertEquals(Sets.<@Nullable String>newHashSet(null, "tree"), keySet.headSet("yahoo")); + assertEquals(newHashSet("google"), keySet.tailSet("yahoo")); + assertEquals(newHashSet("tree"), keySet.subSet("ask", "yahoo")); } @GwtIncompatible // SerializableTester public void testExplicitComparatorSerialization() { - TreeMultimap multimap = createPopulate(); - TreeMultimap copy = SerializableTester.reserializeAndAssert(multimap); + TreeMultimap<@Nullable String, @Nullable Integer> multimap = createPopulate(); + TreeMultimap<@Nullable String, @Nullable Integer> copy = + SerializableTester.reserializeAndAssert(multimap); assertThat(copy.values()).containsExactly(7, 3, 1, null, 0, 6, 2).inOrder(); assertThat(copy.keySet()).containsExactly(null, "tree", "google").inOrder(); assertEquals(multimap.keyComparator(), copy.keyComparator()); diff --git a/android/guava-tests/test/com/google/common/collect/TreeMultimapNaturalTest.java b/android/guava-tests/test/com/google/common/collect/TreeMultimapNaturalTest.java index addcfb74f856..346ba9811e6b 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeMultimapNaturalTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeMultimapNaturalTest.java @@ -17,12 +17,15 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.testing.Helpers.mapEntry; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.DerivedComparable; -import com.google.common.collect.testing.Helpers; import com.google.common.collect.testing.NavigableMapTestSuiteBuilder; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -36,7 +39,6 @@ import com.google.common.collect.testing.google.TestStringSetMultimapGenerator; import com.google.common.testing.SerializableTester; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; @@ -50,6 +52,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; /** * Unit tests for {@code TreeMultimap} with natural ordering. @@ -57,9 +60,12 @@ * @author Jared Levy */ @GwtCompatible(emulated = true) +@NullMarked public class TreeMultimapNaturalTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); // TODO(lowasser): should we force TreeMultimap to be more thorough about checking nulls? @@ -141,27 +147,24 @@ public String[] createKeyArray(int length) { @SuppressWarnings("unchecked") @Override public Collection[] createValueArray(int length) { - return new Collection[length]; + return (Collection[]) new Collection[length]; } @Override public SampleElements>> samples() { return new SampleElements<>( - Helpers.mapEntry("a", (Collection) ImmutableSortedSet.of("alex")), - Helpers.mapEntry( - "b", (Collection) ImmutableSortedSet.of("bob", "bagel")), - Helpers.mapEntry( - "c", (Collection) ImmutableSortedSet.of("carl", "carol")), - Helpers.mapEntry( - "d", (Collection) ImmutableSortedSet.of("david", "dead")), - Helpers.mapEntry( + mapEntry("a", (Collection) ImmutableSortedSet.of("alex")), + mapEntry("b", (Collection) ImmutableSortedSet.of("bob", "bagel")), + mapEntry("c", (Collection) ImmutableSortedSet.of("carl", "carol")), + mapEntry("d", (Collection) ImmutableSortedSet.of("david", "dead")), + mapEntry( "e", (Collection) ImmutableSortedSet.of("eric", "elaine"))); } @SuppressWarnings("unchecked") @Override public Entry>[] createArray(int length) { - return new Entry[length]; + return (Entry>[]) new Entry[length]; } @Override @@ -190,26 +193,22 @@ public NavigableMap> create(Object... elements) { @Override public Entry> belowSamplesLesser() { - return Helpers.mapEntry( - "-- a", (Collection) ImmutableSortedSet.of("--below")); + return mapEntry("-- a", (Collection) ImmutableSortedSet.of("--below")); } @Override public Entry> belowSamplesGreater() { - return Helpers.mapEntry( - "-- b", (Collection) ImmutableSortedSet.of("--below")); + return mapEntry("-- b", (Collection) ImmutableSortedSet.of("--below")); } @Override public Entry> aboveSamplesLesser() { - return Helpers.mapEntry( - "~~ b", (Collection) ImmutableSortedSet.of("~above")); + return mapEntry("~~ b", (Collection) ImmutableSortedSet.of("~above")); } @Override public Entry> aboveSamplesGreater() { - return Helpers.mapEntry( - "~~ c", (Collection) ImmutableSortedSet.of("~above")); + return mapEntry("~~ c", (Collection) ImmutableSortedSet.of("~above")); } }) .named("TreeMultimap.asMap") @@ -227,7 +226,7 @@ public Entry> aboveSamplesGreater() { protected Set create(String[] elements) { TreeMultimap multimap = TreeMultimap.create(Ordering.natural(), Ordering.natural().nullsFirst()); - multimap.putAll(1, Arrays.asList(elements)); + multimap.putAll(1, asList(elements)); return multimap.get(1); } @@ -250,7 +249,7 @@ public List order(List insertionOrder) { protected Set create(String[] elements) { TreeMultimap multimap = TreeMultimap.create(Ordering.natural(), Ordering.natural().nullsFirst()); - multimap.putAll(1, Arrays.asList(elements)); + multimap.putAll(1, asList(elements)); return (Set) multimap.asMap().entrySet().iterator().next().getValue(); } @@ -290,8 +289,8 @@ private TreeMultimap createPopulate() { public void testToString() { SetMultimap multimap = create(); - multimap.putAll("bar", Arrays.asList(3, 1, 2)); - multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4)); + multimap.putAll("bar", asList(3, 1, 2)); + multimap.putAll("foo", asList(2, 3, 1, -1, 4)); assertEquals("{bar=[1, 2, 3], foo=[-1, 1, 2, 3, 4]}", multimap.toString()); } @@ -325,13 +324,13 @@ public void testOrderedEntries() { TreeMultimap multimap = createPopulate(); assertThat(multimap.entries()) .containsExactly( - Maps.immutableEntry("foo", 1), - Maps.immutableEntry("foo", 3), - Maps.immutableEntry("foo", 7), - Maps.immutableEntry("google", 2), - Maps.immutableEntry("google", 6), - Maps.immutableEntry("tree", 0), - Maps.immutableEntry("tree", 4)) + immutableEntry("foo", 1), + immutableEntry("foo", 3), + immutableEntry("foo", 7), + immutableEntry("google", 2), + immutableEntry("google", 6), + immutableEntry("tree", 0), + immutableEntry("tree", 4)) .inOrder(); } @@ -342,8 +341,8 @@ public void testOrderedValues() { public void testMultimapConstructor() { SetMultimap multimap = create(); - multimap.putAll("bar", Arrays.asList(3, 1, 2)); - multimap.putAll("foo", Arrays.asList(2, 3, 1, -1, 4)); + multimap.putAll("bar", asList(3, 1, 2)); + multimap.putAll("foo", asList(2, 3, 1, -1, 4)); TreeMultimap copy = TreeMultimap.create(multimap); assertEquals(multimap, copy); } @@ -351,7 +350,7 @@ public void testMultimapConstructor() { private static final Comparator KEY_COMPARATOR = Ordering.natural(); private static final Comparator VALUE_COMPARATOR = - Ordering.natural().reverse().nullsFirst(); + Ordering.natural().reverse().nullsFirst(); /** * Test that creating one TreeMultimap from another does not copy the comparators from the source @@ -407,6 +406,7 @@ public void testComparators() { assertEquals(Ordering.natural(), multimap.valueComparator()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testExplicitComparatorSerialization() { TreeMultimap multimap = createPopulate(); @@ -417,6 +417,7 @@ public void testExplicitComparatorSerialization() { assertEquals(multimap.valueComparator(), copy.valueComparator()); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testTreeMultimapDerived() { TreeMultimap multimap = TreeMultimap.create(); @@ -443,6 +444,7 @@ public void testTreeMultimapDerived() { SerializableTester.reserializeAndAssert(multimap); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testTreeMultimapNonGeneric() { TreeMultimap multimap = TreeMultimap.create(); @@ -500,6 +502,7 @@ public void testTailSetClear() { assertEquals(4, multimap.keys().size()); } + @J2ktIncompatible @GwtIncompatible // reflection public void testKeySetBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { @@ -510,6 +513,7 @@ public void testKeySetBridgeMethods() { fail("No bridge method found"); } + @J2ktIncompatible @GwtIncompatible // reflection public void testAsMapBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { @@ -519,6 +523,7 @@ public void testAsMapBridgeMethods() { } } + @J2ktIncompatible @GwtIncompatible // reflection public void testGetBridgeMethods() { for (Method m : TreeMultimap.class.getMethods()) { diff --git a/android/guava-tests/test/com/google/common/collect/TreeMultisetTest.java b/android/guava-tests/test/com/google/common/collect/TreeMultisetTest.java index 12da1f1de0e1..9a7ddab9bec4 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeMultisetTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeMultisetTest.java @@ -18,10 +18,12 @@ import static com.google.common.collect.BoundType.CLOSED; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers.NullsBeforeB; import com.google.common.collect.testing.NavigableSetTestSuiteBuilder; import com.google.common.collect.testing.TestStringSetGenerator; @@ -31,7 +33,6 @@ import com.google.common.collect.testing.google.SortedMultisetTestSuiteBuilder; import com.google.common.collect.testing.google.TestStringMultisetGenerator; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -40,6 +41,8 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link TreeMultiset}. @@ -47,9 +50,12 @@ * @author Neal Kanodia */ @GwtCompatible(emulated = true) +@NullMarked public class TreeMultisetTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -57,7 +63,7 @@ public static Test suite() { new TestStringMultisetGenerator() { @Override protected Multiset create(String[] elements) { - return TreeMultiset.create(Arrays.asList(elements)); + return TreeMultiset.create(asList(elements)); } @Override @@ -104,7 +110,7 @@ public List order(List insertionOrder) { new TestStringSetGenerator() { @Override protected Set create(String[] elements) { - return TreeMultiset.create(Arrays.asList(elements)).elementSet(); + return TreeMultiset.create(asList(elements)).elementSet(); } @Override @@ -142,7 +148,7 @@ public void testCreateWithComparator() { } public void testCreateFromIterable() { - Multiset multiset = TreeMultiset.create(Arrays.asList("foo", "bar", "foo")); + Multiset multiset = TreeMultiset.create(asList("foo", "bar", "foo")); assertEquals(3, multiset.size()); assertEquals(2, multiset.count("foo")); assertEquals("[bar, foo x 2]", multiset.toString()); @@ -212,7 +218,7 @@ public void testElementSetSubsetRemoveAll() { SortedSet subset = elementSet.subSet("b", "f"); assertThat(subset).containsExactly("b", "c", "d", "e").inOrder(); - assertTrue(subset.removeAll(Arrays.asList("a", "c"))); + assertTrue(subset.removeAll(asList("a", "c"))); assertThat(elementSet).containsExactly("a", "b", "d", "e", "f").inOrder(); assertThat(subset).containsExactly("b", "d", "e").inOrder(); assertEquals(10, ms.size()); @@ -232,7 +238,7 @@ public void testElementSetSubsetRetainAll() { SortedSet subset = elementSet.subSet("b", "f"); assertThat(subset).containsExactly("b", "c", "d", "e").inOrder(); - assertTrue(subset.retainAll(Arrays.asList("a", "c"))); + assertTrue(subset.retainAll(asList("a", "c"))); assertThat(elementSet).containsExactly("a", "c", "f").inOrder(); assertThat(subset).containsExactly("c"); assertEquals(5, ms.size()); @@ -283,8 +289,8 @@ public int compare(String o1, String o2) { } public void testNullAcceptingComparator() throws Exception { - Comparator comparator = Ordering.natural().nullsFirst(); - TreeMultiset ms = TreeMultiset.create(comparator); + Comparator<@Nullable String> comparator = Ordering.natural().nullsFirst(); + TreeMultiset<@Nullable String> ms = TreeMultiset.create(comparator); ms.add("b"); ms.add(null); @@ -295,7 +301,7 @@ public void testNullAcceptingComparator() throws Exception { assertThat(ms).containsExactly(null, null, null, "a", "b", "b").inOrder(); assertEquals(3, ms.count(null)); - SortedSet elementSet = ms.elementSet(); + SortedSet<@Nullable String> elementSet = ms.elementSet(); assertEquals(null, elementSet.first()); assertEquals("b", elementSet.last()); assertEquals(comparator, elementSet.comparator()); @@ -355,6 +361,7 @@ public void testSubMultisetSize() { assertEquals(Integer.MAX_VALUE, ms.tailMultiset("a", CLOSED).size()); } + @J2ktIncompatible @GwtIncompatible // reflection @AndroidIncompatible // Reflection bug, or actual binary compatibility problem? public void testElementSetBridgeMethods() { diff --git a/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java b/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java index db68b89ec30d..ab5437caa8c5 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeRangeMapTest.java @@ -16,6 +16,7 @@ import static com.google.common.collect.BoundType.OPEN; import static com.google.common.collect.testing.Helpers.mapEntry; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.testing.MapTestSuiteBuilder; @@ -24,6 +25,7 @@ import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.MapFeature; +import com.google.common.testing.EqualsTester; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -31,6 +33,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code TreeRangeMap}. @@ -38,7 +41,9 @@ * @author Louis Wasserman */ @GwtIncompatible // NavigableMap +@NullUnmarked public class TreeRangeMapTest extends TestCase { + @AndroidIncompatible // test-suite builders public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(TreeRangeMapTest.class); @@ -69,7 +74,7 @@ public Map, String> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry, String>[] createArray(int length) { - return new Entry[length]; + return (Entry, String>[]) new Entry[length]; } @Override @@ -81,7 +86,7 @@ public Iterable, String>> order( @SuppressWarnings("unchecked") @Override public Range[] createKeyArray(int length) { - return new Range[length]; + return (Range[]) new Range[length]; } @Override @@ -125,7 +130,7 @@ public Map, String> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry, String>[] createArray(int length) { - return new Entry[length]; + return (Entry, String>[]) new Entry[length]; } @Override @@ -137,7 +142,7 @@ public Iterable, String>> order( @SuppressWarnings("unchecked") @Override public Range[] createKeyArray(int length) { - return new Range[length]; + return (Range[]) new Range[length]; } @Override @@ -180,7 +185,7 @@ public Map, String> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry, String>[] createArray(int length) { - return new Entry[length]; + return (Entry, String>[]) new Entry[length]; } @Override @@ -195,7 +200,7 @@ public Iterable, String>> order( @SuppressWarnings("unchecked") @Override public Range[] createKeyArray(int length) { - return new Range[length]; + return (Range[]) new Range[length]; } @Override @@ -239,7 +244,7 @@ public Map, String> create(Object... elements) { @SuppressWarnings("unchecked") @Override public Entry, String>[] createArray(int length) { - return new Entry[length]; + return (Entry, String>[]) new Entry[length]; } @Override @@ -254,7 +259,7 @@ public Iterable, String>> order( @SuppressWarnings("unchecked") @Override public Range[] createKeyArray(int length) { - return new Range[length]; + return (Range[]) new Range[length]; } @Override @@ -617,14 +622,10 @@ public void testSubRangeMapPut() { 3), rangeMap.asMapOfRanges()); - try { - sub.put(Range.open(9, 12), 5); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> sub.put(Range.open(9, 12), 5)); - sub = sub.subRangeMap(Range.closedOpen(5, 5)); - sub.put(Range.closedOpen(5, 5), 6); // should be a no-op + RangeMap subSub = sub.subRangeMap(Range.closedOpen(5, 5)); + subSub.put(Range.closedOpen(5, 5), 6); // should be a no-op assertEquals( ImmutableMap.of( Range.open(3, 7), @@ -668,11 +669,7 @@ public void testSubRangeMapPutCoalescing() { 3), rangeMap.asMapOfRanges()); - try { - sub.putCoalescing(Range.open(9, 12), 5); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> sub.putCoalescing(Range.open(9, 12), 5)); } public void testSubRangeMapRemove() { @@ -709,6 +706,53 @@ public void testSubRangeMapClear() { ImmutableMap.of(Range.open(3, 5), 1, Range.closed(12, 16), 3), rangeMap.asMapOfRanges()); } + public void testCopyOfTreeRangeMap() { + RangeMap rangeMap = TreeRangeMap.create(); + rangeMap.put(Range.open(3, 7), 1); + rangeMap.put(Range.closed(9, 10), 2); + rangeMap.put(Range.closed(12, 16), 3); + + RangeMap copy = TreeRangeMap.copyOf(rangeMap); + + assertEquals(rangeMap.asMapOfRanges(), copy.asMapOfRanges()); + } + + public void testCopyOfImmutableRangeMap() { + ImmutableRangeMap rangeMap = + ImmutableRangeMap.builder() + .put(Range.open(3, 7), 1) + .put(Range.closed(9, 10), 2) + .put(Range.closed(12, 16), 3) + .build(); + + RangeMap copy = TreeRangeMap.copyOf(rangeMap); + + assertEquals(rangeMap.asMapOfRanges(), copy.asMapOfRanges()); + } + + // Overriding testEquals because it seems that we get spurious failures when it things empty + // should be unequal to empty. + public void testEquals() { + TreeRangeMap empty = TreeRangeMap.create(); + TreeRangeMap nonEmpty = TreeRangeMap.create(); + nonEmpty.put(Range.all(), 1); + TreeRangeMap coalesced = TreeRangeMap.create(); + coalesced.put(Range.atLeast(1), 1); + coalesced.putCoalescing(Range.atMost(1), 1); + TreeRangeMap differentValues = TreeRangeMap.create(); + differentValues.put(Range.closedOpen(1, 2), 2); + differentValues.put(Range.closedOpen(3, 4), 2); + TreeRangeMap differentTypes = TreeRangeMap.create(); + differentTypes.put(Range.closedOpen(1.0, 2.0), 2); + differentTypes.put(Range.closedOpen(3.0, 4.0), 2); + new EqualsTester() + .addEqualityGroup(empty, TreeRangeMap.create()) + .addEqualityGroup(nonEmpty, coalesced) + .addEqualityGroup(differentValues) + .addEqualityGroup(differentTypes) + .testEquals(); + } + private void verify(Map model, RangeMap test) { for (int i = MIN_BOUND - 1; i <= MAX_BOUND + 1; i++) { assertEquals(model.get(i), test.get(i)); diff --git a/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java b/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java index b33ab2ddea4b..892c988b7991 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeRangeSetTest.java @@ -17,12 +17,13 @@ import static com.google.common.collect.BoundType.OPEN; import static com.google.common.collect.Range.range; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.SerializableTester; -import java.util.Arrays; import java.util.List; import java.util.NavigableMap; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link TreeRangeSet}. @@ -31,6 +32,7 @@ * @author Chris Povirk */ @GwtIncompatible // TreeRangeSet +@NullUnmarked public class TreeRangeSetTest extends AbstractRangeSetTest { // TODO(cpovirk): test all of these with the ranges added in the reverse order @@ -667,14 +669,14 @@ public void testRangeContaining2() { public void testAddAll() { RangeSet rangeSet = TreeRangeSet.create(); rangeSet.add(Range.closed(3, 10)); - rangeSet.addAll(Arrays.asList(Range.open(1, 3), Range.closed(5, 8), Range.closed(9, 11))); + rangeSet.addAll(asList(Range.open(1, 3), Range.closed(5, 8), Range.closed(9, 11))); assertThat(rangeSet.asRanges()).containsExactly(Range.openClosed(1, 11)).inOrder(); } public void testRemoveAll() { RangeSet rangeSet = TreeRangeSet.create(); rangeSet.add(Range.closed(3, 10)); - rangeSet.removeAll(Arrays.asList(Range.open(1, 3), Range.closed(5, 8), Range.closed(9, 11))); + rangeSet.removeAll(asList(Range.open(1, 3), Range.closed(5, 8), Range.closed(9, 11))); assertThat(rangeSet.asRanges()) .containsExactly(Range.closedOpen(3, 5), Range.open(8, 9)) .inOrder(); diff --git a/android/guava-tests/test/com/google/common/collect/TreeTraverserTest.java b/android/guava-tests/test/com/google/common/collect/TreeTraverserTest.java index 4c2abc7683ea..3b1b45cf2146 100644 --- a/android/guava-tests/test/com/google/common/collect/TreeTraverserTest.java +++ b/android/guava-tests/test/com/google/common/collect/TreeTraverserTest.java @@ -15,14 +15,16 @@ package com.google.common.collect; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.testing.NullPointerTester; -import java.util.Arrays; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@code TreeTraverser}. @@ -30,6 +32,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullMarked public class TreeTraverserTest extends TestCase { private static class Node { final char value; @@ -44,7 +47,7 @@ private static final class Tree extends Node { public Tree(char value, Tree... children) { super(value); - this.children = Arrays.asList(children); + this.children = asList(children); } } @@ -105,6 +108,7 @@ public void testUsing() { assertThat(iterationOrder(ADAPTER_USING_USING.preOrderTraversal(h))).isEqualTo("hdabcegf"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableIteratorTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableIteratorTest.java index 5e474aa70f67..452e513a9430 100644 --- a/android/guava-tests/test/com/google/common/collect/UnmodifiableIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableIteratorTest.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import java.util.Iterator; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link UnmodifiableIterator}. @@ -27,6 +30,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class UnmodifiableIteratorTest extends TestCase { @SuppressWarnings("DoNotCall") @@ -53,10 +57,6 @@ public String next() { assertTrue(iterator.hasNext()); assertEquals("a", iterator.next()); - try { - iterator.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); } } diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableListIteratorTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableListIteratorTest.java index 36a017d45cb3..e1d159ae73cc 100644 --- a/android/guava-tests/test/com/google/common/collect/UnmodifiableListIteratorTest.java +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableListIteratorTest.java @@ -16,11 +16,14 @@ package com.google.common.collect; +import static com.google.common.collect.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for UnmodifiableListIterator. @@ -28,6 +31,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullMarked public class UnmodifiableListIteratorTest extends TestCase { @SuppressWarnings("DoNotCall") public void testRemove() { @@ -35,11 +39,7 @@ public void testRemove() { assertTrue(iterator.hasNext()); assertEquals("a", iterator.next()); - try { - iterator.remove(); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.remove()); } @SuppressWarnings("DoNotCall") @@ -50,11 +50,7 @@ public void testAdd() { assertEquals("a", iterator.next()); assertEquals("b", iterator.next()); assertEquals("b", iterator.previous()); - try { - iterator.add("c"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.add("c")); } @SuppressWarnings("DoNotCall") @@ -65,11 +61,7 @@ public void testSet() { assertEquals("a", iterator.next()); assertEquals("b", iterator.next()); assertEquals("b", iterator.previous()); - try { - iterator.set("c"); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> iterator.set("c")); } UnmodifiableListIterator create() { diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableMultimapAsMapImplementsMapTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableMultimapAsMapImplementsMapTest.java index d03d7e351b65..6296f494b9ea 100644 --- a/android/guava-tests/test/com/google/common/collect/UnmodifiableMultimapAsMapImplementsMapTest.java +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableMultimapAsMapImplementsMapTest.java @@ -20,6 +20,7 @@ import com.google.common.collect.testing.MapInterfaceTest; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullMarked; /** * Test {@link Multimap#asMap()} for an unmodifiable multimap with {@link MapInterfaceTest}. @@ -27,6 +28,7 @@ * @author Jared Levy */ @GwtCompatible +@NullMarked public class UnmodifiableMultimapAsMapImplementsMapTest extends AbstractMultimapAsMapImplementsMapTest { diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnMapTest.java new file mode 100644 index 000000000000..dcd19b050459 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnMapTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableRowSortedTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableRowSortedTableColumnMapTest extends ColumnMapTests { + public UnmodifiableRowSortedTableColumnMapTest() { + super(false, false, false, false); + } + + @Override + Table makeTable() { + RowSortedTable original = TreeBasedTable.create(); + return unmodifiableRowSortedTable(original); + } + + @Override + protected Map> makePopulatedMap() { + RowSortedTable table = TreeBasedTable.create(); + table.put(1, "foo", 'a'); + table.put(1, "bar", 'b'); + table.put(3, "foo", 'c'); + return unmodifiableRowSortedTable(table).columnMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnTest.java new file mode 100644 index 000000000000..a18944d4141d --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableColumnTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableRowSortedTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableRowSortedTableColumnTest extends ColumnTests { + public UnmodifiableRowSortedTableColumnTest() { + super(false, false, false, false, false); + } + + @Override + Table makeTable() { + RowSortedTable table = TreeBasedTable.create(); + return unmodifiableRowSortedTable(table); + } + + @Override + protected Map makePopulatedMap() { + RowSortedTable table = TreeBasedTable.create(); + table.put("one", 'a', 1); + table.put("two", 'a', 2); + table.put("three", 'a', 3); + table.put("four", 'b', 4); + return unmodifiableRowSortedTable(table).column('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowMapTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowMapTest.java new file mode 100644 index 000000000000..5fa5c14c9884 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowMapTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableRowSortedTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import java.util.SortedMap; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableRowSortedTableRowMapTest extends RowMapTests { + public UnmodifiableRowSortedTableRowMapTest() { + super(false, false, false, false); + } + + @Override + RowSortedTable makeTable() { + RowSortedTable original = TreeBasedTable.create(); + return unmodifiableRowSortedTable(original); + } + + @Override + protected SortedMap> makePopulatedMap() { + RowSortedTable table = TreeBasedTable.create(); + table.put("foo", 1, 'a'); + table.put("bar", 1, 'b'); + table.put("foo", 3, 'c'); + return unmodifiableRowSortedTable(table).rowMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowTest.java new file mode 100644 index 000000000000..511eb2afb34b --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableRowSortedTableRowTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableRowSortedTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableRowSortedTableRowTest extends RowTests { + public UnmodifiableRowSortedTableRowTest() { + super(false, false, false, false, false); + } + + @Override + Table makeTable() { + RowSortedTable table = TreeBasedTable.create(); + return unmodifiableRowSortedTable(table); + } + + @Override + protected Map makePopulatedMap() { + RowSortedTable table = TreeBasedTable.create(); + table.put('a', "one", 1); + table.put('a', "two", 2); + table.put('a', "three", 3); + table.put('b', "four", 4); + return unmodifiableRowSortedTable(table).row('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnMapTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnMapTest.java new file mode 100644 index 000000000000..a75a0437ee25 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnMapTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableTableColumnMapTest extends ColumnMapTests { + public UnmodifiableTableColumnMapTest() { + super(false, false, false, false); + } + + @Override + Table makeTable() { + Table original = HashBasedTable.create(); + return unmodifiableTable(original); + } + + @Override + protected Map> makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put(1, "foo", 'a'); + table.put(1, "bar", 'b'); + table.put(3, "foo", 'c'); + return unmodifiableTable(table).columnMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnTest.java new file mode 100644 index 000000000000..70a8a7347fbc --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableColumnTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.ColumnTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableTableColumnTest extends ColumnTests { + public UnmodifiableTableColumnTest() { + super(false, false, false, false, false); + } + + @Override + Table makeTable() { + Table table = HashBasedTable.create(); + return unmodifiableTable(table); + } + + @Override + protected Map makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put("one", 'a', 1); + table.put("two", 'a', 2); + table.put("three", 'a', 3); + table.put("four", 'b', 4); + return unmodifiableTable(table).column('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowMapTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowMapTest.java new file mode 100644 index 000000000000..904e6a8482b6 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowMapTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowMapTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableTableRowMapTest extends RowMapTests { + public UnmodifiableTableRowMapTest() { + super(false, false, false, false); + } + + @Override + Table makeTable() { + Table original = HashBasedTable.create(); + return unmodifiableTable(original); + } + + @Override + protected Map> makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put("foo", 1, 'a'); + table.put("bar", 1, 'b'); + table.put("foo", 3, 'c'); + return unmodifiableTable(table).rowMap(); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowTest.java b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowTest.java new file mode 100644 index 000000000000..e01db0878541 --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/UnmodifiableTableRowTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Tables.unmodifiableTable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.TableCollectionTest.RowTests; +import java.util.Map; +import org.jspecify.annotations.NullMarked; + +@GwtCompatible +@NullMarked +public class UnmodifiableTableRowTest extends RowTests { + public UnmodifiableTableRowTest() { + super(false, false, false, false, false); + } + + @Override + Table makeTable() { + Table table = HashBasedTable.create(); + return unmodifiableTable(table); + } + + @Override + protected Map makePopulatedMap() { + Table table = HashBasedTable.create(); + table.put('a', "one", 1); + table.put('a', "two", 2); + table.put('a', "three", 3); + table.put('b', "four", 4); + return unmodifiableTable(table).row('a'); + } +} diff --git a/android/guava-tests/test/com/google/common/collect/WriteReplaceOverridesTest.java b/android/guava-tests/test/com/google/common/collect/WriteReplaceOverridesTest.java new file mode 100644 index 000000000000..61c40de4a87c --- /dev/null +++ b/android/guava-tests/test/com/google/common/collect/WriteReplaceOverridesTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.truth.Truth.assertWithMessage; +import static java.lang.reflect.Modifier.PRIVATE; +import static java.lang.reflect.Modifier.PROTECTED; +import static java.lang.reflect.Modifier.PUBLIC; +import static java.util.Arrays.asList; + +import com.google.common.base.Optional; +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.ClassPath.ClassInfo; +import com.google.common.reflect.TypeToken; +import java.lang.reflect.Method; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** + * Tests that all package-private {@code writeReplace} methods are overridden in any existing + * subclasses. Without such overrides, optimizers might put a {@code writeReplace}-containing class + * and its subclass in different packages, causing the serialization system to fail to invoke {@code + * writeReplace} when serializing an instance of the subclass. For an example of this problem, see + * b/310253115. + */ +@NullUnmarked +public class WriteReplaceOverridesTest extends TestCase { + private static final ImmutableSet GUAVA_PACKAGES = + FluentIterable.of( + "base", + "cache", + "collect", + "escape", + "eventbus", + "graph", + "hash", + "html", + "io", + "math", + "net", + "primitives", + "reflect", + "util.concurrent", + "xml") + .transform("com.google.common."::concat) + .toSet(); + + public void testClassesHaveOverrides() throws Exception { + for (ClassInfo info : ClassPath.from(getClass().getClassLoader()).getAllClasses()) { + if (!GUAVA_PACKAGES.contains(info.getPackageName())) { + continue; + } + if (info.getName().endsWith("GwtSerializationDependencies")) { + continue; // These classes exist only for the GWT compiler, not to be used. + } + if ( + /* + * At least one of the classes nested inside TypeResolverTest triggers a bug under older JDKs: + * https://bugs.openjdk.org/browse/JDK-8215328 -> https://bugs.openjdk.org/browse/JDK-8215470 + * https://github.com/google/guava/blob/4f12c5891a7adedbaa1d99fc9f77d8cc4e9da206/guava-tests/test/com/google/common/reflect/TypeResolverTest.java#L201 + */ + info.getName().contains("TypeResolverTest") + /* + * And at least one of the classes inside TypeTokenTest ends up with a null value in + * TypeMappingIntrospector.mappings. That happens only under older JDKs, too, so it may + * well be a JDK bug. + */ + || info.getName().contains("TypeTokenTest") + /* + * "IllegalAccess tried to access class + * com.google.common.collect.testing.AbstractIteratorTester from class + * com.google.common.collect.MultimapsTest" + * + * ...when we build with JDK 22 and run under JDK 8. + */ + || info.getName().contains("MultimapsTest") + /* + * Luckily, we don't care about analyzing tests at all. We'd skip them all if we could do so + * trivially, but it's enough to skip these ones. + */ + ) { + continue; + } + Class clazz = info.load(); + try { + Method unused = clazz.getDeclaredMethod("writeReplace"); + continue; // It overrides writeReplace, so it's safe. + } catch (NoSuchMethodException e) { + // This is a class whose supertypes we want to examine. We'll do that below. + } + Optional> supersWithPackagePrivateWriteReplace = + FluentIterable.from(TypeToken.of(clazz).getTypes()) + .transform(TypeToken::getRawType) + .transformAndConcat(c -> asList(c.getDeclaredMethods())) + .firstMatch( + m -> + m.getName().equals("writeReplace") + && m.getParameterTypes().length == 0 + // Only package-private methods are a problem. + && (m.getModifiers() & (PUBLIC | PROTECTED | PRIVATE)) == 0) + .transform(Method::getDeclaringClass); + if (!supersWithPackagePrivateWriteReplace.isPresent()) { + continue; + } + assertWithMessage( + "To help optimizers, any class that inherits a package-private writeReplace() method" + + " should override that method.\n" + + "(An override that delegates to the supermethod is fine.)\n" + + "%s has no such override despite inheriting writeReplace() from %s", + clazz.getName(), supersWithPackagePrivateWriteReplace.get().getName()) + .fail(); + } + } +} diff --git a/android/guava-tests/test/com/google/common/escape/ArrayBasedCharEscaperTest.java b/android/guava-tests/test/com/google/common/escape/ArrayBasedCharEscaperTest.java index f0983ef98cfb..9030ff5bf9d9 100644 --- a/android/guava-tests/test/com/google/common/escape/ArrayBasedCharEscaperTest.java +++ b/android/guava-tests/test/com/google/common/escape/ArrayBasedCharEscaperTest.java @@ -16,14 +16,20 @@ package com.google.common.escape; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import com.google.common.escape.testing.EscaperAsserts; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; -/** @author David Beaumont */ +/** + * @author David Beaumont + */ @GwtCompatible +@NullMarked public class ArrayBasedCharEscaperTest extends TestCase { private static final ImmutableMap NO_REPLACEMENTS = ImmutableMap.of(); private static final ImmutableMap SIMPLE_REPLACEMENTS = @@ -43,7 +49,7 @@ protected char[] escapeUnsafe(char c) { }; EscaperAsserts.assertBasic(wrappingEscaper); // '[' and '@' lie either side of [A-Z]. - assertEquals("{[}FOO{@}BAR{]}", wrappingEscaper.escape("[FOO@BAR]")); + assertThat(wrappingEscaper.escape("[FOO@BAR]")).isEqualTo("{[}FOO{@}BAR{]}"); } public void testSafeRange_maxLessThanMin() throws IOException { @@ -57,7 +63,7 @@ protected char[] escapeUnsafe(char c) { }; EscaperAsserts.assertBasic(wrappingEscaper); // escape everything. - assertEquals("{[}{F}{O}{O}{]}", wrappingEscaper.escape("[FOO]")); + assertThat(wrappingEscaper.escape("[FOO]")).isEqualTo("{[}{F}{O}{O}{]}"); } public void testDeleteUnsafeChars() throws IOException { @@ -71,11 +77,11 @@ protected char[] escapeUnsafe(char c) { } }; EscaperAsserts.assertBasic(deletingEscaper); - assertEquals( - "Everything outside the printable ASCII range is deleted.", - deletingEscaper.escape( - "\tEverything\0 outside the\uD800\uDC00 " - + "printable ASCII \uFFFFrange is \u007Fdeleted.\n")); + assertThat( + deletingEscaper.escape( + "\tEverything\0 outside the\uD800\uDC00 " + + "printable ASCII \uFFFFrange is \u007Fdeleted.\n")) + .isEqualTo("Everything outside the printable ASCII range is deleted."); } public void testReplacementPriority() throws IOException { @@ -92,7 +98,7 @@ protected char[] escapeUnsafe(char c) { // Replacements are applied first regardless of whether the character is in // the safe range or not ('&' is a safe char while '\t' and '\n' are not). - assertEquals( - "Fish ? Chips?", replacingEscaper.escape("\tFish &\0 Chips\r\n")); + assertThat(replacingEscaper.escape("\tFish &\0 Chips\r\n")) + .isEqualTo("Fish ? Chips?"); } } diff --git a/android/guava-tests/test/com/google/common/escape/ArrayBasedEscaperMapTest.java b/android/guava-tests/test/com/google/common/escape/ArrayBasedEscaperMapTest.java index 6d9c1d89de8a..c56b4cbefd62 100644 --- a/android/guava-tests/test/com/google/common/escape/ArrayBasedEscaperMapTest.java +++ b/android/guava-tests/test/com/google/common/escape/ArrayBasedEscaperMapTest.java @@ -16,28 +16,30 @@ package com.google.common.escape; +import static com.google.common.escape.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author David Beaumont */ +/** + * @author David Beaumont + */ @GwtCompatible +@NullUnmarked public class ArrayBasedEscaperMapTest extends TestCase { public void testNullMap() { - try { - ArrayBasedEscaperMap.create(null); - fail("expected exception did not occur"); - } catch (NullPointerException e) { - // pass - } + assertThrows(NullPointerException.class, () -> ArrayBasedEscaperMap.create(null)); } public void testEmptyMap() { Map map = ImmutableMap.of(); ArrayBasedEscaperMap fem = ArrayBasedEscaperMap.create(map); // Non-null array of zero length. - assertEquals(0, fem.getReplacementArray().length); + assertThat(fem.getReplacementArray()).isEmpty(); } public void testMapLength() { @@ -47,7 +49,7 @@ public void testMapLength() { 'z', "last"); ArrayBasedEscaperMap fem = ArrayBasedEscaperMap.create(map); // Array length is highest character value + 1 - assertEquals('z' + 1, fem.getReplacementArray().length); + assertThat(fem.getReplacementArray()).hasLength('z' + 1); } public void testMapping() { @@ -61,16 +63,17 @@ public void testMapping() { ArrayBasedEscaperMap fem = ArrayBasedEscaperMap.create(map); char[][] replacementArray = fem.getReplacementArray(); // Array length is highest character value + 1 - assertEquals(65536, replacementArray.length); - // The final element should always be non null. - assertNotNull(replacementArray[replacementArray.length - 1]); + assertThat(replacementArray).hasLength(65536); + // The final element should always be non-null. + assertThat(replacementArray[replacementArray.length - 1]).isNotNull(); // Exhaustively check all mappings (an int index avoids wrapping). - for (int n = 0; n < replacementArray.length; ++n) { + for (int n = 0; n < replacementArray.length; n++) { char c = (char) n; - if (replacementArray[n] != null) { - assertEquals(map.get(c), new String(replacementArray[n])); + String expected = map.get(c); + if (expected == null) { + assertThat(replacementArray[n]).isNull(); } else { - assertFalse(map.containsKey(c)); + assertThat(new String(replacementArray[n])).isEqualTo(expected); } } } diff --git a/android/guava-tests/test/com/google/common/escape/ArrayBasedUnicodeEscaperTest.java b/android/guava-tests/test/com/google/common/escape/ArrayBasedUnicodeEscaperTest.java index 3243240a18cd..f0337566e630 100644 --- a/android/guava-tests/test/com/google/common/escape/ArrayBasedUnicodeEscaperTest.java +++ b/android/guava-tests/test/com/google/common/escape/ArrayBasedUnicodeEscaperTest.java @@ -16,14 +16,21 @@ package com.google.common.escape; +import static com.google.common.escape.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import com.google.common.escape.testing.EscaperAsserts; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; -/** @author David Beaumont */ +/** + * @author David Beaumont + */ @GwtCompatible +@NullMarked public class ArrayBasedUnicodeEscaperTest extends TestCase { private static final ImmutableMap NO_REPLACEMENTS = ImmutableMap.of(); private static final ImmutableMap SIMPLE_REPLACEMENTS = @@ -45,20 +52,15 @@ protected char[] escapeUnsafe(int c) { } }; EscaperAsserts.assertBasic(escaper); - assertEquals("Fish Chips", escaper.escape("\tFish & Chips\n")); + assertThat(escaper.escape("\tFish & Chips\n")).isEqualTo("Fish Chips"); // Verify that everything else is left unescaped. String safeChars = "\0\u0100\uD800\uDC00\uFFFF"; - assertEquals(safeChars, escaper.escape(safeChars)); + assertThat(escaper.escape(safeChars)).isEqualTo(safeChars); // Ensure that Unicode escapers behave correctly wrt badly formed input. String badUnicode = "\uDC00\uD800"; - try { - escaper.escape(badUnicode); - fail("should fail for bad Unicode"); - } catch (IllegalArgumentException e) { - // Pass - } + assertThrows(IllegalArgumentException.class, () -> escaper.escape(badUnicode)); } public void testSafeRange() throws IOException { @@ -72,7 +74,7 @@ protected char[] escapeUnsafe(int c) { }; EscaperAsserts.assertBasic(wrappingEscaper); // '[' and '@' lie either side of [A-Z]. - assertEquals("{[}FOO{@}BAR{]}", wrappingEscaper.escape("[FOO@BAR]")); + assertThat(wrappingEscaper.escape("[FOO@BAR]")).isEqualTo("{[}FOO{@}BAR{]}"); } public void testDeleteUnsafeChars() throws IOException { @@ -84,11 +86,11 @@ protected char[] escapeUnsafe(int c) { } }; EscaperAsserts.assertBasic(deletingEscaper); - assertEquals( - "Everything outside the printable ASCII range is deleted.", - deletingEscaper.escape( - "\tEverything\0 outside the\uD800\uDC00 " - + "printable ASCII \uFFFFrange is \u007Fdeleted.\n")); + assertThat( + deletingEscaper.escape( + "\tEverything\0 outside the\uD800\uDC00 " + + "printable ASCII \uFFFFrange is \u007Fdeleted.\n")) + .isEqualTo("Everything outside the printable ASCII range is deleted."); } public void testReplacementPriority() throws IOException { @@ -105,8 +107,8 @@ protected char[] escapeUnsafe(int c) { // Replacements are applied first regardless of whether the character is in // the safe range or not ('&' is a safe char while '\t' and '\n' are not). - assertEquals( - "Fish ? Chips?", replacingEscaper.escape("\tFish &\0 Chips\r\n")); + assertThat(replacingEscaper.escape("\tFish &\0 Chips\r\n")) + .isEqualTo("Fish ? Chips?"); } public void testCodePointsFromSurrogatePairs() throws IOException { @@ -123,12 +125,12 @@ protected char[] escapeUnsafe(int c) { // A surrogate pair defining a code point within the safe range. String safeInput = "\uD800\uDC00"; // 0x10000 - assertEquals(safeInput, surrogateEscaper.escape(safeInput)); + assertThat(surrogateEscaper.escape(safeInput)).isEqualTo(safeInput); // A surrogate pair defining a code point outside the safe range (but both // of the surrogate characters lie within the safe range). It is important // not to accidentally treat this as a sequence of safe characters. String unsafeInput = "\uDBFF\uDFFF"; // 0x10FFFF - assertEquals("X", surrogateEscaper.escape(unsafeInput)); + assertThat(surrogateEscaper.escape(unsafeInput)).isEqualTo("X"); } } diff --git a/android/guava-tests/test/com/google/common/escape/CharEscaperBuilderTest.java b/android/guava-tests/test/com/google/common/escape/CharEscaperBuilderTest.java index 087e3177ab7d..ccd1c6c4ea29 100644 --- a/android/guava-tests/test/com/google/common/escape/CharEscaperBuilderTest.java +++ b/android/guava-tests/test/com/google/common/escape/CharEscaperBuilderTest.java @@ -16,14 +16,18 @@ package com.google.common.escape; +import static com.google.common.truth.Truth.assertThat; + import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +@NullUnmarked public class CharEscaperBuilderTest extends TestCase { public void testAddEscapes() { char[] cs = {'a', 'b', 'c'}; CharEscaperBuilder builder = new CharEscaperBuilder().addEscapes(cs, "Z"); Escaper escaper = builder.toEscaper(); - assertEquals("ZZZdef", escaper.escape("abcdef")); + assertThat(escaper.escape("abcdef")).isEqualTo("ZZZdef"); } } diff --git a/android/guava-tests/test/com/google/common/escape/EscapersTest.java b/android/guava-tests/test/com/google/common/escape/EscapersTest.java index 245560af61d5..cd25201cbffc 100644 --- a/android/guava-tests/test/com/google/common/escape/EscapersTest.java +++ b/android/guava-tests/test/com/google/common/escape/EscapersTest.java @@ -16,32 +16,39 @@ package com.google.common.escape; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import com.google.common.escape.testing.EscaperAsserts; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author David Beaumont */ +/** + * @author David Beaumont + */ @GwtCompatible +@NullUnmarked public class EscapersTest extends TestCase { public void testNullEscaper() throws IOException { Escaper escaper = Escapers.nullEscaper(); EscaperAsserts.assertBasic(escaper); String s = "\0\n\t\\az09~\uD800\uDC00\uFFFF"; - assertEquals("null escaper should have no effect", s, escaper.escape(s)); + assertWithMessage("null escaper should have no effect").that(escaper.escape(s)).isEqualTo(s); } public void testBuilderInitialStateNoReplacement() { // Unsafe characters aren't modified by default (unsafeReplacement == null). Escaper escaper = Escapers.builder().setSafeRange('a', 'z').build(); - assertEquals("The Quick Brown Fox", escaper.escape("The Quick Brown Fox")); + assertThat(escaper.escape("The Quick Brown Fox")).isEqualTo("The Quick Brown Fox"); } public void testBuilderInitialStateNoneUnsafe() { // No characters are unsafe by default (safeMin == 0, safeMax == 0xFFFF). Escaper escaper = Escapers.builder().setUnsafeReplacement("X").build(); - assertEquals("\0\uFFFF", escaper.escape("\0\uFFFF")); + assertThat(escaper.escape("\0\uFFFF")).isEqualTo("\0\uFFFF"); } public void testBuilderRetainsState() { @@ -49,18 +56,18 @@ public void testBuilderRetainsState() { Escapers.Builder builder = Escapers.builder(); builder.setSafeRange('a', 'z'); builder.setUnsafeReplacement("X"); - assertEquals("XheXXuickXXrownXXoxX", builder.build().escape("The Quick Brown Fox!")); + assertThat(builder.build().escape("The Quick Brown Fox!")).isEqualTo("XheXXuickXXrownXXoxX"); // Explicit replacements take priority over unsafe characters. builder.addEscape(' ', "_"); builder.addEscape('!', "_"); - assertEquals("Xhe_Xuick_Xrown_Xox_", builder.build().escape("The Quick Brown Fox!")); + assertThat(builder.build().escape("The Quick Brown Fox!")).isEqualTo("Xhe_Xuick_Xrown_Xox_"); // Explicit replacements take priority over safe characters. builder.setSafeRange(' ', '~'); - assertEquals("The_Quick_Brown_Fox_", builder.build().escape("The Quick Brown Fox!")); + assertThat(builder.build().escape("The Quick Brown Fox!")).isEqualTo("The_Quick_Brown_Fox_"); } public void testBuilderCreatesIndependentEscapers() { - // Setup a simple builder and create the first escaper. + // Set up a simple builder and create the first escaper. Escapers.Builder builder = Escapers.builder(); builder.setSafeRange('a', 'z'); builder.setUnsafeReplacement("X"); @@ -74,41 +81,11 @@ public void testBuilderCreatesIndependentEscapers() { builder.addEscape(' ', "*"); // Test both escapers after modifying the builder. - assertEquals("Xhe_Xuick_Xrown_XoxX", first.escape("The Quick Brown Fox!")); - assertEquals("Xhe-Xuick-Xrown-Xox$", second.escape("The Quick Brown Fox!")); - } - - public void testAsUnicodeEscaper() throws IOException { - CharEscaper charEscaper = - createSimpleCharEscaper( - ImmutableMap.builder() - .put('x', "".toCharArray()) - .put('\uD800', "".toCharArray()) - .put('\uDC00', "".toCharArray()) - .build()); - UnicodeEscaper unicodeEscaper = Escapers.asUnicodeEscaper(charEscaper); - EscaperAsserts.assertBasic(unicodeEscaper); - assertEquals("", charEscaper.escape("x\uD800\uDC00")); - assertEquals("", unicodeEscaper.escape("x\uD800\uDC00")); - - // Test that wrapped escapers acquire good Unicode semantics. - assertEquals("", charEscaper.escape("\uD800x\uDC00")); - try { - unicodeEscaper.escape("\uD800x\uDC00"); - fail("should have failed for bad Unicode input"); - } catch (IllegalArgumentException e) { - // pass - } - assertEquals("", charEscaper.escape("\uDC00\uD800")); - try { - unicodeEscaper.escape("\uDC00\uD800"); - fail("should have failed for bad Unicode input"); - } catch (IllegalArgumentException e) { - // pass - } + assertThat(first.escape("The Quick Brown Fox!")).isEqualTo("Xhe_Xuick_Xrown_XoxX"); + assertThat(second.escape("The Quick Brown Fox!")).isEqualTo("Xhe-Xuick-Xrown-Xox$"); } - // A trival non-optimized escaper for testing. + // A trivial non-optimized escaper for testing. static CharEscaper createSimpleCharEscaper(final ImmutableMap replacementMap) { return new CharEscaper() { @Override @@ -118,7 +95,7 @@ protected char[] escape(char c) { }; } - // A trival non-optimized escaper for testing. + // A trivial non-optimized escaper for testing. static UnicodeEscaper createSimpleUnicodeEscaper( final ImmutableMap replacementMap) { return new UnicodeEscaper() { diff --git a/android/guava-tests/test/com/google/common/escape/PackageSanityTests.java b/android/guava-tests/test/com/google/common/escape/PackageSanityTests.java index c2af8b742d02..d284c28a6e54 100644 --- a/android/guava-tests/test/com/google/common/escape/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/escape/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.escape; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -24,4 +25,5 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests {} diff --git a/android/guava-tests/test/com/google/common/escape/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/escape/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..7e9747348019 --- /dev/null +++ b/android/guava-tests/test/com/google/common/escape/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/escape/UnicodeEscaperTest.java b/android/guava-tests/test/com/google/common/escape/UnicodeEscaperTest.java index 96cfa10b0333..c9b6459c3e29 100644 --- a/android/guava-tests/test/com/google/common/escape/UnicodeEscaperTest.java +++ b/android/guava-tests/test/com/google/common/escape/UnicodeEscaperTest.java @@ -16,8 +16,13 @@ package com.google.common.escape; +import static com.google.common.escape.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link UnicodeEscaper}. @@ -25,6 +30,7 @@ * @author David Beaumont */ @GwtCompatible +@NullUnmarked public class UnicodeEscaperTest extends TestCase { private static final String SMALLEST_SURROGATE = @@ -39,7 +45,7 @@ public class UnicodeEscaperTest extends TestCase { private static final UnicodeEscaper NOP_ESCAPER = new UnicodeEscaper() { @Override - protected char[] escape(int c) { + protected char @Nullable [] escape(int c) { return null; } }; @@ -48,7 +54,7 @@ protected char[] escape(int c) { private static final UnicodeEscaper SIMPLE_ESCAPER = new UnicodeEscaper() { @Override - protected char[] escape(int cp) { + protected char @Nullable [] escape(int cp) { return ('a' <= cp && cp <= 'z') || ('A' <= cp && cp <= 'Z') || ('0' <= cp && cp <= '9') ? null : ("[" + String.valueOf(cp) + "]").toCharArray(); @@ -57,7 +63,7 @@ protected char[] escape(int cp) { public void testNopEscaper() { UnicodeEscaper e = NOP_ESCAPER; - assertEquals(TEST_STRING, escapeAsString(e, TEST_STRING)); + assertThat(escapeAsString(e, TEST_STRING)).isEqualTo(TEST_STRING); } public void testSimpleEscaper() { @@ -70,7 +76,7 @@ public void testSimpleEscaper() { + "0189[" + Character.MAX_CODE_POINT + "]"; - assertEquals(expected, escapeAsString(e, TEST_STRING)); + assertThat(escapeAsString(e, TEST_STRING)).isEqualTo(expected); } public void testGrowBuffer() { // need to grow past an initial 1024 byte buffer @@ -80,7 +86,7 @@ public void testGrowBuffer() { // need to grow past an initial 1024 byte buffer input.append((char) i); expected.append("[" + i + "]"); } - assertEquals(expected.toString(), SIMPLE_ESCAPER.escape(input.toString())); + assertThat(SIMPLE_ESCAPER.escape(input.toString())).isEqualTo(expected.toString()); } public void testSurrogatePairs() { @@ -107,33 +113,18 @@ public void testSurrogatePairs() { // Get the expected result string String expected = "x[" + min + "][" + s1 + "][" + s2 + "][" + s3 + "][" + max + "]x"; - assertEquals(expected, escapeAsString(e, test)); + assertThat(escapeAsString(e, test)).isEqualTo(expected); } public void testTrailingHighSurrogate() { String test = "abc" + Character.MIN_HIGH_SURROGATE; - try { - escapeAsString(NOP_ESCAPER, test); - fail("Trailing high surrogate should cause exception"); - } catch (IllegalArgumentException expected) { - // Pass - } - try { - escapeAsString(SIMPLE_ESCAPER, test); - fail("Trailing high surrogate should cause exception"); - } catch (IllegalArgumentException expected) { - // Pass - } + assertThrows(IllegalArgumentException.class, () -> escapeAsString(NOP_ESCAPER, test)); + assertThrows(IllegalArgumentException.class, () -> escapeAsString(SIMPLE_ESCAPER, test)); } public void testNullInput() { UnicodeEscaper e = SIMPLE_ESCAPER; - try { - e.escape((String) null); - fail("Null string should cause exception"); - } catch (NullPointerException expected) { - // Pass - } + assertThrows(NullPointerException.class, () -> e.escape((String) null)); } public void testBadStrings() { @@ -149,12 +140,7 @@ public void testBadStrings() { "abc" + Character.MAX_LOW_SURROGATE + "xyz", }; for (String s : BAD_STRINGS) { - try { - escapeAsString(e, s); - fail("Isolated low surrogate should cause exception [" + s + "]"); - } catch (IllegalArgumentException expected) { - // Pass - } + assertThrows(IllegalArgumentException.class, () -> escapeAsString(e, s)); } } @@ -163,9 +149,10 @@ public void testFalsePositivesForNextEscapedIndex() { new UnicodeEscaper() { // Canonical escaper method that only escapes lower case ASCII letters. @Override - protected char[] escape(int cp) { + protected char @Nullable [] escape(int cp) { return ('a' <= cp && cp <= 'z') ? new char[] {Character.toUpperCase((char) cp)} : null; } + // Inefficient implementation that defines all letters as escapable. @Override protected int nextEscapeIndex(CharSequence csq, int index, int end) { @@ -175,15 +162,13 @@ protected int nextEscapeIndex(CharSequence csq, int index, int end) { return index; } }; - assertEquals("\0HELLO \uD800\uDC00 WORLD!\n", e.escape("\0HeLLo \uD800\uDC00 WorlD!\n")); + assertThat(e.escape("\0HeLLo \uD800\uDC00 WorlD!\n")) + .isEqualTo("\0HELLO \uD800\uDC00 WORLD!\n"); } - public void testCodePointAt_IndexOutOfBoundsException() { - try { - UnicodeEscaper.codePointAt("Testing...", 4, 2); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + public void testCodePointAt_indexOutOfBoundsException() { + assertThrows( + IndexOutOfBoundsException.class, () -> UnicodeEscaper.codePointAt("Testing...", 4, 2)); } private static String escapeAsString(Escaper e, String s) { diff --git a/android/guava-tests/test/com/google/common/eventbus/AsyncEventBusTest.java b/android/guava-tests/test/com/google/common/eventbus/AsyncEventBusTest.java index 208667d213f3..ec84a0e368da 100644 --- a/android/guava-tests/test/com/google/common/eventbus/AsyncEventBusTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/AsyncEventBusTest.java @@ -20,12 +20,14 @@ import java.util.List; import java.util.concurrent.Executor; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test case for {@link AsyncEventBus}. * * @author Cliff Biffle */ +@NullUnmarked public class AsyncEventBusTest extends TestCase { private static final String EVENT = "Hello"; diff --git a/android/guava-tests/test/com/google/common/eventbus/DispatcherTest.java b/android/guava-tests/test/com/google/common/eventbus/DispatcherTest.java index b63a99543edc..58c12be8b901 100644 --- a/android/guava-tests/test/com/google/common/eventbus/DispatcherTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/DispatcherTest.java @@ -25,13 +25,14 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Dispatcher} implementations. * * @author Colin Decker */ - +@NullUnmarked public class DispatcherTest extends TestCase { private final EventBus bus = new EventBus(); diff --git a/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java b/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java index 249d3b06c51b..e81efa36bebb 100644 --- a/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/EventBusTest.java @@ -16,6 +16,8 @@ package com.google.common.eventbus; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.List; @@ -24,12 +26,14 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test case for {@link EventBus}. * * @author Cliff Biffle */ +@NullUnmarked public class EventBusTest extends TestCase { private static final String EVENT = "Hello"; private static final String BUS_IDENTIFIER = "test-bus"; @@ -90,7 +94,7 @@ public void eat(Comparable food) { // Two additional event types: Object and Comparable (played by Integer) Object objEvent = new Object(); - Object compEvent = new Integer(6); + Object compEvent = 6; bus.post(EVENT); bus.post(objEvent); @@ -119,7 +123,7 @@ public void testSubscriberThrowsException() throws Exception { final RecordingSubscriberExceptionHandler handler = new RecordingSubscriberExceptionHandler(); final EventBus eventBus = new EventBus(handler); final RuntimeException exception = - new RuntimeException("but culottes have a tendancy to ride up!"); + new RuntimeException("but culottes have a tendency to ride up!"); final Object subscriber = new Object() { @Subscribe @@ -157,18 +161,14 @@ public void throwExceptionOn(String message) { } }; eventBus.register(subscriber); - try { - eventBus.post(EVENT); - } catch (RuntimeException e) { - fail("Exception should not be thrown."); - } + eventBus.post(EVENT); } public void testDeadEventForwarding() { GhostCatcher catcher = new GhostCatcher(); bus.register(catcher); - // A String -- an event for which noone has registered. + // A String -- an event for which no one has registered. bus.post(EVENT); List events = catcher.getEvents(); @@ -194,12 +194,7 @@ public void testMissingSubscribe() { public void testUnregister() { StringCatcher catcher1 = new StringCatcher(); StringCatcher catcher2 = new StringCatcher(); - try { - bus.unregister(catcher1); - fail("Attempting to unregister an unregistered object succeeded"); - } catch (IllegalArgumentException expected) { - // OK. - } + assertThrows(IllegalArgumentException.class, () -> bus.unregister(catcher1)); bus.register(catcher1); bus.post(EVENT); @@ -222,12 +217,7 @@ public void testUnregister() { "Shouldn't catch any more events when unregistered.", expectedEvents, catcher1.getEvents()); assertEquals("Two correct events should be delivered.", expectedEvents, catcher2.getEvents()); - try { - bus.unregister(catcher1); - fail("Attempting to unregister an unregistered object succeeded"); - } catch (IllegalArgumentException expected) { - // OK. - } + assertThrows(IllegalArgumentException.class, () -> bus.unregister(catcher1)); bus.unregister(catcher2); bus.post(EVENT); @@ -239,7 +229,6 @@ public void testUnregister() { // NOTE: This test will always pass if register() is thread-safe but may also // pass if it isn't, though this is unlikely. - public void testRegisterThreadSafety() throws Exception { List catchers = Lists.newCopyOnWriteArrayList(); List> futures = Lists.newArrayList(); @@ -294,11 +283,7 @@ class SubscribesToPrimitive { @Subscribe public void toInt(int i) {} } - try { - bus.register(new SubscribesToPrimitive()); - fail("should have thrown"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> bus.register(new SubscribesToPrimitive())); } /** Records thrown exception information. */ diff --git a/android/guava-tests/test/com/google/common/eventbus/PackageSanityTests.java b/android/guava-tests/test/com/google/common/eventbus/PackageSanityTests.java index 225e55500df3..c1fc3d2512a5 100644 --- a/android/guava-tests/test/com/google/common/eventbus/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/eventbus/PackageSanityTests.java @@ -18,7 +18,8 @@ import com.google.common.testing.AbstractPackageSanityTests; import java.lang.reflect.Method; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Basic sanity tests for the entire package. @@ -26,6 +27,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() throws Exception { @@ -41,7 +43,7 @@ private static class DummySubscriber { private final EventBus eventBus = new EventBus(); @Subscribe - public void handle(@CheckForNull Object anything) {} + public void handle(@Nullable Object unused) {} Subscriber toSubscriber() throws Exception { return Subscriber.create(eventBus, this, subscriberMethod()); diff --git a/android/guava-tests/test/com/google/common/eventbus/ReentrantEventsTest.java b/android/guava-tests/test/com/google/common/eventbus/ReentrantEventsTest.java index f26f0c36f723..6ae77e5b15ab 100644 --- a/android/guava-tests/test/com/google/common/eventbus/ReentrantEventsTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/ReentrantEventsTest.java @@ -19,12 +19,14 @@ import com.google.common.collect.Lists; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Validate that {@link EventBus} behaves carefully when listeners publish their own events. * * @author Jesse Wilson */ +@NullUnmarked public class ReentrantEventsTest extends TestCase { static final String FIRST = "one"; diff --git a/android/guava-tests/test/com/google/common/eventbus/StringCatcher.java b/android/guava-tests/test/com/google/common/eventbus/StringCatcher.java index 198fa2eda8ca..1f30e2384427 100644 --- a/android/guava-tests/test/com/google/common/eventbus/StringCatcher.java +++ b/android/guava-tests/test/com/google/common/eventbus/StringCatcher.java @@ -18,8 +18,9 @@ import com.google.common.collect.Lists; import java.util.List; -import javax.annotation.CheckForNull; import junit.framework.Assert; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * A simple EventSubscriber mock that records Strings. @@ -29,15 +30,16 @@ * * @author Cliff Biffle */ +@NullUnmarked public class StringCatcher { private List events = Lists.newArrayList(); @Subscribe - public void hereHaveAString(@CheckForNull String string) { + public void hereHaveAString(@Nullable String string) { events.add(string); } - public void methodWithoutAnnotation(@CheckForNull String string) { + public void methodWithoutAnnotation(@Nullable String string) { Assert.fail("Event bus must not call methods without @Subscribe!"); } diff --git a/android/guava-tests/test/com/google/common/eventbus/SubscriberRegistryTest.java b/android/guava-tests/test/com/google/common/eventbus/SubscriberRegistryTest.java index c9e5c9a5b492..21ff84066d04 100644 --- a/android/guava-tests/test/com/google/common/eventbus/SubscriberRegistryTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/SubscriberRegistryTest.java @@ -16,16 +16,20 @@ package com.google.common.eventbus; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import java.util.Iterator; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link SubscriberRegistry}. * * @author Colin Decker */ +@NullUnmarked public class SubscriberRegistryTest extends TestCase { private final SubscriberRegistry registry = new SubscriberRegistry(new EventBus()); @@ -59,28 +63,15 @@ public void testUnregister() { } public void testUnregister_notRegistered() { - try { - registry.unregister(new StringSubscriber()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> registry.unregister(new StringSubscriber())); StringSubscriber s1 = new StringSubscriber(); registry.register(s1); - try { - registry.unregister(new StringSubscriber()); - fail(); - } catch (IllegalArgumentException expected) { - // a StringSubscriber was registered, but not the same one we tried to unregister - } + assertThrows(IllegalArgumentException.class, () -> registry.unregister(new StringSubscriber())); registry.unregister(s1); - try { - registry.unregister(s1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> registry.unregister(s1)); } public void testGetSubscribers() { diff --git a/android/guava-tests/test/com/google/common/eventbus/SubscriberTest.java b/android/guava-tests/test/com/google/common/eventbus/SubscriberTest.java index e2380df21349..4c9bbb7b6254 100644 --- a/android/guava-tests/test/com/google/common/eventbus/SubscriberTest.java +++ b/android/guava-tests/test/com/google/common/eventbus/SubscriberTest.java @@ -17,11 +17,14 @@ package com.google.common.eventbus; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.testing.EqualsTester; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Subscriber}. @@ -29,13 +32,14 @@ * @author Cliff Biffle * @author Colin Decker */ +@NullUnmarked public class SubscriberTest extends TestCase { private static final Object FIXTURE_ARGUMENT = new Object(); private EventBus bus; private boolean methodCalled; - private Object methodArgument; + private @Nullable Object methodArgument; @Override protected void setUp() throws Exception { @@ -69,23 +73,18 @@ public void testInvokeSubscriberMethod_exceptionWrapping() throws Throwable { Method method = getTestSubscriberMethod("exceptionThrowingMethod"); Subscriber subscriber = Subscriber.create(bus, this, method); - try { - subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT); - fail("Subscribers whose methods throw must throw InvocationTargetException"); - } catch (InvocationTargetException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(IntentionalException.class); - } + InvocationTargetException expected = + assertThrows( + InvocationTargetException.class, + () -> subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT)); + assertThat(expected).hasCauseThat().isInstanceOf(IntentionalException.class); } public void testInvokeSubscriberMethod_errorPassthrough() throws Throwable { Method method = getTestSubscriberMethod("errorThrowingMethod"); Subscriber subscriber = Subscriber.create(bus, this, method); - try { - subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT); - fail("Subscribers whose methods throw Errors must rethrow them"); - } catch (JudgmentError expected) { - } + assertThrows(JudgmentError.class, () -> subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT)); } public void testEquals() throws Exception { diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/AbstractEventBusTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/AbstractEventBusTest.java new file mode 100644 index 000000000000..b8738c3cb8cb --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/AbstractEventBusTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import com.google.common.eventbus.EventBus; +import junit.framework.TestCase; +import org.jspecify.annotations.Nullable; + +/** + * Abstract base class for tests that EventBus finds the correct subscribers. + * + *

    The actual tests are distributed among the other classes in this package based on whether they + * are annotated or abstract in the superclass. + * + *

    This test must be outside the c.g.c.eventbus package to test correctly. + * + * @author Louis Wasserman + */ +abstract class AbstractEventBusTest extends TestCase { + static final Object EVENT = new Object(); + + abstract H createSubscriber(); + + private @Nullable H subscriber; + + H getSubscriber() { + return subscriber; + } + + @Override + protected void setUp() throws Exception { + subscriber = createSubscriber(); + EventBus bus = new EventBus(); + bus.register(subscriber); + bus.post(EVENT); + } + + @Override + protected void tearDown() throws Exception { + subscriber = null; + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/AbstractNotAnnotatedInSuperclassTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/AbstractNotAnnotatedInSuperclassTest.java new file mode 100644 index 000000000000..a391fcbad687 --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/AbstractNotAnnotatedInSuperclassTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.AbstractNotAnnotatedInSuperclassTest.SubClass; +import java.util.List; + +public class AbstractNotAnnotatedInSuperclassTest extends AbstractEventBusTest { + abstract static class SuperClass { + public abstract void overriddenInSubclassNowhereAnnotated(Object o); + + public abstract void overriddenAndAnnotatedInSubclass(Object o); + } + + static class SubClass extends SuperClass { + final List overriddenInSubclassNowhereAnnotatedEvents = Lists.newArrayList(); + final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); + + @Override + public void overriddenInSubclassNowhereAnnotated(Object o) { + overriddenInSubclassNowhereAnnotatedEvents.add(o); + } + + @Subscribe + @Override + public void overriddenAndAnnotatedInSubclass(Object o) { + overriddenAndAnnotatedInSubclassEvents.add(o); + } + } + + public void testOverriddenAndAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); + } + + public void testOverriddenInSubclassNowhereAnnotated() { + assertThat(getSubscriber().overriddenInSubclassNowhereAnnotatedEvents).isEmpty(); + } + + @Override + SubClass createSubscriber() { + return new SubClass(); + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedAndAbstractInSuperclassTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedAndAbstractInSuperclassTest.java new file mode 100644 index 000000000000..aea17c3b41e7 --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedAndAbstractInSuperclassTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.AnnotatedAndAbstractInSuperclassTest.SubClass; +import java.util.List; + +public class AnnotatedAndAbstractInSuperclassTest extends AbstractEventBusTest { + abstract static class SuperClass { + @Subscribe + public abstract void overriddenAndAnnotatedInSubclass(Object o); + + @Subscribe + public abstract void overriddenInSubclass(Object o); + } + + static class SubClass extends SuperClass { + final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); + final List overriddenInSubclassEvents = Lists.newArrayList(); + + @Subscribe + @Override + public void overriddenAndAnnotatedInSubclass(Object o) { + overriddenAndAnnotatedInSubclassEvents.add(o); + } + + @Override + public void overriddenInSubclass(Object o) { + overriddenInSubclassEvents.add(o); + } + } + + public void testOverriddenAndAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); + } + + public void testOverriddenNotAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenInSubclassEvents).contains(EVENT); + } + + @Override + SubClass createSubscriber() { + return new SubClass(); + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedNotAbstractInSuperclassTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedNotAbstractInSuperclassTest.java new file mode 100644 index 000000000000..886601789ff7 --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedNotAbstractInSuperclassTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.AnnotatedNotAbstractInSuperclassTest.SubClass; +import java.util.List; + +public class AnnotatedNotAbstractInSuperclassTest extends AbstractEventBusTest { + static class SuperClass { + final List notOverriddenInSubclassEvents = Lists.newArrayList(); + final List overriddenNotAnnotatedInSubclassEvents = Lists.newArrayList(); + final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); + final List differentlyOverriddenNotAnnotatedInSubclassBadEvents = Lists.newArrayList(); + final List differentlyOverriddenAnnotatedInSubclassBadEvents = Lists.newArrayList(); + + @Subscribe + public void notOverriddenInSubclass(Object o) { + notOverriddenInSubclassEvents.add(o); + } + + @Subscribe + public void overriddenNotAnnotatedInSubclass(Object o) { + overriddenNotAnnotatedInSubclassEvents.add(o); + } + + @Subscribe + public void overriddenAndAnnotatedInSubclass(Object o) { + overriddenAndAnnotatedInSubclassEvents.add(o); + } + + @Subscribe + public void differentlyOverriddenNotAnnotatedInSubclass(Object o) { + // the subclass overrides this and does *not* call super.dONAIS(o) + differentlyOverriddenNotAnnotatedInSubclassBadEvents.add(o); + } + + @Subscribe + public void differentlyOverriddenAnnotatedInSubclass(Object o) { + // the subclass overrides this and does *not* call super.dOAIS(o) + differentlyOverriddenAnnotatedInSubclassBadEvents.add(o); + } + } + + static class SubClass extends SuperClass { + final List differentlyOverriddenNotAnnotatedInSubclassGoodEvents = Lists.newArrayList(); + final List differentlyOverriddenAnnotatedInSubclassGoodEvents = Lists.newArrayList(); + + @Override + public void overriddenNotAnnotatedInSubclass(Object o) { + super.overriddenNotAnnotatedInSubclass(o); + } + + @Subscribe + @Override + // We are testing how we treat an override with the same behavior and annotations. + @SuppressWarnings("RedundantOverride") + public void overriddenAndAnnotatedInSubclass(Object o) { + super.overriddenAndAnnotatedInSubclass(o); + } + + @Override + public void differentlyOverriddenNotAnnotatedInSubclass(Object o) { + differentlyOverriddenNotAnnotatedInSubclassGoodEvents.add(o); + } + + @Subscribe + @Override + public void differentlyOverriddenAnnotatedInSubclass(Object o) { + differentlyOverriddenAnnotatedInSubclassGoodEvents.add(o); + } + } + + public void testNotOverriddenInSubclass() { + assertThat(getSubscriber().notOverriddenInSubclassEvents).contains(EVENT); + } + + public void testOverriddenNotAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenNotAnnotatedInSubclassEvents).contains(EVENT); + } + + public void testDifferentlyOverriddenNotAnnotatedInSubclass() { + assertThat(getSubscriber().differentlyOverriddenNotAnnotatedInSubclassGoodEvents) + .contains(EVENT); + assertThat(getSubscriber().differentlyOverriddenNotAnnotatedInSubclassBadEvents).isEmpty(); + } + + public void testOverriddenAndAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); + } + + public void testDifferentlyOverriddenAndAnnotatedInSubclass() { + assertThat(getSubscriber().differentlyOverriddenAnnotatedInSubclassGoodEvents).contains(EVENT); + assertThat(getSubscriber().differentlyOverriddenAnnotatedInSubclassBadEvents).isEmpty(); + } + + @Override + SubClass createSubscriber() { + return new SubClass(); + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedSubscriberFinderTests.java b/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedSubscriberFinderTests.java deleted file mode 100644 index a1cbb594481e..000000000000 --- a/android/guava-tests/test/com/google/common/eventbus/outside/AnnotatedSubscriberFinderTests.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2012 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.eventbus.outside; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.collect.Lists; -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; -import java.util.List; -import junit.framework.TestCase; - -/** - * Test that EventBus finds the correct subscribers. - * - *

    This test must be outside the c.g.c.eventbus package to test correctly. - * - * @author Louis Wasserman - */ -public class AnnotatedSubscriberFinderTests { - - private static final Object EVENT = new Object(); - - abstract static class AbstractEventBusTest extends TestCase { - abstract H createSubscriber(); - - private H subscriber; - - H getSubscriber() { - return subscriber; - } - - @Override - protected void setUp() throws Exception { - subscriber = createSubscriber(); - EventBus bus = new EventBus(); - bus.register(subscriber); - bus.post(EVENT); - } - - @Override - protected void tearDown() throws Exception { - subscriber = null; - } - } - - /* - * We break the tests up based on whether they are annotated or abstract in the superclass. - */ - public static class BaseSubscriberFinderTest - extends AbstractEventBusTest { - static class Subscriber { - final List nonSubscriberEvents = Lists.newArrayList(); - final List subscriberEvents = Lists.newArrayList(); - - public void notASubscriber(Object o) { - nonSubscriberEvents.add(o); - } - - @Subscribe - public void subscriber(Object o) { - subscriberEvents.add(o); - } - } - - public void testNonSubscriber() { - assertThat(getSubscriber().nonSubscriberEvents).isEmpty(); - } - - public void testSubscriber() { - assertThat(getSubscriber().subscriberEvents).contains(EVENT); - } - - @Override - Subscriber createSubscriber() { - return new Subscriber(); - } - } - - public static class AnnotatedAndAbstractInSuperclassTest - extends AbstractEventBusTest { - abstract static class SuperClass { - @Subscribe - public abstract void overriddenAndAnnotatedInSubclass(Object o); - - @Subscribe - public abstract void overriddenInSubclass(Object o); - } - - static class SubClass extends SuperClass { - final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); - final List overriddenInSubclassEvents = Lists.newArrayList(); - - @Subscribe - @Override - public void overriddenAndAnnotatedInSubclass(Object o) { - overriddenAndAnnotatedInSubclassEvents.add(o); - } - - @Override - public void overriddenInSubclass(Object o) { - overriddenInSubclassEvents.add(o); - } - } - - public void testOverriddenAndAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); - } - - public void testOverriddenNotAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenInSubclassEvents).contains(EVENT); - } - - @Override - SubClass createSubscriber() { - return new SubClass(); - } - } - - public static class AnnotatedNotAbstractInSuperclassTest - extends AbstractEventBusTest { - static class SuperClass { - final List notOverriddenInSubclassEvents = Lists.newArrayList(); - final List overriddenNotAnnotatedInSubclassEvents = Lists.newArrayList(); - final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); - final List differentlyOverriddenNotAnnotatedInSubclassBadEvents = - Lists.newArrayList(); - final List differentlyOverriddenAnnotatedInSubclassBadEvents = Lists.newArrayList(); - - @Subscribe - public void notOverriddenInSubclass(Object o) { - notOverriddenInSubclassEvents.add(o); - } - - @Subscribe - public void overriddenNotAnnotatedInSubclass(Object o) { - overriddenNotAnnotatedInSubclassEvents.add(o); - } - - @Subscribe - public void overriddenAndAnnotatedInSubclass(Object o) { - overriddenAndAnnotatedInSubclassEvents.add(o); - } - - @Subscribe - public void differentlyOverriddenNotAnnotatedInSubclass(Object o) { - // the subclass overrides this and does *not* call super.dONAIS(o) - differentlyOverriddenNotAnnotatedInSubclassBadEvents.add(o); - } - - @Subscribe - public void differentlyOverriddenAnnotatedInSubclass(Object o) { - // the subclass overrides this and does *not* call super.dOAIS(o) - differentlyOverriddenAnnotatedInSubclassBadEvents.add(o); - } - } - - static class SubClass extends SuperClass { - final List differentlyOverriddenNotAnnotatedInSubclassGoodEvents = - Lists.newArrayList(); - final List differentlyOverriddenAnnotatedInSubclassGoodEvents = Lists.newArrayList(); - - @Override - public void overriddenNotAnnotatedInSubclass(Object o) { - super.overriddenNotAnnotatedInSubclass(o); - } - - @Subscribe - @Override - public void overriddenAndAnnotatedInSubclass(Object o) { - super.overriddenAndAnnotatedInSubclass(o); - } - - @Override - public void differentlyOverriddenNotAnnotatedInSubclass(Object o) { - differentlyOverriddenNotAnnotatedInSubclassGoodEvents.add(o); - } - - @Subscribe - @Override - public void differentlyOverriddenAnnotatedInSubclass(Object o) { - differentlyOverriddenAnnotatedInSubclassGoodEvents.add(o); - } - } - - public void testNotOverriddenInSubclass() { - assertThat(getSubscriber().notOverriddenInSubclassEvents).contains(EVENT); - } - - public void testOverriddenNotAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenNotAnnotatedInSubclassEvents).contains(EVENT); - } - - public void testDifferentlyOverriddenNotAnnotatedInSubclass() { - assertThat(getSubscriber().differentlyOverriddenNotAnnotatedInSubclassGoodEvents) - .contains(EVENT); - assertThat(getSubscriber().differentlyOverriddenNotAnnotatedInSubclassBadEvents).isEmpty(); - } - - public void testOverriddenAndAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); - } - - public void testDifferentlyOverriddenAndAnnotatedInSubclass() { - assertThat(getSubscriber().differentlyOverriddenAnnotatedInSubclassGoodEvents) - .contains(EVENT); - assertThat(getSubscriber().differentlyOverriddenAnnotatedInSubclassBadEvents).isEmpty(); - } - - @Override - SubClass createSubscriber() { - return new SubClass(); - } - } - - public static class AbstractNotAnnotatedInSuperclassTest - extends AbstractEventBusTest { - abstract static class SuperClass { - public abstract void overriddenInSubclassNowhereAnnotated(Object o); - - public abstract void overriddenAndAnnotatedInSubclass(Object o); - } - - static class SubClass extends SuperClass { - final List overriddenInSubclassNowhereAnnotatedEvents = Lists.newArrayList(); - final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); - - @Override - public void overriddenInSubclassNowhereAnnotated(Object o) { - overriddenInSubclassNowhereAnnotatedEvents.add(o); - } - - @Subscribe - @Override - public void overriddenAndAnnotatedInSubclass(Object o) { - overriddenAndAnnotatedInSubclassEvents.add(o); - } - } - - public void testOverriddenAndAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); - } - - public void testOverriddenInSubclassNowhereAnnotated() { - assertThat(getSubscriber().overriddenInSubclassNowhereAnnotatedEvents).isEmpty(); - } - - @Override - SubClass createSubscriber() { - return new SubClass(); - } - } - - public static class NeitherAbstractNorAnnotatedInSuperclassTest - extends AbstractEventBusTest { - static class SuperClass { - final List neitherOverriddenNorAnnotatedEvents = Lists.newArrayList(); - final List overriddenInSubclassNowhereAnnotatedEvents = Lists.newArrayList(); - final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); - - public void neitherOverriddenNorAnnotated(Object o) { - neitherOverriddenNorAnnotatedEvents.add(o); - } - - public void overriddenInSubclassNowhereAnnotated(Object o) { - overriddenInSubclassNowhereAnnotatedEvents.add(o); - } - - public void overriddenAndAnnotatedInSubclass(Object o) { - overriddenAndAnnotatedInSubclassEvents.add(o); - } - } - - static class SubClass extends SuperClass { - @Override - public void overriddenInSubclassNowhereAnnotated(Object o) { - super.overriddenInSubclassNowhereAnnotated(o); - } - - @Subscribe - @Override - public void overriddenAndAnnotatedInSubclass(Object o) { - super.overriddenAndAnnotatedInSubclass(o); - } - } - - public void testNeitherOverriddenNorAnnotated() { - assertThat(getSubscriber().neitherOverriddenNorAnnotatedEvents).isEmpty(); - } - - public void testOverriddenInSubclassNowhereAnnotated() { - assertThat(getSubscriber().overriddenInSubclassNowhereAnnotatedEvents).isEmpty(); - } - - public void testOverriddenAndAnnotatedInSubclass() { - assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); - } - - @Override - SubClass createSubscriber() { - return new SubClass(); - } - } - - public static class DeepInterfaceTest - extends AbstractEventBusTest { - interface Interface1 { - @Subscribe - void annotatedIn1(Object o); - - @Subscribe - void annotatedIn1And2(Object o); - - @Subscribe - void annotatedIn1And2AndClass(Object o); - - void declaredIn1AnnotatedIn2(Object o); - - void declaredIn1AnnotatedInClass(Object o); - - void nowhereAnnotated(Object o); - } - - interface Interface2 extends Interface1 { - @Override - @Subscribe - void declaredIn1AnnotatedIn2(Object o); - - @Override - @Subscribe - void annotatedIn1And2(Object o); - - @Override - @Subscribe - void annotatedIn1And2AndClass(Object o); - - void declaredIn2AnnotatedInClass(Object o); - - @Subscribe - void annotatedIn2(Object o); - } - - static class SubscriberClass implements Interface2 { - final List annotatedIn1Events = Lists.newArrayList(); - final List annotatedIn1And2Events = Lists.newArrayList(); - final List annotatedIn1And2AndClassEvents = Lists.newArrayList(); - final List declaredIn1AnnotatedIn2Events = Lists.newArrayList(); - final List declaredIn1AnnotatedInClassEvents = Lists.newArrayList(); - final List declaredIn2AnnotatedInClassEvents = Lists.newArrayList(); - final List annotatedIn2Events = Lists.newArrayList(); - final List nowhereAnnotatedEvents = Lists.newArrayList(); - - @Override - public void annotatedIn1(Object o) { - annotatedIn1Events.add(o); - } - - @Subscribe - @Override - public void declaredIn1AnnotatedInClass(Object o) { - declaredIn1AnnotatedInClassEvents.add(o); - } - - @Override - public void declaredIn1AnnotatedIn2(Object o) { - declaredIn1AnnotatedIn2Events.add(o); - } - - @Override - public void annotatedIn1And2(Object o) { - annotatedIn1And2Events.add(o); - } - - @Subscribe - @Override - public void annotatedIn1And2AndClass(Object o) { - annotatedIn1And2AndClassEvents.add(o); - } - - @Subscribe - @Override - public void declaredIn2AnnotatedInClass(Object o) { - declaredIn2AnnotatedInClassEvents.add(o); - } - - @Override - public void annotatedIn2(Object o) { - annotatedIn2Events.add(o); - } - - @Override - public void nowhereAnnotated(Object o) { - nowhereAnnotatedEvents.add(o); - } - } - - public void testAnnotatedIn1() { - assertThat(getSubscriber().annotatedIn1Events).contains(EVENT); - } - - public void testAnnotatedIn2() { - assertThat(getSubscriber().annotatedIn2Events).contains(EVENT); - } - - public void testAnnotatedIn1And2() { - assertThat(getSubscriber().annotatedIn1And2Events).contains(EVENT); - } - - public void testAnnotatedIn1And2AndClass() { - assertThat(getSubscriber().annotatedIn1And2AndClassEvents).contains(EVENT); - } - - public void testDeclaredIn1AnnotatedIn2() { - assertThat(getSubscriber().declaredIn1AnnotatedIn2Events).contains(EVENT); - } - - public void testDeclaredIn1AnnotatedInClass() { - assertThat(getSubscriber().declaredIn1AnnotatedInClassEvents).contains(EVENT); - } - - public void testDeclaredIn2AnnotatedInClass() { - assertThat(getSubscriber().declaredIn2AnnotatedInClassEvents).contains(EVENT); - } - - public void testNowhereAnnotated() { - assertThat(getSubscriber().nowhereAnnotatedEvents).isEmpty(); - } - - @Override - SubscriberClass createSubscriber() { - return new SubscriberClass(); - } - } -} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/BaseSubscriberFinderTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/BaseSubscriberFinderTest.java new file mode 100644 index 000000000000..461fb795b07d --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/BaseSubscriberFinderTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.BaseSubscriberFinderTest.Subscriber; +import java.util.List; + +public class BaseSubscriberFinderTest extends AbstractEventBusTest { + static class Subscriber { + final List nonSubscriberEvents = Lists.newArrayList(); + final List subscriberEvents = Lists.newArrayList(); + + public void notASubscriber(Object o) { + nonSubscriberEvents.add(o); + } + + @Subscribe + public void subscriber(Object o) { + subscriberEvents.add(o); + } + } + + public void testNonSubscriber() { + assertThat(getSubscriber().nonSubscriberEvents).isEmpty(); + } + + public void testSubscriber() { + assertThat(getSubscriber().subscriberEvents).contains(EVENT); + } + + @Override + Subscriber createSubscriber() { + return new Subscriber(); + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/DeepInterfaceTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/DeepInterfaceTest.java new file mode 100644 index 000000000000..4fefbc12ab15 --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/DeepInterfaceTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.DeepInterfaceTest.SubscriberClass; +import java.util.List; + +public class DeepInterfaceTest extends AbstractEventBusTest { + interface Interface1 { + @Subscribe + void annotatedIn1(Object o); + + @Subscribe + void annotatedIn1And2(Object o); + + @Subscribe + void annotatedIn1And2AndClass(Object o); + + void declaredIn1AnnotatedIn2(Object o); + + void declaredIn1AnnotatedInClass(Object o); + + void nowhereAnnotated(Object o); + } + + interface Interface2 extends Interface1 { + @Override + @Subscribe + void declaredIn1AnnotatedIn2(Object o); + + @Override + @Subscribe + void annotatedIn1And2(Object o); + + @Override + @Subscribe + void annotatedIn1And2AndClass(Object o); + + void declaredIn2AnnotatedInClass(Object o); + + @Subscribe + void annotatedIn2(Object o); + } + + static class SubscriberClass implements Interface2 { + final List annotatedIn1Events = Lists.newArrayList(); + final List annotatedIn1And2Events = Lists.newArrayList(); + final List annotatedIn1And2AndClassEvents = Lists.newArrayList(); + final List declaredIn1AnnotatedIn2Events = Lists.newArrayList(); + final List declaredIn1AnnotatedInClassEvents = Lists.newArrayList(); + final List declaredIn2AnnotatedInClassEvents = Lists.newArrayList(); + final List annotatedIn2Events = Lists.newArrayList(); + final List nowhereAnnotatedEvents = Lists.newArrayList(); + + @Override + public void annotatedIn1(Object o) { + annotatedIn1Events.add(o); + } + + @Subscribe + @Override + public void declaredIn1AnnotatedInClass(Object o) { + declaredIn1AnnotatedInClassEvents.add(o); + } + + @Override + public void declaredIn1AnnotatedIn2(Object o) { + declaredIn1AnnotatedIn2Events.add(o); + } + + @Override + public void annotatedIn1And2(Object o) { + annotatedIn1And2Events.add(o); + } + + @Subscribe + @Override + public void annotatedIn1And2AndClass(Object o) { + annotatedIn1And2AndClassEvents.add(o); + } + + @Subscribe + @Override + public void declaredIn2AnnotatedInClass(Object o) { + declaredIn2AnnotatedInClassEvents.add(o); + } + + @Override + public void annotatedIn2(Object o) { + annotatedIn2Events.add(o); + } + + @Override + public void nowhereAnnotated(Object o) { + nowhereAnnotatedEvents.add(o); + } + } + + public void testAnnotatedIn1() { + assertThat(getSubscriber().annotatedIn1Events).contains(EVENT); + } + + public void testAnnotatedIn2() { + assertThat(getSubscriber().annotatedIn2Events).contains(EVENT); + } + + public void testAnnotatedIn1And2() { + assertThat(getSubscriber().annotatedIn1And2Events).contains(EVENT); + } + + public void testAnnotatedIn1And2AndClass() { + assertThat(getSubscriber().annotatedIn1And2AndClassEvents).contains(EVENT); + } + + public void testDeclaredIn1AnnotatedIn2() { + assertThat(getSubscriber().declaredIn1AnnotatedIn2Events).contains(EVENT); + } + + public void testDeclaredIn1AnnotatedInClass() { + assertThat(getSubscriber().declaredIn1AnnotatedInClassEvents).contains(EVENT); + } + + public void testDeclaredIn2AnnotatedInClass() { + assertThat(getSubscriber().declaredIn2AnnotatedInClassEvents).contains(EVENT); + } + + public void testNowhereAnnotated() { + assertThat(getSubscriber().nowhereAnnotatedEvents).isEmpty(); + } + + @Override + SubscriberClass createSubscriber() { + return new SubscriberClass(); + } +} diff --git a/android/guava-tests/test/com/google/common/eventbus/outside/NeitherAbstractNorAnnotatedInSuperclassTest.java b/android/guava-tests/test/com/google/common/eventbus/outside/NeitherAbstractNorAnnotatedInSuperclassTest.java new file mode 100644 index 000000000000..b19448772f2a --- /dev/null +++ b/android/guava-tests/test/com/google/common/eventbus/outside/NeitherAbstractNorAnnotatedInSuperclassTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.eventbus.outside; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.Lists; +import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.outside.NeitherAbstractNorAnnotatedInSuperclassTest.SubClass; +import java.util.List; + +public class NeitherAbstractNorAnnotatedInSuperclassTest extends AbstractEventBusTest { + static class SuperClass { + final List neitherOverriddenNorAnnotatedEvents = Lists.newArrayList(); + final List overriddenInSubclassNowhereAnnotatedEvents = Lists.newArrayList(); + final List overriddenAndAnnotatedInSubclassEvents = Lists.newArrayList(); + + public void neitherOverriddenNorAnnotated(Object o) { + neitherOverriddenNorAnnotatedEvents.add(o); + } + + public void overriddenInSubclassNowhereAnnotated(Object o) { + overriddenInSubclassNowhereAnnotatedEvents.add(o); + } + + public void overriddenAndAnnotatedInSubclass(Object o) { + overriddenAndAnnotatedInSubclassEvents.add(o); + } + } + + static class SubClass extends SuperClass { + @Override + // We are testing how we treat an override with the same behavior and annotations. + @SuppressWarnings("RedundantOverride") + public void overriddenInSubclassNowhereAnnotated(Object o) { + super.overriddenInSubclassNowhereAnnotated(o); + } + + @Subscribe + @Override + public void overriddenAndAnnotatedInSubclass(Object o) { + super.overriddenAndAnnotatedInSubclass(o); + } + } + + public void testNeitherOverriddenNorAnnotated() { + assertThat(getSubscriber().neitherOverriddenNorAnnotatedEvents).isEmpty(); + } + + public void testOverriddenInSubclassNowhereAnnotated() { + assertThat(getSubscriber().overriddenInSubclassNowhereAnnotatedEvents).isEmpty(); + } + + public void testOverriddenAndAnnotatedInSubclass() { + assertThat(getSubscriber().overriddenAndAnnotatedInSubclassEvents).contains(EVENT); + } + + @Override + SubClass createSubscriber() { + return new SubClass(); + } +} diff --git a/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java index 3a489a11c37b..bcf9cf6d7680 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java @@ -16,17 +16,18 @@ package com.google.common.graph; -import static com.google.common.graph.TestUtil.ERROR_NODE_NOT_IN_GRAPH; import static com.google.common.graph.TestUtil.assertNodeNotInGraphErrorMessage; +import static com.google.common.graph.TestUtil.assertNodeRemovedFromGraphErrorMessage; import static com.google.common.graph.TestUtil.assertStronglyEquivalent; import static com.google.common.graph.TestUtil.sanityCheckSet; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -47,6 +48,7 @@ * TODO(user): Make this class generic (using ) for all node and edge types. * TODO(user): Differentiate between directed and undirected edge strings. */ +@NullUnmarked public abstract class AbstractGraphTest { Graph graph; @@ -235,12 +237,8 @@ public void adjacentNodes_noAdjacentNodes() { @Test public void adjacentNodes_nodeNotInGraph() { - try { - graph.adjacentNodes(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(NODE_NOT_IN_GRAPH))); } @Test @@ -251,12 +249,8 @@ public void predecessors_noPredecessors() { @Test public void predecessors_nodeNotInGraph() { - try { - graph.predecessors(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.predecessors(NODE_NOT_IN_GRAPH))); } @Test @@ -267,12 +261,8 @@ public void successors_noSuccessors() { @Test public void successors_nodeNotInGraph() { - try { - graph.successors(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.successors(NODE_NOT_IN_GRAPH))); } @Test @@ -283,12 +273,8 @@ public void incidentEdges_noIncidentEdges() { @Test public void incidentEdges_nodeNotInGraph() { - try { - graph.incidentEdges(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.incidentEdges(NODE_NOT_IN_GRAPH))); } @Test @@ -306,12 +292,8 @@ public void degree_isolatedNode() { @Test public void degree_nodeNotInGraph() { - try { - graph.degree(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.degree(NODE_NOT_IN_GRAPH))); } @Test @@ -322,12 +304,8 @@ public void inDegree_isolatedNode() { @Test public void inDegree_nodeNotInGraph() { - try { - graph.inDegree(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.inDegree(NODE_NOT_IN_GRAPH))); } @Test @@ -338,12 +316,8 @@ public void outDegree_isolatedNode() { @Test public void outDegree_nodeNotInGraph() { - try { - graph.outDegree(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.outDegree(NODE_NOT_IN_GRAPH))); } @Test @@ -373,8 +347,24 @@ public void removeNode_existingNode() { assertThat(graphAsMutableGraph.removeNode(N1)).isTrue(); assertThat(graphAsMutableGraph.removeNode(N1)).isFalse(); assertThat(graph.nodes()).containsExactly(N2, N4); + assertThat(graph.adjacentNodes(N2)).isEmpty(); + assertThat(graph.predecessors(N2)).isEmpty(); + assertThat(graph.successors(N2)).isEmpty(); + assertThat(graph.incidentEdges(N2)).isEmpty(); assertThat(graph.adjacentNodes(N4)).isEmpty(); + assertThat(graph.predecessors(N4)).isEmpty(); + assertThat(graph.successors(N4)).isEmpty(); + assertThat(graph.incidentEdges(N4)).isEmpty(); + + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.predecessors(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.successors(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.incidentEdges(N1))); } @Test @@ -404,19 +394,48 @@ public void removeNode_nodeNotPresent() { } @Test - public void removeNode_queryAfterRemoval() { + public void queryAccessorSetAfterElementRemoval() { assume().that(graphIsMutable()).isTrue(); - addNode(N1); - @SuppressWarnings("unused") - Set unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated + putEdge(N1, N2); + putEdge(N2, N1); + Set n1AdjacentNodes = graph.adjacentNodes(N1); + Set n2AdjacentNodes = graph.adjacentNodes(N2); + Set n1Predecessors = graph.predecessors(N1); + Set n2Predecessors = graph.predecessors(N2); + Set n1Successors = graph.successors(N1); + Set n2Successors = graph.successors(N2); + Set> n1IncidentEdges = graph.incidentEdges(N1); + Set> n2IncidentEdges = graph.incidentEdges(N2); assertThat(graphAsMutableGraph.removeNode(N1)).isTrue(); - try { - graph.adjacentNodes(N1); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + + // The choice of the size() method to call here is arbitrary. We assume that if any of the Set + // methods executes the validation check, they all will, and thus we only need to test one of + // them to ensure that the validation check happens and has the expected behavior. + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1AdjacentNodes::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1Predecessors::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1Successors::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1IncidentEdges::size)); + + assertThat(n2AdjacentNodes).isEmpty(); + assertThat(n2Predecessors).isEmpty(); + assertThat(n2Successors).isEmpty(); + assertThat(n2IncidentEdges).isEmpty(); + } + + @Test + public void queryGraphAfterElementRemoval() { + assume().that(graphIsMutable()).isTrue(); + + putEdge(N1, N2); + putEdge(N2, N1); + assertThat(graphAsMutableGraph.removeNode(N1)).isTrue(); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1))); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java index 776e4bfea01f..15a9a989a746 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java @@ -16,15 +16,17 @@ package com.google.common.graph; -import static com.google.common.graph.TestUtil.ERROR_NODE_NOT_IN_GRAPH; import static com.google.common.graph.TestUtil.assertEdgeNotInGraphErrorMessage; +import static com.google.common.graph.TestUtil.assertEdgeRemovedFromGraphErrorMessage; import static com.google.common.graph.TestUtil.assertNodeNotInGraphErrorMessage; +import static com.google.common.graph.TestUtil.assertNodeRemovedFromGraphErrorMessage; import static com.google.common.graph.TestUtil.assertStronglyEquivalent; import static com.google.common.graph.TestUtil.sanityCheckSet; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; import static java.util.concurrent.Executors.newFixedThreadPool; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -36,6 +38,8 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -56,6 +60,7 @@ * TODO(user): Make this class generic (using ) for all node and edge types. * TODO(user): Differentiate between directed and undirected edge strings. */ +@NullUnmarked public abstract class AbstractNetworkTest { Network network; @@ -415,12 +420,9 @@ public void incidentEdges_isolatedNode() { @Test public void incidentEdges_nodeNotInGraph() { - try { - network.incidentEdges(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.incidentEdges(NODE_NOT_IN_GRAPH))); } @Test @@ -431,12 +433,9 @@ public void incidentNodes_oneEdge() { @Test public void incidentNodes_edgeNotInGraph() { - try { - network.incidentNodes(EDGE_NOT_IN_GRAPH); - fail(ERROR_EDGE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertEdgeNotInGraphErrorMessage(e); - } + assertEdgeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.incidentNodes(EDGE_NOT_IN_GRAPH))); } @Test @@ -454,12 +453,9 @@ public void adjacentNodes_noAdjacentNodes() { @Test public void adjacentNodes_nodeNotInGraph() { - try { - network.adjacentNodes(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.adjacentNodes(NODE_NOT_IN_GRAPH))); } @Test @@ -480,12 +476,9 @@ public void adjacentEdges_noAdjacentEdges() { @Test public void adjacentEdges_edgeNotInGraph() { - try { - network.adjacentEdges(EDGE_NOT_IN_GRAPH); - fail(ERROR_EDGE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertEdgeNotInGraphErrorMessage(e); - } + assertEdgeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.adjacentEdges(EDGE_NOT_IN_GRAPH))); } @Test @@ -511,24 +504,16 @@ public void edgesConnecting_disconnectedNodes() { public void edgesConnecting_nodesNotInGraph() { addNode(N1); addNode(N2); - try { - network.edgesConnecting(N1, NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } - try { - network.edgesConnecting(NODE_NOT_IN_GRAPH, N2); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } - try { - network.edgesConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.edgesConnecting(N1, NODE_NOT_IN_GRAPH))); + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.edgesConnecting(NODE_NOT_IN_GRAPH, N2))); + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, + () -> network.edgesConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH))); } @Test @@ -593,12 +578,8 @@ public void inEdges_noInEdges() { @Test public void inEdges_nodeNotInGraph() { - try { - network.inEdges(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.inEdges(NODE_NOT_IN_GRAPH))); } @Test @@ -609,12 +590,8 @@ public void outEdges_noOutEdges() { @Test public void outEdges_nodeNotInGraph() { - try { - network.outEdges(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.outEdges(NODE_NOT_IN_GRAPH))); } @Test @@ -625,12 +602,9 @@ public void predecessors_noPredecessors() { @Test public void predecessors_nodeNotInGraph() { - try { - network.predecessors(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> network.predecessors(NODE_NOT_IN_GRAPH))); } @Test @@ -641,12 +615,8 @@ public void successors_noSuccessors() { @Test public void successors_nodeNotInGraph() { - try { - network.successors(NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.successors(NODE_NOT_IN_GRAPH))); } @Test @@ -678,6 +648,28 @@ public void removeNode_existingNode() { assertThat(networkAsMutableNetwork.nodes()).containsExactly(N2, N4); assertThat(networkAsMutableNetwork.edges()).doesNotContain(E12); assertThat(networkAsMutableNetwork.edges()).doesNotContain(E41); + + assertThat(network.adjacentNodes(N2)).isEmpty(); + assertThat(network.predecessors(N2)).isEmpty(); + assertThat(network.successors(N2)).isEmpty(); + assertThat(network.incidentEdges(N2)).isEmpty(); + assertThat(network.inEdges(N2)).isEmpty(); + assertThat(network.outEdges(N2)).isEmpty(); + assertThat(network.adjacentNodes(N4)).isEmpty(); + assertThat(network.predecessors(N4)).isEmpty(); + assertThat(network.successors(N4)).isEmpty(); + assertThat(network.incidentEdges(N4)).isEmpty(); + assertThat(network.inEdges(N4)).isEmpty(); + assertThat(network.outEdges(N4)).isEmpty(); + + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.adjacentNodes(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.predecessors(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.successors(N1))); + assertNodeNotInGraphErrorMessage( + assertThrows(IllegalArgumentException.class, () -> network.incidentEdges(N1))); } @Test @@ -691,20 +683,52 @@ public void removeNode_nodeNotPresent() { } @Test - public void removeNode_queryAfterRemoval() { + public void queryAccessorSetAfterElementRemoval() { assume().that(graphIsMutable()).isTrue(); - addNode(N1); - @SuppressWarnings("unused") - Set unused = - networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated - assertTrue(networkAsMutableNetwork.removeNode(N1)); - try { - networkAsMutableNetwork.adjacentNodes(N1); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + addEdge(N1, N2, E12); + Set n1AdjacentNodes = network.adjacentNodes(N1); + Set n2AdjacentNodes = network.adjacentNodes(N2); + Set n1Predecessors = network.predecessors(N1); + Set n2Predecessors = network.predecessors(N2); + Set n1Successors = network.successors(N1); + Set n2Successors = network.successors(N2); + Set n1IncidentEdges = network.incidentEdges(N1); + Set n2IncidentEdges = network.incidentEdges(N2); + Set n1InEdges = network.inEdges(N1); + Set n2InEdges = network.inEdges(N2); + Set n1OutEdges = network.outEdges(N1); + Set n2OutEdges = network.outEdges(N2); + Set e12AdjacentEdges = network.adjacentEdges(E12); + Set n12EdgesConnecting = network.edgesConnecting(N1, N2); + assertThat(networkAsMutableNetwork.removeNode(N1)).isTrue(); + + // The choice of the size() method to call here is arbitrary. We assume that if any of the Set + // methods executes the validation check, they all will, and thus we only need to test one of + // them to ensure that the validation check happens and has the expected behavior. + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1AdjacentNodes::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1Predecessors::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1Successors::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1IncidentEdges::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1InEdges::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n1OutEdges::size)); + assertEdgeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, e12AdjacentEdges::size)); + assertNodeRemovedFromGraphErrorMessage( + assertThrows(IllegalStateException.class, n12EdgesConnecting::size)); + + assertThat(n2AdjacentNodes).isEmpty(); + assertThat(n2Predecessors).isEmpty(); + assertThat(n2Successors).isEmpty(); + assertThat(n2IncidentEdges).isEmpty(); + assertThat(n2InEdges).isEmpty(); + assertThat(n2OutEdges).isEmpty(); } @Test @@ -749,12 +773,9 @@ public void removeEdge_queryAfterRemoval() { EndpointPair unused = networkAsMutableNetwork.incidentNodes(E12); // ensure cache (if any) is populated assertTrue(networkAsMutableNetwork.removeEdge(E12)); - try { - networkAsMutableNetwork.incidentNodes(E12); - fail(ERROR_EDGE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertEdgeNotInGraphErrorMessage(e); - } + assertEdgeNotInGraphErrorMessage( + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.incidentNodes(E12))); } @Test @@ -785,7 +806,6 @@ public void removeEdge_parallelSelfLoopEdge() { assertThat(network.edgesConnecting(N1, N2)).containsExactly(E12); } - @Test public void concurrentIteration() throws Exception { addEdge(1, 2, "foo"); @@ -799,9 +819,9 @@ public void concurrentIteration() throws Exception { for (int i = 0; i < threadCount; i++) { futures.add( executor.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Object call() throws Exception { + public @Nullable Void call() throws Exception { barrier.await(); Integer first = network.nodes().iterator().next(); for (Integer node : network.nodes()) { @@ -835,7 +855,7 @@ public Object call() throws Exception { * synchronization actions.) * * All that said: I haven't actually managed to make this particular test produce a TSAN error - * for the field accesses in MapIteratorCache. This teset *has* found other TSAN errors, + * for the field accesses in MapIteratorCache. This test *has* found other TSAN errors, * including in MapRetrievalCache, so I'm not sure why this one is different. I did at least * confirm that my change to MapIteratorCache fixes the TSAN error in the (larger) test it was * originally reported in. diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java index c50a7da673a5..2e650a1fa378 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedGraphTest.java @@ -19,15 +19,17 @@ import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; /** * Abstract base class for testing directed {@link Graph} implementations defined in this package. */ +@NullUnmarked public abstract class AbstractStandardDirectedGraphTest extends AbstractGraphTest { @Override @@ -36,13 +38,9 @@ public void nodes_checkReturnedSetMutability() { assume().that(graphIsMutable()).isTrue(); Set nodes = graph.nodes(); - try { - nodes.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - addNode(N1); - assertThat(graph.nodes()).containsExactlyElementsIn(nodes); - } + assertThrows(UnsupportedOperationException.class, () -> nodes.add(N2)); + addNode(N1); + assertThat(graph.nodes()).containsExactlyElementsIn(nodes); } @Override @@ -52,13 +50,9 @@ public void adjacentNodes_checkReturnedSetMutability() { addNode(N1); Set adjacentNodes = graph.adjacentNodes(N1); - try { - adjacentNodes.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); - } + assertThrows(UnsupportedOperationException.class, () -> adjacentNodes.add(N2)); + putEdge(N1, N2); + assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); } @Override @@ -68,13 +62,9 @@ public void predecessors_checkReturnedSetMutability() { addNode(N2); Set predecessors = graph.predecessors(N2); - try { - predecessors.add(N1); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors); - } + assertThrows(UnsupportedOperationException.class, () -> predecessors.add(N1)); + putEdge(N1, N2); + assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors); } @Override @@ -84,13 +74,9 @@ public void successors_checkReturnedSetMutability() { addNode(N1); Set successors = graph.successors(N1); - try { - successors.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(successors).containsExactlyElementsIn(graph.successors(N1)); - } + assertThrows(UnsupportedOperationException.class, () -> successors.add(N2)); + putEdge(N1, N2); + assertThat(successors).containsExactlyElementsIn(graph.successors(N1)); } @Override @@ -100,13 +86,10 @@ public void incidentEdges_checkReturnedSetMutability() { addNode(N1); Set> incidentEdges = graph.incidentEdges(N1); - try { - incidentEdges.add(EndpointPair.ordered(N1, N2)); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1)); - } + assertThrows( + UnsupportedOperationException.class, () -> incidentEdges.add(EndpointPair.ordered(N1, N2))); + putEdge(N1, N2); + assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1)); } @Test @@ -364,12 +347,9 @@ public void putEdge_orderMismatch() { assume().that(graphIsMutable()).isTrue(); EndpointPair endpoints = EndpointPair.unordered(N1, N2); - try { - graphAsMutableGraph.putEdge(endpoints); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> graphAsMutableGraph.putEdge(endpoints)); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } /** @@ -398,12 +378,9 @@ public void putEdge_doesntAllowSelfLoops() { assume().that(graphIsMutable()).isTrue(); assume().that(graph.allowsSelfLoops()).isFalse(); - try { - graphAsMutableGraph.putEdge(N1, N1); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> graphAsMutableGraph.putEdge(N1, N1)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } @Test @@ -449,12 +426,10 @@ public void removeEdge_orderMismatch() { putEdge(N1, N2); EndpointPair endpoints = EndpointPair.unordered(N1, N2); - try { - graphAsMutableGraph.removeEdge(endpoints); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> graphAsMutableGraph.removeEdge(endpoints)); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java index 98bc2fd6540b..eec759a2c31c 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardDirectedNetworkTest.java @@ -20,18 +20,21 @@ import static com.google.common.graph.TestUtil.assertEdgeNotInGraphErrorMessage; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableSet; import java.util.Collections; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.After; import org.junit.Test; /** * Abstract base class for testing directed {@link Network} implementations defined in this package. */ +@NullUnmarked public abstract class AbstractStandardDirectedNetworkTest extends AbstractNetworkTest { @After @@ -64,13 +67,9 @@ public void nodes_checkReturnedSetMutability() { assume().that(graphIsMutable()).isTrue(); Set nodes = network.nodes(); - try { - nodes.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addNode(N1); - assertThat(network.nodes()).containsExactlyElementsIn(nodes); - } + assertThrows(UnsupportedOperationException.class, () -> nodes.add(N2)); + addNode(N1); + assertThat(network.nodes()).containsExactlyElementsIn(nodes); } @Override @@ -79,13 +78,9 @@ public void edges_checkReturnedSetMutability() { assume().that(graphIsMutable()).isTrue(); Set edges = network.edges(); - try { - edges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.edges()).containsExactlyElementsIn(edges); - } + assertThrows(UnsupportedOperationException.class, () -> edges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.edges()).containsExactlyElementsIn(edges); } @Override @@ -95,13 +90,9 @@ public void incidentEdges_checkReturnedSetMutability() { addNode(N1); Set incidentEdges = network.incidentEdges(N1); - try { - incidentEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges); - } + assertThrows(UnsupportedOperationException.class, () -> incidentEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges); } @Override @@ -111,13 +102,9 @@ public void adjacentNodes_checkReturnedSetMutability() { addNode(N1); Set adjacentNodes = network.adjacentNodes(N1); - try { - adjacentNodes.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); - } + assertThrows(UnsupportedOperationException.class, () -> adjacentNodes.add(N2)); + addEdge(N1, N2, E12); + assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); } @Override @@ -143,13 +130,9 @@ public void edgesConnecting_checkReturnedSetMutability() { addNode(N1); addNode(N2); Set edgesConnecting = network.edgesConnecting(N1, N2); - try { - edgesConnecting.add(E23); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); - } + assertThrows(UnsupportedOperationException.class, () -> edgesConnecting.add(E23)); + addEdge(N1, N2, E12); + assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); } @Override @@ -159,13 +142,9 @@ public void inEdges_checkReturnedSetMutability() { addNode(N2); Set inEdges = network.inEdges(N2); - try { - inEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges); - } + assertThrows(UnsupportedOperationException.class, () -> inEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges); } @Override @@ -175,13 +154,9 @@ public void outEdges_checkReturnedSetMutability() { addNode(N1); Set outEdges = network.outEdges(N1); - try { - outEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges); - } + assertThrows(UnsupportedOperationException.class, () -> outEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges); } @Override @@ -191,13 +166,9 @@ public void predecessors_checkReturnedSetMutability() { addNode(N2); Set predecessors = network.predecessors(N2); - try { - predecessors.add(N1); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors); - } + assertThrows(UnsupportedOperationException.class, () -> predecessors.add(N1)); + addEdge(N1, N2, E12); + assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors); } @Override @@ -207,13 +178,9 @@ public void successors_checkReturnedSetMutability() { addNode(N1); Set successors = network.successors(N1); - try { - successors.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(successors).containsExactlyElementsIn(network.successors(N1)); - } + assertThrows(UnsupportedOperationException.class, () -> successors.add(N2)); + addEdge(N1, N2, E12); + assertThat(successors).containsExactlyElementsIn(network.successors(N1)); } @Test @@ -228,23 +195,25 @@ public void edges_containsOrderMismatch() { @Test public void edgesConnecting_orderMismatch() { addEdge(N1, N2, E12); - try { - Set unused = network.edgesConnecting(EndpointPair.unordered(N1, N2)); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> { + Set unused = network.edgesConnecting(EndpointPair.unordered(N1, N2)); + }); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test public void edgeConnectingOrNull_orderMismatch() { addEdge(N1, N2, E12); - try { - String unused = network.edgeConnectingOrNull(EndpointPair.unordered(N1, N2)); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> { + String unused = network.edgeConnectingOrNull(EndpointPair.unordered(N1, N2)); + }); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Override @@ -304,12 +273,11 @@ public void source_oneEdge() { @Test public void source_edgeNotInGraph() { - try { - network.incidentNodes(EDGE_NOT_IN_GRAPH).source(); - fail(ERROR_EDGE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertEdgeNotInGraphErrorMessage(e); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> network.incidentNodes(EDGE_NOT_IN_GRAPH).source()); + assertEdgeNotInGraphErrorMessage(e); } @Test @@ -320,12 +288,11 @@ public void target_oneEdge() { @Test public void target_edgeNotInGraph() { - try { - network.incidentNodes(EDGE_NOT_IN_GRAPH).target(); - fail(ERROR_EDGE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertEdgeNotInGraphErrorMessage(e); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> network.incidentNodes(EDGE_NOT_IN_GRAPH).target()); + assertEdgeNotInGraphErrorMessage(e); } @Test @@ -516,20 +483,12 @@ public void addEdge_existingEdgeBetweenDifferentNodes() { assume().that(graphIsMutable()).isTrue(); addEdge(N1, N2, E12); - try { - // Edge between totally different nodes - networkAsMutableNetwork.addEdge(N4, N5, E12); - fail(ERROR_ADDED_EXISTING_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); - } - try { - // Edge between same nodes but in reverse direction - addEdge(N2, N1, E12); - fail(ERROR_ADDED_EXISTING_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N4, N5, E12)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); + e = assertThrows(IllegalArgumentException.class, () -> addEdge(N2, N1, E12)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); } @Test @@ -538,12 +497,11 @@ public void addEdge_parallelEdge_notAllowed() { assume().that(network.allowsParallelEdges()).isFalse(); addEdge(N1, N2, E12); - try { - networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); } @Test @@ -561,12 +519,10 @@ public void addEdge_orderMismatch() { assume().that(graphIsMutable()).isTrue(); EndpointPair endpoints = EndpointPair.unordered(N1, N2); - try { - networkAsMutableNetwork.addEdge(endpoints, E12); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(endpoints, E12)); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -574,12 +530,10 @@ public void addEdge_selfLoop_notAllowed() { assume().that(graphIsMutable()).isTrue(); assume().that(network.allowsSelfLoops()).isFalse(); - try { - networkAsMutableNetwork.addEdge(N1, N1, E11); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N1, E11)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } /** @@ -632,25 +586,19 @@ public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() { assume().that(network.allowsSelfLoops()).isTrue(); addEdge(N1, N1, E11); - try { - networkAsMutableNetwork.addEdge(N1, N2, E11); - fail("Reusing an existing self-loop edge to connect different nodes succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } - try { - networkAsMutableNetwork.addEdge(N2, N2, E11); - fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N2, E11)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); + e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N2, N2, E11)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); addEdge(N1, N2, E12); - try { - networkAsMutableNetwork.addEdge(N1, N1, E12); - fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } + e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N1, E12)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); } @Test @@ -660,12 +608,11 @@ public void addEdge_parallelSelfLoopEdge_notAllowed() { assume().that(network.allowsParallelEdges()).isFalse(); addEdge(N1, N1, E11); - try { - networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH); - fail("Adding a parallel self-loop edge succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java index a483f42d1720..d0f8d38163da 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedGraphTest.java @@ -18,17 +18,19 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import com.google.common.testing.EqualsTester; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.After; import org.junit.Test; /** * Abstract base class for testing undirected {@link Graph} implementations defined in this package. */ +@NullUnmarked public abstract class AbstractStandardUndirectedGraphTest extends AbstractGraphTest { @After @@ -47,13 +49,9 @@ public void nodes_checkReturnedSetMutability() { assume().that(graphIsMutable()).isTrue(); Set nodes = graph.nodes(); - try { - nodes.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - addNode(N1); - assertThat(graph.nodes()).containsExactlyElementsIn(nodes); - } + assertThrows(UnsupportedOperationException.class, () -> nodes.add(N2)); + addNode(N1); + assertThat(graph.nodes()).containsExactlyElementsIn(nodes); } @Override @@ -63,13 +61,9 @@ public void adjacentNodes_checkReturnedSetMutability() { addNode(N1); Set adjacentNodes = graph.adjacentNodes(N1); - try { - adjacentNodes.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); - } + assertThrows(UnsupportedOperationException.class, () -> adjacentNodes.add(N2)); + putEdge(N1, N2); + assertThat(graph.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); } @Override @@ -79,13 +73,9 @@ public void predecessors_checkReturnedSetMutability() { addNode(N2); Set predecessors = graph.predecessors(N2); - try { - predecessors.add(N1); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors); - } + assertThrows(UnsupportedOperationException.class, () -> predecessors.add(N1)); + putEdge(N1, N2); + assertThat(graph.predecessors(N2)).containsExactlyElementsIn(predecessors); } @Override @@ -95,13 +85,9 @@ public void successors_checkReturnedSetMutability() { addNode(N1); Set successors = graph.successors(N1); - try { - successors.add(N2); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(graph.successors(N1)).containsExactlyElementsIn(successors); - } + assertThrows(UnsupportedOperationException.class, () -> successors.add(N2)); + putEdge(N1, N2); + assertThat(graph.successors(N1)).containsExactlyElementsIn(successors); } @Override @@ -111,13 +97,11 @@ public void incidentEdges_checkReturnedSetMutability() { addNode(N1); Set> incidentEdges = graph.incidentEdges(N1); - try { - incidentEdges.add(EndpointPair.unordered(N1, N2)); - fail(ERROR_MODIFIABLE_SET); - } catch (UnsupportedOperationException e) { - putEdge(N1, N2); - assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1)); - } + assertThrows( + UnsupportedOperationException.class, + () -> incidentEdges.add(EndpointPair.unordered(N1, N2))); + putEdge(N1, N2); + assertThat(incidentEdges).containsExactlyElementsIn(graph.incidentEdges(N1)); } @Test @@ -166,8 +150,8 @@ public void hasEdgeConnecting_correct() { @Test public void hasEdgeConnecting_mismatch() { putEdge(N1, N2); - assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isTrue(); - assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isTrue(); + assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N1, N2))).isFalse(); + assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(N2, N1))).isFalse(); } @Test @@ -380,12 +364,9 @@ public void putEdge_doesntAllowSelfLoops() { assume().that(graphIsMutable()).isTrue(); assume().that(graph.allowsSelfLoops()).isFalse(); - try { - putEdge(N1, N1); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> putEdge(N1, N1)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java index 5cda1c1bb767..8adbaa40c360 100644 --- a/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/AbstractStandardUndirectedNetworkTest.java @@ -16,14 +16,17 @@ package com.google.common.graph; +import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableSet; import com.google.common.testing.EqualsTester; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.After; import org.junit.Test; @@ -31,6 +34,7 @@ * Abstract base class for testing undirected {@link Network} implementations defined in this * package. */ +@NullUnmarked public abstract class AbstractStandardUndirectedNetworkTest extends AbstractNetworkTest { private static final EndpointPair ENDPOINTS_N1N2 = EndpointPair.ordered(N1, N2); private static final EndpointPair ENDPOINTS_N2N1 = EndpointPair.ordered(N2, N1); @@ -58,26 +62,18 @@ public void validateUndirectedEdges() { @Test public void nodes_checkReturnedSetMutability() { Set nodes = network.nodes(); - try { - nodes.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addNode(N1); - assertThat(network.nodes()).containsExactlyElementsIn(nodes); - } + assertThrows(UnsupportedOperationException.class, () -> nodes.add(N2)); + addNode(N1); + assertThat(network.nodes()).containsExactlyElementsIn(nodes); } @Override @Test public void edges_checkReturnedSetMutability() { Set edges = network.edges(); - try { - edges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.edges()).containsExactlyElementsIn(edges); - } + assertThrows(UnsupportedOperationException.class, () -> edges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.edges()).containsExactlyElementsIn(edges); } @Override @@ -85,13 +81,9 @@ public void edges_checkReturnedSetMutability() { public void incidentEdges_checkReturnedSetMutability() { addNode(N1); Set incidentEdges = network.incidentEdges(N1); - try { - incidentEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges); - } + assertThrows(UnsupportedOperationException.class, () -> incidentEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.incidentEdges(N1)).containsExactlyElementsIn(incidentEdges); } @Override @@ -99,13 +91,9 @@ public void incidentEdges_checkReturnedSetMutability() { public void adjacentNodes_checkReturnedSetMutability() { addNode(N1); Set adjacentNodes = network.adjacentNodes(N1); - try { - adjacentNodes.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); - } + assertThrows(UnsupportedOperationException.class, () -> adjacentNodes.add(N2)); + addEdge(N1, N2, E12); + assertThat(network.adjacentNodes(N1)).containsExactlyElementsIn(adjacentNodes); } @Override @@ -127,13 +115,9 @@ public void edgesConnecting_checkReturnedSetMutability() { addNode(N1); addNode(N2); Set edgesConnecting = network.edgesConnecting(N1, N2); - try { - edgesConnecting.add(E23); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); - } + assertThrows(UnsupportedOperationException.class, () -> edgesConnecting.add(E23)); + addEdge(N1, N2, E12); + assertThat(network.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); } @Override @@ -141,13 +125,9 @@ public void edgesConnecting_checkReturnedSetMutability() { public void inEdges_checkReturnedSetMutability() { addNode(N2); Set inEdges = network.inEdges(N2); - try { - inEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges); - } + assertThrows(UnsupportedOperationException.class, () -> inEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.inEdges(N2)).containsExactlyElementsIn(inEdges); } @Override @@ -155,13 +135,9 @@ public void inEdges_checkReturnedSetMutability() { public void outEdges_checkReturnedSetMutability() { addNode(N1); Set outEdges = network.outEdges(N1); - try { - outEdges.add(E12); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges); - } + assertThrows(UnsupportedOperationException.class, () -> outEdges.add(E12)); + addEdge(N1, N2, E12); + assertThat(network.outEdges(N1)).containsExactlyElementsIn(outEdges); } @Override @@ -169,13 +145,9 @@ public void outEdges_checkReturnedSetMutability() { public void predecessors_checkReturnedSetMutability() { addNode(N2); Set predecessors = network.predecessors(N2); - try { - predecessors.add(N1); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors); - } + assertThrows(UnsupportedOperationException.class, () -> predecessors.add(N1)); + addEdge(N1, N2, E12); + assertThat(network.predecessors(N2)).containsExactlyElementsIn(predecessors); } @Override @@ -183,27 +155,40 @@ public void predecessors_checkReturnedSetMutability() { public void successors_checkReturnedSetMutability() { addNode(N1); Set successors = network.successors(N1); - try { - successors.add(N2); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - addEdge(N1, N2, E12); - assertThat(network.successors(N1)).containsExactlyElementsIn(successors); - } + assertThrows(UnsupportedOperationException.class, () -> successors.add(N2)); + addEdge(N1, N2, E12); + assertThat(network.successors(N1)).containsExactlyElementsIn(successors); } @Test public void edges_containsOrderMismatch() { addEdge(N1, N2, E12); - assertThat(network.asGraph().edges()).contains(ENDPOINTS_N2N1); - assertThat(network.asGraph().edges()).contains(ENDPOINTS_N1N2); + assertThat(network.asGraph().edges()).doesNotContain(ENDPOINTS_N2N1); + assertThat(network.asGraph().edges()).doesNotContain(ENDPOINTS_N1N2); + } + + @Test + public void edgesConnecting_orderMismatch() { + addEdge(N1, N2, E12); + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> { + Set unused = network.edgesConnecting(ENDPOINTS_N1N2); + }); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test public void edgeConnectingOrNull_orderMismatch() { addEdge(N1, N2, E12); - assertThat(network.edgeConnectingOrNull(ENDPOINTS_N2N1)).isEqualTo(E12); - assertThat(network.edgeConnectingOrNull(ENDPOINTS_N1N2)).isEqualTo(E12); + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> { + String unused = network.edgeConnectingOrNull(ENDPOINTS_N1N2); + }); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -413,13 +398,10 @@ public void addEdge_existingEdgeBetweenDifferentNodes() { assume().that(graphIsMutable()).isTrue(); addEdge(N1, N2, E12); - try { - // Edge between totally different nodes - networkAsMutableNetwork.addEdge(N4, N5, E12); - fail(ERROR_ADDED_EXISTING_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N4, N5, E12)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); } @Test @@ -428,18 +410,16 @@ public void addEdge_parallelEdge_notAllowed() { assume().that(network.allowsParallelEdges()).isFalse(); addEdge(N1, N2, E12); - try { - networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } - try { - networkAsMutableNetwork.addEdge(N2, N1, EDGE_NOT_IN_GRAPH); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> networkAsMutableNetwork.addEdge(N1, N2, EDGE_NOT_IN_GRAPH)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); + e = + assertThrows( + IllegalArgumentException.class, + () -> networkAsMutableNetwork.addEdge(N2, N1, EDGE_NOT_IN_GRAPH)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); } @Test @@ -458,7 +438,10 @@ public void addEdge_orderMismatch() { assume().that(graphIsMutable()).isTrue(); EndpointPair endpoints = EndpointPair.ordered(N1, N2); - assertThat(networkAsMutableNetwork.addEdge(endpoints, E12)).isTrue(); + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(endpoints, E12)); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -466,12 +449,10 @@ public void addEdge_selfLoop_notAllowed() { assume().that(graphIsMutable()).isTrue(); assume().that(network.allowsSelfLoops()).isFalse(); - try { - networkAsMutableNetwork.addEdge(N1, N1, E11); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N1, E11)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } /** @@ -523,25 +504,19 @@ public void addEdge_existingEdgeBetweenDifferentNodes_selfLoops() { assume().that(network.allowsSelfLoops()).isTrue(); addEdge(N1, N1, E11); - try { - networkAsMutableNetwork.addEdge(N1, N2, E11); - fail("Reusing an existing self-loop edge to connect different nodes succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } - try { - networkAsMutableNetwork.addEdge(N2, N2, E11); - fail("Reusing an existing self-loop edge to make a different self-loop edge succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N2, E11)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); + e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N2, N2, E11)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); addEdge(N1, N2, E12); - try { - networkAsMutableNetwork.addEdge(N1, N1, E12); - fail("Reusing an existing edge to add a self-loop edge between different nodes succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_REUSE_EDGE); - } + e = + assertThrows( + IllegalArgumentException.class, () -> networkAsMutableNetwork.addEdge(N1, N1, E12)); + assertThat(e).hasMessageThat().contains(ERROR_REUSE_EDGE); } @Test @@ -551,12 +526,11 @@ public void addEdge_parallelSelfLoopEdge_notAllowed() { assume().that(network.allowsParallelEdges()).isFalse(); addEdge(N1, N1, E11); - try { - networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH); - fail("Adding a parallel self-loop edge succeeded"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> networkAsMutableNetwork.addEdge(N1, N1, EDGE_NOT_IN_GRAPH)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/graph/AndroidIncompatible.java index 239cafc8c8a2..489fea33f67d 100644 --- a/android/guava-tests/test/com/google/common/graph/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/graph/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/graph/DefaultNetworkImplementationsTest.java b/android/guava-tests/test/com/google/common/graph/DefaultNetworkImplementationsTest.java index eaddb949c002..bf5a387b0290 100644 --- a/android/guava-tests/test/com/google/common/graph/DefaultNetworkImplementationsTest.java +++ b/android/guava-tests/test/com/google/common/graph/DefaultNetworkImplementationsTest.java @@ -16,18 +16,17 @@ package com.google.common.graph; -import static com.google.common.graph.AbstractNetworkTest.ERROR_MODIFIABLE_COLLECTION; -import static com.google.common.graph.TestUtil.ERROR_NODE_NOT_IN_GRAPH; import static com.google.common.graph.TestUtil.EdgeType.DIRECTED; import static com.google.common.graph.TestUtil.EdgeType.UNDIRECTED; import static com.google.common.graph.TestUtil.assertNodeNotInGraphErrorMessage; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.graph.TestUtil.EdgeType; import java.util.Arrays; import java.util.Collection; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,6 +41,7 @@ @AndroidIncompatible // TODO(cpovirk): Figure out Android JUnit 4 support. Does it work with Gingerbread? @RunWith? @RunWith(Parameterized.class) +@NullUnmarked public final class DefaultNetworkImplementationsTest { private MutableNetwork network; private NetworkForTest networkForTest; @@ -89,24 +89,21 @@ public void edgesConnecting_disconnectedNodes() { public void edgesConnecting_nodesNotInGraph() { network.addNode(N1); network.addNode(N2); - try { - networkForTest.edgesConnecting(N1, NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } - try { - networkForTest.edgesConnecting(NODE_NOT_IN_GRAPH, N2); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } - try { - networkForTest.edgesConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH); - fail(ERROR_NODE_NOT_IN_GRAPH); - } catch (IllegalArgumentException e) { - assertNodeNotInGraphErrorMessage(e); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> networkForTest.edgesConnecting(N1, NODE_NOT_IN_GRAPH)); + assertNodeNotInGraphErrorMessage(e); + e = + assertThrows( + IllegalArgumentException.class, + () -> networkForTest.edgesConnecting(NODE_NOT_IN_GRAPH, N2)); + assertNodeNotInGraphErrorMessage(e); + e = + assertThrows( + IllegalArgumentException.class, + () -> networkForTest.edgesConnecting(NODE_NOT_IN_GRAPH, NODE_NOT_IN_GRAPH)); + assertNodeNotInGraphErrorMessage(e); } @Test @@ -114,13 +111,9 @@ public void edgesConnecting_checkReturnedSetMutability() { network.addNode(N1); network.addNode(N2); Set edgesConnecting = network.edgesConnecting(N1, N2); - try { - edgesConnecting.add(E23); - fail(ERROR_MODIFIABLE_COLLECTION); - } catch (UnsupportedOperationException e) { - network.addEdge(N1, N2, E12); - assertThat(networkForTest.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); - } + assertThrows(UnsupportedOperationException.class, () -> edgesConnecting.add(E23)); + network.addEdge(N1, N2, E12); + assertThat(networkForTest.edgesConnecting(N1, N2)).containsExactlyElementsIn(edgesConnecting); } @Test diff --git a/android/guava-tests/test/com/google/common/graph/ElementOrderTest.java b/android/guava-tests/test/com/google/common/graph/ElementOrderTest.java index 0557fb7babb9..a50b0400fe45 100644 --- a/android/guava-tests/test/com/google/common/graph/ElementOrderTest.java +++ b/android/guava-tests/test/com/google/common/graph/ElementOrderTest.java @@ -23,12 +23,14 @@ import com.google.common.collect.Ordering; import java.util.Comparator; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for ordering the elements of graphs. */ @RunWith(JUnit4.class) +@NullUnmarked public final class ElementOrderTest { // Node order tests @@ -150,7 +152,7 @@ public void edgeOrder_sorted() { // Combined node and edge order tests @Test - public void nodeOrderUnorderedandEdgesSorted() { + public void nodeOrderUnorderedAndEdgesSorted() { MutableNetwork network = NetworkBuilder.directed() .nodeOrder(unordered()) diff --git a/android/guava-tests/test/com/google/common/graph/EndpointPairTest.java b/android/guava-tests/test/com/google/common/graph/EndpointPairTest.java index 099be1d22bd0..a304ed7b8c43 100644 --- a/android/guava-tests/test/com/google/common/graph/EndpointPairTest.java +++ b/android/guava-tests/test/com/google/common/graph/EndpointPairTest.java @@ -17,19 +17,21 @@ package com.google.common.graph; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.testing.EqualsTester; import java.util.Collection; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link EndpointPair} and {@link Graph#edges()}. */ @RunWith(JUnit4.class) +@NullUnmarked public final class EndpointPairTest { private static final Integer N0 = 0; private static final Integer N1 = 1; @@ -91,11 +93,7 @@ public void testAdjacentNode_nodeNotIncident() { for (MutableNetwork network : testNetworks) { network.addEdge(1, 2, "1-2"); EndpointPair endpointPair = network.incidentNodes("1-2"); - try { - endpointPair.adjacentNode(3); - fail("Should have rejected adjacentNode() called with a node not incident to edge."); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> endpointPair.adjacentNode(3)); } } @@ -195,11 +193,8 @@ public void endpointPair_unmodifiableView() { directedGraph.removeEdge(N2, N1); containsExactlySanityCheck(edges); - try { - edges.add(EndpointPair.ordered(N1, N2)); - fail("Set returned by edges() should be unmodifiable"); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> edges.add(EndpointPair.ordered(N1, N2))); } @Test @@ -214,8 +209,8 @@ public void endpointPair_undirected_contains() { assertThat(edges).contains(EndpointPair.unordered(N1, N2)); assertThat(edges).contains(EndpointPair.unordered(N2, N1)); // equal to unordered(N1, N2) - // ordered endpoints OK for undirected graph (because ordering is irrelevant) - assertThat(edges).contains(EndpointPair.ordered(N1, N2)); + // ordered endpoints not compatible with undirected graph + assertThat(edges).doesNotContain(EndpointPair.ordered(N1, N2)); assertThat(edges).doesNotContain(EndpointPair.unordered(N2, N2)); // edge not present assertThat(edges).doesNotContain(EndpointPair.unordered(N3, N4)); // nodes not in graph diff --git a/android/guava-tests/test/com/google/common/graph/GraphEquivalenceTest.java b/android/guava-tests/test/com/google/common/graph/GraphEquivalenceTest.java index 38e3903aaa87..7d45f55913d5 100644 --- a/android/guava-tests/test/com/google/common/graph/GraphEquivalenceTest.java +++ b/android/guava-tests/test/com/google/common/graph/GraphEquivalenceTest.java @@ -23,6 +23,7 @@ import com.google.common.graph.TestUtil.EdgeType; import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,6 +32,7 @@ @AndroidIncompatible // TODO(cpovirk): Figure out Android JUnit 4 support. Does it work with Gingerbread? @RunWith? @RunWith(Parameterized.class) +@NullUnmarked public final class GraphEquivalenceTest { private static final Integer N1 = 1; private static final Integer N2 = 2; @@ -56,9 +58,8 @@ private static MutableGraph createGraph(EdgeType edgeType) { return GraphBuilder.undirected().allowsSelfLoops(true).build(); case DIRECTED: return GraphBuilder.directed().allowsSelfLoops(true).build(); - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } + throw new IllegalStateException("Unexpected edge type: " + edgeType); } private static EdgeType oppositeType(EdgeType edgeType) { @@ -67,9 +68,8 @@ private static EdgeType oppositeType(EdgeType edgeType) { return EdgeType.DIRECTED; case DIRECTED: return EdgeType.UNDIRECTED; - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } + throw new IllegalStateException("Unexpected edge type: " + edgeType); } @Test @@ -150,8 +150,6 @@ public void equivalent_edgeDirectionsDiffer() { case DIRECTED: assertThat(graph).isNotEqualTo(g2); break; - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } } } diff --git a/android/guava-tests/test/com/google/common/graph/GraphMutationTest.java b/android/guava-tests/test/com/google/common/graph/GraphMutationTest.java index 82ff96756723..3d8d51d04466 100644 --- a/android/guava-tests/test/com/google/common/graph/GraphMutationTest.java +++ b/android/guava-tests/test/com/google/common/graph/GraphMutationTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Random; import java.util.RandomAccess; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -30,6 +31,7 @@ /** Tests for repeated node and edge addition and removal in a {@link Graph}. */ @RunWith(JUnit4.class) +@NullUnmarked public final class GraphMutationTest { private static final int NUM_TRIALS = 50; private static final int NUM_NODES = 100; diff --git a/android/guava-tests/test/com/google/common/graph/GraphPropertiesTest.java b/android/guava-tests/test/com/google/common/graph/GraphPropertiesTest.java index d81edaf6678c..bb1d25e77f99 100644 --- a/android/guava-tests/test/com/google/common/graph/GraphPropertiesTest.java +++ b/android/guava-tests/test/com/google/common/graph/GraphPropertiesTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; +import org.jspecify.annotations.NullUnmarked; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,6 +29,7 @@ /** Tests for {@link Graphs#hasCycle(Graph)} and {@link Graphs#hasCycle(Network)}. */ // TODO(user): Consider moving this to GraphsTest. @RunWith(JUnit4.class) +@NullUnmarked public class GraphPropertiesTest { ImmutableList> graphsToTest; Graph directedGraph; @@ -155,6 +157,17 @@ public void hasCycle_multipleCycles() { assertThat(hasCycle(undirectedGraph)).isTrue(); } + @Test + public void hasCycle_deepPathGraph() { + for (MutableGraph graph : graphsToTest) { + for (int i = 0; i < 100000; i++) { + graph.putEdge(i, i + 1); + } + } + assertThat(hasCycle(directedNetwork)).isFalse(); + assertThat(hasCycle(undirectedNetwork)).isFalse(); + } + @Test public void hasCycle_twoParallelEdges() { for (MutableNetwork network : networksToTest) { @@ -176,4 +189,15 @@ public void hasCycle_cyclicMultigraph() { assertThat(hasCycle(directedNetwork)).isTrue(); assertThat(hasCycle(undirectedNetwork)).isTrue(); } + + @Test + public void hasCycle_deepPathNetwork() { + for (MutableNetwork network : networksToTest) { + for (int i = 0; i < 100000; i++) { + network.addEdge(i, i + 1, Integer.toString(i)); + } + } + assertThat(hasCycle(directedNetwork)).isFalse(); + assertThat(hasCycle(undirectedNetwork)).isFalse(); + } } diff --git a/android/guava-tests/test/com/google/common/graph/GraphsTest.java b/android/guava-tests/test/com/google/common/graph/GraphsTest.java index a07def019c68..20199ee8feb3 100644 --- a/android/guava-tests/test/com/google/common/graph/GraphsTest.java +++ b/android/guava-tests/test/com/google/common/graph/GraphsTest.java @@ -22,10 +22,11 @@ import static com.google.common.graph.Graphs.transitiveClosure; import static com.google.common.graph.Graphs.transpose; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -35,6 +36,7 @@ * the missing nodes to the graph, then adds the edge between them. */ @RunWith(JUnit4.class) +@NullUnmarked public class GraphsTest { private static final Integer N1 = 1; private static final Integer N2 = 2; @@ -56,10 +58,6 @@ public class GraphsTest { // in one class (may be a utility class for error messages). private static final String ERROR_PARALLEL_EDGE = "connected by a different edge"; private static final String ERROR_NEGATIVE_COUNT = "is non-negative"; - private static final String ERROR_ADDED_PARALLEL_EDGE = - "Should not be allowed to add a parallel edge."; - private static final String ERROR_ADDED_SELF_LOOP = - "Should not be allowed to add a self-loop edge."; static final String ERROR_SELF_LOOP = "self-loops are not allowed"; @Test @@ -399,20 +397,14 @@ public void inducedSubgraph_network() { public void inducedSubgraph_nodeNotInGraph() { MutableNetwork undirectedGraph = NetworkBuilder.undirected().build(); - try { - inducedSubgraph(undirectedGraph, ImmutableSet.of(N1)); - fail("Should have rejected getting induced subgraph with node not in original graph."); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> inducedSubgraph(undirectedGraph, ImmutableSet.of(N1))); } @Test public void copyOf_nullArgument() { - try { - copyOf((Graph) null); - fail("Should have rejected a null graph."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> copyOf((Graph) null)); } @Test @@ -475,20 +467,13 @@ public void createDirected() { assertThat(directedGraph.edgesConnecting(N2, N1)).isEmpty(); // By default, parallel edges are not allowed. - try { - directedGraph.addEdge(N1, N2, E12_A); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> directedGraph.addEdge(N1, N2, E12_A)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); // By default, self-loop edges are not allowed. - try { - directedGraph.addEdge(N1, N1, E11); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + e = assertThrows(IllegalArgumentException.class, () -> directedGraph.addEdge(N1, N1, E11)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } @Test @@ -501,26 +486,15 @@ public void createUndirected() { assertThat(undirectedGraph.edgesConnecting(N2, N1)).isEqualTo(ImmutableSet.of(E12)); // By default, parallel edges are not allowed. - try { - undirectedGraph.addEdge(N1, N2, E12_A); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } - try { - undirectedGraph.addEdge(N2, N1, E21); - fail(ERROR_ADDED_PARALLEL_EDGE); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_PARALLEL_EDGE); - } + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> undirectedGraph.addEdge(N1, N2, E12_A)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); + e = assertThrows(IllegalArgumentException.class, () -> undirectedGraph.addEdge(N2, N1, E21)); + assertThat(e).hasMessageThat().contains(ERROR_PARALLEL_EDGE); // By default, self-loop edges are not allowed. - try { - undirectedGraph.addEdge(N1, N1, E11); - fail(ERROR_ADDED_SELF_LOOP); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); - } + e = assertThrows(IllegalArgumentException.class, () -> undirectedGraph.addEdge(N1, N1, E11)); + assertThat(e).hasMessageThat().contains(ERROR_SELF_LOOP); } @Test @@ -564,12 +538,10 @@ public void createUndirected_expectedNodeCount() { @Test public void builder_expectedNodeCount_negative() { - try { - NetworkBuilder.directed().expectedNodeCount(-1); - fail("Should have rejected negative expected node count."); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_NEGATIVE_COUNT); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> NetworkBuilder.directed().expectedNodeCount(-1)); + assertThat(e).hasMessageThat().contains(ERROR_NEGATIVE_COUNT); } @Test @@ -592,12 +564,10 @@ public void createUndirected_expectedEdgeCount() { @Test public void builder_expectedEdgeCount_negative() { - try { - NetworkBuilder.directed().expectedEdgeCount(-1); - fail("Should have rejected negative expected edge count."); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).contains(ERROR_NEGATIVE_COUNT); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> NetworkBuilder.directed().expectedEdgeCount(-1)); + assertThat(e).hasMessageThat().contains(ERROR_NEGATIVE_COUNT); } private static void checkTransitiveClosure(Graph originalGraph, Graph expectedClosure) { diff --git a/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java b/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java index 5de3cf751f8e..9d4889fd2634 100644 --- a/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/ImmutableNetworkTest.java @@ -18,12 +18,14 @@ import static com.google.common.truth.Truth.assertThat; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link ImmutableNetwork}. */ @RunWith(JUnit4.class) +@NullUnmarked public class ImmutableNetworkTest { @Test diff --git a/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java b/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java index 8e5e67f3046e..43f48d91c986 100644 --- a/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/ImmutableValueGraphTest.java @@ -18,12 +18,14 @@ import static com.google.common.truth.Truth.assertThat; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link ImmutableValueGraph} . */ @RunWith(JUnit4.class) +@NullUnmarked public class ImmutableValueGraphTest { @Test diff --git a/android/guava-tests/test/com/google/common/graph/InvalidatableSetTest.java b/android/guava-tests/test/com/google/common/graph/InvalidatableSetTest.java new file mode 100644 index 000000000000..1f393acd25ce --- /dev/null +++ b/android/guava-tests/test/com/google/common/graph/InvalidatableSetTest.java @@ -0,0 +1,62 @@ +package com.google.common.graph; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import com.google.common.collect.ImmutableSet; +import java.util.HashSet; +import java.util.Set; +import org.jspecify.annotations.NullUnmarked; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@NullUnmarked +public final class InvalidatableSetTest { + Set wrappedSet; + Set copyOfWrappedSet; + InvalidatableSet setToTest; + + @Before + public void createSets() { + wrappedSet = new HashSet<>(); + wrappedSet.add(1); + wrappedSet.add(2); + wrappedSet.add(3); + + copyOfWrappedSet = ImmutableSet.copyOf(wrappedSet); + setToTest = + InvalidatableSet.of(wrappedSet, () -> wrappedSet.contains(1), () -> 1 + "is not present"); + } + + @Test + @SuppressWarnings("TruthSelfEquals") + public void testEquals() { + // sanity check on construction of copyOfWrappedSet + assertThat(wrappedSet).isEqualTo(copyOfWrappedSet); + + // test that setToTest is still valid + assertThat(setToTest).isEqualTo(wrappedSet); + assertThat(setToTest).isEqualTo(copyOfWrappedSet); + + // invalidate setToTest + wrappedSet.remove(1); + // sanity check on update of wrappedSet + assertThat(wrappedSet).isNotEqualTo(copyOfWrappedSet); + + ImmutableSet copyOfModifiedSet = ImmutableSet.copyOf(wrappedSet); // {2,3} + // sanity check on construction of copyOfModifiedSet + assertThat(wrappedSet).isEqualTo(copyOfModifiedSet); + + // setToTest should throw when it calls equals(), or equals is called on it, except for itself + assertThat(setToTest).isEqualTo(setToTest); + assertThrows(IllegalStateException.class, () -> setToTest.equals(wrappedSet)); + assertThrows(IllegalStateException.class, () -> setToTest.equals(copyOfWrappedSet)); + assertThrows(IllegalStateException.class, () -> setToTest.equals(copyOfModifiedSet)); + assertThrows(IllegalStateException.class, () -> wrappedSet.equals(setToTest)); + assertThrows(IllegalStateException.class, () -> copyOfWrappedSet.equals(setToTest)); + assertThrows(IllegalStateException.class, () -> copyOfModifiedSet.equals(setToTest)); + } +} diff --git a/android/guava-tests/test/com/google/common/graph/MapCacheTest.java b/android/guava-tests/test/com/google/common/graph/MapCacheTest.java index f04f010c84c9..e129443530f8 100644 --- a/android/guava-tests/test/com/google/common/graph/MapCacheTest.java +++ b/android/guava-tests/test/com/google/common/graph/MapCacheTest.java @@ -24,6 +24,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.TreeMap; +import org.jspecify.annotations.NullUnmarked; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +35,7 @@ @AndroidIncompatible // TODO(cpovirk): Figure out Android JUnit 4 support. Does it work with Gingerbread? @RunWith? @RunWith(Parameterized.class) +@NullUnmarked public final class MapCacheTest { private final MapIteratorCache mapCache; diff --git a/android/guava-tests/test/com/google/common/graph/NetworkEquivalenceTest.java b/android/guava-tests/test/com/google/common/graph/NetworkEquivalenceTest.java index 4aab1271b5f9..de8ff654c9fb 100644 --- a/android/guava-tests/test/com/google/common/graph/NetworkEquivalenceTest.java +++ b/android/guava-tests/test/com/google/common/graph/NetworkEquivalenceTest.java @@ -23,6 +23,7 @@ import com.google.common.graph.TestUtil.EdgeType; import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,6 +32,7 @@ @AndroidIncompatible // TODO(cpovirk): Figure out Android JUnit 4 support. Does it work with Gingerbread? @RunWith? @RunWith(Parameterized.class) +@NullUnmarked public final class NetworkEquivalenceTest { private static final Integer N1 = 1; private static final Integer N2 = 2; @@ -61,9 +63,8 @@ private static MutableNetwork createNetwork(EdgeType edgeType) return NetworkBuilder.undirected().allowsSelfLoops(true).build(); case DIRECTED: return NetworkBuilder.directed().allowsSelfLoops(true).build(); - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } + throw new IllegalStateException("Unexpected edge type: " + edgeType); } private static EdgeType oppositeType(EdgeType edgeType) { @@ -72,9 +73,8 @@ private static EdgeType oppositeType(EdgeType edgeType) { return EdgeType.DIRECTED; case DIRECTED: return EdgeType.UNDIRECTED; - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } + throw new IllegalStateException("Unexpected edge type: " + edgeType); } @Test @@ -184,8 +184,6 @@ public void equivalent_edgeDirectionsDiffer() { case DIRECTED: assertThat(network).isNotEqualTo(g2); break; - default: - throw new IllegalStateException("Unexpected edge type: " + edgeType); } } } diff --git a/android/guava-tests/test/com/google/common/graph/NetworkMutationTest.java b/android/guava-tests/test/com/google/common/graph/NetworkMutationTest.java index fd232dcdad17..f3629370c9e5 100644 --- a/android/guava-tests/test/com/google/common/graph/NetworkMutationTest.java +++ b/android/guava-tests/test/com/google/common/graph/NetworkMutationTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Random; import java.util.RandomAccess; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -30,10 +31,11 @@ /** Tests for repeated node and edge addition and removal in a {@link Network}. */ @RunWith(JUnit4.class) +@NullUnmarked public final class NetworkMutationTest { - private static final int NUM_TRIALS = 25; - private static final int NUM_NODES = 100; - private static final int NUM_EDGES = 1000; + private static final int NUM_TRIALS = 5; + private static final int NUM_NODES = 20; + private static final int NUM_EDGES = 100; private static final int NODE_POOL_SIZE = 1000; // must be >> NUM_NODES @Test diff --git a/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java b/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java index f1526c235f1c..2ddcbc1d5d8d 100644 --- a/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/graph/PackageSanityTests.java @@ -20,7 +20,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import com.google.common.testing.AbstractPackageSanityTests; -import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullUnmarked; /** * Covers basic sanity checks for the entire package. @@ -28,11 +28,12 @@ * @author Kurt Alfred Kluever */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { - private static final AbstractGraphBuilder GRAPH_BUILDER_A = + private static final AbstractGraphBuilder graphBuilderA = GraphBuilder.directed().expectedNodeCount(10); - private static final AbstractGraphBuilder GRAPH_BUILDER_B = + private static final AbstractGraphBuilder graphBuilderB = ValueGraphBuilder.directed().allowsSelfLoops(true).expectedNodeCount(16); private static final ImmutableGraph IMMUTABLE_GRAPH_A = @@ -40,9 +41,9 @@ public class PackageSanityTests extends AbstractPackageSanityTests { private static final ImmutableGraph IMMUTABLE_GRAPH_B = GraphBuilder.directed().immutable().addNode("B").build(); - private static final NetworkBuilder NETWORK_BUILDER_A = + private static final NetworkBuilder networkBuilderA = NetworkBuilder.directed().allowsParallelEdges(true).expectedNodeCount(10); - private static final NetworkBuilder NETWORK_BUILDER_B = + private static final NetworkBuilder networkBuilderB = NetworkBuilder.directed().allowsSelfLoops(true).expectedNodeCount(16); private static final ImmutableNetwork IMMUTABLE_NETWORK_A = @@ -51,9 +52,15 @@ public class PackageSanityTests extends AbstractPackageSanityTests { NetworkBuilder.directed().immutable().addNode("B").build(); public PackageSanityTests() { - setDistinctValues(AbstractGraphBuilder.class, GRAPH_BUILDER_A, GRAPH_BUILDER_B); + MutableNetwork mutableNetworkA = NetworkBuilder.directed().build(); + mutableNetworkA.addNode("a"); + MutableNetwork mutableNetworkB = NetworkBuilder.directed().build(); + mutableNetworkB.addNode("b"); + + setDistinctValues(AbstractGraphBuilder.class, graphBuilderA, graphBuilderB); setDistinctValues(Graph.class, IMMUTABLE_GRAPH_A, IMMUTABLE_GRAPH_B); - setDistinctValues(NetworkBuilder.class, NETWORK_BUILDER_A, NETWORK_BUILDER_B); + setDistinctValues(MutableNetwork.class, mutableNetworkA, mutableNetworkB); + setDistinctValues(NetworkBuilder.class, networkBuilderA, networkBuilderB); setDistinctValues(Network.class, IMMUTABLE_NETWORK_A, IMMUTABLE_NETWORK_B); setDefault(EndpointPair.class, EndpointPair.ordered("A", "B")); } @@ -62,7 +69,7 @@ public PackageSanityTests() { public void testNulls() throws Exception { try { super.testNulls(); - } catch (AssertionFailedError e) { + } catch (AssertionError e) { assertWithMessage("Method did not throw null pointer OR element not in graph exception.") .that(e) .hasCauseThat() diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java index d89baa0634bc..710f7890526b 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedGraphTest.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -25,6 +26,7 @@ /** Tests for a directed {@link StandardMutableGraph}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public final class StandardImmutableDirectedGraphTest extends AbstractStandardDirectedGraphTest { @Parameters(name = "allowsSelfLoops={0}") diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java index b237ff91f07c..bc3e194d969b 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableDirectedNetworkTest.java @@ -19,6 +19,7 @@ import com.google.common.collect.Ordering; import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -26,6 +27,7 @@ /** Tests for a directed {@link ImmutableNetwork}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public class StandardImmutableDirectedNetworkTest extends AbstractStandardDirectedNetworkTest { @Parameters(name = "allowsSelfLoops={0}, allowsParallelEdges={1}, nodeOrder={2}, edgeOrder={3}") diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java index 47cd6a037426..1a709ac6a227 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableGraphAdditionalTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -27,6 +28,7 @@ * {@link StandardImmutableDirectedGraphTest}. */ @RunWith(JUnit4.class) +@NullUnmarked public class StandardImmutableGraphAdditionalTest { @Test diff --git a/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java index 7f580a6db80d..290306d4a09d 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardImmutableUndirectedGraphTest.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -25,6 +26,7 @@ /** Tests for an undirected {@link StandardMutableGraph}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public final class StandardImmutableUndirectedGraphTest extends AbstractStandardUndirectedGraphTest { diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java index 191010e48f40..8b849a8cd2c6 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedGraphTest.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -25,6 +26,7 @@ /** Tests for a directed {@link StandardMutableGraph}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public final class StandardMutableDirectedGraphTest extends AbstractStandardDirectedGraphTest { @Parameters(name = "allowsSelfLoops={0}, incidentEdgeOrder={1}") diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java index fb8be1d34d7a..1e8960a39c86 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardMutableDirectedNetworkTest.java @@ -19,6 +19,7 @@ import com.google.common.collect.Ordering; import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -26,6 +27,7 @@ /** Tests for a directed {@link StandardMutableNetwork} allowing self-loops. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public class StandardMutableDirectedNetworkTest extends AbstractStandardDirectedNetworkTest { @Parameters(name = "allowsSelfLoops={0}, allowsParallelEdges={1}, nodeOrder={2}, edgeOrder={3}") diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java index aa0372aa8aae..fdb3bef224e6 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedGraphTest.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -25,6 +26,7 @@ /** Tests for an undirected {@link StandardMutableGraph}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public class StandardMutableUndirectedGraphTest extends AbstractStandardUndirectedGraphTest { @Parameters(name = "allowsSelfLoops={0}, incidentEdgeOrder={1}") diff --git a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java index c021b5d17354..f5e12b33f7ef 100644 --- a/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java +++ b/android/guava-tests/test/com/google/common/graph/StandardMutableUndirectedNetworkTest.java @@ -19,6 +19,7 @@ import com.google.common.collect.Ordering; import java.util.Arrays; import java.util.Collection; +import org.jspecify.annotations.NullUnmarked; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -26,6 +27,7 @@ /** Tests for an undirected {@link StandardMutableNetwork}. */ @AndroidIncompatible @RunWith(Parameterized.class) +@NullUnmarked public final class StandardMutableUndirectedNetworkTest extends AbstractStandardUndirectedNetworkTest { diff --git a/android/guava-tests/test/com/google/common/graph/TestUtil.java b/android/guava-tests/test/com/google/common/graph/TestUtil.java index 68a2503e223f..e1aea12d76cc 100644 --- a/android/guava-tests/test/com/google/common/graph/TestUtil.java +++ b/android/guava-tests/test/com/google/common/graph/TestUtil.java @@ -22,12 +22,15 @@ import com.google.common.collect.Iterators; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; /** Utility methods used in various common.graph tests. */ +@NullUnmarked final class TestUtil { static final String ERROR_ELEMENT_NOT_IN_GRAPH = "not an element of this graph"; static final String ERROR_NODE_NOT_IN_GRAPH = "Should not be allowed to pass a node that is not an element of the graph."; + static final String ERROR_ELEMENT_REMOVED = "used to generate this set"; private static final String NODE_STRING = "Node"; private static final String EDGE_STRING = "Edge"; @@ -48,6 +51,16 @@ static void assertEdgeNotInGraphErrorMessage(Throwable throwable) { assertThat(throwable).hasMessageThat().contains(ERROR_ELEMENT_NOT_IN_GRAPH); } + static void assertNodeRemovedFromGraphErrorMessage(Throwable throwable) { + assertThat(throwable).hasMessageThat().startsWith(NODE_STRING); + assertThat(throwable).hasMessageThat().contains(ERROR_ELEMENT_REMOVED); + } + + static void assertEdgeRemovedFromGraphErrorMessage(Throwable throwable) { + assertThat(throwable).hasMessageThat().startsWith(EDGE_STRING); + assertThat(throwable).hasMessageThat().contains(ERROR_ELEMENT_REMOVED); + } + static void assertStronglyEquivalent(Graph graphA, Graph graphB) { // Properties not covered by equals() assertThat(graphA.allowsSelfLoops()).isEqualTo(graphB.allowsSelfLoops()); diff --git a/android/guava-tests/test/com/google/common/graph/TraverserTest.java b/android/guava-tests/test/com/google/common/graph/TraverserTest.java index d4c8cf7521c3..5694d7f69ad8 100644 --- a/android/guava-tests/test/com/google/common/graph/TraverserTest.java +++ b/android/guava-tests/test/com/google/common/graph/TraverserTest.java @@ -21,7 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.charactersOf; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; @@ -30,11 +30,13 @@ import com.google.common.collect.Multiset; import com.google.common.collect.Ordering; import com.google.common.primitives.Chars; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@NullUnmarked public class TraverserTest { /** @@ -311,11 +313,9 @@ public void forGraph_breadthFirstIterable_singleRoot() { @Test public void forGraph_breadthFirst_emptyGraph() { - try { - Traverser.forGraph(createDirectedGraph()).breadthFirst('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).breadthFirst('a')); } /** @@ -326,11 +326,9 @@ public void forGraph_breadthFirst_emptyGraph() { public void forGraph_breadthFirstIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).breadthFirst(charactersOf("")), ""); - try { - Traverser.forGraph(createDirectedGraph()).breadthFirst(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).breadthFirst(charactersOf("a"))); } /** @@ -509,22 +507,18 @@ public void forGraph_depthFirstPreOrderIterable_singleRoot() { @Test public void forGraph_depthFirstPreOrder_emptyGraph() { - try { - Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder('a')); } @Test public void forGraph_depthFirstPreOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder(charactersOf("")), ""); - try { - Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).depthFirstPreOrder(charactersOf("a"))); } @Test @@ -691,22 +685,18 @@ public void forGraph_depthFirstPostOrderIterable_singleRoot() { @Test public void forGraph_depthFirstPostOrder_emptyGraph() { - try { - Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder('a')); } @Test public void forGraph_depthFirstPostOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder(charactersOf("")), ""); - try { - Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forGraph(createDirectedGraph()).depthFirstPostOrder(charactersOf("a"))); } @Test @@ -749,11 +739,7 @@ public void forTree_withUndirectedGraph_throws() throws Exception { MutableGraph graph = GraphBuilder.undirected().build(); graph.putEdge("a", "b"); - try { - Traverser.forTree(graph); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Traverser.forTree(graph)); } @Test @@ -770,11 +756,7 @@ public void forTree_withUndirectedValueGraph_throws() throws Exception { MutableValueGraph valueGraph = ValueGraphBuilder.undirected().build(); valueGraph.putEdgeValue("a", "b", 11); - try { - Traverser.forTree(valueGraph); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Traverser.forTree(valueGraph)); } @Test @@ -791,11 +773,7 @@ public void forTree_withUndirectedNetwork_throws() throws Exception { MutableNetwork network = NetworkBuilder.undirected().build(); network.addEdge("a", "b", 11); - try { - Traverser.forTree(network); - fail("Expected exception"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Traverser.forTree(network)); } @Test @@ -889,22 +867,18 @@ public void forTree_breadthFirstIterable_singleRoot() { @Test public void forTree_breadthFirst_emptyGraph() { - try { - Traverser.forTree(createDirectedGraph()).breadthFirst('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).breadthFirst('a')); } @Test public void forTree_breadthFirstIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).breadthFirst(charactersOf("")), ""); - try { - Traverser.forTree(createDirectedGraph()).breadthFirst(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).breadthFirst(charactersOf("a"))); } @Test @@ -1026,22 +1000,18 @@ public void forTree_depthFirstPreOrderIterable_singleRoot() { @Test public void forTree_depthFirstPreOrder_emptyGraph() { - try { - Traverser.forTree(createDirectedGraph()).depthFirstPreOrder('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).depthFirstPreOrder('a')); } @Test public void forTree_depthFirstPreOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).depthFirstPreOrder(charactersOf("")), ""); - try { - Traverser.forTree(createDirectedGraph()).depthFirstPreOrder(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).depthFirstPreOrder(charactersOf("a"))); } @Test @@ -1156,22 +1126,18 @@ public void forTree_depthFirstPostOrderIterable_singleRoot() { @Test public void forTree_depthFirstPostOrder_emptyGraph() { - try { - Traverser.forTree(createDirectedGraph()).depthFirstPostOrder('a'); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).depthFirstPostOrder('a')); } @Test public void forTree_depthFirstPostOrderIterable_emptyGraph() { assertEqualCharNodes( Traverser.forTree(createDirectedGraph()).depthFirstPostOrder(charactersOf("")), ""); - try { - Traverser.forTree(createDirectedGraph()).depthFirstPostOrder(charactersOf("a")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Traverser.forTree(createDirectedGraph()).depthFirstPostOrder(charactersOf("a"))); } @Test @@ -1201,11 +1167,11 @@ public void forTree_depthFirstPostOrderIterable_iterableIsLazy() { } private static SuccessorsFunction createDirectedGraph(String... edges) { - return createGraph(/* directed = */ true, edges); + return createGraph(/* directed= */ true, edges); } private static SuccessorsFunction createUndirectedGraph(String... edges) { - return createGraph(/* directed = */ false, edges); + return createGraph(/* directed= */ false, edges); } /** diff --git a/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java b/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java index b17c91dfae36..effd9a494735 100644 --- a/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java +++ b/android/guava-tests/test/com/google/common/graph/ValueGraphTest.java @@ -20,7 +20,7 @@ import static com.google.common.graph.TestUtil.assertStronglyEquivalent; import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.Executors.newFixedThreadPool; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import java.util.Set; @@ -28,6 +28,8 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,6 +38,7 @@ /** Tests for {@link StandardMutableValueGraph} and related functionality. */ // TODO(user): Expand coverage and move to proper test suite. @RunWith(JUnit4.class) +@NullUnmarked public final class ValueGraphTest { private static final String DEFAULT = "default"; @@ -173,8 +176,8 @@ public void hasEdgeConnecting_undirected_backwards() { public void hasEdgeConnecting_undirected_mismatch() { graph = ValueGraphBuilder.undirected().build(); graph.putEdgeValue(1, 2, "A"); - assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(1, 2))).isTrue(); - assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(2, 1))).isTrue(); + assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(1, 2))).isFalse(); + assertThat(graph.hasEdgeConnecting(EndpointPair.ordered(2, 1))).isFalse(); } @Test @@ -196,13 +199,16 @@ public void edgeValueOrDefault_directed_backwards() { public void edgeValueOrDefault_directed_mismatch() { graph = ValueGraphBuilder.directed().build(); graph.putEdgeValue(1, 2, "A"); - try { - String unused = graph.edgeValueOrDefault(EndpointPair.unordered(1, 2), "default"); - unused = graph.edgeValueOrDefault(EndpointPair.unordered(2, 1), "default"); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> graph.edgeValueOrDefault(EndpointPair.unordered(1, 2), "default")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); + e = + assertThrows( + IllegalArgumentException.class, + () -> graph.edgeValueOrDefault(EndpointPair.unordered(2, 1), "default")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -223,8 +229,17 @@ public void edgeValueOrDefault_undirected_backwards() { public void edgeValueOrDefault_undirected_mismatch() { graph = ValueGraphBuilder.undirected().build(); graph.putEdgeValue(1, 2, "A"); - assertThat(graph.edgeValueOrDefault(EndpointPair.ordered(2, 1), "default")).isEqualTo("A"); - assertThat(graph.edgeValueOrDefault(EndpointPair.ordered(2, 1), "default")).isEqualTo("A"); + // Check that edgeValueOrDefault() throws on each possible ordering of an ordered EndpointPair + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> graph.edgeValueOrDefault(EndpointPair.ordered(1, 2), "default")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); + e = + assertThrows( + IllegalArgumentException.class, + () -> graph.edgeValueOrDefault(EndpointPair.ordered(2, 1), "default")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -240,18 +255,21 @@ public void putEdgeValue_directed() { @Test public void putEdgeValue_directed_orderMismatch() { graph = ValueGraphBuilder.directed().build(); - try { - graph.putEdgeValue(EndpointPair.unordered(1, 2), "irrelevant"); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> graph.putEdgeValue(EndpointPair.unordered(1, 2), "irrelevant")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test public void putEdgeValue_undirected_orderMismatch() { graph = ValueGraphBuilder.undirected().build(); - assertThat(graph.putEdgeValue(EndpointPair.ordered(1, 2), "irrelevant")).isNull(); + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> graph.putEdgeValue(EndpointPair.ordered(1, 2), "irrelevant")); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -298,20 +316,29 @@ public void removeEdge_directed_orderMismatch() { graph = ValueGraphBuilder.directed().build(); graph.putEdgeValue(1, 2, "1->2"); graph.putEdgeValue(2, 1, "2->1"); - try { - graph.removeEdge(EndpointPair.unordered(1, 2)); - graph.removeEdge(EndpointPair.unordered(2, 1)); - fail("Expected IllegalArgumentException: " + ENDPOINTS_MISMATCH); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.unordered(1, 2))); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); + e = + assertThrows( + IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.unordered(2, 1))); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test public void removeEdge_undirected_orderMismatch() { graph = ValueGraphBuilder.undirected().build(); graph.putEdgeValue(1, 2, "1-2"); - assertThat(graph.removeEdge(EndpointPair.ordered(1, 2))).isEqualTo("1-2"); + // Check that removeEdge() throws on each possible ordering of an ordered EndpointPair + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.ordered(1, 2))); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); + e = + assertThrows( + IllegalArgumentException.class, () -> graph.removeEdge(EndpointPair.ordered(2, 1))); + assertThat(e).hasMessageThat().contains(ENDPOINTS_MISMATCH); } @Test @@ -380,7 +407,6 @@ public void incidentEdges_stableIncidentEdgeOrder_preservesIncidentEdgesOrder_un .inOrder(); } - @Test public void concurrentIteration() throws Exception { graph = ValueGraphBuilder.directed().build(); @@ -395,9 +421,9 @@ public void concurrentIteration() throws Exception { for (int i = 0; i < threadCount; i++) { futures.add( executor.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Object call() throws Exception { + public @Nullable Void call() throws Exception { barrier.await(); Integer first = graph.nodes().iterator().next(); for (Integer node : graph.nodes()) { diff --git a/android/guava-tests/test/com/google/common/hash/AbstractByteHasherTest.java b/android/guava-tests/test/com/google/common/hash/AbstractByteHasherTest.java index e5c359aaf123..fe35bf3d9820 100644 --- a/android/guava-tests/test/com/google/common/hash/AbstractByteHasherTest.java +++ b/android/guava-tests/test/com/google/common/hash/AbstractByteHasherTest.java @@ -14,19 +14,21 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_16LE; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayOutputStream; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for AbstractByteHasher. * * @author Colin Decker */ +@NullUnmarked public class AbstractByteHasherTest extends TestCase { public void testBytes() { @@ -93,24 +95,11 @@ public void testDouble() { public void testCorrectExceptions() { TestHasher hasher = new TestHasher(); - try { - hasher.putBytes(new byte[8], -1, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - hasher.putBytes(new byte[8], 0, 16); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - hasher.putBytes(new byte[8], 0, -1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> hasher.putBytes(new byte[8], -1, 4)); + assertThrows(IndexOutOfBoundsException.class, () -> hasher.putBytes(new byte[8], 0, 16)); + assertThrows(IndexOutOfBoundsException.class, () -> hasher.putBytes(new byte[8], 0, -1)); } - @CanIgnoreReturnValue private class TestHasher extends AbstractByteHasher { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/android/guava-tests/test/com/google/common/hash/AbstractNonStreamingHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/AbstractNonStreamingHashFunctionTest.java index 754147152fd9..281ec4fef5a4 100644 --- a/android/guava-tests/test/com/google/common/hash/AbstractNonStreamingHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/AbstractNonStreamingHashFunctionTest.java @@ -24,8 +24,10 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for AbstractNonStreamingHashFunction. */ +@NullUnmarked public class AbstractNonStreamingHashFunctionTest extends TestCase { /** * Constructs two trivial HashFunctions (output := input), one streaming and one non-streaming, diff --git a/android/guava-tests/test/com/google/common/hash/AbstractStreamingHasherTest.java b/android/guava-tests/test/com/google/common/hash/AbstractStreamingHasherTest.java index 99b2c71a07d5..2a93936a90ef 100644 --- a/android/guava-tests/test/com/google/common/hash/AbstractStreamingHasherTest.java +++ b/android/guava-tests/test/com/google/common/hash/AbstractStreamingHasherTest.java @@ -16,7 +16,8 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static org.junit.Assert.assertThrows; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -29,12 +30,14 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for AbstractStreamingHasher. * * @author Dimitris Andreou */ +@NullUnmarked public class AbstractStreamingHasherTest extends TestCase { public void testBytes() { Sink sink = new Sink(4); // byte order insignificant here @@ -113,21 +116,9 @@ public void testDouble() { public void testCorrectExceptions() { Sink sink = new Sink(4); - try { - sink.putBytes(new byte[8], -1, 4); - fail(); - } catch (IndexOutOfBoundsException ok) { - } - try { - sink.putBytes(new byte[8], 0, 16); - fail(); - } catch (IndexOutOfBoundsException ok) { - } - try { - sink.putBytes(new byte[8], 0, -1); - fail(); - } catch (IndexOutOfBoundsException ok) { - } + assertThrows(IndexOutOfBoundsException.class, () -> sink.putBytes(new byte[8], -1, 4)); + assertThrows(IndexOutOfBoundsException.class, () -> sink.putBytes(new byte[8], 0, 16)); + assertThrows(IndexOutOfBoundsException.class, () -> sink.putBytes(new byte[8], 0, -1)); } /** diff --git a/android/guava-tests/test/com/google/common/hash/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/hash/AndroidIncompatible.java index f807acb8868a..1139e4336f3d 100644 --- a/android/guava-tests/test/com/google/common/hash/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/hash/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java b/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java index 94c922b8ca6c..2196d1850883 100644 --- a/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java +++ b/android/guava-tests/test/com/google/common/hash/BloomFilterTest.java @@ -16,8 +16,10 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; @@ -35,15 +37,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for SimpleGenericBloomFilter and derived BloomFilter views. * * @author Dimitris Andreou */ +@NullUnmarked public class BloomFilterTest extends TestCase { private static final int NUM_PUTS = 100_000; private static final ThreadLocal random = @@ -121,7 +124,7 @@ public void testCreateAndCheckMitz32BloomFilterWithKnownFalsePositives() { assertEquals(knownNumberOfFalsePositives, numFpp); double expectedReportedFpp = (double) knownNumberOfFalsePositives / numInsertions; double actualReportedFpp = bf.expectedFpp(); - assertEquals(expectedReportedFpp, actualReportedFpp, 0.00015); + assertThat(actualReportedFpp).isWithin(0.00015).of(expectedReportedFpp); } public void testCreateAndCheckBloomFilterWithKnownFalsePositives64() { @@ -165,7 +168,7 @@ public void testCreateAndCheckBloomFilterWithKnownFalsePositives64() { assertEquals(knownNumberOfFalsePositives, numFpp); double expectedReportedFpp = (double) knownNumberOfFalsePositives / numInsertions; double actualReportedFpp = bf.expectedFpp(); - assertEquals(expectedReportedFpp, actualReportedFpp, 0.00033); + assertThat(actualReportedFpp).isWithin(0.00033).of(expectedReportedFpp); } public void testCreateAndCheckBloomFilterWithKnownUtf8FalsePositives64() { @@ -208,7 +211,7 @@ public void testCreateAndCheckBloomFilterWithKnownUtf8FalsePositives64() { assertEquals(knownNumberOfFalsePositives, numFpp); double expectedReportedFpp = (double) knownNumberOfFalsePositives / numInsertions; double actualReportedFpp = bf.expectedFpp(); - assertEquals(expectedReportedFpp, actualReportedFpp, 0.00033); + assertThat(actualReportedFpp).isWithin(0.00033).of(expectedReportedFpp); } /** Sanity checking with many combinations of false positive rates and expected insertions */ @@ -221,36 +224,28 @@ public void testBasic() { } public void testPreconditions() { - try { - BloomFilter.create(Funnels.unencodedCharsFunnel(), -1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - BloomFilter.create(Funnels.unencodedCharsFunnel(), -1, 0.03); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - BloomFilter.create(Funnels.unencodedCharsFunnel(), 1, 0.0); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - BloomFilter.create(Funnels.unencodedCharsFunnel(), 1, 1.0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> BloomFilter.create(Funnels.unencodedCharsFunnel(), -1)); + assertThrows( + IllegalArgumentException.class, + () -> BloomFilter.create(Funnels.unencodedCharsFunnel(), -1, 0.03)); + assertThrows( + IllegalArgumentException.class, + () -> BloomFilter.create(Funnels.unencodedCharsFunnel(), 1, 0.0)); + assertThrows( + IllegalArgumentException.class, + () -> BloomFilter.create(Funnels.unencodedCharsFunnel(), 1, 1.0)); } public void testFailureWhenMoreThan255HashFunctionsAreNeeded() { - try { - int n = 1000; - double p = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000001; - BloomFilter.create(Funnels.unencodedCharsFunnel(), n, p); - fail(); - } catch (IllegalArgumentException expected) { - } + int n = 1000; + double p = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000001; + assertThrows( + IllegalArgumentException.class, + () -> { + BloomFilter.create(Funnels.unencodedCharsFunnel(), n, p); + }); } public void testNullPointers() { @@ -262,15 +257,15 @@ public void testNullPointers() { /** Tests that we never get an optimal hashes number of zero. */ public void testOptimalHashes() { for (int n = 1; n < 1000; n++) { - for (int m = 0; m < 1000; m++) { - assertTrue(BloomFilter.optimalNumOfHashFunctions(n, m) > 0); + for (double p = 0.1; p > 1e-10; p /= 10) { + assertThat(BloomFilter.optimalNumOfHashFunctions(p)).isGreaterThan(0); } } } - // https://code.google.com/p/guava-libraries/issues/detail?id=1781 + // https://github.com/google/guava/issues/1781 public void testOptimalNumOfHashFunctionsRounding() { - assertEquals(7, BloomFilter.optimalNumOfHashFunctions(319, 3072)); + assertEquals(5, BloomFilter.optimalNumOfHashFunctions(0.03)); } /** Tests that we always get a non-negative optimal size. */ @@ -289,15 +284,16 @@ public void testOptimalSize() { // and some crazy values (this used to be capped to Integer.MAX_VALUE, now it can go bigger assertEquals(3327428144502L, BloomFilter.optimalNumOfBits(Integer.MAX_VALUE, Double.MIN_VALUE)); - try { - BloomFilter unused = - BloomFilter.create(HashTestUtils.BAD_FUNNEL, Integer.MAX_VALUE, Double.MIN_VALUE); - fail("we can't represent such a large BF!"); - } catch (IllegalArgumentException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("Could not create BloomFilter of 3327428144502 bits"); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> { + BloomFilter unused = + BloomFilter.create(HashTestUtils.BAD_FUNNEL, Integer.MAX_VALUE, Double.MIN_VALUE); + }); + assertThat(expected) + .hasMessageThat() + .isEqualTo("Could not create BloomFilter of 3327428144502 bits"); } @AndroidIncompatible // OutOfMemoryError @@ -308,6 +304,7 @@ public void testLargeNumberOfInsertions() { unused = BloomFilter.create(Funnels.unencodedCharsFunnel(), 45L * Integer.MAX_VALUE, 0.99); } + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method private static void checkSanity(BloomFilter bf) { assertFalse(bf.mightContain(new Object())); assertFalse(bf.apply(new Object())); @@ -329,7 +326,7 @@ public void testCopy() { public void testExpectedFpp() { BloomFilter bf = BloomFilter.create(HashTestUtils.BAD_FUNNEL, 10, 0.03); double fpp = bf.expectedFpp(); - assertEquals(0.0, fpp); + assertThat(fpp).isEqualTo(0.0); // usually completed in less than 200 iterations while (fpp != 1.0) { boolean changed = bf.put(new Object()); @@ -408,7 +405,7 @@ public void funnel(Long value, PrimitiveSink into) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return (object instanceof CustomFunnel); } @@ -456,29 +453,29 @@ public void testPutAllDifferentSizes() { BloomFilter bf1 = BloomFilter.create(Funnels.integerFunnel(), 1); BloomFilter bf2 = BloomFilter.create(Funnels.integerFunnel(), 10); - try { - assertFalse(bf1.isCompatible(bf2)); - bf1.putAll(bf2); - fail(); - } catch (IllegalArgumentException expected) { - } + assertFalse(bf1.isCompatible(bf2)); + assertThrows( + IllegalArgumentException.class, + () -> { + bf1.putAll(bf2); + }); - try { - assertFalse(bf2.isCompatible(bf1)); - bf2.putAll(bf1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertFalse(bf2.isCompatible(bf1)); + assertThrows( + IllegalArgumentException.class, + () -> { + bf2.putAll(bf1); + }); } public void testPutAllWithSelf() { BloomFilter bf1 = BloomFilter.create(Funnels.integerFunnel(), 1); - try { - assertFalse(bf1.isCompatible(bf1)); - bf1.putAll(bf1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertFalse(bf1.isCompatible(bf1)); + assertThrows( + IllegalArgumentException.class, + () -> { + bf1.putAll(bf1); + }); } public void testJavaSerialization() { @@ -491,7 +488,7 @@ public void testJavaSerialization() { for (int i = 0; i < 10; i++) { assertTrue(copy.mightContain(Ints.toByteArray(i))); } - assertEquals(bf.expectedFpp(), copy.expectedFpp()); + assertThat(copy.expectedFpp()).isEqualTo(bf.expectedFpp()); SerializableTester.reserializeAndAssert(bf); } @@ -506,13 +503,18 @@ public void testCustomSerialization() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); bf.writeTo(out); - assertEquals(bf, BloomFilter.readFrom(new ByteArrayInputStream(out.toByteArray()), funnel)); + BloomFilter read = + BloomFilter.readFrom(new ByteArrayInputStream(out.toByteArray()), funnel); + assertThat(read).isEqualTo(bf); + assertThat(read.expectedFpp()).isGreaterThan(0); } /** * This test will fail whenever someone updates/reorders the BloomFilterStrategies constants. Only * appending a new constant is allowed. */ + // This test ensures that our reliance on the ordering elsewhere is safe. + @SuppressWarnings("EnumOrdinal") public void testBloomFilterStrategies() { assertThat(BloomFilterStrategies.values()).hasLength(2); assertEquals(BloomFilterStrategies.MURMUR128_MITZ_32, BloomFilterStrategies.values()[0]); @@ -560,7 +562,7 @@ public void run() { // Don't forget, the bloom filter slowly saturates over time and the // expected false positive probability goes up! assertThat(bloomFilter.expectedFpp()).isLessThan(safetyFalsePositiveRate); - } while (stopwatch.elapsed(TimeUnit.SECONDS) < 1); + } while (stopwatch.elapsed(SECONDS) < 1); } }; diff --git a/android/guava-tests/test/com/google/common/hash/ChecksumHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/ChecksumHashFunctionTest.java index 14a106a62825..1cdc8cdfcf8c 100644 --- a/android/guava-tests/test/com/google/common/hash/ChecksumHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/ChecksumHashFunctionTest.java @@ -19,12 +19,14 @@ import java.util.zip.Checksum; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for ChecksumHashFunction. * * @author Colin Decker */ +@NullUnmarked public class ChecksumHashFunctionTest extends TestCase { public void testCrc32_equalsChecksumValue() throws Exception { diff --git a/android/guava-tests/test/com/google/common/hash/Crc32cHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/Crc32cHashFunctionTest.java index 3bea975e1427..2d63f46b20ca 100644 --- a/android/guava-tests/test/com/google/common/hash/Crc32cHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/Crc32cHashFunctionTest.java @@ -14,11 +14,12 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import java.util.Arrays; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Crc32c}. Known test values are from RFC 3720, Section B.4. @@ -26,6 +27,7 @@ * @author Patrick Costello * @author Kurt Alfred Kluever */ +@NullUnmarked public class Crc32cHashFunctionTest extends TestCase { public void testEmpty() { assertCrc(0, new byte[0]); @@ -124,7 +126,7 @@ public void testAgainstSimplerImplementation() { private static int referenceCrc(byte[] bytes) { int crc = ~0; for (byte b : bytes) { - crc = (crc >>> 8) ^ Crc32cHashFunction.Crc32cHasher.BYTE_TABLE[(crc ^ b) & 0xFF]; + crc = (crc >>> 8) ^ Crc32cHashFunction.Crc32cHasher.byteTable[(crc ^ b) & 0xFF]; } return ~crc; } @@ -167,7 +169,7 @@ public void testCrc32cByteTable() { expected[i] = crc; } - int[] actual = Crc32cHashFunction.Crc32cHasher.BYTE_TABLE; + int[] actual = Crc32cHashFunction.Crc32cHasher.byteTable; assertTrue( "Expected: \n" + Arrays.toString(expected) + "\nActual:\n" + Arrays.toString(actual), Arrays.equals(expected, actual)); @@ -184,7 +186,7 @@ static int advanceOneBit(int next) { public void testCrc32cStrideTable() { int next = CRC32C_GENERATOR_FLIPPED; for (int i = 0; i < 12; i++) { // for 3 ints = 12 bytes in between each stride window - next = (next >>> 8) ^ Crc32cHashFunction.Crc32cHasher.BYTE_TABLE[next & 0xFF]; + next = (next >>> 8) ^ Crc32cHashFunction.Crc32cHasher.byteTable[next & 0xFF]; } int[][] expected = new int[4][256]; for (int b = 0; b < 4; ++b) { @@ -202,7 +204,7 @@ public void testCrc32cStrideTable() { } } - int[][] actual = Crc32cHashFunction.Crc32cHasher.STRIDE_TABLE; + int[][] actual = Crc32cHashFunction.Crc32cHasher.strideTable; assertTrue( "Expected: \n" + Arrays.deepToString(expected) diff --git a/android/guava-tests/test/com/google/common/hash/FarmHashFingerprint64Test.java b/android/guava-tests/test/com/google/common/hash/FarmHashFingerprint64Test.java index 82a5750ba14e..4f6987f645fe 100644 --- a/android/guava-tests/test/com/google/common/hash/FarmHashFingerprint64Test.java +++ b/android/guava-tests/test/com/google/common/hash/FarmHashFingerprint64Test.java @@ -16,13 +16,14 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.ISO_8859_1; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Strings; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for FarmHashFingerprint64. @@ -30,11 +31,13 @@ * @author Kyle Maddison * @author Geoff Pike */ +@NullUnmarked public class FarmHashFingerprint64Test extends TestCase { private static final HashFunction HASH_FN = Hashing.farmHashFingerprint64(); // If this test fails, all bets are off + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 public void testReallySimpleFingerprints() { assertEquals(8581389452482819506L, fingerprint("test".getBytes(UTF_8))); // 32 characters long diff --git a/android/guava-tests/test/com/google/common/hash/Fingerprint2011Test.java b/android/guava-tests/test/com/google/common/hash/Fingerprint2011Test.java new file mode 100644 index 000000000000..a764a0af527f --- /dev/null +++ b/android/guava-tests/test/com/google/common/hash/Fingerprint2011Test.java @@ -0,0 +1,236 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package com.google.common.hash; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Ordering; +import com.google.common.primitives.UnsignedLong; +import java.util.Arrays; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** + * Unit test for Fingerprint2011. + * + * @author kylemaddison@google.com (Kyle Maddison) + */ +@NullUnmarked +public class Fingerprint2011Test extends TestCase { + + // Length of the sample string to produce + private static final int MAX_BYTES = 1000; + + // Map from sample string lengths to the fingerprint + private static final ImmutableSortedMap LENGTH_FINGERPRINTS = + new ImmutableSortedMap.Builder(Ordering.natural()) + .put(1000, 0x433109b33e13e6edL) + .put(800, 0x5f2f123bfc815f81L) + .put(640, 0x6396fc6a67293cf4L) + .put(512, 0x45c01b4934ddbbbeL) + .put(409, 0xfcd19b617551db45L) + .put(327, 0x4eee69e12854871eL) + .put(261, 0xab753446a3bbd532L) + .put(208, 0x54242fe06a291c3fL) + .put(166, 0x4f7acff7703a635bL) + .put(132, 0xa784bd0a1f22cc7fL) + .put(105, 0xf19118e187456638L) + .put(84, 0x3e2e58f9196abfe5L) + .put(67, 0xd38ae3dec0107aeaL) + .put(53, 0xea3033885868e10eL) + .put(42, 0x1394a146d0d7e04bL) + .put(33, 0x9962499315d2e8daL) + .put(26, 0x0849f5cfa85489b5L) + .put(20, 0x83b395ff19bf2171L) + .put(16, 0x9d33dd141bd55d9aL) + .put(12, 0x196248eb0b02466aL) + .put(9, 0x1cf73a50ff120336L) + .put(7, 0xb451c339457dbf51L) + .put(5, 0x681982c5e7b74064L) + .put(4, 0xc5ce47450ca6c021L) + .put(3, 0x9fcc3c3fde4d5ff7L) + .put(2, 0x090966a836e5fa4bL) + .put(1, 0x8199675ecaa6fe64L) + .put(0, 0x23ad7c904aa665e3L) + .build(); + private static final HashFunction HASH_FN = Hashing.fingerprint2011(); + + // If this test fails, all bets are off + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 + public void testReallySimpleFingerprints() { + assertEquals(8473225671271759044L, fingerprint("test".getBytes(UTF_8))); + // 32 characters long + assertEquals(7345148637025587076L, fingerprint(Strings.repeat("test", 8).getBytes(UTF_8))); + // 256 characters long + assertEquals(4904844928629814570L, fingerprint(Strings.repeat("test", 64).getBytes(UTF_8))); + } + + public void testStringsConsistency() { + for (String s : Arrays.asList("", "some", "test", "strings", "to", "try")) { + assertEquals(HASH_FN.newHasher().putUnencodedChars(s).hash(), HASH_FN.hashUnencodedChars(s)); + } + } + + public void testUtf8() { + char[] charsA = new char[128]; + char[] charsB = new char[128]; + + for (int i = 0; i < charsA.length; i++) { + if (i < 100) { + charsA[i] = 'a'; + charsB[i] = 'a'; + } else { + // Both two-byte characters, but must be different + charsA[i] = (char) (0x0180 + i); + charsB[i] = (char) (0x0280 + i); + } + } + + String stringA = new String(charsA); + String stringB = new String(charsB); + assertThat(stringA).isNotEqualTo(stringB); + assertThat(HASH_FN.hashUnencodedChars(stringA)) + .isNotEqualTo(HASH_FN.hashUnencodedChars(stringB)); + assertThat(fingerprint(stringA.getBytes(UTF_8))) + .isNotEqualTo(fingerprint(stringB.getBytes(UTF_8))); + + // ISO 8859-1 only has 0-255 (ubyte) representation so throws away UTF-8 characters + // greater than 127 (ie with their top bit set). + // Don't attempt to do this in real code. + assertEquals( + fingerprint(stringA.getBytes(ISO_8859_1)), fingerprint(stringB.getBytes(ISO_8859_1))); + } + + public void testMumurHash64() { + byte[] bytes = "test".getBytes(UTF_8); + assertEquals( + 1618900948208871284L, Fingerprint2011.murmurHash64WithSeed(bytes, 0, bytes.length, 1)); + + bytes = "test test test".getBytes(UTF_8); + assertEquals( + UnsignedLong.valueOf("12313169684067793560").longValue(), + Fingerprint2011.murmurHash64WithSeed(bytes, 0, bytes.length, 1)); + } + + public void testPutNonChars() { + Hasher hasher = HASH_FN.newHasher(); + // Expected data is 0x0100010100000000 + hasher + .putBoolean(true) + .putBoolean(true) + .putBoolean(false) + .putBoolean(true) + .putBoolean(false) + .putBoolean(false) + .putBoolean(false) + .putBoolean(false); + final long hashCode = hasher.hash().asLong(); + + hasher = HASH_FN.newHasher(); + hasher + .putByte((byte) 0x01) + .putByte((byte) 0x01) + .putByte((byte) 0x00) + .putByte((byte) 0x01) + .putByte((byte) 0x00) + .putByte((byte) 0x00) + .putByte((byte) 0x00) + .putByte((byte) 0x00); + assertEquals(hashCode, hasher.hash().asLong()); + + hasher = HASH_FN.newHasher(); + hasher + .putChar((char) 0x0101) + .putChar((char) 0x0100) + .putChar((char) 0x0000) + .putChar((char) 0x0000); + assertEquals(hashCode, hasher.hash().asLong()); + + hasher = HASH_FN.newHasher(); + hasher.putBytes(new byte[] {0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}); + assertEquals(hashCode, hasher.hash().asLong()); + + hasher = HASH_FN.newHasher(); + hasher.putLong(0x0000000001000101L); + assertEquals(hashCode, hasher.hash().asLong()); + + hasher = HASH_FN.newHasher(); + hasher + .putShort((short) 0x0101) + .putShort((short) 0x0100) + .putShort((short) 0x0000) + .putShort((short) 0x0000); + assertEquals(hashCode, hasher.hash().asLong()); + } + + public void testHashFloatIsStable() { + // This is about the best we can do for floating-point + Hasher hasher = HASH_FN.newHasher(); + hasher.putFloat(0x01000101f).putFloat(0f); + assertEquals(0x96a4f8cc6ecbf16L, hasher.hash().asLong()); + + hasher = HASH_FN.newHasher(); + hasher.putDouble(0x0000000001000101d); + assertEquals(0xcf54171253fdc198L, hasher.hash().asLong()); + } + + /** Convenience method to compute a fingerprint on a full bytes array. */ + private static long fingerprint(byte[] bytes) { + return fingerprint(bytes, bytes.length); + } + + /** Convenience method to compute a fingerprint on a subset of a byte array. */ + private static long fingerprint(byte[] bytes, int length) { + return HASH_FN.hashBytes(bytes, 0, length).asLong(); + } + + /** + * Tests that the Java port of Fingerprint2011 provides the same results on buffers up to 800 + * bytes long as the original implementation in C++. See http://cl/106539598 + */ + public void testMultipleLengths() { + int iterations = 800; + byte[] buf = new byte[iterations * 4]; + int bufLen = 0; + long h = 0; + for (int i = 0; i < iterations; ++i) { + h ^= fingerprint(buf, i); + h = remix(h); + buf[bufLen++] = getChar(h); + + h ^= fingerprint(buf, i * i % bufLen); + h = remix(h); + buf[bufLen++] = getChar(h); + + h ^= fingerprint(buf, i * i * i % bufLen); + h = remix(h); + buf[bufLen++] = getChar(h); + + h ^= fingerprint(buf, bufLen); + h = remix(h); + buf[bufLen++] = getChar(h); + + int x0 = buf[bufLen - 1] & 0xff; + int x1 = buf[bufLen - 2] & 0xff; + int x2 = buf[bufLen - 3] & 0xff; + int x3 = buf[bufLen / 2] & 0xff; + buf[((x0 << 16) + (x1 << 8) + x2) % bufLen] ^= x3; + buf[((x1 << 16) + (x2 << 8) + x3) % bufLen] ^= i % 256; + } + assertEquals(0xeaa3b1c985261632L, h); + } + + private static long remix(long h) { + h ^= h >>> 41; + h *= 949921979; + return h; + } + + private static byte getChar(long h) { + return (byte) ('a' + ((h & 0xfffff) % 26)); + } +} diff --git a/android/guava-tests/test/com/google/common/hash/FunnelsTest.java b/android/guava-tests/test/com/google/common/hash/FunnelsTest.java index 6b0c75862cac..862ddcb6d153 100644 --- a/android/guava-tests/test/com/google/common/hash/FunnelsTest.java +++ b/android/guava-tests/test/com/google/common/hash/FunnelsTest.java @@ -16,11 +16,12 @@ package com.google.common.hash; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import com.google.common.base.Charsets; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; import java.io.OutputStream; @@ -28,6 +29,7 @@ import java.nio.charset.Charset; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; import org.mockito.InOrder; /** @@ -35,6 +37,7 @@ * * @author Dimitris Andreou */ +@NullUnmarked public class FunnelsTest extends TestCase { public void testForBytes() { PrimitiveSink primitiveSink = mock(PrimitiveSink.class); @@ -151,8 +154,8 @@ public void testSerialization() { Funnels.sequentialFunnel(Funnels.integerFunnel()), SerializableTester.reserialize(Funnels.sequentialFunnel(Funnels.integerFunnel()))); assertEquals( - Funnels.stringFunnel(Charsets.US_ASCII), - SerializableTester.reserialize(Funnels.stringFunnel(Charsets.US_ASCII))); + Funnels.stringFunnel(US_ASCII), + SerializableTester.reserialize(Funnels.stringFunnel(US_ASCII))); } public void testEquals() { @@ -161,8 +164,8 @@ public void testEquals() { .addEqualityGroup(Funnels.integerFunnel()) .addEqualityGroup(Funnels.longFunnel()) .addEqualityGroup(Funnels.unencodedCharsFunnel()) - .addEqualityGroup(Funnels.stringFunnel(Charsets.UTF_8)) - .addEqualityGroup(Funnels.stringFunnel(Charsets.US_ASCII)) + .addEqualityGroup(Funnels.stringFunnel(UTF_8)) + .addEqualityGroup(Funnels.stringFunnel(US_ASCII)) .addEqualityGroup( Funnels.sequentialFunnel(Funnels.integerFunnel()), SerializableTester.reserialize(Funnels.sequentialFunnel(Funnels.integerFunnel()))) diff --git a/android/guava-tests/test/com/google/common/hash/HashCodeTest.java b/android/guava-tests/test/com/google/common/hash/HashCodeTest.java index 4cccefe7e36d..46c6ac4b7f0e 100644 --- a/android/guava-tests/test/com/google/common/hash/HashCodeTest.java +++ b/android/guava-tests/test/com/google/common/hash/HashCodeTest.java @@ -17,13 +17,16 @@ package com.google.common.hash; import static com.google.common.io.BaseEncoding.base16; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.common.testing.ClassSanityTester; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@link HashCode}. @@ -31,6 +34,7 @@ * @author Dimitris Andreou * @author Kurt Alfred Kluever */ +@NullUnmarked public class HashCodeTest extends TestCase { // note: asInt(), asLong() are in little endian private static final ImmutableList expectedHashCodes = @@ -181,7 +185,7 @@ public void testHashCode_equalsAndSerializable() throws Exception { } public void testRoundTripHashCodeUsingBaseEncoding() { - HashCode hash1 = Hashing.sha1().hashString("foo", Charsets.US_ASCII); + HashCode hash1 = Hashing.sha1().hashString("foo", US_ASCII); HashCode hash2 = HashCode.fromBytes(BaseEncoding.base16().lowerCase().decode(hash1.toString())); assertEquals(hash1, hash2); } @@ -191,7 +195,7 @@ public void testObjectHashCode() { assertEquals(42, hashCode42.hashCode()); } - // See https://code.google.com/p/guava-libraries/issues/detail?id=1494 + // See https://github.com/google/guava/issues/1494 public void testObjectHashCodeWithSameLowOrderBytes() { // These will have the same first 4 bytes (all 0). byte[] bytesA = new byte[5]; @@ -213,7 +217,7 @@ public void testObjectHashCodeWithSameLowOrderBytes() { } public void testRoundTripHashCodeUsingFromString() { - HashCode hash1 = Hashing.sha1().hashString("foo", Charsets.US_ASCII); + HashCode hash1 = Hashing.sha1().hashString("foo", US_ASCII); HashCode hash2 = HashCode.fromString(hash1.toString()); assertEquals(hash1, hash2); } @@ -229,42 +233,22 @@ public void testRoundTrip() { } public void testFromStringFailsWithInvalidHexChar() { - try { - HashCode.fromString("7f8005ff0z"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashCode.fromString("7f8005ff0z")); } public void testFromStringFailsWithUpperCaseString() { - String string = Hashing.sha1().hashString("foo", Charsets.US_ASCII).toString().toUpperCase(); - try { - HashCode.fromString(string); - fail(); - } catch (IllegalArgumentException expected) { - } + String string = Hashing.sha1().hashString("foo", US_ASCII).toString().toUpperCase(); + assertThrows(IllegalArgumentException.class, () -> HashCode.fromString(string)); } public void testFromStringFailsWithShortInputs() { - try { - HashCode.fromString(""); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - HashCode.fromString("7"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashCode.fromString("")); + assertThrows(IllegalArgumentException.class, () -> HashCode.fromString("7")); HashCode unused = HashCode.fromString("7f"); } public void testFromStringFailsWithOddLengthInput() { - try { - HashCode.fromString("7f8"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HashCode.fromString("7f8")); } public void testIntWriteBytesTo() { @@ -315,20 +299,12 @@ public void testWriteBytesToOversizedArrayShortMaxLength() { public void testWriteBytesToUndersizedArray() { byte[] dest = new byte[3]; - try { - HASH_ABCD.writeBytesTo(dest, 0, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> HASH_ABCD.writeBytesTo(dest, 0, 4)); } public void testWriteBytesToUndersizedArrayLongMaxLength() { byte[] dest = new byte[3]; - try { - HASH_ABCD.writeBytesTo(dest, 0, 5); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> HASH_ABCD.writeBytesTo(dest, 0, 5)); } public void testWriteBytesToUndersizedArrayShortMaxLength() { @@ -391,7 +367,7 @@ private static class ExpectedHashCode { final Long asLong; // null means that asLong should throw an exception final String toString; - ExpectedHashCode(byte[] bytes, int asInt, Long asLong, String toString) { + ExpectedHashCode(byte[] bytes, int asInt, @Nullable Long asLong, String toString) { this.bytes = bytes; this.asInt = asInt; this.asLong = asLong; diff --git a/android/guava-tests/test/com/google/common/hash/HashFunctionEnum.java b/android/guava-tests/test/com/google/common/hash/HashFunctionEnum.java index 34717477e645..2533ce270629 100644 --- a/android/guava-tests/test/com/google/common/hash/HashFunctionEnum.java +++ b/android/guava-tests/test/com/google/common/hash/HashFunctionEnum.java @@ -16,12 +16,14 @@ package com.google.common.hash; +import org.jspecify.annotations.NullUnmarked; /** * An enum that contains all of the known hash functions. * * @author Kurt Alfred Kluever */ +@NullUnmarked enum HashFunctionEnum { ADLER32(Hashing.adler32()), CRC32(Hashing.crc32()), @@ -46,7 +48,7 @@ enum HashFunctionEnum { private final HashFunction hashFunction; - private HashFunctionEnum(HashFunction hashFunction) { + HashFunctionEnum(HashFunction hashFunction) { this.hashFunction = hashFunction; } diff --git a/android/guava-tests/test/com/google/common/hash/HashTestUtils.java b/android/guava-tests/test/com/google/common/hash/HashTestUtils.java index 9e9944b5abfb..85096df34df9 100644 --- a/android/guava-tests/test/com/google/common/hash/HashTestUtils.java +++ b/android/guava-tests/test/com/google/common/hash/HashTestUtils.java @@ -16,10 +16,16 @@ package com.google.common.hash; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_16; +import static java.nio.charset.StandardCharsets.UTF_16BE; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; @@ -30,6 +36,7 @@ import java.util.Arrays; import java.util.Random; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; import org.junit.Assert; /** @@ -38,6 +45,7 @@ * @author Dimitris Andreou * @author Kurt Alfred Kluever */ +@NullUnmarked final class HashTestUtils { private HashTestUtils() {} @@ -353,7 +361,7 @@ static void checkAvalanche(HashFunction function, int trials, double epsilon) { // measure probability and assert it's within margin of error for (int j = 0; j < hashBits; j++) { double prob = (double) diff[j] / (double) (diff[j] + same[j]); - Assert.assertEquals(0.50d, prob, epsilon); + assertThat(prob).isWithin(epsilon).of(0.50d); } } } @@ -376,7 +384,7 @@ static void checkNo2BitCharacteristics(HashFunction function) { for (int j = 0; j < keyBits; j++) { if (j <= i) continue; int count = 0; - int maxCount = 20; // the probability of error here is miniscule + int maxCount = 20; // the probability of error here is minuscule boolean diff = false; while (!diff) { @@ -450,7 +458,7 @@ static void check2BitAvalanche(HashFunction function, int trials, double epsilon // measure probability and assert it's within margin of error for (int j = 0; j < hashBits; j++) { double prob = (double) diff[j] / (double) (diff[j] + same[j]); - Assert.assertEquals(0.50d, prob, epsilon); + assertThat(prob).isWithin(epsilon).of(0.50d); } } } @@ -627,13 +635,7 @@ private static void assertHashLongEquivalence(HashFunction hashFunction, Random } private static final ImmutableSet CHARSETS = - ImmutableSet.of( - Charsets.ISO_8859_1, - Charsets.US_ASCII, - Charsets.UTF_16, - Charsets.UTF_16BE, - Charsets.UTF_16LE, - Charsets.UTF_8); + ImmutableSet.of(ISO_8859_1, US_ASCII, UTF_16, UTF_16BE, UTF_16LE, UTF_8); private static void assertHashStringEquivalence(HashFunction hashFunction, Random random) { // Test that only data and data-order is important, not the individual operations. @@ -657,7 +659,7 @@ private static void assertHashStringEquivalence(HashFunction hashFunction, Rando int size = random.nextInt(2048); byte[] bytes = new byte[size]; random.nextBytes(bytes); - String string = new String(bytes, Charsets.US_ASCII); + String string = new String(bytes, US_ASCII); assertEquals( hashFunction.hashUnencodedChars(string), hashFunction.newHasher().putUnencodedChars(string).hash()); diff --git a/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java b/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java index 05351d94cdde..51c65cb5f78e 100644 --- a/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java +++ b/android/guava-tests/test/com/google/common/hash/HashingInputStreamTest.java @@ -23,12 +23,14 @@ import java.io.ByteArrayInputStream; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link HashingInputStream}. * * @author Qian Huang */ +@NullUnmarked public class HashingInputStreamTest extends TestCase { private Hasher hasher; private HashFunction hashFunction; diff --git a/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java b/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java index 55e8fbf4a5b6..9f4bf00f848d 100644 --- a/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java +++ b/android/guava-tests/test/com/google/common/hash/HashingOutputStreamTest.java @@ -22,12 +22,14 @@ import com.google.common.testing.NullPointerTester; import java.io.ByteArrayOutputStream; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link HashingOutputStream}. * - * @author Nick Piepmeier + * @author Zoe Piepmeier */ +@NullUnmarked public class HashingOutputStreamTest extends TestCase { private Hasher hasher; private HashFunction hashFunction; diff --git a/android/guava-tests/test/com/google/common/hash/HashingTest.java b/android/guava-tests/test/com/google/common/hash/HashingTest.java index bc3db34f050a..a9d4433b3ea8 100644 --- a/android/guava-tests/test/com/google/common/hash/HashingTest.java +++ b/android/guava-tests/test/com/google/common/hash/HashingTest.java @@ -16,10 +16,12 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Lists; import com.google.common.collect.Table.Cell; @@ -35,16 +37,18 @@ import java.util.Locale; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Hashing}. * - *

    TODO(b/33919189): Migrate repeated testing methods to {@link #HashTestUtils} and tweak unit + *

    TODO(b/33919189): Migrate repeated testing methods to {@link HashTestUtils} and tweak unit * tests to reference them from there. * * @author Dimitris Andreou * @author Kurt Alfred Kluever */ +@NullUnmarked public class HashingTest extends TestCase { public void testMd5() { HashTestUtils.checkAvalanche(Hashing.md5(), 100, 0.4); @@ -125,6 +129,15 @@ public void testSipHash24() { Hashing.sipHash24().toString()); } + public void testFingerprint2011() { + HashTestUtils.check2BitAvalanche(Hashing.fingerprint2011(), 100, 0.4); + HashTestUtils.checkAvalanche(Hashing.fingerprint2011(), 100, 0.4); + HashTestUtils.checkNo2BitCharacteristics(Hashing.fingerprint2011()); + HashTestUtils.checkNoFunnels(Hashing.fingerprint2011()); + HashTestUtils.assertInvariants(Hashing.fingerprint2011()); + assertEquals("Hashing.fingerprint2011()", Hashing.fingerprint2011().toString()); + } + @AndroidIncompatible // slow TODO(cpovirk): Maybe just reduce iterations under Android. public void testGoodFastHash() { for (int i = 1; i < 200; i += 17) { @@ -210,11 +223,7 @@ private void countRemaps(long h, AtomicLongMap map) { private static final int MAX_SHARDS = 500; public void testConsistentHash_outOfRange() { - try { - Hashing.consistentHash(5L, 0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Hashing.consistentHash(5L, 0)); } public void testConsistentHash_ofHashCode() { @@ -251,20 +260,19 @@ public void testConsistentHash_linearCongruentialGeneratorCompatibility() { private static final long RANDOM_SEED = 177L; public void testCombineOrdered_empty() { - try { - Hashing.combineOrdered(Collections.emptySet()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Hashing.combineOrdered(Collections.emptySet())); } public void testCombineOrdered_differentBitLengths() { - try { - HashCode unused = - Hashing.combineOrdered(ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + HashCode unused = + Hashing.combineOrdered( + ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); + }); } public void testCombineOrdered() { @@ -296,20 +304,19 @@ public void testCombineOrdered_randomHashCodes() { } public void testCombineUnordered_empty() { - try { - Hashing.combineUnordered(Collections.emptySet()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Hashing.combineUnordered(Collections.emptySet())); } public void testCombineUnordered_differentBitLengths() { - try { - HashCode unused = - Hashing.combineUnordered(ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + HashCode unused = + Hashing.combineUnordered( + ImmutableList.of(HashCode.fromInt(32), HashCode.fromLong(32L))); + }); } public void testCombineUnordered() { @@ -414,33 +421,32 @@ public void testHashIntVsForLoop() { assertEquals(expected, actual); } - private static final String EMPTY_STRING = ""; private static final String TQBFJOTLD = "The quick brown fox jumps over the lazy dog"; private static final String TQBFJOTLDP = "The quick brown fox jumps over the lazy dog."; private static final ImmutableTable KNOWN_HASHES = ImmutableTable.builder() - .put(Hashing.adler32(), EMPTY_STRING, "01000000") + .put(Hashing.adler32(), "", "01000000") .put(Hashing.adler32(), TQBFJOTLD, "da0fdc5b") .put(Hashing.adler32(), TQBFJOTLDP, "0810e46b") - .put(Hashing.md5(), EMPTY_STRING, "d41d8cd98f00b204e9800998ecf8427e") + .put(Hashing.md5(), "", "d41d8cd98f00b204e9800998ecf8427e") .put(Hashing.md5(), TQBFJOTLD, "9e107d9d372bb6826bd81d3542a419d6") .put(Hashing.md5(), TQBFJOTLDP, "e4d909c290d0fb1ca068ffaddf22cbd0") - .put(Hashing.murmur3_128(), EMPTY_STRING, "00000000000000000000000000000000") + .put(Hashing.murmur3_128(), "", "00000000000000000000000000000000") .put(Hashing.murmur3_128(), TQBFJOTLD, "6c1b07bc7bbc4be347939ac4a93c437a") .put(Hashing.murmur3_128(), TQBFJOTLDP, "c902e99e1f4899cde7b68789a3a15d69") - .put(Hashing.murmur3_32(), EMPTY_STRING, "00000000") + .put(Hashing.murmur3_32(), "", "00000000") .put(Hashing.murmur3_32(), TQBFJOTLD, "23f74f2e") .put(Hashing.murmur3_32(), TQBFJOTLDP, "fc8bc4d5") - .put(Hashing.murmur3_32_fixed(), EMPTY_STRING, "00000000") + .put(Hashing.murmur3_32_fixed(), "", "00000000") .put(Hashing.murmur3_32_fixed(), TQBFJOTLD, "23f74f2e") .put(Hashing.murmur3_32_fixed(), TQBFJOTLDP, "fc8bc4d5") - .put(Hashing.sha1(), EMPTY_STRING, "da39a3ee5e6b4b0d3255bfef95601890afd80709") + .put(Hashing.sha1(), "", "da39a3ee5e6b4b0d3255bfef95601890afd80709") .put(Hashing.sha1(), TQBFJOTLD, "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12") .put(Hashing.sha1(), TQBFJOTLDP, "408d94384216f890ff7a0c3528e8bed1e0b01621") .put( Hashing.sha256(), - EMPTY_STRING, + "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") .put( Hashing.sha256(), @@ -452,7 +458,7 @@ public void testHashIntVsForLoop() { "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c") .put( Hashing.sha384(), - EMPTY_STRING, + "", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da2" + "74edebfe76f65fbd51ad2f14898b95b") .put( @@ -467,7 +473,7 @@ public void testHashIntVsForLoop() { + "a7af2819a021c2fc34e91bdb63409d7") .put( Hashing.sha512(), - EMPTY_STRING, + "", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" + "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e") .put( @@ -480,28 +486,26 @@ public void testHashIntVsForLoop() { TQBFJOTLDP, "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bb" + "c6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed") - .put(Hashing.crc32(), EMPTY_STRING, "00000000") + .put(Hashing.crc32(), "", "00000000") .put(Hashing.crc32(), TQBFJOTLD, "39a34f41") .put(Hashing.crc32(), TQBFJOTLDP, "e9259051") - .put(Hashing.sipHash24(), EMPTY_STRING, "310e0edd47db6f72") + .put(Hashing.sipHash24(), "", "310e0edd47db6f72") .put(Hashing.sipHash24(), TQBFJOTLD, "e46f1fdc05612752") .put(Hashing.sipHash24(), TQBFJOTLDP, "9b602581fce4d4f8") - .put(Hashing.crc32c(), EMPTY_STRING, "00000000") + .put(Hashing.crc32c(), "", "00000000") .put(Hashing.crc32c(), TQBFJOTLD, "04046222") .put(Hashing.crc32c(), TQBFJOTLDP, "b3970019") - .put(Hashing.farmHashFingerprint64(), EMPTY_STRING, "4f40902f3b6ae19a") + .put(Hashing.farmHashFingerprint64(), "", "4f40902f3b6ae19a") .put(Hashing.farmHashFingerprint64(), TQBFJOTLD, "34511b3bf383beab") .put(Hashing.farmHashFingerprint64(), TQBFJOTLDP, "737d7e5f8660653e") + .put(Hashing.fingerprint2011(), "", "e365a64a907cad23") + .put(Hashing.fingerprint2011(), TQBFJOTLD, "c9688c84e813b089") + .put(Hashing.fingerprint2011(), TQBFJOTLDP, "a714d70f1d569cd0") .build(); public void testAllHashFunctionsHaveKnownHashes() throws Exception { - // The following legacy hashing function methods have been covered by unit testing already. - List legacyHashingMethodNames = ImmutableList.of("murmur2_64", "fprint96"); for (Method method : Hashing.class.getDeclaredMethods()) { - if (method.getReturnType().equals(HashFunction.class) // must return HashFunction - && Modifier.isPublic(method.getModifiers()) // only the public methods - && method.getParameterTypes().length == 0 // only the seed-less grapes^W hash functions - && !legacyHashingMethodNames.contains(method.getName())) { + if (shouldHaveKnownHashes(method)) { HashFunction hashFunction = (HashFunction) method.invoke(Hashing.class); assertTrue( "There should be at least 3 entries in KNOWN_HASHES for " + hashFunction, @@ -570,9 +574,7 @@ public void testGoodFastHashEquals() throws Exception { static void assertSeedlessHashFunctionEquals(Class clazz) throws Exception { for (Method method : clazz.getDeclaredMethods()) { - if (method.getReturnType().equals(HashFunction.class) // must return HashFunction - && Modifier.isPublic(method.getModifiers()) // only the public methods - && method.getParameterTypes().length == 0) { // only the seed-less hash functions + if (shouldHaveKnownHashes(method)) { HashFunction hashFunction1a = (HashFunction) method.invoke(clazz); HashFunction hashFunction1b = (HashFunction) method.invoke(clazz); @@ -586,6 +588,16 @@ static void assertSeedlessHashFunctionEquals(Class clazz) throws Exception { } } + private static boolean shouldHaveKnownHashes(Method method) { + // The following legacy hashing function methods have been covered by unit testing already. + ImmutableSet legacyHashingMethodNames = + ImmutableSet.of("murmur2_64", "fprint96", "highwayFingerprint64", "highwayFingerprint128"); + return method.getReturnType().equals(HashFunction.class) // must return HashFunction + && Modifier.isPublic(method.getModifiers()) // only the public methods + && method.getParameterTypes().length == 0 // only the seedless hash functions + && !legacyHashingMethodNames.contains(method.getName()); + } + static void assertSeededHashFunctionEquals(Class clazz) throws Exception { Random random = new Random(RANDOM_SEED); for (Method method : clazz.getDeclaredMethods()) { diff --git a/android/guava-tests/test/com/google/common/hash/MacHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/MacHashFunctionTest.java index 4dbb4241e0b6..9bfe7af85a99 100644 --- a/android/guava-tests/test/com/google/common/hash/MacHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/MacHashFunctionTest.java @@ -16,8 +16,9 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.io.BaseEncoding.base16; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableTable; @@ -29,6 +30,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import sun.security.jca.ProviderList; import sun.security.jca.Providers; @@ -37,6 +40,7 @@ * * @author Kurt Alfred Kluever */ +@NullUnmarked public class MacHashFunctionTest extends TestCase { private static final ImmutableSet INPUTS = ImmutableSet.of("", "Z", "foobar"); @@ -57,7 +61,7 @@ public class MacHashFunctionTest extends TestCase { .put("HmacSHA1", SHA1_KEY, Hashing.hmacSha1(SHA1_KEY)) .put("HmacSHA256", SHA256_KEY, Hashing.hmacSha256(SHA256_KEY)) .put("HmacSHA512", SHA512_KEY, Hashing.hmacSha512(SHA512_KEY)) - .build(); + .buildOrThrow(); public void testNulls() { NullPointerTester tester = @@ -155,7 +159,7 @@ public String getAlgorithm() { } @Override - public byte[] getEncoded() { + public byte @Nullable [] getEncoded() { return null; } @@ -224,11 +228,7 @@ public void testPutAfterHash() { assertEquals( "9753980fe94daa8ecaa82216519393a9", hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); - try { - hasher.putInt(42); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> hasher.putInt(42)); } public void testHashTwice() { @@ -237,11 +237,7 @@ public void testHashTwice() { assertEquals( "9753980fe94daa8ecaa82216519393a9", hasher.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); - try { - hasher.hash(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> hasher.hash()); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/hash/MessageDigestHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/MessageDigestHashFunctionTest.java index 535d455212d4..127b0e7eeac7 100644 --- a/android/guava-tests/test/com/google/common/hash/MessageDigestHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/MessageDigestHashFunctionTest.java @@ -16,19 +16,23 @@ package com.google.common.hash; -import com.google.common.base.Charsets; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the MessageDigestHashFunction. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class MessageDigestHashFunctionTest extends TestCase { private static final ImmutableSet INPUTS = ImmutableSet.of("", "Z", "foobar"); @@ -62,14 +66,8 @@ public void testPutAfterHash() { assertEquals( "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - sha1.putString("The quick brown fox jumps over the lazy dog", Charsets.UTF_8) - .hash() - .toString()); - try { - sha1.putInt(42); - fail(); - } catch (IllegalStateException expected) { - } + sha1.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); + assertThrows(IllegalStateException.class, () -> sha1.putInt(42)); } public void testHashTwice() { @@ -77,14 +75,8 @@ public void testHashTwice() { assertEquals( "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - sha1.putString("The quick brown fox jumps over the lazy dog", Charsets.UTF_8) - .hash() - .toString()); - try { - sha1.hash(); - fail(); - } catch (IllegalStateException expected) { - } + sha1.putString("The quick brown fox jumps over the lazy dog", UTF_8).hash().toString()); + assertThrows(IllegalStateException.class, () -> sha1.hash()); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/hash/Murmur3Hash128Test.java b/android/guava-tests/test/com/google/common/hash/Murmur3Hash128Test.java index 89b072cf5eee..be1d5fb548f9 100644 --- a/android/guava-tests/test/com/google/common/hash/Murmur3Hash128Test.java +++ b/android/guava-tests/test/com/google/common/hash/Murmur3Hash128Test.java @@ -17,14 +17,16 @@ package com.google.common.hash; import static com.google.common.hash.Hashing.murmur3_128; +import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Charsets; import com.google.common.hash.HashTestUtils.HashFn; import java.nio.ByteBuffer; import java.nio.ByteOrder; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for {@link Murmur3_128HashFunction}. */ +@NullUnmarked public class Murmur3Hash128Test extends TestCase { public void testKnownValues() { assertHash(0, 0x629942693e10f867L, 0x92db0b82baeb5347L, "hell"); @@ -40,7 +42,7 @@ public void testKnownValues() { // Known output from Python smhasher HashCode foxHash = - murmur3_128(0).hashString("The quick brown fox jumps over the lazy dog", Charsets.UTF_8); + murmur3_128(0).hashString("The quick brown fox jumps over the lazy dog", UTF_8); assertEquals("6c1b07bc7bbc4be347939ac4a93c437a", foxHash.toString()); } diff --git a/android/guava-tests/test/com/google/common/hash/Murmur3Hash32Test.java b/android/guava-tests/test/com/google/common/hash/Murmur3Hash32Test.java index 181b2a7bb071..3728b44b5bf7 100644 --- a/android/guava-tests/test/com/google/common/hash/Murmur3Hash32Test.java +++ b/android/guava-tests/test/com/google/common/hash/Murmur3Hash32Test.java @@ -18,14 +18,18 @@ import static com.google.common.hash.Hashing.murmur3_32; import static com.google.common.hash.Hashing.murmur3_32_fixed; +import static java.nio.charset.StandardCharsets.UTF_16; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Charsets; import com.google.common.hash.HashTestUtils.HashFn; import java.nio.charset.Charset; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for {@link Murmur3_32HashFunction}. */ +@NullUnmarked public class Murmur3Hash32Test extends TestCase { public void testKnownIntegerInputs() { assertHash(593689054, murmur3_32().hashInt(0)); @@ -55,25 +59,25 @@ public void testKnownStringInputs() { @SuppressWarnings("deprecation") public void testKnownEncodedStringInputs() { - assertStringHash(0, "", Charsets.UTF_8); - assertStringHash(0xcfbda5d1, "k", Charsets.UTF_8); - assertStringHash(0xa167dbf3, "hell", Charsets.UTF_8); - assertStringHash(0x248bfa47, "hello", Charsets.UTF_8); - assertStringHash(0x3d41b97c, "http://www.google.com/", Charsets.UTF_8); - assertStringHash(0x2e4ff723, "The quick brown fox jumps over the lazy dog", Charsets.UTF_8); - assertStringHash(0xb5a4be05, "ABCDefGHI\u0799", Charsets.UTF_8); - assertStringHash(0xfc5ba834, "毎月1日,毎週月曜日", Charsets.UTF_8); - assertStringHash(0x8a5c3699, "surrogate pair: \uD83D\uDCB0", Charsets.UTF_8); - - assertStringHash(0, "", Charsets.UTF_16LE); - assertStringHash(0x288418e4, "k", Charsets.UTF_16LE); - assertStringHash(0x5a0cb7c3, "hell", Charsets.UTF_16LE); - assertStringHash(0xd7c31989, "hello", Charsets.UTF_16LE); - assertStringHash(0x73564d8c, "http://www.google.com/", Charsets.UTF_16LE); - assertStringHash(0xe07db09c, "The quick brown fox jumps over the lazy dog", Charsets.UTF_16LE); - assertStringHash(0xfefa3e76, "ABCDefGHI\u0799", Charsets.UTF_16LE); - assertStringHash(0x6a7be132, "毎月1日,毎週月曜日", Charsets.UTF_16LE); - assertStringHash(0x5a2d41c7, "surrogate pair: \uD83D\uDCB0", Charsets.UTF_16LE); + assertStringHash(0, "", UTF_8); + assertStringHash(0xcfbda5d1, "k", UTF_8); + assertStringHash(0xa167dbf3, "hell", UTF_8); + assertStringHash(0x248bfa47, "hello", UTF_8); + assertStringHash(0x3d41b97c, "http://www.google.com/", UTF_8); + assertStringHash(0x2e4ff723, "The quick brown fox jumps over the lazy dog", UTF_8); + assertStringHash(0xb5a4be05, "ABCDefGHI\u0799", UTF_8); + assertStringHash(0xfc5ba834, "毎月1日,毎週月曜日", UTF_8); + assertStringHash(0x8a5c3699, "surrogate pair: \uD83D\uDCB0", UTF_8); + + assertStringHash(0, "", UTF_16LE); + assertStringHash(0x288418e4, "k", UTF_16LE); + assertStringHash(0x5a0cb7c3, "hell", UTF_16LE); + assertStringHash(0xd7c31989, "hello", UTF_16LE); + assertStringHash(0x73564d8c, "http://www.google.com/", UTF_16LE); + assertStringHash(0xe07db09c, "The quick brown fox jumps over the lazy dog", UTF_16LE); + assertStringHash(0xfefa3e76, "ABCDefGHI\u0799", UTF_16LE); + assertStringHash(0x6a7be132, "毎月1日,毎週月曜日", UTF_16LE); + assertStringHash(0x5a2d41c7, "surrogate pair: \uD83D\uDCB0", UTF_16LE); } @SuppressWarnings("deprecation") @@ -105,15 +109,14 @@ private boolean allBmp(String string) { @SuppressWarnings("deprecation") public void testSimpleStringUtf8() { assertEquals( - murmur3_32().hashBytes("ABCDefGHI\u0799".getBytes(Charsets.UTF_8)), - murmur3_32().hashString("ABCDefGHI\u0799", Charsets.UTF_8)); + murmur3_32().hashBytes("ABCDefGHI\u0799".getBytes(UTF_8)), + murmur3_32().hashString("ABCDefGHI\u0799", UTF_8)); } @SuppressWarnings("deprecation") public void testEncodedStringInputs() { Random rng = new Random(0); for (int z = 0; z < 100; z++) { - String str; int[] codePoints = new int[rng.nextInt(8)]; for (int i = 0; i < codePoints.length; i++) { do { @@ -126,17 +129,15 @@ public void testEncodedStringInputs() { for (int i = 0; i < codePoints.length; i++) { builder.appendCodePoint(codePoints[i]); } - str = builder.toString(); - HashCode hashUtf8 = murmur3_32().hashBytes(str.getBytes(Charsets.UTF_8)); - assertEquals( - hashUtf8, murmur3_32().newHasher().putBytes(str.getBytes(Charsets.UTF_8)).hash()); - assertEquals(hashUtf8, murmur3_32().hashString(str, Charsets.UTF_8)); - assertEquals(hashUtf8, murmur3_32().newHasher().putString(str, Charsets.UTF_8).hash()); - HashCode hashUtf16 = murmur3_32().hashBytes(str.getBytes(Charsets.UTF_16)); - assertEquals( - hashUtf16, murmur3_32().newHasher().putBytes(str.getBytes(Charsets.UTF_16)).hash()); - assertEquals(hashUtf16, murmur3_32().hashString(str, Charsets.UTF_16)); - assertEquals(hashUtf16, murmur3_32().newHasher().putString(str, Charsets.UTF_16).hash()); + String str = builder.toString(); + HashCode hashUtf8 = murmur3_32().hashBytes(str.getBytes(UTF_8)); + assertEquals(hashUtf8, murmur3_32().newHasher().putBytes(str.getBytes(UTF_8)).hash()); + assertEquals(hashUtf8, murmur3_32().hashString(str, UTF_8)); + assertEquals(hashUtf8, murmur3_32().newHasher().putString(str, UTF_8).hash()); + HashCode hashUtf16 = murmur3_32().hashBytes(str.getBytes(UTF_16)); + assertEquals(hashUtf16, murmur3_32().newHasher().putBytes(str.getBytes(UTF_16)).hash()); + assertEquals(hashUtf16, murmur3_32().hashString(str, UTF_16)); + assertEquals(hashUtf16, murmur3_32().newHasher().putString(str, UTF_16).hash()); } } @@ -181,12 +182,9 @@ public void testInvalidUnicodeHashString() { String str = new String( new char[] {'a', Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE, 'z'}); + assertEquals(murmur3_32().hashBytes(str.getBytes(UTF_8)), murmur3_32().hashString(str, UTF_8)); assertEquals( - murmur3_32().hashBytes(str.getBytes(Charsets.UTF_8)), - murmur3_32().hashString(str, Charsets.UTF_8)); - assertEquals( - murmur3_32_fixed().hashBytes(str.getBytes(Charsets.UTF_8)), - murmur3_32().hashString(str, Charsets.UTF_8)); + murmur3_32_fixed().hashBytes(str.getBytes(UTF_8)), murmur3_32().hashString(str, UTF_8)); } @SuppressWarnings("deprecation") @@ -195,10 +193,10 @@ public void testInvalidUnicodeHasherPutString() { new String( new char[] {'a', Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE, 'z'}); assertEquals( - murmur3_32().hashBytes(str.getBytes(Charsets.UTF_8)), - murmur3_32().newHasher().putString(str, Charsets.UTF_8).hash()); + murmur3_32().hashBytes(str.getBytes(UTF_8)), + murmur3_32().newHasher().putString(str, UTF_8).hash()); assertEquals( - murmur3_32_fixed().hashBytes(str.getBytes(Charsets.UTF_8)), - murmur3_32_fixed().newHasher().putString(str, Charsets.UTF_8).hash()); + murmur3_32_fixed().hashBytes(str.getBytes(UTF_8)), + murmur3_32_fixed().newHasher().putString(str, UTF_8).hash()); } } diff --git a/android/guava-tests/test/com/google/common/hash/PackageSanityTests.java b/android/guava-tests/test/com/google/common/hash/PackageSanityTests.java index d2b0ef5261bb..e81d60515839 100644 --- a/android/guava-tests/test/com/google/common/hash/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/hash/PackageSanityTests.java @@ -18,6 +18,7 @@ import com.google.common.hash.BloomFilterStrategies.LockFreeBitArray; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -25,6 +26,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { setDefault(LockFreeBitArray.class, new LockFreeBitArray(1)); diff --git a/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java b/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java index ded444752d27..64ff4e20c99d 100644 --- a/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java +++ b/android/guava-tests/test/com/google/common/hash/SipHashFunctionTest.java @@ -14,16 +14,18 @@ package com.google.common.hash; -import static com.google.common.base.Charsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link SipHashFunction}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class SipHashFunctionTest extends TestCase { // From https://131002.net/siphash/siphash24.c diff --git a/android/guava-tests/test/com/google/common/html/HtmlEscapersTest.java b/android/guava-tests/test/com/google/common/html/HtmlEscapersTest.java index 776aa4c75eff..3f7b4a5b0805 100644 --- a/android/guava-tests/test/com/google/common/html/HtmlEscapersTest.java +++ b/android/guava-tests/test/com/google/common/html/HtmlEscapersTest.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the {@link HtmlEscapers} class. @@ -25,6 +26,7 @@ * @author David Beaumont */ @GwtCompatible +@NullUnmarked public class HtmlEscapersTest extends TestCase { public void testHtmlEscaper() throws Exception { diff --git a/android/guava-tests/test/com/google/common/io/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/io/AndroidIncompatible.java index b0d80aec30d7..afd626ec9636 100644 --- a/android/guava-tests/test/com/google/common/io/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/io/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/io/AppendableWriterTest.java b/android/guava-tests/test/com/google/common/io/AppendableWriterTest.java index dd0408362bcd..7784e2d6b4bc 100644 --- a/android/guava-tests/test/com/google/common/io/AppendableWriterTest.java +++ b/android/guava-tests/test/com/google/common/io/AppendableWriterTest.java @@ -16,16 +16,20 @@ package com.google.common.io; +import static org.junit.Assert.assertThrows; + import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.Writer; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link AppendableWriter}. * * @author Alan Green */ +@NullUnmarked public class AppendableWriterTest extends IoTestCase { /** Helper class for testing behavior with Flushable and Closeable targets. */ @@ -113,17 +117,9 @@ public void testCloseIsFinal() throws IOException { writer.write("Hi"); writer.close(); - try { - writer.write(" Greg"); - fail("Should have thrown IOException due to writer already closed"); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> writer.write(" Greg")); - try { - writer.flush(); - fail("Should have thrown IOException due to writer already closed"); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> writer.flush()); // close()ing already closed writer is allowed writer.close(); diff --git a/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java b/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java index c2fdf025731a..1fb945643cd7 100644 --- a/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java +++ b/android/guava-tests/test/com/google/common/io/BaseEncodingTest.java @@ -14,13 +14,14 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.io.BaseEncoding.base16; import static com.google.common.io.BaseEncoding.base32; import static com.google.common.io.BaseEncoding.base32Hex; import static com.google.common.io.BaseEncoding.base64; import static com.google.common.io.BaseEncoding.base64Url; +import static com.google.common.io.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -35,8 +36,9 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@code BaseEncoding}. @@ -44,6 +46,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class BaseEncodingTest extends TestCase { public void testSeparatorsExplicitly() { @@ -53,26 +56,15 @@ public void testSeparatorsExplicitly() { } public void testSeparatorSameAsPadChar() { - try { - base64().withSeparator("=", 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> base64().withSeparator("=", 3)); - try { - base64().withPadChar('#').withSeparator("!#!", 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> base64().withPadChar('#').withSeparator("!#!", 3)); } public void testAtMostOneSeparator() { BaseEncoding separated = base64().withSeparator("\n", 3); - try { - separated.withSeparator("$", 4); - fail("Expected UnsupportedOperationException"); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> separated.withSeparator("$", 4)); } public void testBase64() { @@ -121,21 +113,15 @@ public void testBase64InvalidDecodings() { } public void testBase64CannotUpperCase() { - try { - base64().upperCase(); - fail(); - } catch (IllegalStateException expected) { - // success - } + assertThrows(IllegalStateException.class, () -> base64().upperCase()); } public void testBase64CannotLowerCase() { - try { - base64().lowerCase(); - fail(); - } catch (IllegalStateException expected) { - // success - } + assertThrows(IllegalStateException.class, () -> base64().lowerCase()); + } + + public void testBase64CannotIgnoreCase() { + assertThrows(IllegalStateException.class, () -> base64().ignoreCase()); } public void testBase64AlternatePadding() { @@ -262,7 +248,19 @@ public void testBase32InvalidDecodings() { } public void testBase32UpperCaseIsNoOp() { - assertSame(base32(), base32().upperCase()); + assertThat(base32().upperCase()).isSameInstanceAs(base32()); + } + + public void testBase32LowerCase() { + testEncodingWithCasing(base32().lowerCase(), "foobar", "mzxw6ytboi======"); + } + + public void testBase32IgnoreCase() { + BaseEncoding ignoreCase = base32().ignoreCase(); + assertThat(ignoreCase).isNotSameInstanceAs(base32()); + assertThat(ignoreCase).isSameInstanceAs(base32().ignoreCase()); + testDecodes(ignoreCase, "MZXW6YTBOI======", "foobar"); + testDecodes(ignoreCase, "mzxw6ytboi======", "foobar"); } public void testBase32Offset() { @@ -318,7 +316,7 @@ public void testBase32HexInvalidDecodings() { } public void testBase32HexUpperCaseIsNoOp() { - assertSame(base32Hex(), base32Hex().upperCase()); + assertThat(base32Hex().upperCase()).isSameInstanceAs(base32Hex()); } public void testBase16() { @@ -332,7 +330,44 @@ public void testBase16() { } public void testBase16UpperCaseIsNoOp() { - assertSame(base16(), base16().upperCase()); + assertThat(base16().upperCase()).isSameInstanceAs(base16()); + } + + public void testBase16LowerCase() { + BaseEncoding lowerCase = base16().lowerCase(); + assertThat(lowerCase).isNotSameInstanceAs(base16()); + assertThat(lowerCase).isSameInstanceAs(base16().lowerCase()); + testEncodingWithCasing(lowerCase, "foobar", "666f6f626172"); + } + + public void testBase16IgnoreCase() { + BaseEncoding ignoreCase = base16().ignoreCase(); + assertThat(ignoreCase).isNotSameInstanceAs(base16()); + assertThat(ignoreCase).isSameInstanceAs(base16().ignoreCase()); + testEncodingWithCasing(ignoreCase, "foobar", "666F6F626172"); + testDecodes(ignoreCase, "666F6F626172", "foobar"); + testDecodes(ignoreCase, "666f6f626172", "foobar"); + testDecodes(ignoreCase, "666F6f626172", "foobar"); + } + + public void testBase16LowerCaseIgnoreCase() { + BaseEncoding ignoreCase = base16().lowerCase().ignoreCase(); + assertThat(ignoreCase).isNotSameInstanceAs(base16()); + assertThat(ignoreCase).isSameInstanceAs(base16().lowerCase().ignoreCase()); + testEncodingWithCasing(ignoreCase, "foobar", "666f6f626172"); + testDecodes(ignoreCase, "666F6F626172", "foobar"); + testDecodes(ignoreCase, "666f6f626172", "foobar"); + testDecodes(ignoreCase, "666F6f626172", "foobar"); + } + + // order the methods are called should not matter + public void testBase16IgnoreCaseLowerCase() { + BaseEncoding ignoreCase = base16().ignoreCase().lowerCase(); + assertThat(ignoreCase).isNotSameInstanceAs(base16()); + testEncodingWithCasing(ignoreCase, "foobar", "666f6f626172"); + testDecodes(ignoreCase, "666F6F626172", "foobar"); + testDecodes(ignoreCase, "666f6f626172", "foobar"); + testDecodes(ignoreCase, "666F6f626172", "foobar"); } public void testBase16InvalidDecodings() { @@ -344,6 +379,8 @@ public void testBase16InvalidDecodings() { assertFailsToDecode(base16(), "ABC"); // These have a combination of invalid length and unrecognized characters. assertFailsToDecode(base16(), "?", "Invalid input length 1"); + assertFailsToDecode(base16(), "ab"); + assertFailsToDecode(base16().lowerCase(), "AB"); } public void testBase16Offset() { @@ -391,12 +428,12 @@ private static void testEncodesWithOffset( } private static void testDecodes(BaseEncoding encoding, String encoded, String decoded) { - assertTrue(encoding.canDecode(encoded)); + assertThat(encoding.canDecode(encoded)).isTrue(); assertThat(encoding.decode(encoded)).isEqualTo(decoded.getBytes(UTF_8)); } private static void testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded) { - assertTrue(encoding.canDecode(encoded)); + assertThat(encoding.canDecode(encoded)).isTrue(); assertThat(encoding.decode(encoded)).isEqualTo(decoded); } @@ -405,7 +442,7 @@ private static void assertFailsToDecode(BaseEncoding encoding, String cannotDeco } private static void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage) { + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { // We use this somewhat weird pattern with an enum for each assertion we want to make as a way // of dealing with the fact that one of the assertions is @GwtIncompatible but we don't want to // have to have duplicate @GwtIncompatible test methods just to make that assertion. @@ -415,39 +452,17 @@ private static void assertFailsToDecode( } enum AssertFailsToDecodeStrategy { - @GwtIncompatible // decodingStream(Reader) - DECODING_STREAM { - @Override - void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage) { - // Regression test for case where DecodingException was swallowed by default implementation - // of - // InputStream.read(byte[], int, int) - // See https://github.com/google/guava/issues/3542 - Reader reader = new StringReader(cannotDecode); - InputStream decodingStream = encoding.decodingStream(reader); - try { - ByteStreams.exhaust(decodingStream); - fail("Expected DecodingException"); - } catch (DecodingException expected) { - // Don't assert on the expectedMessage; the messages for exceptions thrown from the - // decoding stream may differ from the messages for the decode methods. - } catch (IOException e) { - fail("Expected DecodingException but got: " + e); - } - } - }, CAN_DECODE { @Override void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage) { - assertFalse(encoding.canDecode(cannotDecode)); + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { + assertThat(encoding.canDecode(cannotDecode)).isFalse(); } }, DECODE { @Override void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage) { + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { try { encoding.decode(cannotDecode); fail("Expected IllegalArgumentException"); @@ -461,7 +476,7 @@ void assertFailsToDecode( DECODE_CHECKED { @Override void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage) { + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { try { encoding.decodeChecked(cannotDecode); fail("Expected DecodingException"); @@ -471,10 +486,37 @@ void assertFailsToDecode( } } } + }, + /* + * This one comes last to work around b/367716565. (One *possible* alternative would be to not + * declare any methods in this enum, converting assertFailsToDecode into a static method that is + * implemented with a `switch`. I haven't tested that.) + */ + @GwtIncompatible // decodingStream(Reader) + DECODING_STREAM { + @Override + void assertFailsToDecode( + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { + // Regression test for case where DecodingException was swallowed by default implementation + // of + // InputStream.read(byte[], int, int) + // See https://github.com/google/guava/issues/3542 + Reader reader = new StringReader(cannotDecode); + InputStream decodingStream = encoding.decodingStream(reader); + try { + ByteStreams.exhaust(decodingStream); + fail("Expected DecodingException"); + } catch (DecodingException expected) { + // Don't assert on the expectedMessage; the messages for exceptions thrown from the + // decoding stream may differ from the messages for the decode methods. + } catch (IOException e) { + fail("Expected DecodingException but got: " + e); + } + } }; abstract void assertFailsToDecode( - BaseEncoding encoding, String cannotDecode, @CheckForNull String expectedMessage); + BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage); } @GwtIncompatible // Reader/Writer @@ -512,9 +554,9 @@ private static void testStreamingEncoding(BaseEncoding encoding, String decoded, private static void testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded) throws IOException { StringWriter writer = new StringWriter(); - OutputStream encodingStream = encoding.encodingStream(writer); - encodingStream.write(decoded.getBytes(UTF_8)); - encodingStream.close(); + try (OutputStream encodingStream = encoding.encodingStream(writer)) { + encodingStream.write(decoded.getBytes(UTF_8)); + } assertThat(writer.toString()).isEqualTo(encoded); } @@ -522,22 +564,21 @@ private static void testStreamingEncodes(BaseEncoding encoding, String decoded, private static void testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded) throws IOException { byte[] bytes = decoded.getBytes(UTF_8); - InputStream decodingStream = encoding.decodingStream(new StringReader(encoded)); - for (int i = 0; i < bytes.length; i++) { - assertThat(decodingStream.read()).isEqualTo(bytes[i] & 0xFF); + try (InputStream decodingStream = encoding.decodingStream(new StringReader(encoded))) { + for (int i = 0; i < bytes.length; i++) { + assertThat(decodingStream.read()).isEqualTo(bytes[i] & 0xFF); + } + assertThat(decodingStream.read()).isEqualTo(-1); } - assertThat(decodingStream.read()).isEqualTo(-1); - decodingStream.close(); } public void testToString() { - assertEquals("BaseEncoding.base64().withPadChar('=')", base64().toString()); - assertEquals("BaseEncoding.base32Hex().omitPadding()", base32Hex().omitPadding().toString()); - assertEquals( - "BaseEncoding.base32().lowerCase().withPadChar('$')", - base32().lowerCase().withPadChar('$').toString()); - assertEquals( - "BaseEncoding.base16().withSeparator(\"\n\", 10)", - base16().withSeparator("\n", 10).toString()); + assertThat(base64().toString()).isEqualTo("BaseEncoding.base64().withPadChar('=')"); + assertThat(base32Hex().omitPadding().toString()) + .isEqualTo("BaseEncoding.base32Hex().omitPadding()"); + assertThat(base32().lowerCase().withPadChar('$').toString()) + .isEqualTo("BaseEncoding.base32().lowerCase().withPadChar('$')"); + assertThat(base16().withSeparator("\n", 10).toString()) + .isEqualTo("BaseEncoding.base16().withSeparator(\"\n\", 10)"); } } diff --git a/android/guava-tests/test/com/google/common/io/ByteSinkTest.java b/android/guava-tests/test/com/google/common/io/ByteSinkTest.java index 208a8fc5e5ee..6fd8b634750a 100644 --- a/android/guava-tests/test/com/google/common/io/ByteSinkTest.java +++ b/android/guava-tests/test/com/google/common/io/ByteSinkTest.java @@ -21,17 +21,20 @@ import static com.google.common.io.TestOption.READ_THROWS; import static com.google.common.io.TestOption.WRITE_THROWS; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.util.EnumSet; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the default implementations of {@code ByteSink} methods. * * @author Colin Decker */ +@NullUnmarked public class ByteSinkTest extends IoTestCase { private final byte[] bytes = newPreFilledByteArray(10000); @@ -82,11 +85,7 @@ public void testClosesOnErrors_copyingFromByteSourceThatThrows() { for (TestOption option : EnumSet.of(OPEN_THROWS, READ_THROWS, CLOSE_THROWS)) { TestByteSource failSource = new TestByteSource(new byte[10], option); TestByteSink okSink = new TestByteSink(); - try { - failSource.copyTo(okSink); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> failSource.copyTo(okSink)); // ensure stream was closed IF it was opened (depends on implementation whether or not it's // opened at all if source.newInputStream() throws). assertTrue( @@ -97,22 +96,14 @@ public void testClosesOnErrors_copyingFromByteSourceThatThrows() { public void testClosesOnErrors_whenWriteThrows() { TestByteSink failSink = new TestByteSink(WRITE_THROWS); - try { - new TestByteSource(new byte[10]).copyTo(failSink); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> new TestByteSource(new byte[10]).copyTo(failSink)); assertTrue(failSink.wasStreamClosed()); } - public void testClosesOnErrors_writingFromInputStreamThatThrows() { + public void testClosesOnErrors_writingFromInputStreamThatThrows() throws IOException { TestByteSink okSink = new TestByteSink(); - try { - TestInputStream in = new TestInputStream(new ByteArrayInputStream(new byte[10]), READ_THROWS); - okSink.writeFrom(in); - fail(); - } catch (IOException expected) { - } + TestInputStream in = new TestInputStream(new ByteArrayInputStream(new byte[10]), READ_THROWS); + assertThrows(IOException.class, () -> okSink.writeFrom(in)); assertTrue(okSink.wasStreamClosed()); } } diff --git a/android/guava-tests/test/com/google/common/io/ByteSinkTester.java b/android/guava-tests/test/com/google/common/io/ByteSinkTester.java index a8ed36f8118b..ebbaf5c1080f 100644 --- a/android/guava-tests/test/com/google/common/io/ByteSinkTester.java +++ b/android/guava-tests/test/com/google/common/io/ByteSinkTester.java @@ -18,9 +18,9 @@ import static com.google.common.io.SourceSinkFactory.ByteSinkFactory; import static com.google.common.io.SourceSinkFactory.CharSinkFactory; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertArrayEquals; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -28,15 +28,17 @@ import java.lang.reflect.Method; import java.util.Map.Entry; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * A generator of {@code TestSuite} instances for testing {@code ByteSink} implementations. - * Generates tests of a all methods on a {@code ByteSink} given various inputs written to it as well + * Generates tests of all methods on a {@code ByteSink} given various inputs written to it as well * as sub-suites for testing the {@code CharSink} view in the same way. * * @author Colin Decker */ -@AndroidIncompatible // Android doesn't understand tests that lack default constructors. +@AndroidIncompatible // TODO(b/230620681): Make this available (even though we won't run it). +@NullUnmarked public class ByteSinkTester extends SourceSinkTester { private static final ImmutableList testMethods = getTestMethods(ByteSinkTester.class); @@ -53,7 +55,7 @@ static TestSuite tests(String name, ByteSinkFactory factory) { private static TestSuite suiteForString( String name, ByteSinkFactory factory, String string, String desc) { - byte[] bytes = string.getBytes(Charsets.UTF_8); + byte[] bytes = string.getBytes(UTF_8); TestSuite suite = suiteForBytes(name, factory, desc, bytes); CharSinkFactory charSinkFactory = SourceSinkFactories.asCharSinkFactory(factory); suite.addTest( diff --git a/android/guava-tests/test/com/google/common/io/ByteSourceTest.java b/android/guava-tests/test/com/google/common/io/ByteSourceTest.java index 3cf6cca44259..580e3ae0c3f7 100644 --- a/android/guava-tests/test/com/google/common/io/ByteSourceTest.java +++ b/android/guava-tests/test/com/google/common/io/ByteSourceTest.java @@ -23,16 +23,18 @@ import static com.google.common.io.TestOption.READ_THROWS; import static com.google.common.io.TestOption.SKIP_THROWS; import static com.google.common.io.TestOption.WRITE_THROWS; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.nio.charset.StandardCharsets.US_ASCII; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.hash.Hashing; -import com.google.common.io.Closer.LoggingSuppressor; import com.google.common.primitives.UnsignedBytes; -import com.google.common.testing.TestLogHandler; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -40,12 +42,15 @@ import java.util.Arrays; import java.util.EnumSet; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for the default implementations of {@code ByteSource} methods. * * @author Colin Decker */ +@NullUnmarked public class ByteSourceTest extends IoTestCase { @AndroidIncompatible // Android doesn't understand suites whose tests lack default constructors. @@ -151,8 +156,8 @@ public byte[] getResult() { } public void testRead_withProcessor_stopsOnFalse() throws IOException { - ByteProcessor processor = - new ByteProcessor() { + ByteProcessor<@Nullable Void> processor = + new ByteProcessor<@Nullable Void>() { boolean firstCall = true; @Override @@ -163,7 +168,7 @@ public boolean processBytes(byte[] buf, int off, int len) throws IOException { } @Override - public Void getResult() { + public @Nullable Void getResult() { return null; } }; @@ -173,7 +178,7 @@ public Void getResult() { } public void testHash() throws IOException { - ByteSource byteSource = new TestByteSource("hamburger\n".getBytes(Charsets.US_ASCII)); + ByteSource byteSource = new TestByteSource("hamburger\n".getBytes(US_ASCII)); // Pasted this expected string from `echo hamburger | md5sum` assertEquals("cfa0c5002275c90508338a5cdb2a9781", byteSource.hash(Hashing.md5()).toString()); @@ -198,17 +203,9 @@ public void testContentEquals() throws IOException { public void testSlice() throws IOException { // Test preconditions - try { - source.slice(-1, 10); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> source.slice(-1, 10)); - try { - source.slice(0, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> source.slice(0, -1)); assertCorrectSlice(0, 0, 0, 0); assertCorrectSlice(0, 0, 1, 0); @@ -283,7 +280,7 @@ public int read(byte[] b, int off, int len) { return -1; } - int lenToRead = Math.min(len, bytes.length - pos); + int lenToRead = min(len, bytes.length - pos); System.arraycopy(bytes, pos, b, off, lenToRead); pos += lenToRead; return lenToRead; @@ -299,7 +296,7 @@ public int read(byte[] b, int off, int len) { */ private static void assertCorrectSlice(int input, int offset, long length, int expectRead) throws IOException { - checkArgument(expectRead == (int) Math.max(0, Math.min(input, offset + length) - offset)); + checkArgument(expectRead == (int) max(0, min(input, offset + length) - offset)); byte[] expected = newPreFilledByteArray(offset, expectRead); @@ -319,11 +316,7 @@ public void testCopyToStream_doesNotCloseThatStream() throws IOException { public void testClosesOnErrors_copyingToByteSinkThatThrows() { for (TestOption option : EnumSet.of(OPEN_THROWS, WRITE_THROWS, CLOSE_THROWS)) { TestByteSource okSource = new TestByteSource(bytes); - try { - okSource.copyTo(new TestByteSink(option)); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> okSource.copyTo(new TestByteSink(option))); // ensure stream was closed IF it was opened (depends on implementation whether or not it's // opened at all if sink.newOutputStream() throws). assertTrue( @@ -334,22 +327,14 @@ public void testClosesOnErrors_copyingToByteSinkThatThrows() { public void testClosesOnErrors_whenReadThrows() { TestByteSource failSource = new TestByteSource(bytes, READ_THROWS); - try { - failSource.copyTo(new TestByteSink()); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> failSource.copyTo(new TestByteSink())); assertTrue(failSource.wasStreamClosed()); } - public void testClosesOnErrors_copyingToOutputStreamThatThrows() { + public void testClosesOnErrors_copyingToOutputStreamThatThrows() throws IOException { TestByteSource okSource = new TestByteSource(bytes); - try { - OutputStream out = new TestOutputStream(ByteStreams.nullOutputStream(), WRITE_THROWS); - okSource.copyTo(out); - fail(); - } catch (IOException expected) { - } + OutputStream out = new TestOutputStream(ByteStreams.nullOutputStream(), WRITE_THROWS); + assertThrows(IOException.class, () -> okSource.copyTo(out)); assertTrue(okSource.wasStreamClosed()); } @@ -396,61 +381,28 @@ public void testConcat_infiniteIterable() throws IOException { ImmutableSet.of(BROKEN_CLOSE_SINK, BROKEN_OPEN_SINK, BROKEN_WRITE_SINK); public void testCopyExceptions() { - if (Closer.create().suppressor instanceof LoggingSuppressor) { - // test that exceptions are logged - - TestLogHandler logHandler = new TestLogHandler(); - Closeables.logger.addHandler(logHandler); - try { - for (ByteSource in : BROKEN_SOURCES) { - runFailureTest(in, newNormalByteSink()); - assertTrue(logHandler.getStoredLogRecords().isEmpty()); - - runFailureTest(in, BROKEN_CLOSE_SINK); - assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, getAndResetRecords(logHandler)); - } - - for (ByteSink out : BROKEN_SINKS) { - runFailureTest(newNormalByteSource(), out); - assertTrue(logHandler.getStoredLogRecords().isEmpty()); + // test that exceptions are suppressed - runFailureTest(BROKEN_CLOSE_SOURCE, out); - assertEquals(1, getAndResetRecords(logHandler)); - } + for (ByteSource in : BROKEN_SOURCES) { + int suppressed = runSuppressionFailureTest(in, newNormalByteSink()); + assertEquals(0, suppressed); - for (ByteSource in : BROKEN_SOURCES) { - for (ByteSink out : BROKEN_SINKS) { - runFailureTest(in, out); - assertTrue(getAndResetRecords(logHandler) <= 1); - } - } - } finally { - Closeables.logger.removeHandler(logHandler); - } - } else { - // test that exceptions are suppressed + suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK); + assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed); + } - for (ByteSource in : BROKEN_SOURCES) { - int suppressed = runSuppressionFailureTest(in, newNormalByteSink()); - assertEquals(0, suppressed); + for (ByteSink out : BROKEN_SINKS) { + int suppressed = runSuppressionFailureTest(newNormalByteSource(), out); + assertEquals(0, suppressed); - suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK); - assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed); - } + suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out); + assertEquals(1, suppressed); + } + for (ByteSource in : BROKEN_SOURCES) { for (ByteSink out : BROKEN_SINKS) { - int suppressed = runSuppressionFailureTest(newNormalByteSource(), out); - assertEquals(0, suppressed); - - suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out); - assertEquals(1, suppressed); - } - - for (ByteSource in : BROKEN_SOURCES) { - for (ByteSink out : BROKEN_SINKS) { - int suppressed = runSuppressionFailureTest(in, out); - assertTrue(suppressed <= 1); - } + int suppressed = runSuppressionFailureTest(in, out); + assertThat(suppressed).isAtMost(1); } } } @@ -459,27 +411,15 @@ public void testSlice_returnEmptySource() { assertEquals(ByteSource.empty(), source.slice(0, 3).slice(4, 3)); } - private static int getAndResetRecords(TestLogHandler logHandler) { - int records = logHandler.getStoredLogRecords().size(); - logHandler.clear(); - return records; - } - - private static void runFailureTest(ByteSource in, ByteSink out) { - try { - in.copyTo(out); - fail(); - } catch (IOException expected) { - } - } - - /** @return the number of exceptions that were suppressed on the expected thrown exception */ + /** + * @return the number of exceptions that were suppressed on the expected thrown exception + */ private static int runSuppressionFailureTest(ByteSource in, ByteSink out) { try { in.copyTo(out); fail(); } catch (IOException expected) { - return CloserTest.getSuppressed(expected).length; + return expected.getSuppressed().length; } throw new AssertionError(); // can't happen } diff --git a/android/guava-tests/test/com/google/common/io/ByteSourceTester.java b/android/guava-tests/test/com/google/common/io/ByteSourceTester.java index 75854125606a..4404ada69f6a 100644 --- a/android/guava-tests/test/com/google/common/io/ByteSourceTester.java +++ b/android/guava-tests/test/com/google/common/io/ByteSourceTester.java @@ -18,9 +18,10 @@ import static com.google.common.io.SourceSinkFactory.ByteSourceFactory; import static com.google.common.io.SourceSinkFactory.CharSourceFactory; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.hash.HashCode; @@ -34,16 +35,18 @@ import java.util.Map.Entry; import java.util.Random; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * A generator of {@code TestSuite} instances for testing {@code ByteSource} implementations. - * Generates tests of a all methods on a {@code ByteSource} given various inputs the source is + * Generates tests of all methods on a {@code ByteSource} given various inputs the source is * expected to contain as well as sub-suites for testing the {@code CharSource} view and {@code * slice()} views in the same way. * * @author Colin Decker */ -@AndroidIncompatible // Android doesn't understand tests that lack default constructors. +@AndroidIncompatible // TODO(b/230620681): Make this available (even though we won't run it). +@NullUnmarked public class ByteSourceTester extends SourceSinkTester { private static final ImmutableList testMethods = getTestMethods(ByteSourceTester.class); @@ -55,8 +58,7 @@ static TestSuite tests(String name, ByteSourceFactory factory, boolean testAsCha suite.addTest(suiteForString(factory, entry.getValue(), name, entry.getKey())); } else { suite.addTest( - suiteForBytes( - factory, entry.getValue().getBytes(Charsets.UTF_8), name, entry.getKey(), true)); + suiteForBytes(factory, entry.getValue().getBytes(UTF_8), name, entry.getKey(), true)); } } return suite; @@ -64,7 +66,7 @@ static TestSuite tests(String name, ByteSourceFactory factory, boolean testAsCha static TestSuite suiteForString( ByteSourceFactory factory, String string, String name, String desc) { - TestSuite suite = suiteForBytes(factory, string.getBytes(Charsets.UTF_8), name, desc, true); + TestSuite suite = suiteForBytes(factory, string.getBytes(UTF_8), name, desc, true); CharSourceFactory charSourceFactory = SourceSinkFactories.asCharSourceFactory(factory); suite.addTest( CharSourceTester.suiteForString( @@ -219,17 +221,15 @@ public void testHash() throws IOException { } public void testSlice_illegalArguments() { - try { - source.slice(-1, 0); - fail("expected IllegalArgumentException for call to slice with offset -1: " + source); - } catch (IllegalArgumentException expected) { - } - - try { - source.slice(0, -1); - fail("expected IllegalArgumentException for call to slice with length -1: " + source); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "expected IllegalArgumentException for call to slice with offset -1: " + source, + IllegalArgumentException.class, + () -> source.slice(-1, 0)); + + assertThrows( + "expected IllegalArgumentException for call to slice with length -1: " + source, + IllegalArgumentException.class, + () -> source.slice(0, -1)); } // Test that you can not expand the readable data in a previously sliced ByteSource. diff --git a/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java b/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java index 3ae2c2559827..9c0048f0ca5d 100644 --- a/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java +++ b/android/guava-tests/test/com/google/common/io/ByteStreamsTest.java @@ -17,8 +17,12 @@ package com.google.common.io; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_16; +import static java.nio.charset.StandardCharsets.UTF_16BE; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; @@ -33,12 +37,14 @@ import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ByteStreams}. * * @author Chris Nokleberg */ +@NullUnmarked public class ByteStreamsTest extends IoTestCase { public void testCopyChannel() throws IOException { @@ -76,47 +82,24 @@ public void testCopyFileChannel() throws IOException { public void testReadFully() throws IOException { byte[] b = new byte[10]; - try { - ByteStreams.readFully(newTestStream(10), null, 0, 10); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, () -> ByteStreams.readFully(newTestStream(10), null, 0, 10)); - try { - ByteStreams.readFully(null, b, 0, 10); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> ByteStreams.readFully(null, b, 0, 10)); - try { - ByteStreams.readFully(newTestStream(10), b, -1, 10); - fail("expected exception"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> ByteStreams.readFully(newTestStream(10), b, -1, 10)); - try { - ByteStreams.readFully(newTestStream(10), b, 0, -1); - fail("expected exception"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> ByteStreams.readFully(newTestStream(10), b, 0, -1)); - try { - ByteStreams.readFully(newTestStream(10), b, 0, -1); - fail("expected exception"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> ByteStreams.readFully(newTestStream(10), b, 0, -1)); - try { - ByteStreams.readFully(newTestStream(10), b, 2, 10); - fail("expected exception"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + IndexOutOfBoundsException.class, () -> ByteStreams.readFully(newTestStream(10), b, 2, 10)); - try { - ByteStreams.readFully(newTestStream(5), b, 0, 10); - fail("expected exception"); - } catch (EOFException expected) { - } + assertThrows(EOFException.class, () -> ByteStreams.readFully(newTestStream(5), b, 0, 10)); Arrays.fill(b, (byte) 0); ByteStreams.readFully(newTestStream(10), b, 0, 0); @@ -138,11 +121,7 @@ public void testSkipFully() throws IOException { skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 1)); skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 0)); skipHelper(100, -1, new ByteArrayInputStream(bytes)); - try { - skipHelper(101, 0, new ByteArrayInputStream(bytes)); - fail("expected exception"); - } catch (EOFException expected) { - } + assertThrows(EOFException.class, () -> skipHelper(101, 0, new ByteArrayInputStream(bytes))); } private static void skipHelper(long n, int expect, InputStream in) throws IOException { @@ -156,22 +135,14 @@ private static void skipHelper(long n, int expect, InputStream in) throws IOExce public void testNewDataInput_empty() { byte[] b = new byte[0]; ByteArrayDataInput in = ByteStreams.newDataInput(b); - try { - in.readInt(); - fail("expected exception"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> in.readInt()); } public void testNewDataInput_normal() { ByteArrayDataInput in = ByteStreams.newDataInput(bytes); assertEquals(0x12345678, in.readInt()); assertEquals(0x76543210, in.readInt()); - try { - in.readInt(); - fail("expected exception"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> in.readInt()); } public void testNewDataInput_readFully() { @@ -184,12 +155,9 @@ public void testNewDataInput_readFully() { public void testNewDataInput_readFullyAndThenSome() { ByteArrayDataInput in = ByteStreams.newDataInput(bytes); byte[] actual = new byte[bytes.length * 2]; - try { - in.readFully(actual); - fail("expected exception"); - } catch (IllegalStateException ex) { - assertThat(ex).hasCauseThat().isInstanceOf(EOFException.class); - } + IllegalStateException ex = + assertThrows(IllegalStateException.class, () -> in.readFully(actual)); + assertThat(ex).hasCauseThat().isInstanceOf(EOFException.class); } public void testNewDataInput_readFullyWithOffset() { @@ -205,7 +173,7 @@ public void testNewDataInput_readFullyWithOffset() { public void testNewDataInput_readLine() { ByteArrayDataInput in = ByteStreams.newDataInput( - "This is a line\r\nThis too\rand this\nand also this".getBytes(Charsets.UTF_8)); + "This is a line\r\nThis too\rand this\nand also this".getBytes(UTF_8)); assertEquals("This is a line", in.readLine()); assertEquals("This too", in.readLine()); assertEquals("and this", in.readLine()); @@ -215,26 +183,26 @@ public void testNewDataInput_readLine() { public void testNewDataInput_readFloat() { byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10}; ByteArrayDataInput in = ByteStreams.newDataInput(data); - assertEquals(Float.intBitsToFloat(0x12345678), in.readFloat(), 0.0); - assertEquals(Float.intBitsToFloat(0x76543210), in.readFloat(), 0.0); + assertThat(in.readFloat()).isEqualTo(Float.intBitsToFloat(0x12345678)); + assertThat(in.readFloat()).isEqualTo(Float.intBitsToFloat(0x76543210)); } public void testNewDataInput_readDouble() { byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10}; ByteArrayDataInput in = ByteStreams.newDataInput(data); - assertEquals(Double.longBitsToDouble(0x1234567876543210L), in.readDouble(), 0.0); + assertThat(in.readDouble()).isEqualTo(Double.longBitsToDouble(0x1234567876543210L)); } public void testNewDataInput_readUTF() { byte[] data = new byte[17]; data[1] = 15; - System.arraycopy("Kilroy was here".getBytes(Charsets.UTF_8), 0, data, 2, 15); + System.arraycopy("Kilroy was here".getBytes(UTF_8), 0, data, 2, 15); ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals("Kilroy was here", in.readUTF()); } public void testNewDataInput_readChar() { - byte[] data = "qed".getBytes(Charsets.UTF_16BE); + byte[] data = "qed".getBytes(UTF_16BE); ByteArrayDataInput in = ByteStreams.newDataInput(data); assertEquals('q', in.readChar()); assertEquals('e', in.readChar()); @@ -266,12 +234,8 @@ public void testNewDataInput_readByte() { for (byte aByte : bytes) { assertEquals(aByte, in.readByte()); } - try { - in.readByte(); - fail("expected exception"); - } catch (IllegalStateException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class); - } + IllegalStateException expected = assertThrows(IllegalStateException.class, () -> in.readByte()); + assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class); } public void testNewDataInput_readUnsignedByte() { @@ -279,22 +243,15 @@ public void testNewDataInput_readUnsignedByte() { for (byte aByte : bytes) { assertEquals(aByte, in.readUnsignedByte()); } - try { - in.readUnsignedByte(); - fail("expected exception"); - } catch (IllegalStateException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> in.readUnsignedByte()); + assertThat(expected).hasCauseThat().isInstanceOf(EOFException.class); } public void testNewDataInput_offset() { ByteArrayDataInput in = ByteStreams.newDataInput(bytes, 2); assertEquals(0x56787654, in.readInt()); - try { - in.readInt(); - fail("expected exception"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> in.readInt()); } public void testNewDataInput_skip() { @@ -303,7 +260,7 @@ public void testNewDataInput_skip() { assertEquals(0, in.skipBytes(1)); } - public void testNewDataInput_BAIS() { + public void testNewDataInput_bais() { ByteArrayInputStream bais = new ByteArrayInputStream(new byte[] {0x12, 0x34, 0x56, 0x78}); ByteArrayDataInput in = ByteStreams.newDataInput(bais); assertEquals(0x12345678, in.readInt()); @@ -380,17 +337,17 @@ public void testNewDataOutput_writeChars() { assertThat(out.toByteArray()).isEqualTo(expected); } - @AndroidIncompatible // https://code.google.com/p/android/issues/detail?id=196848 + @AndroidIncompatible // https://issuetracker.google.com/issues/37074504 public void testUtf16Expected() { byte[] hardcodedExpected = utf16ExpectedWithBom; - byte[] computedExpected = "r\u00C9sum\u00C9".getBytes(Charsets.UTF_16); + byte[] computedExpected = "r\u00C9sum\u00C9".getBytes(UTF_16); assertThat(computedExpected).isEqualTo(hardcodedExpected); } public void testNewDataOutput_writeUTF() { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("r\u00C9sum\u00C9"); - byte[] expected = "r\u00C9sum\u00C9".getBytes(Charsets.UTF_8); + byte[] expected = "r\u00C9sum\u00C9".getBytes(UTF_8); byte[] actual = out.toByteArray(); // writeUTF writes the length of the string in 2 bytes assertEquals(0, actual[0]); @@ -417,7 +374,7 @@ public void testNewDataOutput_writeFloat() { assertThat(out.toByteArray()).isEqualTo(bytes); } - public void testNewDataOutput_BAOS() { + public void testNewDataOutput_baos() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayDataOutput out = ByteStreams.newDataOutput(baos); out.writeInt(0x12345678); @@ -562,12 +519,25 @@ public void testNullOutputStream() throws Exception { // write to the output stream nos.write('n'); String test = "Test string for NullOutputStream"; - nos.write(test.getBytes()); - nos.write(test.getBytes(), 2, 10); + byte[] bytes = test.getBytes(US_ASCII); + nos.write(bytes); + nos.write(bytes, 2, 10); + nos.write(bytes, bytes.length - 5, 5); // nothing really to assert? assertSame(ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream()); } + public void testNullOutputStream_exceptions() throws Exception { + OutputStream nos = ByteStreams.nullOutputStream(); + assertThrows(NullPointerException.class, () -> nos.write(null)); + assertThrows(NullPointerException.class, () -> nos.write(null, 0, 1)); + byte[] tenBytes = new byte[10]; + assertThrows(IndexOutOfBoundsException.class, () -> nos.write(tenBytes, -1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> nos.write(tenBytes, 1, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> nos.write(tenBytes, 9, 2)); + assertThrows(IndexOutOfBoundsException.class, () -> nos.write(tenBytes, 9, 100)); + } + public void testLimit() throws Exception { byte[] big = newPreFilledByteArray(5); InputStream bin = new ByteArrayInputStream(big); @@ -642,23 +612,15 @@ public void testLimit_markNotSet() { InputStream bin = new ByteArrayInputStream(big); InputStream lin = ByteStreams.limit(bin, 2); - try { - lin.reset(); - fail(); - } catch (IOException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Mark not set"); - } + IOException expected = assertThrows(IOException.class, () -> lin.reset()); + assertThat(expected).hasMessageThat().isEqualTo("Mark not set"); } public void testLimit_markNotSupported() { InputStream lin = ByteStreams.limit(new UnmarkableInputStream(), 2); - try { - lin.reset(); - fail(); - } catch (IOException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Mark not supported"); - } + IOException expected = assertThrows(IOException.class, () -> lin.reset()); + assertThat(expected).hasMessageThat().isEqualTo("Mark not supported"); } private static class UnmarkableInputStream extends InputStream { diff --git a/android/guava-tests/test/com/google/common/io/CharSequenceReaderTest.java b/android/guava-tests/test/com/google/common/io/CharSequenceReaderTest.java index dbe94fcf71fa..bb97b591ffd7 100644 --- a/android/guava-tests/test/com/google/common/io/CharSequenceReaderTest.java +++ b/android/guava-tests/test/com/google/common/io/CharSequenceReaderTest.java @@ -16,15 +16,19 @@ package com.google.common.io; +import static org.junit.Assert.assertThrows; + import java.io.IOException; import java.nio.CharBuffer; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link CharSequenceReader}. * * @author Colin Decker */ +@NullUnmarked public class CharSequenceReaderTest extends TestCase { public void testReadEmptyString() throws IOException { @@ -73,106 +77,42 @@ public void testIllegalArguments() throws IOException { CharSequenceReader reader = new CharSequenceReader("12345"); char[] buf = new char[10]; - try { - reader.read(buf, 0, 11); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, 0, 11)); - try { - reader.read(buf, 10, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, 10, 1)); - try { - reader.read(buf, 11, 0); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, 11, 0)); - try { - reader.read(buf, -1, 5); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, -1, 5)); - try { - reader.read(buf, 5, -1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, 5, -1)); - try { - reader.read(buf, 0, 11); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> reader.read(buf, 0, 11)); - try { - reader.skip(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> reader.skip(-1)); - try { - reader.mark(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> reader.mark(-1)); } public void testMethodsThrowWhenClosed() throws IOException { CharSequenceReader reader = new CharSequenceReader(""); reader.close(); - try { - reader.read(); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.read()); - try { - reader.read(new char[10]); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.read(new char[10])); - try { - reader.read(new char[10], 0, 10); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.read(new char[10], 0, 10)); - try { - reader.read(CharBuffer.allocate(10)); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.read(CharBuffer.allocate(10))); - try { - reader.skip(10); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.skip(10)); - try { - reader.ready(); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.ready()); - try { - reader.mark(10); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.mark(10)); - try { - reader.reset(); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> reader.reset()); } /** diff --git a/android/guava-tests/test/com/google/common/io/CharSinkTest.java b/android/guava-tests/test/com/google/common/io/CharSinkTest.java index ac96d906451f..d2d59d35be0f 100644 --- a/android/guava-tests/test/com/google/common/io/CharSinkTest.java +++ b/android/guava-tests/test/com/google/common/io/CharSinkTest.java @@ -16,22 +16,26 @@ package com.google.common.io; +import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR; import static com.google.common.io.TestOption.CLOSE_THROWS; import static com.google.common.io.TestOption.OPEN_THROWS; import static com.google.common.io.TestOption.READ_THROWS; import static com.google.common.io.TestOption.WRITE_THROWS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.StringReader; import java.io.Writer; import java.util.EnumSet; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the default implementations of {@code CharSink} methods. * * @author Colin Decker */ +@NullUnmarked public class CharSinkTest extends IoTestCase { private static final String STRING = ASCII + I18N; @@ -89,15 +93,22 @@ public void testWriteLines_withDefaultSeparator() throws IOException { assertEquals("foo" + separator + "bar" + separator + "baz" + separator, sink.getString()); } + public void testWriteLines_stream() throws IOException { + sink.writeLines(ImmutableList.of("foo", "bar", "baz").stream()); + String separator = LINE_SEPARATOR.value(); + assertEquals("foo" + separator + "bar" + separator + "baz" + separator, sink.getString()); + } + + public void testWriteLines_stream_separator() throws IOException { + sink.writeLines(ImmutableList.of("foo", "bar", "baz").stream(), "!"); + assertEquals("foo!bar!baz!", sink.getString()); + } + public void testClosesOnErrors_copyingFromCharSourceThatThrows() { for (TestOption option : EnumSet.of(OPEN_THROWS, READ_THROWS, CLOSE_THROWS)) { TestCharSource failSource = new TestCharSource(STRING, option); TestCharSink okSink = new TestCharSink(); - try { - failSource.copyTo(okSink); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> failSource.copyTo(okSink)); // ensure writer was closed IF it was opened (depends on implementation whether or not it's // opened at all if source.newReader() throws). assertTrue( @@ -108,21 +119,13 @@ public void testClosesOnErrors_copyingFromCharSourceThatThrows() { public void testClosesOnErrors_whenWriteThrows() { TestCharSink failSink = new TestCharSink(WRITE_THROWS); - try { - new TestCharSource(STRING).copyTo(failSink); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> new TestCharSource(STRING).copyTo(failSink)); assertTrue(failSink.wasStreamClosed()); } public void testClosesOnErrors_whenWritingFromReaderThatThrows() { TestCharSink okSink = new TestCharSink(); - try { - okSink.writeFrom(new TestReader(READ_THROWS)); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> okSink.writeFrom(new TestReader(READ_THROWS))); assertTrue(okSink.wasStreamClosed()); } } diff --git a/android/guava-tests/test/com/google/common/io/CharSinkTester.java b/android/guava-tests/test/com/google/common/io/CharSinkTester.java index 788f9c02e1e8..fb1fbd59b81e 100644 --- a/android/guava-tests/test/com/google/common/io/CharSinkTester.java +++ b/android/guava-tests/test/com/google/common/io/CharSinkTester.java @@ -25,14 +25,16 @@ import java.lang.reflect.Method; import java.util.Map.Entry; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * A generator of {@code TestSuite} instances for testing {@code CharSink} implementations. - * Generates tests of a all methods on a {@code CharSink} given various inputs written to it. + * Generates tests of all methods on a {@code CharSink} given various inputs written to it. * * @author Colin Decker */ -@AndroidIncompatible // Android doesn't understand tests that lack default constructors. +@AndroidIncompatible // TODO(b/230620681): Make this available (even though we won't run it). +@NullUnmarked public class CharSinkTester extends SourceSinkTester { private static final ImmutableList testMethods = getTestMethods(CharSinkTester.class); diff --git a/android/guava-tests/test/com/google/common/io/CharSourceTest.java b/android/guava-tests/test/com/google/common/io/CharSourceTest.java index 6cc210b56c4e..165f27f887e3 100644 --- a/android/guava-tests/test/com/google/common/io/CharSourceTest.java +++ b/android/guava-tests/test/com/google/common/io/CharSourceTest.java @@ -16,17 +16,18 @@ package com.google.common.io; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.io.TestOption.CLOSE_THROWS; import static com.google.common.io.TestOption.OPEN_THROWS; import static com.google.common.io.TestOption.READ_THROWS; import static com.google.common.io.TestOption.WRITE_THROWS; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.io.Closer.LoggingSuppressor; -import com.google.common.testing.TestLogHandler; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; @@ -34,13 +35,16 @@ import java.io.Writer; import java.util.EnumSet; import java.util.List; +import java.util.stream.Stream; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the default implementations of {@code CharSource} methods. * * @author Colin Decker */ +@NullUnmarked public class CharSourceTest extends IoTestCase { @AndroidIncompatible // Android doesn't understand suites whose tests lack default constructors. @@ -62,6 +66,8 @@ public static TestSuite suite() { private static final String STRING = ASCII + I18N; private static final String LINES = "foo\nbar\r\nbaz\rsomething"; + private static final ImmutableList SPLIT_LINES = + ImmutableList.of("foo", "bar", "baz", "something"); private TestCharSource source; @@ -88,6 +94,21 @@ public void testOpenBufferedStream() throws IOException { assertEquals(STRING, writer.toString()); } + public void testLines() throws IOException { + source = new TestCharSource(LINES); + + ImmutableList lines; + try (Stream linesStream = source.lines()) { + assertTrue(source.wasStreamOpened()); + assertFalse(source.wasStreamClosed()); + + lines = linesStream.collect(toImmutableList()); + } + + assertTrue(source.wasStreamClosed()); + assertEquals(SPLIT_LINES, lines); + } + public void testCopyTo_appendable() throws IOException { StringBuilder builder = new StringBuilder(); @@ -170,6 +191,17 @@ public List getResult() { assertTrue(lines.wasStreamOpened() && lines.wasStreamClosed()); } + public void testForEachLine() throws IOException { + source = new TestCharSource(LINES); + + ImmutableList.Builder builder = ImmutableList.builder(); + source.forEachLine(builder::add); + + assertEquals(SPLIT_LINES, builder.build()); + assertTrue(source.wasStreamOpened()); + assertTrue(source.wasStreamClosed()); + } + public void testCopyToAppendable_doesNotCloseIfWriter() throws IOException { TestWriter writer = new TestWriter(); assertFalse(writer.closed()); @@ -180,11 +212,7 @@ public void testCopyToAppendable_doesNotCloseIfWriter() throws IOException { public void testClosesOnErrors_copyingToCharSinkThatThrows() { for (TestOption option : EnumSet.of(OPEN_THROWS, WRITE_THROWS, CLOSE_THROWS)) { TestCharSource okSource = new TestCharSource(STRING); - try { - okSource.copyTo(new TestCharSink(option)); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> okSource.copyTo(new TestCharSink(option))); // ensure reader was closed IF it was opened (depends on implementation whether or not it's // opened at all if sink.newWriter() throws). assertTrue( @@ -195,21 +223,13 @@ public void testClosesOnErrors_copyingToCharSinkThatThrows() { public void testClosesOnErrors_whenReadThrows() { TestCharSource failSource = new TestCharSource(STRING, READ_THROWS); - try { - failSource.copyTo(new TestCharSink()); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> failSource.copyTo(new TestCharSink())); assertTrue(failSource.wasStreamClosed()); } public void testClosesOnErrors_copyingToWriterThatThrows() { TestCharSource okSource = new TestCharSource(STRING); - try { - okSource.copyTo(new TestWriter(WRITE_THROWS)); - fail(); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> okSource.copyTo(new TestWriter(WRITE_THROWS))); assertTrue(okSource.wasStreamClosed()); } @@ -259,86 +279,41 @@ public void testConcat_infiniteIterable() throws IOException { ImmutableSet.of(BROKEN_CLOSE_SINK, BROKEN_OPEN_SINK, BROKEN_WRITE_SINK); public void testCopyExceptions() { - if (Closer.create().suppressor instanceof LoggingSuppressor) { - // test that exceptions are logged - - TestLogHandler logHandler = new TestLogHandler(); - Closeables.logger.addHandler(logHandler); - try { - for (CharSource in : BROKEN_SOURCES) { - runFailureTest(in, newNormalCharSink()); - assertTrue(logHandler.getStoredLogRecords().isEmpty()); - - runFailureTest(in, BROKEN_CLOSE_SINK); - assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, getAndResetRecords(logHandler)); - } - - for (CharSink out : BROKEN_SINKS) { - runFailureTest(newNormalCharSource(), out); - assertTrue(logHandler.getStoredLogRecords().isEmpty()); - - runFailureTest(BROKEN_CLOSE_SOURCE, out); - assertEquals(1, getAndResetRecords(logHandler)); - } - - for (CharSource in : BROKEN_SOURCES) { - for (CharSink out : BROKEN_SINKS) { - runFailureTest(in, out); - assertTrue(getAndResetRecords(logHandler) <= 1); - } - } - } finally { - Closeables.logger.removeHandler(logHandler); - } - } else { - // test that exceptions are suppressed - - for (CharSource in : BROKEN_SOURCES) { - int suppressed = runSuppressionFailureTest(in, newNormalCharSink()); - assertEquals(0, suppressed); + // test that exceptions are suppressed - suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK); - assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed); - } + for (CharSource in : BROKEN_SOURCES) { + int suppressed = runSuppressionFailureTest(in, newNormalCharSink()); + assertEquals(0, suppressed); - for (CharSink out : BROKEN_SINKS) { - int suppressed = runSuppressionFailureTest(newNormalCharSource(), out); - assertEquals(0, suppressed); + suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK); + assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed); + } - suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out); - assertEquals(1, suppressed); - } + for (CharSink out : BROKEN_SINKS) { + int suppressed = runSuppressionFailureTest(newNormalCharSource(), out); + assertEquals(0, suppressed); - for (CharSource in : BROKEN_SOURCES) { - for (CharSink out : BROKEN_SINKS) { - int suppressed = runSuppressionFailureTest(in, out); - assertTrue(suppressed <= 1); - } - } + suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out); + assertEquals(1, suppressed); } - } - - private static int getAndResetRecords(TestLogHandler logHandler) { - int records = logHandler.getStoredLogRecords().size(); - logHandler.clear(); - return records; - } - private static void runFailureTest(CharSource in, CharSink out) { - try { - in.copyTo(out); - fail(); - } catch (IOException expected) { + for (CharSource in : BROKEN_SOURCES) { + for (CharSink out : BROKEN_SINKS) { + int suppressed = runSuppressionFailureTest(in, out); + assertThat(suppressed).isAtMost(1); + } } } - /** @return the number of exceptions that were suppressed on the expected thrown exception */ + /** + * @return the number of exceptions that were suppressed on the expected thrown exception + */ private static int runSuppressionFailureTest(CharSource in, CharSink out) { try { in.copyTo(out); fail(); } catch (IOException expected) { - return CloserTest.getSuppressed(expected).length; + return expected.getSuppressed().length; } throw new AssertionError(); // can't happen } diff --git a/android/guava-tests/test/com/google/common/io/CharSourceTester.java b/android/guava-tests/test/com/google/common/io/CharSourceTester.java index e73fcdc17649..7d72a818f49c 100644 --- a/android/guava-tests/test/com/google/common/io/CharSourceTester.java +++ b/android/guava-tests/test/com/google/common/io/CharSourceTester.java @@ -16,7 +16,8 @@ package com.google.common.io; -import com.google.common.base.Charsets; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -30,15 +31,17 @@ import java.util.List; import java.util.Map.Entry; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * A generator of {@code TestSuite} instances for testing {@code CharSource} implementations. - * Generates tests of a all methods on a {@code CharSource} given various inputs the source is + * Generates tests of all methods on a {@code CharSource} given various inputs the source is * expected to contain. * * @author Colin Decker */ -@AndroidIncompatible // Android doesn't understand tests that lack default constructors. +@AndroidIncompatible // TODO(b/230620681): Make this available (even though we won't run it). +@NullUnmarked public class CharSourceTester extends SourceSinkTester { private static final ImmutableList testMethods = getTestMethods(CharSourceTester.class); @@ -48,8 +51,7 @@ static TestSuite tests(String name, CharSourceFactory factory, boolean testAsByt for (Entry entry : TEST_STRINGS.entrySet()) { if (testAsByteSource) { suite.addTest( - suiteForBytes( - factory, entry.getValue().getBytes(Charsets.UTF_8), name, entry.getKey(), true)); + suiteForBytes(factory, entry.getValue().getBytes(UTF_8), name, entry.getKey(), true)); } else { suite.addTest(suiteForString(factory, entry.getValue(), name, entry.getKey())); } @@ -59,7 +61,7 @@ static TestSuite tests(String name, CharSourceFactory factory, boolean testAsByt static TestSuite suiteForBytes( CharSourceFactory factory, byte[] bytes, String name, String desc, boolean slice) { - TestSuite suite = suiteForString(factory, new String(bytes, Charsets.UTF_8), name, desc); + TestSuite suite = suiteForString(factory, new String(bytes, UTF_8), name, desc); ByteSourceFactory byteSourceFactory = SourceSinkFactories.asByteSourceFactory(factory); suite.addTest( ByteSourceTester.suiteForBytes( diff --git a/android/guava-tests/test/com/google/common/io/CharStreamsTest.java b/android/guava-tests/test/com/google/common/io/CharStreamsTest.java index 9b2c24e28549..023542225976 100644 --- a/android/guava-tests/test/com/google/common/io/CharStreamsTest.java +++ b/android/guava-tests/test/com/google/common/io/CharStreamsTest.java @@ -16,6 +16,8 @@ package com.google.common.io; +import static org.junit.Assert.assertThrows; + import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.io.EOFException; @@ -27,12 +29,14 @@ import java.io.Writer; import java.nio.CharBuffer; import java.util.List; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link CharStreams}. * * @author Chris Nokleberg */ +@NullUnmarked public class CharStreamsTest extends IoTestCase { private static final String TEXT = "The quick brown fox jumped over the lazy dog."; @@ -116,13 +120,9 @@ public Integer getResult() { assertEquals("ab", sb.toString()); } - public void testSkipFully_EOF() throws IOException { + public void testSkipFully_eof() throws IOException { Reader reader = new StringReader("abcde"); - try { - CharStreams.skipFully(reader, 6); - fail("expected EOFException"); - } catch (EOFException expected) { - } + assertThrows(EOFException.class, () -> CharStreams.skipFully(reader, 6)); } public void testSkipFully() throws IOException { @@ -218,7 +218,7 @@ public void testCopy_toWriter_fromReadable() throws IOException { } /** - * Test for Guava issue 1061: http://code.google.com/p/guava-libraries/issues/detail?id=1061 + * Test for Guava issue 1061: https://github.com/google/guava/issues/1061 * *

    CharStreams.copy was failing to clear its CharBuffer after each read call, which effectively * reduced the available size of the buffer each time a call to read didn't fill up the available @@ -226,6 +226,7 @@ public void testCopy_toWriter_fromReadable() throws IOException { * is permanently reduced, but with certain Reader implementations it could also cause the buffer * size to reach 0, causing an infinite loop. */ + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 public void testCopyWithReaderThatDoesNotFillBuffer() throws IOException { // need a long enough string for the buffer to hit 0 remaining before the copy completes String string = Strings.repeat("0123456789", 100); @@ -270,17 +271,9 @@ public void testNullWriter() throws Exception { nullWriter.append(null); nullWriter.append(null, 0, 4); - try { - nullWriter.append(null, -1, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - - try { - nullWriter.append(null, 0, 5); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> nullWriter.append(null, -1, 4)); + + assertThrows(IndexOutOfBoundsException.class, () -> nullWriter.append(null, 0, 5)); // nothing really to assert? assertSame(CharStreams.nullWriter(), CharStreams.nullWriter()); diff --git a/android/guava-tests/test/com/google/common/io/CloseablesTest.java b/android/guava-tests/test/com/google/common/io/CloseablesTest.java index 34648b46e88d..2ac954c11f34 100644 --- a/android/guava-tests/test/com/google/common/io/CloseablesTest.java +++ b/android/guava-tests/test/com/google/common/io/CloseablesTest.java @@ -26,6 +26,7 @@ import java.io.InputStream; import java.io.Reader; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Closeables}. @@ -35,6 +36,7 @@ * * @author Michael Lancaster */ +@NullUnmarked public class CloseablesTest extends TestCase { private Closeable mockCloseable; diff --git a/android/guava-tests/test/com/google/common/io/CloserTest.java b/android/guava-tests/test/com/google/common/io/CloserTest.java index 38ff700c8c14..1181b9c46740 100644 --- a/android/guava-tests/test/com/google/common/io/CloserTest.java +++ b/android/guava-tests/test/com/google/common/io/CloserTest.java @@ -16,29 +16,28 @@ package com.google.common.io; +import static com.google.common.base.Throwables.throwIfInstanceOf; +import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import com.google.common.io.Closer.LoggingSuppressor; -import com.google.common.testing.TestLogHandler; import java.io.Closeable; import java.io.IOException; -import java.lang.reflect.Method; import java.util.List; -import java.util.logging.LogRecord; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Closer}. * * @author Colin Decker */ +@NullUnmarked public class CloserTest extends TestCase { private TestSuppressor suppressor; @@ -48,11 +47,6 @@ protected void setUp() throws Exception { suppressor = new TestSuppressor(); } - @AndroidIncompatible // TODO(cpovirk): Look up Build.VERSION.SDK_INT reflectively. - public void testCreate() { - assertThat(Closer.create().suppressor).isInstanceOf(Closer.SuppressingSuppressor.class); - } - public void testNoExceptionsThrown() throws IOException { Closer closer = new Closer(suppressor); @@ -281,42 +275,8 @@ public void testErrors() throws IOException { new Suppression(c1, c3Exception, c1Exception)); } - public static void testLoggingSuppressor() throws IOException { - TestLogHandler logHandler = new TestLogHandler(); - - Closeables.logger.addHandler(logHandler); - try { - Closer closer = new Closer(new Closer.LoggingSuppressor()); - - TestCloseable c1 = closer.register(TestCloseable.throwsOnClose(new IOException())); - TestCloseable c2 = closer.register(TestCloseable.throwsOnClose(new RuntimeException())); - try { - throw closer.rethrow(new IOException("thrown"), IOException.class); - } catch (IOException expected) { - } - - assertTrue(logHandler.getStoredLogRecords().isEmpty()); - - closer.close(); - - assertEquals(2, logHandler.getStoredLogRecords().size()); - - LogRecord record = logHandler.getStoredLogRecords().get(0); - assertEquals("Suppressing exception thrown when closing " + c2, record.getMessage()); - - record = logHandler.getStoredLogRecords().get(1); - assertEquals("Suppressing exception thrown when closing " + c1, record.getMessage()); - } finally { - Closeables.logger.removeHandler(logHandler); - } - } - - public static void testSuppressingSuppressorIfPossible() throws IOException { + public static void testSuppressingSuppressor() throws IOException { Closer closer = Closer.create(); - // can't test the JDK7 suppressor when not running on JDK7 - if (closer.suppressor instanceof LoggingSuppressor) { - return; - } IOException thrownException = new IOException(); IOException c1Exception = new IOException(); @@ -330,7 +290,7 @@ public static void testSuppressingSuppressorIfPossible() throws IOException { } catch (Throwable e) { throw closer.rethrow(thrownException, IOException.class); } finally { - assertThat(getSuppressed(thrownException)).isEmpty(); + assertThat(thrownException.getSuppressed()).isEmpty(); closer.close(); } } catch (IOException expected) { @@ -340,7 +300,7 @@ public static void testSuppressingSuppressorIfPossible() throws IOException { assertTrue(c1.isClosed()); assertTrue(c2.isClosed()); - ImmutableSet suppressed = ImmutableSet.copyOf(getSuppressed(thrownException)); + ImmutableSet suppressed = ImmutableSet.copyOf(thrownException.getSuppressed()); assertEquals(2, suppressed.size()); assertEquals(ImmutableSet.of(c1Exception, c2Exception), suppressed); @@ -352,15 +312,6 @@ public void testNullCloseable() throws IOException { closer.close(); } - static Throwable[] getSuppressed(Throwable throwable) { - try { - Method getSuppressed = Throwable.class.getDeclaredMethod("getSuppressed"); - return (Throwable[]) getSuppressed.invoke(throwable); - } catch (Exception e) { - throw new AssertionError(e); // only called if running on JDK7 - } - } - /** * Asserts that an exception was thrown when trying to close each of the given throwables and that * each such exception was suppressed because of the given thrown exception. @@ -369,6 +320,7 @@ private void assertSuppressed(Suppression... expected) { assertEquals(ImmutableList.copyOf(expected), suppressor.suppressions); } + // TODO(cpovirk): Just use addSuppressed+getSuppressed now that we can rely on it. /** Suppressor that records suppressions. */ private static class TestSuppressor implements Closer.Suppressor { @@ -393,7 +345,7 @@ private Suppression(Closeable closeable, Throwable thrown, Throwable suppressed) } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Suppression) { Suppression other = (Suppression) obj; return closeable.equals(other.closeable) @@ -435,7 +387,7 @@ static TestCloseable throwsOnCreate() throws IOException { throw new IOException(); } - private TestCloseable(@CheckForNull Throwable throwOnClose) { + private TestCloseable(@Nullable Throwable throwOnClose) { this.throwOnClose = throwOnClose; } @@ -447,7 +399,8 @@ public boolean isClosed() { public void close() throws IOException { closed = true; if (throwOnClose != null) { - Throwables.propagateIfPossible(throwOnClose, IOException.class); + throwIfInstanceOf(throwOnClose, IOException.class); + throwIfUnchecked(throwOnClose); throw new AssertionError(throwOnClose); } } diff --git a/android/guava-tests/test/com/google/common/io/CountingInputStreamTest.java b/android/guava-tests/test/com/google/common/io/CountingInputStreamTest.java index 163027b49f6c..20212ae40955 100644 --- a/android/guava-tests/test/com/google/common/io/CountingInputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/CountingInputStreamTest.java @@ -17,16 +17,19 @@ package com.google.common.io; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link CountingInputStream}. * * @author Chris Nokleberg */ +@NullUnmarked public class CountingInputStreamTest extends IoTestCase { private CountingInputStream counter; @@ -90,23 +93,15 @@ public void testMark() throws Exception { } public void testMarkNotSet() { - try { - counter.reset(); - fail(); - } catch (IOException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Mark not set"); - } + IOException expected = assertThrows(IOException.class, () -> counter.reset()); + assertThat(expected).hasMessageThat().isEqualTo("Mark not set"); } public void testMarkNotSupported() { counter = new CountingInputStream(new UnmarkableInputStream()); - try { - counter.reset(); - fail(); - } catch (IOException expected) { - assertThat(expected).hasMessageThat().isEqualTo("Mark not supported"); - } + IOException expected = assertThrows(IOException.class, () -> counter.reset()); + assertThat(expected).hasMessageThat().isEqualTo("Mark not supported"); } private static class UnmarkableInputStream extends InputStream { diff --git a/android/guava-tests/test/com/google/common/io/CountingOutputStreamTest.java b/android/guava-tests/test/com/google/common/io/CountingOutputStreamTest.java index 870692b327f4..6a1a1e70180d 100644 --- a/android/guava-tests/test/com/google/common/io/CountingOutputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/CountingOutputStreamTest.java @@ -16,13 +16,17 @@ package com.google.common.io; +import static org.junit.Assert.assertThrows; + import java.io.ByteArrayOutputStream; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link CountingOutputStream}. * * @author Chris Nokleberg */ +@NullUnmarked public class CountingOutputStreamTest extends IoTestCase { public void testCount() throws Exception { @@ -54,11 +58,7 @@ public void testCount() throws Exception { assertEquals(written, counter.getCount()); // Test that illegal arguments do not affect count - try { - counter.write(data, 0, data.length + 1); - fail("expected exception"); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> counter.write(data, 0, data.length + 1)); assertEquals(written, out.size()); assertEquals(written, counter.getCount()); } diff --git a/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamAndroidIncompatibleTest.java b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamAndroidIncompatibleTest.java new file mode 100644 index 000000000000..1386de3c45b6 --- /dev/null +++ b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamAndroidIncompatibleTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import static com.google.common.io.FileBackedOutputStreamTest.write; + +import com.google.common.testing.GcFinalization; +import java.io.File; +import org.jspecify.annotations.NullUnmarked; + +/** + * Android-incompatible tests for {@link FileBackedOutputStream}. + * + * @author Chris Nokleberg + */ +@AndroidIncompatible // Finalization probably just doesn't happen fast enough? +@NullUnmarked +public class FileBackedOutputStreamAndroidIncompatibleTest extends IoTestCase { + + public void testFinalizeDeletesFile() throws Exception { + byte[] data = newPreFilledByteArray(100); + FileBackedOutputStream out = new FileBackedOutputStream(0, true); + + write(out, data, 0, 100, true); + final File file = out.getFile(); + assertEquals(100, file.length()); + assertTrue(file.exists()); + out.close(); + + // Make sure that finalize deletes the file + out = null; + + // times out and throws RuntimeException on failure + GcFinalization.awaitDone( + new GcFinalization.FinalizationPredicate() { + @Override + public boolean isDone() { + return !file.exists(); + } + }); + } +} diff --git a/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java index 6841a41a9742..3cbf5a1028a7 100644 --- a/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/FileBackedOutputStreamTest.java @@ -16,18 +16,30 @@ package com.google.common.io; +import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; +import static com.google.common.base.StandardSystemProperty.OS_NAME; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.min; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.junit.Assert.assertThrows; -import com.google.common.testing.GcFinalization; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link FileBackedOutputStream}. * + *

    For a tiny bit more testing, see {@link FileBackedOutputStreamAndroidIncompatibleTest}. + * * @author Chris Nokleberg */ +@NullUnmarked public class FileBackedOutputStreamTest extends IoTestCase { @@ -48,7 +60,7 @@ private void testThreshold( byte[] data = newPreFilledByteArray(dataSize); FileBackedOutputStream out = new FileBackedOutputStream(fileThreshold, resetOnFinalize); ByteSource source = out.asByteSource(); - int chunk1 = Math.min(dataSize, fileThreshold); + int chunk1 = min(dataSize, fileThreshold); int chunk2 = dataSize - chunk1; // Write just enough to not trip the threshold @@ -61,10 +73,21 @@ private void testThreshold( // Write data to go over the threshold if (chunk2 > 0) { + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IOException.class, () -> write(out, data, chunk1, chunk2, singleByte)); + return; + } write(out, data, chunk1, chunk2, singleByte); file = out.getFile(); assertEquals(dataSize, file.length()); assertTrue(file.exists()); + assertThat(file.getName()).contains("FileBackedOutputStream"); + if (!isAndroid() && !isWindows()) { + PosixFileAttributes attributes = + java.nio.file.Files.getFileAttributeView(file.toPath(), PosixFileAttributeView.class) + .readAttributes(); + assertThat(attributes.permissions()).containsExactly(OWNER_READ, OWNER_WRITE); + } } out.close(); @@ -79,30 +102,6 @@ private void testThreshold( } - public void testFinalizeDeletesFile() throws Exception { - byte[] data = newPreFilledByteArray(100); - FileBackedOutputStream out = new FileBackedOutputStream(0, true); - - write(out, data, 0, 100, true); - final File file = out.getFile(); - assertEquals(100, file.length()); - assertTrue(file.exists()); - out.close(); - - // Make sure that finalize deletes the file - out = null; - - // times out and throws RuntimeException on failure - GcFinalization.awaitDone( - new GcFinalization.FinalizationPredicate() { - @Override - public boolean isDone() { - return !file.exists(); - } - }); - } - - public void testThreshold_resetOnFinalize() throws Exception { testThreshold(0, 100, true, true); testThreshold(10, 100, true, true); @@ -114,7 +113,7 @@ public void testThreshold_resetOnFinalize() throws Exception { testThreshold(1000, 100, false, true); } - private static void write(OutputStream out, byte[] b, int off, int len, boolean singleByte) + static void write(OutputStream out, byte[] b, int off, int len, boolean singleByte) throws IOException { if (singleByte) { for (int i = off; i < off + len; i++) { @@ -133,15 +132,15 @@ public void testWriteErrorAfterClose() throws Exception { FileBackedOutputStream out = new FileBackedOutputStream(50); ByteSource source = out.asByteSource(); + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IOException.class, () -> out.write(data)); + return; + } out.write(data); assertTrue(Arrays.equals(data, source.read())); out.close(); - try { - out.write(42); - fail("expected exception"); - } catch (IOException expected) { - } + assertThrows(IOException.class, () -> out.write(42)); // Verify that write had no effect assertTrue(Arrays.equals(data, source.read())); @@ -164,4 +163,12 @@ public void testReset() throws Exception { out.close(); } + + private static boolean isAndroid() { + return System.getProperty("java.runtime.name", "").contains("Android"); + } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } } diff --git a/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java new file mode 100644 index 000000000000..1b15f3c8569b --- /dev/null +++ b/android/guava-tests/test/com/google/common/io/FilesCreateTempDirTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.base.StandardSystemProperty.OS_NAME; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.junit.Assert.assertThrows; + +import java.io.File; +import java.io.IOException; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** + * Unit test for {@link Files#createTempDir}. + * + * @author Chris Nokleberg + */ + +@SuppressWarnings("deprecation") // tests of a deprecated method +@NullUnmarked +public class FilesCreateTempDirTest extends TestCase { + public void testCreateTempDir() throws IOException { + if (JAVA_IO_TMPDIR.value().equals("/sdcard")) { + assertThrows(IllegalStateException.class, Files::createTempDir); + return; + } + File temp = Files.createTempDir(); + try { + assertThat(temp.exists()).isTrue(); + assertThat(temp.isDirectory()).isTrue(); + assertThat(temp.listFiles()).isEmpty(); + File child = new File(temp, "child"); + assertThat(child.createNewFile()).isTrue(); + assertThat(child.delete()).isTrue(); + + if (!isAndroid() && !isWindows()) { + PosixFileAttributes attributes = + java.nio.file.Files.getFileAttributeView(temp.toPath(), PosixFileAttributeView.class) + .readAttributes(); + assertThat(attributes.permissions()) + .containsExactly(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); + } + } finally { + assertThat(temp.delete()).isTrue(); + } + } + + public void testBogusSystemPropertiesUsername() { + if (isAndroid()) { + /* + * The test calls directly into the "ACL-based filesystem" code, which isn't available under + * old versions of Android. Since Android doesn't use that code path, anyway, there's no need + * to test it. + */ + return; + } + + /* + * Only under Windows (or hypothetically when running with some other non-POSIX, ACL-based + * filesystem) does our prod code look up the username. Thus, this test doesn't necessarily test + * anything interesting under most environments. Still, we can run it (except for Android, at + * least old versions), so we mostly do. This is useful because we don't actually run our CI on + * Windows under Java 8, at least as of this writing. + * + * Under Windows in particular, we want to test that: + * + * - Under Java 9+, createTempDir() succeeds because it can look up the *real* username, rather + * than relying on the one from the system property. + * + * - Under Java 8, createTempDir() fails because it falls back to the bogus username from the + * system property. + */ + + String save = System.getProperty("user.name"); + System.setProperty("user.name", "-this-is-definitely-not-the-username-we-are-running-as//?"); + try { + TempFileCreator.testMakingUserPermissionsFromScratch(); + assertThat(isJava8()).isFalse(); + } catch (IOException expectedIfJava8) { + assertThat(isJava8()).isTrue(); + } finally { + System.setProperty("user.name", save); + } + } + + private static boolean isAndroid() { + return System.getProperty("java.runtime.name", "").contains("Android"); + } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } + + private static boolean isJava8() { + return JAVA_SPECIFICATION_VERSION.value().equals("1.8"); + } +} diff --git a/android/guava-tests/test/com/google/common/io/FilesFileTraverserTest.java b/android/guava-tests/test/com/google/common/io/FilesFileTraverserTest.java index c13240685b82..5353bd709a7b 100644 --- a/android/guava-tests/test/com/google/common/io/FilesFileTraverserTest.java +++ b/android/guava-tests/test/com/google/common/io/FilesFileTraverserTest.java @@ -22,11 +22,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.File; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Files#fileTraverser()}. @@ -34,37 +30,14 @@ * @author Jens Nyman */ -public class FilesFileTraverserTest extends TestCase { +@NullUnmarked +public class FilesFileTraverserTest extends IoTestCase { private File rootDir; @Override public void setUp() throws IOException { - rootDir = Files.createTempDir(); - } - - @Override - public void tearDown() throws IOException { - // delete rootDir and its contents - java.nio.file.Files.walkFileTree( - rootDir.toPath(), - new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - java.nio.file.Files.deleteIfExists(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - if (exc != null) { - return FileVisitResult.TERMINATE; - } - java.nio.file.Files.deleteIfExists(dir); - return FileVisitResult.CONTINUE; - } - }); + rootDir = createTempDir(); } public void testFileTraverser_emptyDirectory() throws Exception { diff --git a/android/guava-tests/test/com/google/common/io/FilesSimplifyPathTest.java b/android/guava-tests/test/com/google/common/io/FilesSimplifyPathTest.java index 69eb934de8d5..eb87be285ead 100644 --- a/android/guava-tests/test/com/google/common/io/FilesSimplifyPathTest.java +++ b/android/guava-tests/test/com/google/common/io/FilesSimplifyPathTest.java @@ -16,8 +16,8 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.io.Files.simplifyPath; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; @@ -25,12 +25,14 @@ import java.net.URL; import java.util.Iterator; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Files#simplifyPath}. * * @author Pablo Bellver */ +@NullUnmarked public class FilesSimplifyPathTest extends TestCase { public void testSimplifyEmptyString() { @@ -120,13 +122,13 @@ public void testMadbotsBug() { assertEquals("../ok", simplifyPath("../this/../ok")); } - // https://code.google.com/p/guava-libraries/issues/detail?id=705 + // https://github.com/google/guava/issues/705 public void test705() { assertEquals("../b", simplifyPath("x/../../b")); assertEquals("b", simplifyPath("x/../b")); } - // https://code.google.com/p/guava-libraries/issues/detail?id=716 + // https://github.com/google/guava/issues/716 public void test716() { assertEquals("b", simplifyPath("./b")); assertEquals("b", simplifyPath("./b/.")); @@ -142,7 +144,7 @@ public void testHiddenFiles() { assertEquals(".metadata/b", simplifyPath("./.metadata/b")); } - // https://code.google.com/p/guava-libraries/issues/detail?id=716 + // https://github.com/google/guava/issues/716 public void testMultipleDotFilenames() { assertEquals("..a", simplifyPath("..a")); assertEquals("/..a", simplifyPath("/..a")); @@ -156,18 +158,18 @@ public void testSlashDot() { assertEquals("/", simplifyPath("/.")); } - // http://code.google.com/p/guava-libraries/issues/detail?id=722 + // https://github.com/google/guava/issues/722 public void testInitialSlashDotDot() { assertEquals("/c", simplifyPath("/../c")); } - // http://code.google.com/p/guava-libraries/issues/detail?id=722 + // https://github.com/google/guava/issues/722 public void testInitialSlashDot() { assertEquals("/a", simplifyPath("/./a")); assertEquals("/.a", simplifyPath("/.a/a/..")); } - // http://code.google.com/p/guava-libraries/issues/detail?id=722 + // https://github.com/google/guava/issues/722 public void testConsecutiveParentsAfterPresent() { assertEquals("../..", simplifyPath("./../../")); assertEquals("../..", simplifyPath("./.././../")); diff --git a/android/guava-tests/test/com/google/common/io/FilesTest.java b/android/guava-tests/test/com/google/common/io/FilesTest.java index 8446da1d55d2..1111471e185e 100644 --- a/android/guava-tests/test/com/google/common/io/FilesTest.java +++ b/android/guava-tests/test/com/google/common/io/FilesTest.java @@ -17,8 +17,11 @@ package com.google.common.io; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.hash.Hashing; import com.google.common.primitives.Bytes; @@ -38,17 +41,26 @@ import java.util.List; import java.util.Random; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Files}. * - *

    Note: {@link Files#fileTraverser()} is tested in {@link FilesFileTraverserTest}. + *

    Some methods are tested in separate files: + * + *

      + *
    • {@link Files#fileTraverser()} is tested in {@link FilesFileTraverserTest}. + *
    • {@link Files#createTempDir()} is tested in {@link FilesCreateTempDirTest}. + *
    * * @author Chris Nokleberg */ +@SuppressWarnings("InlineMeInliner") // many tests of deprecated methods +@NullUnmarked public class FilesTest extends IoTestCase { + @AndroidIncompatible // suites, ByteSourceTester (b/230620681) public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -78,15 +90,15 @@ public static TestSuite suite() { public void testRoundTripSources() throws Exception { File asciiFile = getTestFile("ascii.txt"); ByteSource byteSource = Files.asByteSource(asciiFile); - assertSame(byteSource, byteSource.asCharSource(Charsets.UTF_8).asByteSource(Charsets.UTF_8)); + assertSame(byteSource, byteSource.asCharSource(UTF_8).asByteSource(UTF_8)); } public void testToByteArray() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); - assertTrue(Arrays.equals(ASCII.getBytes(Charsets.US_ASCII), Files.toByteArray(asciiFile))); - assertTrue(Arrays.equals(I18N.getBytes(Charsets.UTF_8), Files.toByteArray(i18nFile))); - assertTrue(Arrays.equals(I18N.getBytes(Charsets.UTF_8), Files.asByteSource(i18nFile).read())); + assertTrue(Arrays.equals(ASCII.getBytes(US_ASCII), Files.toByteArray(asciiFile))); + assertTrue(Arrays.equals(I18N.getBytes(UTF_8), Files.toByteArray(i18nFile))); + assertTrue(Arrays.equals(I18N.getBytes(UTF_8), Files.asByteSource(i18nFile).read())); } /** A {@link File} that provides a specialized value for {@link File#length()}. */ @@ -110,15 +122,15 @@ public long length() { public void testToString() throws IOException { File asciiFile = getTestFile("ascii.txt"); File i18nFile = getTestFile("i18n.txt"); - assertEquals(ASCII, Files.toString(asciiFile, Charsets.US_ASCII)); - assertEquals(I18N, Files.toString(i18nFile, Charsets.UTF_8)); - assertThat(Files.toString(i18nFile, Charsets.US_ASCII)).isNotEqualTo(I18N); + assertEquals(ASCII, Files.toString(asciiFile, US_ASCII)); + assertEquals(I18N, Files.toString(i18nFile, UTF_8)); + assertThat(Files.toString(i18nFile, US_ASCII)).isNotEqualTo(I18N); } public void testWriteString() throws IOException { File temp = createTempFile(); - Files.write(I18N, temp, Charsets.UTF_16LE); - assertEquals(I18N, Files.toString(temp, Charsets.UTF_16LE)); + Files.write(I18N, temp, UTF_16LE); + assertEquals(I18N, Files.toString(temp, UTF_16LE)); } public void testWriteBytes() throws IOException { @@ -127,21 +139,17 @@ public void testWriteBytes() throws IOException { Files.write(data, temp); assertTrue(Arrays.equals(data, Files.toByteArray(temp))); - try { - Files.write(null, temp); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Files.write(null, temp)); } public void testAppendString() throws IOException { File temp = createTempFile(); - Files.append(I18N, temp, Charsets.UTF_16LE); - assertEquals(I18N, Files.toString(temp, Charsets.UTF_16LE)); - Files.append(I18N, temp, Charsets.UTF_16LE); - assertEquals(I18N + I18N, Files.toString(temp, Charsets.UTF_16LE)); - Files.append(I18N, temp, Charsets.UTF_16LE); - assertEquals(I18N + I18N + I18N, Files.toString(temp, Charsets.UTF_16LE)); + Files.append(I18N, temp, UTF_16LE); + assertEquals(I18N, Files.toString(temp, UTF_16LE)); + Files.append(I18N, temp, UTF_16LE); + assertEquals(I18N + I18N, Files.toString(temp, UTF_16LE)); + Files.append(I18N, temp, UTF_16LE); + assertEquals(I18N + I18N + I18N, Files.toString(temp, UTF_16LE)); } public void testCopyToOutputStream() throws IOException { @@ -154,7 +162,7 @@ public void testCopyToOutputStream() throws IOException { public void testCopyToAppendable() throws IOException { File i18nFile = getTestFile("i18n.txt"); StringBuilder sb = new StringBuilder(); - Files.copy(i18nFile, Charsets.UTF_8, sb); + Files.copy(i18nFile, UTF_8, sb); assertEquals(I18N, sb.toString()); } @@ -162,40 +170,32 @@ public void testCopyFile() throws IOException { File i18nFile = getTestFile("i18n.txt"); File temp = createTempFile(); Files.copy(i18nFile, temp); - assertEquals(I18N, Files.toString(temp, Charsets.UTF_8)); + assertEquals(I18N, Files.toString(temp, UTF_8)); } public void testCopyEqualFiles() throws IOException { File temp1 = createTempFile(); File temp2 = file(temp1.getPath()); assertEquals(temp1, temp2); - Files.write(ASCII, temp1, Charsets.UTF_8); - try { - Files.copy(temp1, temp2); - fail("Expected an IAE to be thrown but wasn't"); - } catch (IllegalArgumentException expected) { - } - assertEquals(ASCII, Files.toString(temp1, Charsets.UTF_8)); + Files.write(ASCII, temp1, UTF_8); + assertThrows(IllegalArgumentException.class, () -> Files.copy(temp1, temp2)); + assertEquals(ASCII, Files.toString(temp1, UTF_8)); } public void testCopySameFile() throws IOException { File temp = createTempFile(); - Files.write(ASCII, temp, Charsets.UTF_8); - try { - Files.copy(temp, temp); - fail("Expected an IAE to be thrown but wasn't"); - } catch (IllegalArgumentException expected) { - } - assertEquals(ASCII, Files.toString(temp, Charsets.UTF_8)); + Files.write(ASCII, temp, UTF_8); + assertThrows(IllegalArgumentException.class, () -> Files.copy(temp, temp)); + assertEquals(ASCII, Files.toString(temp, UTF_8)); } public void testCopyIdenticalFiles() throws IOException { File temp1 = createTempFile(); - Files.write(ASCII, temp1, Charsets.UTF_8); + Files.write(ASCII, temp1, UTF_8); File temp2 = createTempFile(); - Files.write(ASCII, temp2, Charsets.UTF_8); + Files.write(ASCII, temp2, UTF_8); Files.copy(temp1, temp2); - assertEquals(ASCII, Files.toString(temp2, Charsets.UTF_8)); + assertEquals(ASCII, Files.toString(temp2, UTF_8)); } public void testEqual() throws IOException { @@ -226,19 +226,11 @@ public void testEqual() throws IOException { public void testNewReader() throws IOException { File asciiFile = getTestFile("ascii.txt"); - try { - Files.newReader(asciiFile, null); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Files.newReader(asciiFile, null)); - try { - Files.newReader(null, Charsets.UTF_8); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Files.newReader(null, UTF_8)); - BufferedReader r = Files.newReader(asciiFile, Charsets.US_ASCII); + BufferedReader r = Files.newReader(asciiFile, US_ASCII); try { assertEquals(ASCII, r.readLine()); } finally { @@ -248,19 +240,11 @@ public void testNewReader() throws IOException { public void testNewWriter() throws IOException { File temp = createTempFile(); - try { - Files.newWriter(temp, null); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Files.newWriter(temp, null)); - try { - Files.newWriter(null, Charsets.UTF_8); - fail("expected exception"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Files.newWriter(null, UTF_8)); - BufferedWriter w = Files.newWriter(temp, Charsets.UTF_8); + BufferedWriter w = Files.newWriter(temp, UTF_8); try { w.write(I18N); } finally { @@ -281,19 +265,18 @@ public void testTouch() throws IOException { Files.touch(temp); assertTrue(temp.exists()); - try { - Files.touch( - new File(temp.getPath()) { - @Override - public boolean setLastModified(long t) { - return false; - } + assertThrows( + IOException.class, + () -> + Files.touch( + new File(temp.getPath()) { + @Override + public boolean setLastModified(long t) { + return false; + } - private static final long serialVersionUID = 0; - }); - fail("expected exception"); - } catch (IOException expected) { - } + private static final long serialVersionUID = 0; + })); } public void testTouchTime() throws IOException { @@ -350,19 +333,7 @@ public void testCreateParentDirs_nonDirectoryParentExists() throws IOException { File parent = getTestFile("ascii.txt"); assertTrue(parent.isFile()); File file = file(parent, "foo"); - try { - Files.createParentDirs(file); - fail(); - } catch (IOException expected) { - } - } - - public void testCreateTempDir() { - File temp = Files.createTempDir(); - assertTrue(temp.exists()); - assertTrue(temp.isDirectory()); - assertThat(temp.listFiles()).isEmpty(); - assertTrue(temp.delete()); + assertThrows(IOException.class, () -> Files.createParentDirs(file)); } public void testMove() throws IOException { @@ -393,12 +364,8 @@ public void testMoveFailures() throws IOException { moveHelper( false, new UnmovableFile(temp1, false, false), new UnmovableFile(temp2, true, false)); - try { - File asciiFile = getTestFile("ascii.txt"); - moveHelper(false, asciiFile, asciiFile); - fail("expected exception"); - } catch (IllegalArgumentException expected) { - } + File asciiFile = getTestFile("ascii.txt"); + assertThrows(IllegalArgumentException.class, () -> moveHelper(false, asciiFile, asciiFile)); } private void moveHelper(boolean success, File from, File to) throws IOException { @@ -443,19 +410,18 @@ public boolean delete() { public void testLineReading() throws IOException { File temp = createTempFile(); - assertNull(Files.readFirstLine(temp, Charsets.UTF_8)); - assertTrue(Files.readLines(temp, Charsets.UTF_8).isEmpty()); + assertNull(Files.readFirstLine(temp, UTF_8)); + assertTrue(Files.readLines(temp, UTF_8).isEmpty()); - PrintWriter w = new PrintWriter(Files.newWriter(temp, Charsets.UTF_8)); + PrintWriter w = new PrintWriter(Files.newWriter(temp, UTF_8)); w.println("hello"); w.println(""); w.println(" world "); w.println(""); w.close(); - assertEquals("hello", Files.readFirstLine(temp, Charsets.UTF_8)); - assertEquals( - ImmutableList.of("hello", "", " world ", ""), Files.readLines(temp, Charsets.UTF_8)); + assertEquals("hello", Files.readFirstLine(temp, UTF_8)); + assertEquals(ImmutableList.of("hello", "", " world ", ""), Files.readLines(temp, UTF_8)); assertTrue(temp.delete()); } @@ -477,15 +443,15 @@ public List getResult() { return collector; } }; - assertThat(Files.readLines(temp, Charsets.UTF_8, collect)).isEmpty(); + assertThat(Files.readLines(temp, UTF_8, collect)).isEmpty(); - PrintWriter w = new PrintWriter(Files.newWriter(temp, Charsets.UTF_8)); + PrintWriter w = new PrintWriter(Files.newWriter(temp, UTF_8)); w.println("hello"); w.println(""); w.println(" world "); w.println(""); w.close(); - Files.readLines(temp, Charsets.UTF_8, collect); + Files.readLines(temp, UTF_8, collect); assertThat(collect.getResult()).containsExactly("hello", "", " world ", "").inOrder(); LineProcessor> collectNonEmptyLines = @@ -505,7 +471,7 @@ public List getResult() { return collector; } }; - Files.readLines(temp, Charsets.UTF_8, collectNonEmptyLines); + Files.readLines(temp, UTF_8, collectNonEmptyLines); assertThat(collectNonEmptyLines.getResult()).containsExactly("hello", " world ").inOrder(); assertTrue(temp.delete()); @@ -549,11 +515,7 @@ public void testMap_noSuchFile() throws IOException { assertTrue(deleted); // Test - try { - Files.map(file); - fail("Should have thrown FileNotFoundException."); - } catch (FileNotFoundException expected) { - } + assertThrows(FileNotFoundException.class, () -> Files.map(file)); } public void testMap_readWrite() throws IOException { @@ -605,11 +567,9 @@ public void testMap_readWrite_max_value_plus_1() throws IOException { // Setup File file = createTempFile(); // Test - try { - Files.map(file, MapMode.READ_WRITE, (long) Integer.MAX_VALUE + 1); - fail("Should throw when size exceeds Integer.MAX_VALUE"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Files.map(file, MapMode.READ_WRITE, (long) Integer.MAX_VALUE + 1)); } public void testGetFileExtension() { diff --git a/android/guava-tests/test/com/google/common/io/FlushablesTest.java b/android/guava-tests/test/com/google/common/io/FlushablesTest.java index b45150a43d44..ddb8bdeace43 100644 --- a/android/guava-tests/test/com/google/common/io/FlushablesTest.java +++ b/android/guava-tests/test/com/google/common/io/FlushablesTest.java @@ -23,6 +23,7 @@ import java.io.Flushable; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Flushables}. @@ -32,6 +33,7 @@ * * @author Michael Lancaster */ +@NullUnmarked public class FlushablesTest extends TestCase { private Flushable mockFlushable; diff --git a/android/guava-tests/test/com/google/common/io/IoTestCase.java b/android/guava-tests/test/com/google/common/io/IoTestCase.java index fa8961905931..a8c462734d42 100644 --- a/android/guava-tests/test/com/google/common/io/IoTestCase.java +++ b/android/guava-tests/test/com/google/common/io/IoTestCase.java @@ -16,7 +16,6 @@ package com.google.common.io; -import com.google.common.collect.Sets; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.File; import java.io.FileOutputStream; @@ -24,10 +23,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Base test case class for I/O tests. @@ -35,6 +37,7 @@ * @author Chris Nokleberg * @author Colin Decker */ +@NullUnmarked public abstract class IoTestCase extends TestCase { private static final Logger logger = Logger.getLogger(IoTestCase.class.getName()); @@ -50,7 +53,7 @@ public abstract class IoTestCase extends TestCase { private File testDir; private File tempDir; - private final Set filesToDelete = Sets.newHashSet(); + private final Set filesToDelete = new HashSet<>(); @Override protected void tearDown() { @@ -92,7 +95,7 @@ private File getTestDir() throws IOException { } /** Returns the file with the given name under the testdata directory. */ - protected final File getTestFile(String name) throws IOException { + protected final @Nullable File getTestFile(String name) throws IOException { File file = new File(getTestDir(), name); if (!file.exists()) { URL resourceUrl = IoTestCase.class.getResource("testdata/" + name); diff --git a/android/guava-tests/test/com/google/common/io/LineBufferTest.java b/android/guava-tests/test/com/google/common/io/LineBufferTest.java index efe476669916..cc1ba24095ae 100644 --- a/android/guava-tests/test/com/google/common/io/LineBufferTest.java +++ b/android/guava-tests/test/com/google/common/io/LineBufferTest.java @@ -16,7 +16,11 @@ package com.google.common.io; +import static java.lang.Math.max; +import static java.lang.Math.min; + import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import java.io.BufferedReader; import java.io.FilterReader; @@ -26,6 +30,7 @@ import java.nio.CharBuffer; import java.util.Arrays; import java.util.List; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link LineBuffer} and {@link LineReader}. @@ -33,6 +38,7 @@ * @author Chris Nokleberg */ @AndroidIncompatible // occasionally very slow +@NullUnmarked public class LineBufferTest extends IoTestCase { public void testProcess() throws IOException { @@ -53,7 +59,8 @@ public void testProcess() throws IOException { bufferHelper("mixed\nline\rendings\r\n", "mixed\n", "line\r", "endings\r\n"); } - private static final int[] CHUNK_SIZES = {1, 2, 3, Integer.MAX_VALUE}; + private static final ImmutableSet CHUNK_SIZES = + ImmutableSet.of(1, 2, 3, Integer.MAX_VALUE); private static void bufferHelper(String input, String... expect) throws IOException { @@ -69,7 +76,7 @@ public String apply(String value) { }); for (int chunk : CHUNK_SIZES) { - chunk = Math.max(1, Math.min(chunk, input.length())); + chunk = max(1, min(chunk, input.length())); assertEquals(expectProcess, bufferHelper(input, chunk)); assertEquals(expectRead, readUsingJava(input, chunk)); assertEquals(expectRead, readUsingReader(input, chunk, true)); @@ -89,7 +96,7 @@ protected void handleLine(String line, String end) { char[] chars = input.toCharArray(); int off = 0; while (off < chars.length) { - int len = Math.min(chars.length, off + chunk) - off; + int len = min(chars.length, off + chunk) - off; lineBuf.add(chars, off, len); off += len; } @@ -136,7 +143,7 @@ private static Reader getChunkedReader(String input, final int chunk) { return new FilterReader(new StringReader(input)) { @Override public int read(char[] cbuf, int off, int len) throws IOException { - return super.read(cbuf, off, Math.min(chunk, len)); + return super.read(cbuf, off, min(chunk, len)); } }; } diff --git a/android/guava-tests/test/com/google/common/io/LittleEndianDataInputStreamTest.java b/android/guava-tests/test/com/google/common/io/LittleEndianDataInputStreamTest.java index 160df410bed6..e5280bc90aee 100644 --- a/android/guava-tests/test/com/google/common/io/LittleEndianDataInputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/LittleEndianDataInputStreamTest.java @@ -17,6 +17,7 @@ package com.google.common.io; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.primitives.Bytes; import java.io.ByteArrayInputStream; @@ -26,12 +27,14 @@ import java.io.EOFException; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test class for {@link LittleEndianDataInputStream}. * * @author Chris Nokleberg */ +@NullUnmarked public class LittleEndianDataInputStreamTest extends TestCase { private byte[] data; @@ -75,32 +78,21 @@ public void testReadFully() throws IOException { public void testReadUnsignedByte_eof() throws IOException { DataInput in = new LittleEndianDataInputStream(new ByteArrayInputStream(new byte[0])); - try { - in.readUnsignedByte(); - fail(); - } catch (EOFException expected) { - } + assertThrows(EOFException.class, () -> in.readUnsignedByte()); } public void testReadUnsignedShort_eof() throws IOException { byte[] buf = {23}; DataInput in = new LittleEndianDataInputStream(new ByteArrayInputStream(buf)); - try { - in.readUnsignedShort(); - fail(); - } catch (EOFException expected) { - } + assertThrows(EOFException.class, () -> in.readUnsignedShort()); } @SuppressWarnings("DoNotCall") public void testReadLine() throws IOException { DataInput in = new LittleEndianDataInputStream(new ByteArrayInputStream(data)); - try { - in.readLine(); - fail(); - } catch (UnsupportedOperationException expected) { - assertThat(expected).hasMessageThat().isEqualTo("readLine is not supported"); - } + UnsupportedOperationException expected = + assertThrows(UnsupportedOperationException.class, () -> in.readLine()); + assertThat(expected).hasMessageThat().isEqualTo("readLine is not supported"); } public void testReadLittleEndian() throws IOException { diff --git a/android/guava-tests/test/com/google/common/io/LittleEndianDataOutputStreamTest.java b/android/guava-tests/test/com/google/common/io/LittleEndianDataOutputStreamTest.java index 2568aae1de7d..a1643a62a8b2 100644 --- a/android/guava-tests/test/com/google/common/io/LittleEndianDataOutputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/LittleEndianDataOutputStreamTest.java @@ -16,7 +16,8 @@ package com.google.common.io; -import com.google.common.base.Charsets; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + import com.google.common.primitives.Bytes; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -24,12 +25,14 @@ import java.io.DataInputStream; import java.io.IOException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test class for {@link LittleEndianDataOutputStream}. * * @author Keith Bottner */ +@NullUnmarked public class LittleEndianDataOutputStreamTest extends TestCase { private ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -92,7 +95,7 @@ public void testWriteBytes() throws IOException { /* Read in various values NORMALLY */ byte[] b = new byte[6]; in.readFully(b); - assertEquals("r\u00C9sum\u00C9".getBytes(Charsets.ISO_8859_1), b); + assertEquals("r\u00C9sum\u00C9".getBytes(ISO_8859_1), b); } @SuppressWarnings("deprecation") // testing a deprecated method diff --git a/android/guava-tests/test/com/google/common/io/MoreFilesTest.java b/android/guava-tests/test/com/google/common/io/MoreFilesTest.java new file mode 100644 index 000000000000..8028759ff971 --- /dev/null +++ b/android/guava-tests/test/com/google/common/io/MoreFilesTest.java @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import static com.google.common.base.StandardSystemProperty.OS_NAME; +import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE; +import static com.google.common.jimfs.Feature.SECURE_DIRECTORY_STREAM; +import static com.google.common.jimfs.Feature.SYMBOLIC_LINKS; +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.LinkOption.NOFOLLOW_LINKS; +import static org.junit.Assert.assertThrows; + +import com.google.common.collect.ObjectArrays; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Feature; +import com.google.common.jimfs.Jimfs; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.EnumSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** + * Tests for {@link MoreFiles}. + * + *

    Note: {@link MoreFiles#fileTraverser()} is tested in {@link MoreFilesFileTraverserTest}. + * + * @author Colin Decker + */ + +@NullUnmarked +public class MoreFilesTest extends TestCase { + + /* + * Note: We don't include suite() in the backport. I've lost track of whether the Android test + * runner would run it even if we did, but part of the problem is b/230620681. + */ + + private static final FileSystem FS = FileSystems.getDefault(); + + private static Path root() { + return FS.getRootDirectories().iterator().next(); + } + + private Path tempDir; + + @Override + protected void setUp() throws Exception { + tempDir = Files.createTempDirectory("MoreFilesTest"); + } + + @Override + protected void tearDown() throws Exception { + if (tempDir != null) { + // delete tempDir and its contents + Files.walkFileTree( + tempDir, + new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.deleteIfExists(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException { + if (exc != null) { + return FileVisitResult.TERMINATE; + } + Files.deleteIfExists(dir); + return FileVisitResult.CONTINUE; + } + }); + } + } + + private Path createTempFile() throws IOException { + return Files.createTempFile(tempDir, "test", ".test"); + } + + public void testByteSource_size_ofDirectory() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path dir = fs.getPath("dir"); + Files.createDirectory(dir); + + ByteSource source = MoreFiles.asByteSource(dir); + + assertThat(source.sizeIfKnown()).isAbsent(); + + assertThrows(IOException.class, () -> source.size()); + } + } + + public void testByteSource_size_ofSymlinkToDirectory() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path dir = fs.getPath("dir"); + Files.createDirectory(dir); + Path link = fs.getPath("link"); + Files.createSymbolicLink(link, dir); + + ByteSource source = MoreFiles.asByteSource(link); + + assertThat(source.sizeIfKnown()).isAbsent(); + + assertThrows(IOException.class, () -> source.size()); + } + } + + public void testByteSource_size_ofSymlinkToRegularFile() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path file = fs.getPath("file"); + Files.write(file, new byte[10]); + Path link = fs.getPath("link"); + Files.createSymbolicLink(link, file); + + ByteSource source = MoreFiles.asByteSource(link); + + assertEquals(10L, (long) source.sizeIfKnown().get()); + assertEquals(10L, source.size()); + } + } + + public void testByteSource_size_ofSymlinkToRegularFile_nofollowLinks() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path file = fs.getPath("file"); + Files.write(file, new byte[10]); + Path link = fs.getPath("link"); + Files.createSymbolicLink(link, file); + + ByteSource source = MoreFiles.asByteSource(link, NOFOLLOW_LINKS); + + assertThat(source.sizeIfKnown()).isAbsent(); + + assertThrows(IOException.class, () -> source.size()); + } + } + + public void testEqual() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path fooPath = fs.getPath("foo"); + Path barPath = fs.getPath("bar"); + MoreFiles.asCharSink(fooPath, UTF_8).write("foo"); + MoreFiles.asCharSink(barPath, UTF_8).write("barbar"); + + assertThat(MoreFiles.equal(fooPath, barPath)).isFalse(); + assertThat(MoreFiles.equal(fooPath, fooPath)).isTrue(); + assertThat(MoreFiles.asByteSource(fooPath).contentEquals(MoreFiles.asByteSource(fooPath))) + .isTrue(); + + Path fooCopy = Files.copy(fooPath, fs.getPath("fooCopy")); + assertThat(Files.isSameFile(fooPath, fooCopy)).isFalse(); + assertThat(MoreFiles.equal(fooPath, fooCopy)).isTrue(); + + MoreFiles.asCharSink(fooCopy, UTF_8).write("boo"); + assertThat(MoreFiles.asByteSource(fooPath).size()) + .isEqualTo(MoreFiles.asByteSource(fooCopy).size()); + assertThat(MoreFiles.equal(fooPath, fooCopy)).isFalse(); + + // should also assert that a Path that erroneously reports a size 0 can still be compared, + // not sure how to do that with the Path API + } + } + + public void testEqual_links() throws IOException { + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path fooPath = fs.getPath("foo"); + MoreFiles.asCharSink(fooPath, UTF_8).write("foo"); + + Path fooSymlink = fs.getPath("symlink"); + Files.createSymbolicLink(fooSymlink, fooPath); + + Path fooHardlink = fs.getPath("hardlink"); + Files.createLink(fooHardlink, fooPath); + + assertThat(MoreFiles.equal(fooPath, fooSymlink)).isTrue(); + assertThat(MoreFiles.equal(fooPath, fooHardlink)).isTrue(); + assertThat(MoreFiles.equal(fooSymlink, fooHardlink)).isTrue(); + } + } + + public void testTouch() throws IOException { + Path temp = createTempFile(); + assertTrue(Files.exists(temp)); + Files.delete(temp); + assertFalse(Files.exists(temp)); + + MoreFiles.touch(temp); + assertTrue(Files.exists(temp)); + MoreFiles.touch(temp); + assertTrue(Files.exists(temp)); + } + + public void testTouchTime() throws IOException { + Path temp = createTempFile(); + assertTrue(Files.exists(temp)); + Files.setLastModifiedTime(temp, FileTime.fromMillis(0)); + assertEquals(0, Files.getLastModifiedTime(temp).toMillis()); + MoreFiles.touch(temp); + assertThat(Files.getLastModifiedTime(temp).toMillis()).isNotEqualTo(0); + } + + public void testCreateParentDirectories_root() throws IOException { + // We use a fake filesystem to sidestep flaky problems with Windows (b/136041958). + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path root = fs.getRootDirectories().iterator().next(); + assertNull(root.getParent()); + assertNull(root.toRealPath().getParent()); + MoreFiles.createParentDirectories(root); // test that there's no exception + } + } + + public void testCreateParentDirectories_relativePath() throws IOException { + Path path = FS.getPath("nonexistent.file"); + assertNull(path.getParent()); + assertNotNull(path.toAbsolutePath().getParent()); + MoreFiles.createParentDirectories(path); // test that there's no exception + } + + public void testCreateParentDirectories_noParentsNeeded() throws IOException { + Path path = tempDir.resolve("nonexistent.file"); + assertTrue(Files.exists(path.getParent())); + MoreFiles.createParentDirectories(path); // test that there's no exception + } + + public void testCreateParentDirectories_oneParentNeeded() throws IOException { + Path path = tempDir.resolve("parent/nonexistent.file"); + Path parent = path.getParent(); + assertFalse(Files.exists(parent)); + MoreFiles.createParentDirectories(path); + assertTrue(Files.exists(parent)); + } + + public void testCreateParentDirectories_multipleParentsNeeded() throws IOException { + Path path = tempDir.resolve("grandparent/parent/nonexistent.file"); + Path parent = path.getParent(); + Path grandparent = parent.getParent(); + assertFalse(Files.exists(grandparent)); + assertFalse(Files.exists(parent)); + + MoreFiles.createParentDirectories(path); + assertTrue(Files.exists(parent)); + assertTrue(Files.exists(grandparent)); + } + + public void testCreateParentDirectories_noPermission() { + if (isWindows()) { + return; // TODO: b/136041958 - Create/find a directory that we don't have permissions on? + } + Path file = root().resolve("parent/nonexistent.file"); + Path parent = file.getParent(); + assertFalse(Files.exists(parent)); + assertThrows(IOException.class, () -> MoreFiles.createParentDirectories(file)); + } + + public void testCreateParentDirectories_nonDirectoryParentExists() throws IOException { + Path parent = createTempFile(); + assertTrue(Files.isRegularFile(parent)); + Path file = parent.resolve("foo"); + assertThrows(IOException.class, () -> MoreFiles.createParentDirectories(file)); + } + + public void testCreateParentDirectories_symlinkParentExists() throws IOException { + /* + * We use a fake filesystem to sidestep: + * + * - flaky problems with Windows (b/136041958) + * + * - the lack of support for symlinks in the default filesystem under Android's desugared + * java.nio.file + */ + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path symlink = fs.getPath("linkToDir"); + Files.createSymbolicLink(symlink, fs.getRootDirectories().iterator().next()); + Path file = symlink.resolve("foo"); + MoreFiles.createParentDirectories(file); + } + } + + public void testGetFileExtension() { + assertEquals("txt", MoreFiles.getFileExtension(FS.getPath(".txt"))); + assertEquals("txt", MoreFiles.getFileExtension(FS.getPath("blah.txt"))); + assertEquals("txt", MoreFiles.getFileExtension(FS.getPath("blah..txt"))); + assertEquals("txt", MoreFiles.getFileExtension(FS.getPath(".blah.txt"))); + assertEquals("txt", MoreFiles.getFileExtension(root().resolve("tmp/blah.txt"))); + assertEquals("gz", MoreFiles.getFileExtension(FS.getPath("blah.tar.gz"))); + assertEquals("", MoreFiles.getFileExtension(root())); + assertEquals("", MoreFiles.getFileExtension(FS.getPath("."))); + assertEquals("", MoreFiles.getFileExtension(FS.getPath(".."))); + assertEquals("", MoreFiles.getFileExtension(FS.getPath("..."))); + assertEquals("", MoreFiles.getFileExtension(FS.getPath("blah"))); + assertEquals("", MoreFiles.getFileExtension(FS.getPath("blah."))); + assertEquals("", MoreFiles.getFileExtension(FS.getPath(".blah."))); + assertEquals("", MoreFiles.getFileExtension(root().resolve("foo.bar/blah"))); + assertEquals("", MoreFiles.getFileExtension(root().resolve("foo/.bar/blah"))); + } + + public void testGetNameWithoutExtension() { + assertEquals("", MoreFiles.getNameWithoutExtension(FS.getPath(".txt"))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(FS.getPath("blah.txt"))); + assertEquals("blah.", MoreFiles.getNameWithoutExtension(FS.getPath("blah..txt"))); + assertEquals(".blah", MoreFiles.getNameWithoutExtension(FS.getPath(".blah.txt"))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(root().resolve("tmp/blah.txt"))); + assertEquals("blah.tar", MoreFiles.getNameWithoutExtension(FS.getPath("blah.tar.gz"))); + assertEquals("", MoreFiles.getNameWithoutExtension(root())); + assertEquals("", MoreFiles.getNameWithoutExtension(FS.getPath("."))); + assertEquals(".", MoreFiles.getNameWithoutExtension(FS.getPath(".."))); + assertEquals("..", MoreFiles.getNameWithoutExtension(FS.getPath("..."))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(FS.getPath("blah"))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(FS.getPath("blah."))); + assertEquals(".blah", MoreFiles.getNameWithoutExtension(FS.getPath(".blah."))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(root().resolve("foo.bar/blah"))); + assertEquals("blah", MoreFiles.getNameWithoutExtension(root().resolve("foo/.bar/blah"))); + } + + public void testPredicates() throws IOException { + /* + * We use a fake filesystem to sidestep the lack of support for symlinks in the default + * filesystem under Android's desugared java.nio.file. + */ + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path file = fs.getPath("file"); + Files.createFile(file); + Path dir = fs.getPath("dir"); + Files.createDirectory(dir); + + assertTrue(MoreFiles.isDirectory().apply(dir)); + assertFalse(MoreFiles.isRegularFile().apply(dir)); + + assertFalse(MoreFiles.isDirectory().apply(file)); + assertTrue(MoreFiles.isRegularFile().apply(file)); + + Path symlinkToDir = fs.getPath("symlinkToDir"); + Path symlinkToFile = fs.getPath("symlinkToFile"); + + Files.createSymbolicLink(symlinkToDir, dir); + Files.createSymbolicLink(symlinkToFile, file); + + assertTrue(MoreFiles.isDirectory().apply(symlinkToDir)); + assertFalse(MoreFiles.isRegularFile().apply(symlinkToDir)); + + assertFalse(MoreFiles.isDirectory().apply(symlinkToFile)); + assertTrue(MoreFiles.isRegularFile().apply(symlinkToFile)); + + assertFalse(MoreFiles.isDirectory(NOFOLLOW_LINKS).apply(symlinkToDir)); + assertFalse(MoreFiles.isRegularFile(NOFOLLOW_LINKS).apply(symlinkToFile)); + } + } + + /** + * Creates a new file system for testing that supports the given features in addition to + * supporting symbolic links. The file system is created initially having the following file + * structure: + * + *

    +   *   /
    +   *      work/
    +   *         dir/
    +   *            a
    +   *            b/
    +   *               g
    +   *               h -> ../a
    +   *               i/
    +   *                  j/
    +   *                     k
    +   *                     l/
    +   *            c
    +   *            d -> b/i
    +   *            e/
    +   *            f -> /dontdelete
    +   *      dontdelete/
    +   *         a
    +   *         b/
    +   *         c
    +   *      symlinktodir -> work/dir
    +   * 
    + */ + static FileSystem newTestFileSystem(Feature... supportedFeatures) throws IOException { + FileSystem fs = + Jimfs.newFileSystem( + Configuration.unix().toBuilder() + .setSupportedFeatures(ObjectArrays.concat(SYMBOLIC_LINKS, supportedFeatures)) + .build()); + Files.createDirectories(fs.getPath("dir/b/i/j/l")); + Files.createFile(fs.getPath("dir/a")); + Files.createFile(fs.getPath("dir/c")); + Files.createSymbolicLink(fs.getPath("dir/d"), fs.getPath("b/i")); + Files.createDirectory(fs.getPath("dir/e")); + Files.createSymbolicLink(fs.getPath("dir/f"), fs.getPath("/dontdelete")); + Files.createFile(fs.getPath("dir/b/g")); + Files.createSymbolicLink(fs.getPath("dir/b/h"), fs.getPath("../a")); + Files.createFile(fs.getPath("dir/b/i/j/k")); + Files.createDirectory(fs.getPath("/dontdelete")); + Files.createFile(fs.getPath("/dontdelete/a")); + Files.createDirectory(fs.getPath("/dontdelete/b")); + Files.createFile(fs.getPath("/dontdelete/c")); + Files.createSymbolicLink(fs.getPath("/symlinktodir"), fs.getPath("work/dir")); + return fs; + } + + public void testDirectoryDeletion_basic() throws IOException { + for (DirectoryDeleteMethod method : EnumSet.allOf(DirectoryDeleteMethod.class)) { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path dir = fs.getPath("dir"); + assertEquals(6, MoreFiles.listFiles(dir).size()); + + method.delete(dir); + method.assertDeleteSucceeded(dir); + + assertEquals( + "contents of /dontdelete deleted by delete method " + method, + 3, + MoreFiles.listFiles(fs.getPath("/dontdelete")).size()); + } + } + } + + public void testDirectoryDeletion_emptyDir() throws IOException { + for (DirectoryDeleteMethod method : EnumSet.allOf(DirectoryDeleteMethod.class)) { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path emptyDir = fs.getPath("dir/e"); + assertEquals(0, MoreFiles.listFiles(emptyDir).size()); + + method.delete(emptyDir); + method.assertDeleteSucceeded(emptyDir); + } + } + } + + public void testDeleteRecursively_symlinkToDir() throws IOException { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path symlink = fs.getPath("/symlinktodir"); + Path dir = fs.getPath("dir"); + + assertEquals(6, MoreFiles.listFiles(dir).size()); + + MoreFiles.deleteRecursively(symlink); + + assertFalse(Files.exists(symlink)); + assertTrue(Files.exists(dir)); + assertEquals(6, MoreFiles.listFiles(dir).size()); + } + } + + public void testDeleteDirectoryContents_symlinkToDir() throws IOException { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path symlink = fs.getPath("/symlinktodir"); + Path dir = fs.getPath("dir"); + + assertEquals(6, MoreFiles.listFiles(symlink).size()); + + MoreFiles.deleteDirectoryContents(symlink); + + assertTrue(Files.exists(symlink, NOFOLLOW_LINKS)); + assertTrue(Files.exists(symlink)); + assertTrue(Files.exists(dir)); + assertEquals(0, MoreFiles.listFiles(symlink).size()); + } + } + + public void testDirectoryDeletion_sdsNotSupported_fails() throws IOException { + for (DirectoryDeleteMethod method : EnumSet.allOf(DirectoryDeleteMethod.class)) { + try (FileSystem fs = newTestFileSystem()) { + Path dir = fs.getPath("dir"); + assertEquals(6, MoreFiles.listFiles(dir).size()); + + assertThrows(InsecureRecursiveDeleteException.class, () -> method.delete(dir)); + + assertTrue(Files.exists(dir)); + assertEquals(6, MoreFiles.listFiles(dir).size()); + } + } + } + + public void testDirectoryDeletion_sdsNotSupported_allowInsecure() throws IOException { + for (DirectoryDeleteMethod method : EnumSet.allOf(DirectoryDeleteMethod.class)) { + try (FileSystem fs = newTestFileSystem()) { + Path dir = fs.getPath("dir"); + assertEquals(6, MoreFiles.listFiles(dir).size()); + + method.delete(dir, ALLOW_INSECURE); + method.assertDeleteSucceeded(dir); + + assertEquals( + "contents of /dontdelete deleted by delete method " + method, + 3, + MoreFiles.listFiles(fs.getPath("/dontdelete")).size()); + } + } + } + + public void testDeleteRecursively_symlinkToDir_sdsNotSupported_allowInsecure() + throws IOException { + try (FileSystem fs = newTestFileSystem()) { + Path symlink = fs.getPath("/symlinktodir"); + Path dir = fs.getPath("dir"); + + assertEquals(6, MoreFiles.listFiles(dir).size()); + + MoreFiles.deleteRecursively(symlink, ALLOW_INSECURE); + + assertFalse(Files.exists(symlink)); + assertTrue(Files.exists(dir)); + assertEquals(6, MoreFiles.listFiles(dir).size()); + } + } + + public void testDeleteRecursively_nonexistingFile_throwsNoSuchFileException() throws IOException { + try (FileSystem fs = newTestFileSystem()) { + NoSuchFileException expected = + assertThrows( + NoSuchFileException.class, + () -> MoreFiles.deleteRecursively(fs.getPath("/work/nothere"), ALLOW_INSECURE)); + assertThat(expected.getFile()).isEqualTo("/work/nothere"); + } + } + + public void testDeleteDirectoryContents_symlinkToDir_sdsNotSupported_allowInsecure() + throws IOException { + try (FileSystem fs = newTestFileSystem()) { + Path symlink = fs.getPath("/symlinktodir"); + Path dir = fs.getPath("dir"); + + assertEquals(6, MoreFiles.listFiles(dir).size()); + + MoreFiles.deleteDirectoryContents(symlink, ALLOW_INSECURE); + assertEquals(0, MoreFiles.listFiles(dir).size()); + } + } + + /** + * This test attempts to create a situation in which one thread is constantly changing a file from + * being a real directory to being a symlink to another directory. It then calls + * deleteDirectoryContents thousands of times on a directory whose subtree contains the file + * that's switching between directory and symlink to try to ensure that under no circumstance does + * deleteDirectoryContents follow the symlink to the other directory and delete that directory's + * contents. + * + *

    We can only test this with a file system that supports SecureDirectoryStream, because it's + * not possible to protect against this if the file system doesn't. + */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. + public void testDirectoryDeletion_directorySymlinkRace() throws IOException { + int iterations = isAndroid() ? 100 : 5000; + for (DirectoryDeleteMethod method : EnumSet.allOf(DirectoryDeleteMethod.class)) { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path dirToDelete = fs.getPath("dir/b/i"); + Path changingFile = dirToDelete.resolve("j/l"); + Path symlinkTarget = fs.getPath("/dontdelete"); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + startDirectorySymlinkSwitching(changingFile, symlinkTarget, executor); + + try { + for (int i = 0; i < iterations; i++) { + try { + Files.createDirectories(changingFile); + Files.createFile(dirToDelete.resolve("j/k")); + } catch (FileAlreadyExistsException expected) { + // if a file already exists, that's fine... just continue + } + + try { + method.delete(dirToDelete); + } catch (FileSystemException expected) { + // the delete method may or may not throw an exception, but if it does that's fine + // and expected + } + + // this test is mainly checking that the contents of /dontdelete aren't deleted under + // any circumstances + assertEquals(3, MoreFiles.listFiles(symlinkTarget).size()); + + Thread.yield(); + } + } finally { + executor.shutdownNow(); + } + } + } + } + + public void testDeleteRecursively_nonDirectoryFile() throws IOException { + try (FileSystem fs = newTestFileSystem(SECURE_DIRECTORY_STREAM)) { + Path file = fs.getPath("dir/a"); + assertTrue(Files.isRegularFile(file, NOFOLLOW_LINKS)); + + MoreFiles.deleteRecursively(file); + + assertFalse(Files.exists(file, NOFOLLOW_LINKS)); + + Path symlink = fs.getPath("/symlinktodir"); + assertTrue(Files.isSymbolicLink(symlink)); + + Path realSymlinkTarget = symlink.toRealPath(); + assertTrue(Files.isDirectory(realSymlinkTarget, NOFOLLOW_LINKS)); + + MoreFiles.deleteRecursively(symlink); + + assertFalse(Files.exists(symlink, NOFOLLOW_LINKS)); + assertTrue(Files.isDirectory(realSymlinkTarget, NOFOLLOW_LINKS)); + } + } + + /** + * Starts a new task on the given executor that switches (deletes and replaces) a file between + * being a directory and being a symlink. The given {@code file} is the file that should switch + * between being a directory and being a symlink, while the given {@code target} is the target the + * symlink should have. + */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. + private static void startDirectorySymlinkSwitching( + final Path file, final Path target, ExecutorService executor) { + @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored + Future possiblyIgnoredError = + executor.submit( + new Runnable() { + @Override + public void run() { + boolean createSymlink = false; + while (!Thread.interrupted()) { + try { + // trying to switch between a real directory and a symlink (dir -> /a) + if (Files.deleteIfExists(file)) { + if (createSymlink) { + Files.createSymbolicLink(file, target); + } else { + Files.createDirectory(file); + } + createSymlink = !createSymlink; + } + } catch (IOException tolerated) { + // it's expected that some of these will fail + } + + Thread.yield(); + } + } + }); + } + + /** Enum defining the two MoreFiles methods that delete directory contents. */ + private enum DirectoryDeleteMethod { + DELETE_DIRECTORY_CONTENTS { + @Override + public void delete(Path path, RecursiveDeleteOption... options) throws IOException { + MoreFiles.deleteDirectoryContents(path, options); + } + + @Override + public void assertDeleteSucceeded(Path path) throws IOException { + assertEquals( + "contents of directory " + path + " not deleted with delete method " + this, + 0, + MoreFiles.listFiles(path).size()); + } + }, + DELETE_RECURSIVELY { + @Override + public void delete(Path path, RecursiveDeleteOption... options) throws IOException { + MoreFiles.deleteRecursively(path, options); + } + + @Override + public void assertDeleteSucceeded(Path path) throws IOException { + assertFalse("file " + path + " not deleted with delete method " + this, Files.exists(path)); + } + }; + + public abstract void delete(Path path, RecursiveDeleteOption... options) throws IOException; + + public abstract void assertDeleteSucceeded(Path path) throws IOException; + } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } + + private static boolean isAndroid() { + return System.getProperty("java.runtime.name", "").contains("Android"); + } +} diff --git a/android/guava-tests/test/com/google/common/io/MultiInputStreamTest.java b/android/guava-tests/test/com/google/common/io/MultiInputStreamTest.java index 2b68595201af..d9e30620ea62 100644 --- a/android/guava-tests/test/com/google/common/io/MultiInputStreamTest.java +++ b/android/guava-tests/test/com/google/common/io/MultiInputStreamTest.java @@ -23,12 +23,14 @@ import java.io.InputStream; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.NullUnmarked; /** * Test class for {@link MultiInputStream}. * * @author Chris Nokleberg */ +@NullUnmarked public class MultiInputStreamTest extends IoTestCase { public void testJoin() throws Exception { diff --git a/android/guava-tests/test/com/google/common/io/MultiReaderTest.java b/android/guava-tests/test/com/google/common/io/MultiReaderTest.java index 20b4042b6c24..0b335d9da3ac 100644 --- a/android/guava-tests/test/com/google/common/io/MultiReaderTest.java +++ b/android/guava-tests/test/com/google/common/io/MultiReaderTest.java @@ -22,8 +22,12 @@ import java.io.Reader; import java.io.StringReader; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author ricebin */ +/** + * @author ricebin + */ +@NullUnmarked public class MultiReaderTest extends TestCase { public void testOnlyOneOpen() throws Exception { diff --git a/android/guava-tests/test/com/google/common/io/PackageSanityTests.java b/android/guava-tests/test/com/google/common/io/PackageSanityTests.java index 68aad1832019..3beef50c6bae 100644 --- a/android/guava-tests/test/com/google/common/io/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/io/PackageSanityTests.java @@ -16,11 +16,13 @@ package com.google.common.io; -import com.google.common.base.Charsets; +import static java.nio.charset.StandardCharsets.UTF_8; + import com.google.common.testing.AbstractPackageSanityTests; import java.lang.reflect.Method; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.CharsetEncoder; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -28,6 +30,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { setDefault(BaseEncoding.class, BaseEncoding.base64()); @@ -35,6 +38,6 @@ public PackageSanityTests() { setDefault(String.class, "abcd"); setDefault(Method.class, AbstractPackageSanityTests.class.getDeclaredMethods()[0]); setDefault(MapMode.class, MapMode.READ_ONLY); - setDefault(CharsetEncoder.class, Charsets.UTF_8.newEncoder()); + setDefault(CharsetEncoder.class, UTF_8.newEncoder()); } } diff --git a/android/guava-tests/test/com/google/common/io/PatternFilenameFilterTest.java b/android/guava-tests/test/com/google/common/io/PatternFilenameFilterTest.java index 94ab1f01683a..ecd914ae1821 100644 --- a/android/guava-tests/test/com/google/common/io/PatternFilenameFilterTest.java +++ b/android/guava-tests/test/com/google/common/io/PatternFilenameFilterTest.java @@ -16,26 +16,26 @@ package com.google.common.io; +import static org.junit.Assert.assertThrows; + import com.google.common.testing.NullPointerTester; import com.google.common.testing.NullPointerTester.Visibility; import java.io.File; import java.io.FilenameFilter; import java.util.regex.PatternSyntaxException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link PatternFilenameFilter}. * * @author Chris Nokleberg */ +@NullUnmarked public class PatternFilenameFilterTest extends TestCase { public void testSyntaxException() { - try { - new PatternFilenameFilter("("); - fail("expected exception"); - } catch (PatternSyntaxException expected) { - } + assertThrows(PatternSyntaxException.class, () -> new PatternFilenameFilter("(")); } public void testAccept() { diff --git a/android/guava-tests/test/com/google/common/io/RandomAmountInputStream.java b/android/guava-tests/test/com/google/common/io/RandomAmountInputStream.java index d457ec7dcf8c..468db514b4cd 100644 --- a/android/guava-tests/test/com/google/common/io/RandomAmountInputStream.java +++ b/android/guava-tests/test/com/google/common/io/RandomAmountInputStream.java @@ -22,8 +22,10 @@ import java.io.IOException; import java.io.InputStream; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** Returns a random portion of the requested bytes on each call. */ +@NullUnmarked class RandomAmountInputStream extends FilterInputStream { private final Random random; diff --git a/android/guava-tests/test/com/google/common/io/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/io/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..b428136b0265 --- /dev/null +++ b/android/guava-tests/test/com/google/common/io/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.io; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/io/ResourcesTest.java b/android/guava-tests/test/com/google/common/io/ResourcesTest.java index af2abbbc627a..1e8a3026d7c1 100644 --- a/android/guava-tests/test/com/google/common/io/ResourcesTest.java +++ b/android/guava-tests/test/com/google/common/io/ResourcesTest.java @@ -18,13 +18,13 @@ import static com.google.common.base.CharMatcher.whitespace; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertThrows; -import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.List; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Resources}. @@ -40,8 +41,10 @@ * @author Chris Nokleberg */ +@NullUnmarked public class ResourcesTest extends IoTestCase { + @AndroidIncompatible // wouldn't run anyway, but strip the source entirely because of b/230620681 public static TestSuite suite() { TestSuite suite = new TestSuite(); suite.addTest( @@ -58,19 +61,19 @@ public static TestSuite suite() { public void testToString() throws IOException { URL resource = getClass().getResource("testdata/i18n.txt"); - assertEquals(I18N, Resources.toString(resource, Charsets.UTF_8)); - assertThat(Resources.toString(resource, Charsets.US_ASCII)).isNotEqualTo(I18N); + assertEquals(I18N, Resources.toString(resource, UTF_8)); + assertThat(Resources.toString(resource, US_ASCII)).isNotEqualTo(I18N); } - public void testToToByteArray() throws IOException { - byte[] data = Resources.toByteArray(classfile(Resources.class)); - assertEquals(0xCAFEBABE, new DataInputStream(new ByteArrayInputStream(data)).readInt()); + public void testToByteArray() throws IOException { + URL resource = getClass().getResource("testdata/i18n.txt"); + assertThat(Resources.toByteArray(resource)).isEqualTo(I18N.getBytes(UTF_8)); } public void testReadLines() throws IOException { // TODO(chrisn): Check in a better resource URL resource = getClass().getResource("testdata/i18n.txt"); - assertEquals(ImmutableList.of(I18N), Resources.readLines(resource, Charsets.UTF_8)); + assertEquals(ImmutableList.of(I18N), Resources.readLines(resource, UTF_8)); } public void testReadLines_withLineProcessor() throws IOException { @@ -90,8 +93,7 @@ public List getResult() { return collector; } }; - List result = - Resources.readLines(resource, Charsets.US_ASCII, collectAndLowercaseAndTrim); + List result = Resources.readLines(resource, US_ASCII, collectAndLowercaseAndTrim); assertEquals(3600, result.size()); assertEquals("ALICE'S ADVENTURES IN WONDERLAND", result.get(0)); assertEquals("THE END", result.get(result.size() - 1)); @@ -105,12 +107,10 @@ public void testCopyToOutputStream() throws IOException { } public void testGetResource_notFound() { - try { - Resources.getResource("no such resource"); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e).hasMessageThat().isEqualTo("resource no such resource not found."); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, () -> Resources.getResource("no such resource")); + assertThat(e).hasMessageThat().isEqualTo("resource no such resource not found."); } public void testGetResource() { @@ -118,16 +118,15 @@ public void testGetResource() { } public void testGetResource_relativePath_notFound() { - try { - Resources.getResource(getClass(), "com/google/common/io/testdata/i18n.txt"); - fail(); - } catch (IllegalArgumentException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo( - "resource com/google/common/io/testdata/i18n.txt" - + " relative to com.google.common.io.ResourcesTest not found."); - } + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> Resources.getResource(getClass(), "com/google/common/io/testdata/i18n.txt")); + assertThat(e) + .hasMessageThat() + .isEqualTo( + "resource com/google/common/io/testdata/i18n.txt" + + " relative to com.google.common.io.ResourcesTest not found."); } public void testGetResource_relativePath() { @@ -146,11 +145,7 @@ public void testGetResource_contextClassLoader() throws IOException { // First check that we can't find it without setting the context loader. // This is a sanity check that the test doesn't spuriously pass because // the resource is visible to the system class loader. - try { - Resources.getResource(tempFile.getName()); - fail("Should get IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Resources.getResource(tempFile.getName())); // Now set the context loader to one that should find the resource. URL baseUrl = tempFile.getParentFile().toURI().toURL(); @@ -159,8 +154,8 @@ public void testGetResource_contextClassLoader() throws IOException { try { Thread.currentThread().setContextClassLoader(loader); URL url = Resources.getResource(tempFile.getName()); - String text = Resources.toString(url, Charsets.UTF_8); - assertEquals("rud a chur ar an méar fhada\n", text); + String text = Resources.toString(url, UTF_8); + assertEquals("rud a chur ar an méar fhada" + System.lineSeparator(), text); } finally { Thread.currentThread().setContextClassLoader(oldContextLoader); } @@ -171,16 +166,13 @@ public void testGetResource_contextClassLoaderNull() { try { Thread.currentThread().setContextClassLoader(null); assertNotNull(Resources.getResource("com/google/common/io/testdata/i18n.txt")); - try { - Resources.getResource("no such resource"); - fail("Should get IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Resources.getResource("no such resource")); } finally { Thread.currentThread().setContextClassLoader(oldContextLoader); } } + @AndroidIncompatible // .class files aren't available public void testNulls() { new NullPointerTester() .setDefault(URL.class, classfile(ResourcesTest.class)) diff --git a/android/guava-tests/test/com/google/common/io/SourceSinkFactories.java b/android/guava-tests/test/com/google/common/io/SourceSinkFactories.java index e70370e1d69b..1fdcc8c13d21 100644 --- a/android/guava-tests/test/com/google/common/io/SourceSinkFactories.java +++ b/android/guava-tests/test/com/google/common/io/SourceSinkFactories.java @@ -21,8 +21,9 @@ import static com.google.common.io.SourceSinkFactory.ByteSourceFactory; import static com.google.common.io.SourceSinkFactory.CharSinkFactory; import static com.google.common.io.SourceSinkFactory.CharSourceFactory; +import static java.lang.Math.min; +import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Charsets; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -37,13 +38,15 @@ import java.nio.CharBuffer; import java.util.Arrays; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * {@link SourceSinkFactory} implementations. * * @author Colin Decker */ +@NullUnmarked public class SourceSinkFactories { private SourceSinkFactories() {} @@ -74,7 +77,7 @@ public static ByteSinkFactory fileByteSinkFactory() { public static ByteSinkFactory appendingFileByteSinkFactory() { String initialString = IoTestCase.ASCII + IoTestCase.I18N; - return new FileByteSinkFactory(initialString.getBytes(Charsets.UTF_8)); + return new FileByteSinkFactory(initialString.getBytes(UTF_8)); } public static CharSourceFactory fileCharSourceFactory() { @@ -103,12 +106,12 @@ public static ByteSourceFactory asByteSourceFactory(final CharSourceFactory fact return new ByteSourceFactory() { @Override public ByteSource createSource(byte[] data) throws IOException { - return factory.createSource(new String(data, Charsets.UTF_8)).asByteSource(Charsets.UTF_8); + return factory.createSource(new String(data, UTF_8)).asByteSource(UTF_8); } @Override public byte[] getExpected(byte[] data) { - return factory.getExpected(new String(data, Charsets.UTF_8)).getBytes(Charsets.UTF_8); + return factory.getExpected(new String(data, UTF_8)).getBytes(UTF_8); } @Override @@ -123,12 +126,12 @@ public static CharSourceFactory asCharSourceFactory(final ByteSourceFactory fact return new CharSourceFactory() { @Override public CharSource createSource(String string) throws IOException { - return factory.createSource(string.getBytes(Charsets.UTF_8)).asCharSource(Charsets.UTF_8); + return factory.createSource(string.getBytes(UTF_8)).asCharSource(UTF_8); } @Override public String getExpected(String data) { - return new String(factory.getExpected(data.getBytes(Charsets.UTF_8)), Charsets.UTF_8); + return new String(factory.getExpected(data.getBytes(UTF_8)), UTF_8); } @Override @@ -143,12 +146,12 @@ public static CharSinkFactory asCharSinkFactory(final ByteSinkFactory factory) { return new CharSinkFactory() { @Override public CharSink createSink() throws IOException { - return factory.createSink().asCharSink(Charsets.UTF_8); + return factory.createSink().asCharSink(UTF_8); } @Override public String getSinkContents() throws IOException { - return new String(factory.getSinkContents(), Charsets.UTF_8); + return new String(factory.getSinkContents(), UTF_8); } @Override @@ -158,7 +161,7 @@ public String getExpected(String data) { * string to that. */ byte[] factoryExpectedForNothing = factory.getExpected(new byte[0]); - return new String(factoryExpectedForNothing, Charsets.UTF_8) + checkNotNull(data); + return new String(factoryExpectedForNothing, UTF_8) + checkNotNull(data); } @Override @@ -180,8 +183,8 @@ public ByteSource createSource(byte[] bytes) throws IOException { @Override public byte[] getExpected(byte[] bytes) { byte[] baseExpected = factory.getExpected(bytes); - int startOffset = (int) Math.min(off, baseExpected.length); - int actualLen = (int) Math.min(len, baseExpected.length - startOffset); + int startOffset = (int) min(off, baseExpected.length); + int actualLen = (int) min(len, baseExpected.length - startOffset); return Arrays.copyOfRange(baseExpected, startOffset, startOffset + actualLen); } @@ -305,7 +308,7 @@ private static class FileByteSinkFactory extends FileFactory implements ByteSink private final byte[] initialBytes; - private FileByteSinkFactory(@CheckForNull byte[] initialBytes) { + private FileByteSinkFactory(byte @Nullable [] initialBytes) { this.initialBytes = initialBytes; } @@ -356,13 +359,13 @@ private static class FileCharSourceFactory extends FileFactory implements CharSo public CharSource createSource(String string) throws IOException { checkNotNull(string); File file = createFile(); - Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8); + Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF_8); try { writer.write(string); } finally { writer.close(); } - return Files.asCharSource(file, Charsets.UTF_8); + return Files.asCharSource(file, UTF_8); } @Override @@ -375,7 +378,7 @@ private static class FileCharSinkFactory extends FileFactory implements CharSink private final String initialString; - private FileCharSinkFactory(@CheckForNull String initialString) { + private FileCharSinkFactory(@Nullable String initialString) { this.initialString = initialString; } @@ -383,15 +386,15 @@ private FileCharSinkFactory(@CheckForNull String initialString) { public CharSink createSink() throws IOException { File file = createFile(); if (initialString != null) { - Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8); + Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF_8); try { writer.write(initialString); } finally { writer.close(); } - return Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND); + return Files.asCharSink(file, UTF_8, FileWriteMode.APPEND); } - return Files.asCharSink(file, Charsets.UTF_8); + return Files.asCharSink(file, UTF_8); } @Override @@ -403,7 +406,7 @@ public String getExpected(String string) { @Override public String getSinkContents() throws IOException { File file = getFile(); - Reader reader = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8); + Reader reader = new InputStreamReader(new FileInputStream(file), UTF_8); StringBuilder builder = new StringBuilder(); CharBuffer buffer = CharBuffer.allocate(100); while (reader.read(buffer) != -1) { @@ -431,7 +434,7 @@ private static class UrlCharSourceFactory extends FileCharSourceFactory { @Override public CharSource createSource(String string) throws IOException { super.createSource(string); // just ignore returned CharSource - return Resources.asCharSource(getFile().toURI().toURL(), Charsets.UTF_8); + return Resources.asCharSource(getFile().toURI().toURL(), UTF_8); } } } diff --git a/android/guava-tests/test/com/google/common/io/SourceSinkFactory.java b/android/guava-tests/test/com/google/common/io/SourceSinkFactory.java index b8cbc919ecf5..ca8c4ab61ffe 100644 --- a/android/guava-tests/test/com/google/common/io/SourceSinkFactory.java +++ b/android/guava-tests/test/com/google/common/io/SourceSinkFactory.java @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; +import org.jspecify.annotations.NullUnmarked; /** * A test factory for byte or char sources or sinks. In addition to creating sources or sinks, the @@ -32,6 +33,7 @@ * @param the data type (byte[] or String) * @author Colin Decker */ +@NullUnmarked public interface SourceSinkFactory { /** @@ -44,17 +46,17 @@ public interface SourceSinkFactory { T getExpected(T data); /** Cleans up anything created when creating the source or sink. */ - public abstract void tearDown() throws IOException; + void tearDown() throws IOException; /** Factory for byte or char sources. */ - public interface SourceFactory extends SourceSinkFactory { + interface SourceFactory extends SourceSinkFactory { /** Creates a new source containing some or all of the given data. */ S createSource(T data) throws IOException; } /** Factory for byte or char sinks. */ - public interface SinkFactory extends SourceSinkFactory { + interface SinkFactory extends SourceSinkFactory { /** Creates a new sink. */ S createSink() throws IOException; @@ -64,14 +66,14 @@ public interface SinkFactory extends SourceSinkFactory { } /** Factory for {@link ByteSource} instances. */ - public interface ByteSourceFactory extends SourceFactory {} + interface ByteSourceFactory extends SourceFactory {} /** Factory for {@link ByteSink} instances. */ - public interface ByteSinkFactory extends SinkFactory {} + interface ByteSinkFactory extends SinkFactory {} /** Factory for {@link CharSource} instances. */ - public interface CharSourceFactory extends SourceFactory {} + interface CharSourceFactory extends SourceFactory {} /** Factory for {@link CharSink} instances. */ - public interface CharSinkFactory extends SinkFactory {} + interface CharSinkFactory extends SinkFactory {} } diff --git a/android/guava-tests/test/com/google/common/io/SourceSinkTester.java b/android/guava-tests/test/com/google/common/io/SourceSinkTester.java index 9b07355a6939..28bf66f853d3 100644 --- a/android/guava-tests/test/com/google/common/io/SourceSinkTester.java +++ b/android/guava-tests/test/com/google/common/io/SourceSinkTester.java @@ -28,6 +28,7 @@ import java.lang.reflect.Modifier; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * @param the source or sink type @@ -35,7 +36,8 @@ * @param the factory type * @author Colin Decker */ -@AndroidIncompatible // Android doesn't understand tests that lack default constructors. +@AndroidIncompatible // TODO(b/230620681): Make this available (even though we won't run it). +@NullUnmarked public class SourceSinkTester> extends TestCase { static final String LOREM_IPSUM = @@ -69,7 +71,7 @@ public class SourceSinkTester> extends T .put("\\n at EOF", "hello\nworld\n") .put("\\r at EOF", "hello\nworld\r") .put("lorem ipsum", LOREM_IPSUM) - .build(); + .buildOrThrow(); protected final F factory; protected final T data; diff --git a/android/guava-tests/test/com/google/common/io/TestByteSink.java b/android/guava-tests/test/com/google/common/io/TestByteSink.java index b7eeef0d7ac8..756fdd133d9f 100644 --- a/android/guava-tests/test/com/google/common/io/TestByteSink.java +++ b/android/guava-tests/test/com/google/common/io/TestByteSink.java @@ -20,12 +20,14 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import org.jspecify.annotations.NullUnmarked; /** * A byte sink for testing that has configurable behavior. * * @author Colin Decker */ +@NullUnmarked public class TestByteSink extends ByteSink implements TestStreamSupplier { private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); diff --git a/android/guava-tests/test/com/google/common/io/TestByteSource.java b/android/guava-tests/test/com/google/common/io/TestByteSource.java index 54ee982dad5e..1f068c423b23 100644 --- a/android/guava-tests/test/com/google/common/io/TestByteSource.java +++ b/android/guava-tests/test/com/google/common/io/TestByteSource.java @@ -23,12 +23,14 @@ import java.io.IOException; import java.io.InputStream; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * A byte source for testing that has configurable behavior. * * @author Colin Decker */ +@NullUnmarked public final class TestByteSource extends ByteSource implements TestStreamSupplier { private final byte[] bytes; diff --git a/android/guava-tests/test/com/google/common/io/TestCharSink.java b/android/guava-tests/test/com/google/common/io/TestCharSink.java index 6f7686f7671e..e87642cab8a6 100644 --- a/android/guava-tests/test/com/google/common/io/TestCharSink.java +++ b/android/guava-tests/test/com/google/common/io/TestCharSink.java @@ -16,18 +16,20 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.FilterWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; +import org.jspecify.annotations.NullUnmarked; /** * A char sink for testing that has configurable behavior. * * @author Colin Decker */ +@NullUnmarked public class TestCharSink extends CharSink implements TestStreamSupplier { private final TestByteSink byteSink; diff --git a/android/guava-tests/test/com/google/common/io/TestCharSource.java b/android/guava-tests/test/com/google/common/io/TestCharSource.java index 37ee8dcd4e5a..f7c589a33624 100644 --- a/android/guava-tests/test/com/google/common/io/TestCharSource.java +++ b/android/guava-tests/test/com/google/common/io/TestCharSource.java @@ -16,17 +16,19 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import org.jspecify.annotations.NullUnmarked; /** * A char source for testing that has configurable options. * * @author Colin Decker */ +@NullUnmarked public class TestCharSource extends CharSource implements TestStreamSupplier { private final TestByteSource byteSource; diff --git a/android/guava-tests/test/com/google/common/io/TestInputStream.java b/android/guava-tests/test/com/google/common/io/TestInputStream.java index c885cf75f4cf..18fa18e8ba60 100644 --- a/android/guava-tests/test/com/google/common/io/TestInputStream.java +++ b/android/guava-tests/test/com/google/common/io/TestInputStream.java @@ -27,8 +27,12 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; -/** @author Colin Decker */ +/** + * @author Colin Decker + */ +@NullUnmarked public class TestInputStream extends FilterInputStream { private final ImmutableSet options; diff --git a/android/guava-tests/test/com/google/common/io/TestOption.java b/android/guava-tests/test/com/google/common/io/TestOption.java index 5ebd1f1f6c6e..b370476161d4 100644 --- a/android/guava-tests/test/com/google/common/io/TestOption.java +++ b/android/guava-tests/test/com/google/common/io/TestOption.java @@ -16,11 +16,14 @@ package com.google.common.io; +import org.jspecify.annotations.NullUnmarked; + /** * Options controlling the behavior of sources/sinks/streams for testing. * * @author Colin Decker */ +@NullUnmarked public enum TestOption { OPEN_THROWS, SKIP_THROWS, diff --git a/android/guava-tests/test/com/google/common/io/TestOutputStream.java b/android/guava-tests/test/com/google/common/io/TestOutputStream.java index 1a40b837cfed..56847e360b55 100644 --- a/android/guava-tests/test/com/google/common/io/TestOutputStream.java +++ b/android/guava-tests/test/com/google/common/io/TestOutputStream.java @@ -26,8 +26,12 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; -/** @author Colin Decker */ +/** + * @author Colin Decker + */ +@NullUnmarked public class TestOutputStream extends FilterOutputStream { private final ImmutableSet options; diff --git a/android/guava-tests/test/com/google/common/io/TestReader.java b/android/guava-tests/test/com/google/common/io/TestReader.java index d6bf01795069..9f41cbdc8312 100644 --- a/android/guava-tests/test/com/google/common/io/TestReader.java +++ b/android/guava-tests/test/com/google/common/io/TestReader.java @@ -16,15 +16,19 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.ByteArrayInputStream; import java.io.FilterReader; import java.io.IOException; import java.io.InputStreamReader; +import org.jspecify.annotations.NullUnmarked; -/** @author Colin Decker */ +/** + * @author Colin Decker + */ +@NullUnmarked public class TestReader extends FilterReader { private final TestInputStream in; diff --git a/android/guava-tests/test/com/google/common/io/TestStreamSupplier.java b/android/guava-tests/test/com/google/common/io/TestStreamSupplier.java index dcaa20b8944b..e5beeed6f8aa 100644 --- a/android/guava-tests/test/com/google/common/io/TestStreamSupplier.java +++ b/android/guava-tests/test/com/google/common/io/TestStreamSupplier.java @@ -16,6 +16,8 @@ package com.google.common.io; +import org.jspecify.annotations.NullUnmarked; + /** * Interface for a supplier of streams that can report whether a stream was opened and whether that * stream was closed. Intended for use in a test where only a single stream should be opened and @@ -23,6 +25,7 @@ * * @author Colin Decker */ +@NullUnmarked public interface TestStreamSupplier { /** Returns whether or not a new stream was opened. */ diff --git a/android/guava-tests/test/com/google/common/io/TestWriter.java b/android/guava-tests/test/com/google/common/io/TestWriter.java index 34c2690ddccd..50f5fe9608c5 100644 --- a/android/guava-tests/test/com/google/common/io/TestWriter.java +++ b/android/guava-tests/test/com/google/common/io/TestWriter.java @@ -16,14 +16,18 @@ package com.google.common.io; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; import java.io.FilterWriter; import java.io.IOException; import java.io.OutputStreamWriter; +import org.jspecify.annotations.NullUnmarked; -/** @author Colin Decker */ +/** + * @author Colin Decker + */ +@NullUnmarked public class TestWriter extends FilterWriter { private final TestOutputStream out; diff --git a/android/guava-tests/test/com/google/common/math/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/math/AndroidIncompatible.java index b9d81c019087..26a8fb4e42c0 100644 --- a/android/guava-tests/test/com/google/common/math/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/math/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java b/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java index ff86fd52f5f2..8fc54faf15c3 100644 --- a/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java +++ b/android/guava-tests/test/com/google/common/math/BigDecimalMathTest.java @@ -25,8 +25,10 @@ import static java.math.RoundingMode.UNNECESSARY; import static java.math.RoundingMode.UP; import static java.math.RoundingMode.values; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; @@ -34,8 +36,10 @@ import java.util.EnumSet; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; @GwtIncompatible +@NullUnmarked public class BigDecimalMathTest extends TestCase { private static final class RoundToDoubleTester { private final BigDecimal input; @@ -46,6 +50,7 @@ private static final class RoundToDoubleTester { this.input = input; } + @CanIgnoreReturnValue RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { for (RoundingMode mode : modes) { Double previous = expectedValues.put(mode, expectedValue); @@ -56,6 +61,7 @@ RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) return this; } + @CanIgnoreReturnValue public RoundToDoubleTester roundUnnecessaryShouldThrow() { unnecessaryShouldThrow = true; return this; @@ -76,12 +82,10 @@ public void test() { assertWithMessage("Expected roundUnnecessaryShouldThrow call") .that(unnecessaryShouldThrow) .isTrue(); - try { - BigDecimalMath.roundToDouble(input, UNNECESSARY); - fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); - } catch (ArithmeticException expected) { - // expected - } + assertThrows( + "Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)", + ArithmeticException.class, + () -> BigDecimalMath.roundToDouble(input, UNNECESSARY)); } } } @@ -180,13 +184,15 @@ public void testRoundToDouble_twoToThe54PlusFour() { } public void testRoundToDouble_maxDouble() { - BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE); - new RoundToDoubleTester(maxDoubleAsBD).setExpectation(Double.MAX_VALUE, values()).test(); + BigDecimal maxDoubleAsBigDecimal = new BigDecimal(Double.MAX_VALUE); + new RoundToDoubleTester(maxDoubleAsBigDecimal) + .setExpectation(Double.MAX_VALUE, values()) + .test(); } public void testRoundToDouble_maxDoublePlusOne() { - BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE).add(BigDecimal.ONE); - new RoundToDoubleTester(maxDoubleAsBD) + BigDecimal maxDoubleAsBigDecimal = new BigDecimal(Double.MAX_VALUE).add(BigDecimal.ONE); + new RoundToDoubleTester(maxDoubleAsBigDecimal) .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) .roundUnnecessaryShouldThrow() @@ -246,13 +252,15 @@ public void testRoundToDouble_negativeTwoToThe54MinusFour() { } public void testRoundToDouble_minDouble() { - BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE); - new RoundToDoubleTester(minDoubleAsBD).setExpectation(-Double.MAX_VALUE, values()).test(); + BigDecimal minDoubleAsBigDecimal = new BigDecimal(-Double.MAX_VALUE); + new RoundToDoubleTester(minDoubleAsBigDecimal) + .setExpectation(-Double.MAX_VALUE, values()) + .test(); } public void testRoundToDouble_minDoubleMinusOne() { - BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE).subtract(BigDecimal.ONE); - new RoundToDoubleTester(minDoubleAsBD) + BigDecimal minDoubleAsBigDecimal = new BigDecimal(-Double.MAX_VALUE).subtract(BigDecimal.ONE); + new RoundToDoubleTester(minDoubleAsBigDecimal) .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) .roundUnnecessaryShouldThrow() diff --git a/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java b/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java index 170261bb547f..f710ecdaa683 100644 --- a/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java +++ b/android/guava-tests/test/com/google/common/math/BigIntegerMathTest.java @@ -22,6 +22,7 @@ import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; +import static com.google.common.math.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.math.BigInteger.ONE; @@ -40,7 +41,10 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.FormatMethod; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; @@ -48,12 +52,14 @@ import java.util.EnumSet; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for BigIntegerMath. * * @author Louis Wasserman */ +@NullMarked @GwtCompatible(emulated = true) public class BigIntegerMathTest extends TestCase { public void testCeilingPowerOfTwo() { @@ -76,38 +82,24 @@ public void testFloorPowerOfTwo() { public void testCeilingPowerOfTwoNegative() { for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { - try { - BigIntegerMath.ceilingPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.ceilingPowerOfTwo(x)); } } public void testFloorPowerOfTwoNegative() { for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { - try { - BigIntegerMath.floorPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.floorPowerOfTwo(x)); } } public void testCeilingPowerOfTwoZero() { - try { - BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO)); } public void testFloorPowerOfTwoZero() { - try { - BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO)); } @GwtIncompatible // TODO @@ -128,21 +120,14 @@ public void testIsPowerOfTwo() { public void testLog2ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.log2(ZERO, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.log2(ZERO, mode)); } } public void testLog2NegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.log2(BigInteger.valueOf(-1), mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> BigIntegerMath.log2(BigInteger.valueOf(-1), mode)); } } @@ -216,22 +201,15 @@ public void testLog2HalfEven() { @GwtIncompatible // TODO public void testLog10ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.log10(ZERO, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.log10(ZERO, mode)); } } @GwtIncompatible // TODO public void testLog10NegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.log10(BigInteger.valueOf(-1), mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> BigIntegerMath.log10(BigInteger.valueOf(-1), mode)); } } @@ -326,11 +304,8 @@ public void testSqrtZeroAlwaysZero() { @GwtIncompatible // TODO public void testSqrtNegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode)); } } @@ -435,21 +410,15 @@ public void testDivNonZero() { private static final BigInteger BAD_FOR_ANDROID_P = new BigInteger("-9223372036854775808"); private static final BigInteger BAD_FOR_ANDROID_Q = new BigInteger("-1"); - private static final BigInteger BAD_FOR_GINGERBREAD_P = new BigInteger("-9223372036854775808"); - private static final BigInteger BAD_FOR_GINGERBREAD_Q = new BigInteger("-4294967296"); - @GwtIncompatible // TODO @AndroidIncompatible // slow public void testDivNonZeroExact() { - boolean isAndroid = System.getProperty("java.runtime.name").contains("Android"); + String runtimeName = System.getProperty("java.runtime.name"); + boolean isAndroid = runtimeName != null && runtimeName.contains("Android"); for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { if (isAndroid && p.equals(BAD_FOR_ANDROID_P) && q.equals(BAD_FOR_ANDROID_Q)) { - // https://code.google.com/p/android/issues/detail?id=196555 - continue; - } - if (isAndroid && p.equals(BAD_FOR_GINGERBREAD_P) && q.equals(BAD_FOR_GINGERBREAD_Q)) { - // Works fine under Marshmallow, so I haven't filed a bug. + // https://issuetracker.google.com/issues/37074172 continue; } @@ -482,11 +451,7 @@ public void testZeroDivIsAlwaysZero() { public void testDivByZeroAlwaysFails() { for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - BigIntegerMath.divide(p, ZERO, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> BigIntegerMath.divide(p, ZERO, mode)); } } } @@ -504,11 +469,7 @@ public void testFactorial0() { } public void testFactorialNegative() { - try { - BigIntegerMath.factorial(-1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.factorial(-1)); } public void testBinomialSmall() { @@ -534,21 +495,15 @@ private static void runBinomialTest(int firstN, int lastN) { } public void testBinomialOutside() { - for (int n = 0; n <= 50; n++) { - try { - BigIntegerMath.binomial(n, -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - BigIntegerMath.binomial(n, n + 1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + for (int i = 0; i <= 50; i++) { + final int n = i; + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.binomial(n, -1)); + assertThrows(IllegalArgumentException.class, () -> BigIntegerMath.binomial(n, n + 1)); } } - @GwtIncompatible + @J2ktIncompatible + @GwtIncompatible // EnumSet.complementOf private static final class RoundToDoubleTester { private final BigInteger input; private final Map expectedValues = new EnumMap<>(RoundingMode.class); @@ -558,6 +513,7 @@ private static final class RoundToDoubleTester { this.input = input; } + @CanIgnoreReturnValue RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { for (RoundingMode mode : modes) { Double previous = expectedValues.put(mode, expectedValue); @@ -568,6 +524,7 @@ RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) return this; } + @CanIgnoreReturnValue public RoundToDoubleTester roundUnnecessaryShouldThrow() { unnecessaryShouldThrow = true; return this; @@ -588,26 +545,25 @@ public void test() { assertWithMessage("Expected roundUnnecessaryShouldThrow call") .that(unnecessaryShouldThrow) .isTrue(); - try { - BigIntegerMath.roundToDouble(input, UNNECESSARY); - fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); - } catch (ArithmeticException expected) { - // expected - } + assertThrows( + ArithmeticException.class, () -> BigIntegerMath.roundToDouble(input, UNNECESSARY)); } } } + @J2ktIncompatible @GwtIncompatible - public void testRoundToDouble_Zero() { + public void testRoundToDouble_zero() { new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_smallPositive() { new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_maxPreciselyRepresentable() { new RoundToDoubleTester(BigInteger.valueOf(1L << 53)) @@ -615,6 +571,7 @@ public void testRoundToDouble_maxPreciselyRepresentable() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { double twoToThe53 = Math.pow(2, 53); @@ -627,6 +584,7 @@ public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_twoToThe54PlusOne() { double twoToThe54 = Math.pow(2, 54); @@ -639,6 +597,7 @@ public void testRoundToDouble_twoToThe54PlusOne() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_twoToThe54PlusThree() { double twoToThe54 = Math.pow(2, 54); @@ -651,6 +610,7 @@ public void testRoundToDouble_twoToThe54PlusThree() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_twoToThe54PlusFour() { new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4)) @@ -658,23 +618,28 @@ public void testRoundToDouble_twoToThe54PlusFour() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_maxDouble() { - BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); - new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test(); + BigInteger maxDoubleAsBigInteger = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); + new RoundToDoubleTester(maxDoubleAsBigInteger) + .setExpectation(Double.MAX_VALUE, values()) + .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_maxDoublePlusOne() { - BigInteger maxDoubleAsBI = + BigInteger maxDoubleAsBigInteger = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE); - new RoundToDoubleTester(maxDoubleAsBI) + new RoundToDoubleTester(maxDoubleAsBigInteger) .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) .roundUnnecessaryShouldThrow() .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_wayTooBig() { BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT); @@ -685,11 +650,13 @@ public void testRoundToDouble_wayTooBig() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_smallNegative() { new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_minPreciselyRepresentable() { new RoundToDoubleTester(BigInteger.valueOf(-1L << 53)) @@ -697,6 +664,7 @@ public void testRoundToDouble_minPreciselyRepresentable() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_minPreciselyRepresentableMinusOne() { // the representable doubles are -2^53 and -2^53 - 2. @@ -708,6 +676,7 @@ public void testRoundToDouble_minPreciselyRepresentableMinusOne() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusOne() { new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1)) @@ -717,6 +686,7 @@ public void testRoundToDouble_negativeTwoToThe54MinusOne() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusThree() { new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3)) @@ -727,6 +697,7 @@ public void testRoundToDouble_negativeTwoToThe54MinusThree() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusFour() { new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4)) @@ -734,23 +705,28 @@ public void testRoundToDouble_negativeTwoToThe54MinusFour() { .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_minDouble() { - BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); - new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test(); + BigInteger minDoubleAsBigInteger = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); + new RoundToDoubleTester(minDoubleAsBigInteger) + .setExpectation(-Double.MAX_VALUE, values()) + .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_minDoubleMinusOne() { - BigInteger minDoubleAsBI = + BigInteger minDoubleAsBigInteger = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE); - new RoundToDoubleTester(minDoubleAsBI) + new RoundToDoubleTester(minDoubleAsBigInteger) .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) .roundUnnecessaryShouldThrow() .test(); } + @J2ktIncompatible @GwtIncompatible public void testRoundToDouble_negativeWayTooBig() { BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate(); @@ -761,6 +737,7 @@ public void testRoundToDouble_negativeWayTooBig() { .test(); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -771,6 +748,7 @@ public void testNullPointers() { } @GwtIncompatible // String.format + @FormatMethod private static void failFormat(String template, Object... args) { fail(String.format(template, args)); } diff --git a/android/guava-tests/test/com/google/common/math/DoubleMathTest.java b/android/guava-tests/test/com/google/common/math/DoubleMathTest.java index 724ae96d8f1f..27f9f481e1b4 100644 --- a/android/guava-tests/test/com/google/common/math/DoubleMathTest.java +++ b/android/guava-tests/test/com/google/common/math/DoubleMathTest.java @@ -28,6 +28,8 @@ import static com.google.common.math.MathTesting.INTEGRAL_DOUBLE_CANDIDATES; import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_FINITE_DOUBLE_CANDIDATES; +import static com.google.common.math.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.DOWN; import static java.math.RoundingMode.FLOOR; @@ -40,6 +42,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.primitives.Doubles; @@ -48,8 +51,8 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.util.Arrays; -import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code DoubleMath}. @@ -57,6 +60,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class DoubleMathTest extends TestCase { private static final BigDecimal MAX_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MAX_VALUE); @@ -74,8 +78,8 @@ public void testConstantsMaxFactorial() { public void testConstantsEverySixteenthFactorial() { for (int i = 0, n = 0; n <= DoubleMath.MAX_FACTORIAL; i++, n += 16) { - assertEquals( - BigIntegerMath.factorial(n).doubleValue(), DoubleMath.everySixteenthFactorial[i]); + assertThat(DoubleMath.everySixteenthFactorial[i]) + .isEqualTo(BigIntegerMath.factorial(n).doubleValue()); } } @@ -140,38 +144,24 @@ public void testRoundExactIntegralDoubleToInt() { @GwtIncompatible // DoubleMath.roundToInt(double, RoundingMode) public void testRoundExactFractionalDoubleToIntFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { - try { - DoubleMath.roundToInt(d, UNNECESSARY); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToInt(d, UNNECESSARY)); } } @GwtIncompatible // DoubleMath.roundToInt(double, RoundingMode) public void testRoundNaNToIntAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToInt(Double.NaN, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToInt(Double.NaN, mode)); } } @GwtIncompatible // DoubleMath.roundToInt(double, RoundingMode) public void testRoundInfiniteToIntAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToInt(Double.POSITIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } - try { - DoubleMath.roundToInt(Double.NEGATIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, () -> DoubleMath.roundToInt(Double.POSITIVE_INFINITY, mode)); + assertThrows( + ArithmeticException.class, () -> DoubleMath.roundToInt(Double.NEGATIVE_INFINITY, mode)); } } @@ -234,38 +224,24 @@ public void testRoundExactIntegralDoubleToLong() { @GwtIncompatible // DoubleMath.roundToLong(double, RoundingMode) public void testRoundExactFractionalDoubleToLongFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { - try { - DoubleMath.roundToLong(d, UNNECESSARY); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToLong(d, UNNECESSARY)); } } @GwtIncompatible // DoubleMath.roundToLong(double, RoundingMode) public void testRoundNaNToLongAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToLong(Double.NaN, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToLong(Double.NaN, mode)); } } @GwtIncompatible // DoubleMath.roundToLong(double, RoundingMode) public void testRoundInfiniteToLongAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToLong(Double.POSITIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } - try { - DoubleMath.roundToLong(Double.NEGATIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, () -> DoubleMath.roundToLong(Double.POSITIVE_INFINITY, mode)); + assertThrows( + ArithmeticException.class, () -> DoubleMath.roundToLong(Double.NEGATIVE_INFINITY, mode)); } } @@ -300,38 +276,26 @@ public void testRoundExactIntegralDoubleToBigInteger() { @GwtIncompatible // DoubleMath.roundToBigInteger(double, RoundingMode) public void testRoundExactFractionalDoubleToBigIntegerFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { - try { - DoubleMath.roundToBigInteger(d, UNNECESSARY); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToBigInteger(d, UNNECESSARY)); } } @GwtIncompatible // DoubleMath.roundToBigInteger(double, RoundingMode) public void testRoundNaNToBigIntegerAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToBigInteger(Double.NaN, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> DoubleMath.roundToBigInteger(Double.NaN, mode)); } } @GwtIncompatible // DoubleMath.roundToBigInteger(double, RoundingMode) public void testRoundInfiniteToBigIntegerAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - DoubleMath.roundToBigInteger(Double.POSITIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } - try { - DoubleMath.roundToBigInteger(Double.NEGATIVE_INFINITY, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, + () -> DoubleMath.roundToBigInteger(Double.POSITIVE_INFINITY, mode)); + assertThrows( + ArithmeticException.class, + () -> DoubleMath.roundToBigInteger(Double.NEGATIVE_INFINITY, mode)); } } @@ -393,13 +357,8 @@ public void testRoundLog2Half() { for (RoundingMode mode : asList(HALF_EVEN, HALF_UP, HALF_DOWN)) { double x = Math.scalb(Math.sqrt(2) + 0.001, exp); double y = Math.scalb(Math.sqrt(2) - 0.001, exp); - if (exp < 0) { - assertEquals(exp + 1, DoubleMath.log2(x, mode)); - assertEquals(exp, DoubleMath.log2(y, mode)); - } else { - assertEquals(exp + 1, DoubleMath.log2(x, mode)); - assertEquals(exp, DoubleMath.log2(y, mode)); - } + assertEquals(exp + 1, DoubleMath.log2(x, mode)); + assertEquals(exp, DoubleMath.log2(y, mode)); } } } @@ -410,7 +369,7 @@ public void testRoundLog2Exact() { boolean isPowerOfTwo = StrictMath.pow(2.0, DoubleMath.log2(x, FLOOR)) == x; try { int log2 = DoubleMath.log2(x, UNNECESSARY); - assertEquals(x, Math.scalb(1.0, log2)); + assertThat(Math.scalb(1.0, log2)).isEqualTo(x); assertTrue(isPowerOfTwo); } catch (ArithmeticException e) { assertFalse(isPowerOfTwo); @@ -423,11 +382,7 @@ public void testRoundLog2ThrowsOnZerosInfinitiesAndNaN() { for (RoundingMode mode : ALL_ROUNDING_MODES) { for (double d : asList(0.0, -0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) { - try { - DoubleMath.log2(d, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.log2(d, mode)); } } } @@ -436,11 +391,7 @@ public void testRoundLog2ThrowsOnZerosInfinitiesAndNaN() { public void testRoundLog2ThrowsOnNegative() { for (RoundingMode mode : ALL_ROUNDING_MODES) { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { - try { - DoubleMath.log2(-d, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.log2(-d, mode)); } } } @@ -486,17 +437,18 @@ public void testLog2Negative() { } public void testLog2Zero() { - assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(0.0)); - assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(-0.0)); + assertThat(DoubleMath.log2(0.0)).isNegativeInfinity(); + assertThat(DoubleMath.log2(-0.0)).isNegativeInfinity(); } public void testLog2NaNInfinity() { - assertEquals(Double.POSITIVE_INFINITY, DoubleMath.log2(Double.POSITIVE_INFINITY)); + assertThat(DoubleMath.log2(Double.POSITIVE_INFINITY)).isPositiveInfinity(); assertTrue(Double.isNaN(DoubleMath.log2(Double.NEGATIVE_INFINITY))); assertTrue(Double.isNaN(DoubleMath.log2(Double.NaN))); } @GwtIncompatible // StrictMath + @SuppressWarnings("strictfp") // Guava still supports Java 8 private strictfp double trueLog2(double d) { double trueLog2 = StrictMath.log(d) / StrictMath.log(2); // increment until it's >= the true value @@ -540,22 +492,18 @@ public void testFactorial() { for (int i = 0; i <= DoubleMath.MAX_FACTORIAL; i++) { double actual = BigIntegerMath.factorial(i).doubleValue(); double result = DoubleMath.factorial(i); - assertEquals(actual, result, Math.ulp(actual)); + assertThat(result).isWithin(Math.ulp(actual)).of(actual); } } public void testFactorialTooHigh() { - assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 1)); - assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 20)); + assertThat(DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 1)).isPositiveInfinity(); + assertThat(DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 20)).isPositiveInfinity(); } public void testFactorialNegative() { for (int n : NEGATIVE_INTEGER_CANDIDATES) { - try { - DoubleMath.factorial(n); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.factorial(n)); } } @@ -563,17 +511,20 @@ public void testFactorialNegative() { ImmutableList.of(-0.0, 0.0, 1.0, 100.0, 10000.0, Double.MAX_VALUE); private static final Iterable TOLERANCE_CANDIDATES = - Iterables.concat(FINITE_TOLERANCE_CANDIDATES, ImmutableList.of(Double.POSITIVE_INFINITY)); - - private static final List BAD_TOLERANCE_CANDIDATES = - Doubles.asList( - -Double.MIN_VALUE, - -Double.MIN_NORMAL, - -1, - -20, - Double.NaN, - Double.NEGATIVE_INFINITY, - -0.001); + ImmutableList.copyOf( + Iterables.concat( + FINITE_TOLERANCE_CANDIDATES, ImmutableList.of(Double.POSITIVE_INFINITY))); + + private static final ImmutableList BAD_TOLERANCE_CANDIDATES = + ImmutableList.copyOf( + Doubles.asList( + -Double.MIN_VALUE, + -Double.MIN_NORMAL, + -1, + -20, + Double.NaN, + Double.NEGATIVE_INFINITY, + -0.001)); public void testFuzzyEqualsFinite() { for (double a : FINITE_DOUBLE_CANDIDATES) { @@ -642,12 +593,7 @@ public void testFuzzyEqualsZeroTolerance() { public void testFuzzyEqualsBadTolerance() { for (double tolerance : BAD_TOLERANCE_CANDIDATES) { - try { - DoubleMath.fuzzyEquals(1, 2, tolerance); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // success - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.fuzzyEquals(1, 2, tolerance)); } } @@ -701,118 +647,97 @@ private static void runTestFuzzyCompare(int toleranceIndex) { public void testFuzzyCompareBadTolerance() { for (double tolerance : BAD_TOLERANCE_CANDIDATES) { - try { - DoubleMath.fuzzyCompare(1, 2, tolerance); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - // success - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.fuzzyCompare(1, 2, tolerance)); } } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_doubleVarargs() { - assertEquals(-1.375, DoubleMath.mean(1.1, -2.2, 4.4, -8.8), 1.0e-10); - assertEquals(1.1, DoubleMath.mean(1.1), 1.0e-10); - try { - DoubleMath.mean(Double.NaN); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - DoubleMath.mean(Double.POSITIVE_INFINITY); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(1.1, -2.2, 4.4, -8.8)).isWithin(1.0e-10).of(-1.375); + assertThat(DoubleMath.mean(1.1)).isWithin(1.0e-10).of(1.1); + assertThrows(IllegalArgumentException.class, () -> DoubleMath.mean(Double.NaN)); + assertThrows(IllegalArgumentException.class, () -> DoubleMath.mean(Double.POSITIVE_INFINITY)); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_intVarargs() { - assertEquals(-13.75, DoubleMath.mean(11, -22, 44, -88), 1.0e-10); - assertEquals(11.0, DoubleMath.mean(11), 1.0e-10); + assertThat(DoubleMath.mean(11, -22, 44, -88)).isWithin(1.0e-10).of(-13.75); + assertThat(DoubleMath.mean(11)).isWithin(1.0e-10).of(11.0); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_longVarargs() { - assertEquals(-13.75, DoubleMath.mean(11L, -22L, 44L, -88L), 1.0e-10); - assertEquals(11.0, DoubleMath.mean(11L), 1.0e-10); + assertThat(DoubleMath.mean(11L, -22L, 44L, -88L)).isWithin(1.0e-10).of(-13.75); + assertThat(DoubleMath.mean(11L)).isWithin(1.0e-10).of(11.0); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_emptyVarargs() { - try { - DoubleMath.mean(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> DoubleMath.mean()); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_doubleIterable() { - assertEquals(-1.375, DoubleMath.mean(ImmutableList.of(1.1, -2.2, 4.4, -8.8)), 1.0e-10); - assertEquals(1.1, DoubleMath.mean(ImmutableList.of(1.1)), 1.0e-10); - try { - DoubleMath.mean(ImmutableList.of()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - DoubleMath.mean(ImmutableList.of(Double.NaN)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - DoubleMath.mean(ImmutableList.of(Double.POSITIVE_INFINITY)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(ImmutableList.of(1.1, -2.2, 4.4, -8.8))) + .isWithin(1.0e-10) + .of(-1.375); + assertThat(DoubleMath.mean(ImmutableList.of(1.1))).isWithin(1.0e-10).of(1.1); + assertThrows(IllegalArgumentException.class, () -> DoubleMath.mean(ImmutableList.of())); + assertThrows( + IllegalArgumentException.class, () -> DoubleMath.mean(ImmutableList.of(Double.NaN))); + assertThrows( + IllegalArgumentException.class, + () -> DoubleMath.mean(ImmutableList.of(Double.POSITIVE_INFINITY))); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_intIterable() { - assertEquals(-13.75, DoubleMath.mean(ImmutableList.of(11, -22, 44, -88)), 1.0e-10); - assertEquals(11, DoubleMath.mean(ImmutableList.of(11)), 1.0e-10); - try { - DoubleMath.mean(ImmutableList.of()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(ImmutableList.of(11, -22, 44, -88))).isWithin(1.0e-10).of(-13.75); + assertThat(DoubleMath.mean(ImmutableList.of(11))).isWithin(1.0e-10).of(11); + assertThrows( + IllegalArgumentException.class, () -> DoubleMath.mean(ImmutableList.of())); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_longIterable() { - assertEquals(-13.75, DoubleMath.mean(ImmutableList.of(11L, -22L, 44L, -88L)), 1.0e-10); - assertEquals(11, DoubleMath.mean(ImmutableList.of(11L)), 1.0e-10); - try { - DoubleMath.mean(ImmutableList.of()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(ImmutableList.of(11L, -22L, 44L, -88L))) + .isWithin(1.0e-10) + .of(-13.75); + assertThat(DoubleMath.mean(ImmutableList.of(11L))).isWithin(1.0e-10).of(11); + assertThrows(IllegalArgumentException.class, () -> DoubleMath.mean(ImmutableList.of())); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_intIterator() { - assertEquals(-13.75, DoubleMath.mean(ImmutableList.of(11, -22, 44, -88).iterator()), 1.0e-10); - assertEquals(11, DoubleMath.mean(ImmutableList.of(11).iterator()), 1.0e-10); - try { - DoubleMath.mean(ImmutableList.of().iterator()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(ImmutableList.of(11, -22, 44, -88).iterator())) + .isWithin(1.0e-10) + .of(-13.75); + assertThat(DoubleMath.mean(ImmutableList.of(11).iterator())).isWithin(1.0e-10).of(11); + assertThrows( + IllegalArgumentException.class, + () -> DoubleMath.mean(ImmutableList.of().iterator())); } @GwtIncompatible // DoubleMath.mean + @SuppressWarnings("deprecation") // test of deprecated method public void testMean_longIterator() { - assertEquals( - -13.75, DoubleMath.mean(ImmutableList.of(11L, -22L, 44L, -88L).iterator()), 1.0e-10); - assertEquals(11, DoubleMath.mean(ImmutableList.of(11L).iterator()), 1.0e-10); - try { - DoubleMath.mean(ImmutableList.of().iterator()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThat(DoubleMath.mean(ImmutableList.of(11L, -22L, 44L, -88L).iterator())) + .isWithin(1.0e-10) + .of(-13.75); + assertThat(DoubleMath.mean(ImmutableList.of(11L).iterator())).isWithin(1.0e-10).of(11); + assertThrows( + IllegalArgumentException.class, () -> DoubleMath.mean(ImmutableList.of().iterator())); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/math/DoubleUtilsTest.java b/android/guava-tests/test/com/google/common/math/DoubleUtilsTest.java index 2f6263ea9602..94c6c5a625a5 100644 --- a/android/guava-tests/test/com/google/common/math/DoubleUtilsTest.java +++ b/android/guava-tests/test/com/google/common/math/DoubleUtilsTest.java @@ -19,16 +19,20 @@ import static com.google.common.math.MathTesting.ALL_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.FINITE_DOUBLE_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_FINITE_DOUBLE_CANDIDATES; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import java.lang.reflect.Method; import java.math.BigInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link DoubleUtils}. * * @author Louis Wasserman */ +@NullUnmarked public class DoubleUtilsTest extends TestCase { @AndroidIncompatible // no FpUtils and no Math.nextDown in old versions public void testNextDown() throws Exception { @@ -58,18 +62,14 @@ public void testBigToDouble() { } public void testEnsureNonNegative() { - assertEquals(0.0, DoubleUtils.ensureNonNegative(0.0)); + assertThat(DoubleUtils.ensureNonNegative(0.0)).isEqualTo(0.0); for (double positiveValue : POSITIVE_FINITE_DOUBLE_CANDIDATES) { - assertEquals(positiveValue, DoubleUtils.ensureNonNegative(positiveValue)); - assertEquals(0.0, DoubleUtils.ensureNonNegative(-positiveValue)); - } - assertEquals(Double.POSITIVE_INFINITY, DoubleUtils.ensureNonNegative(Double.POSITIVE_INFINITY)); - assertEquals(0.0, DoubleUtils.ensureNonNegative(Double.NEGATIVE_INFINITY)); - try { - DoubleUtils.ensureNonNegative(Double.NaN); - fail("Expected IllegalArgumentException from ensureNonNegative(Double.NaN)"); - } catch (IllegalArgumentException expected) { + assertThat(DoubleUtils.ensureNonNegative(positiveValue)).isEqualTo(positiveValue); + assertThat(DoubleUtils.ensureNonNegative(-positiveValue)).isEqualTo(0.0); } + assertThat(DoubleUtils.ensureNonNegative(Double.POSITIVE_INFINITY)).isPositiveInfinity(); + assertThat(DoubleUtils.ensureNonNegative(Double.NEGATIVE_INFINITY)).isEqualTo(0.0); + assertThrows(IllegalArgumentException.class, () -> DoubleUtils.ensureNonNegative(Double.NaN)); } public void testOneBits() { diff --git a/android/guava-tests/test/com/google/common/math/IntMathTest.java b/android/guava-tests/test/com/google/common/math/IntMathTest.java index 4db13adbddd5..38044cd4a6db 100644 --- a/android/guava-tests/test/com/google/common/math/IntMathTest.java +++ b/android/guava-tests/test/com/google/common/math/IntMathTest.java @@ -23,19 +23,23 @@ import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES; import static com.google.common.math.MathTesting.NONZERO_INTEGER_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES; +import static com.google.common.math.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.math.TestPlatform.intsCanGoOutOfRange; +import static java.lang.Math.min; import static java.math.BigInteger.valueOf; import static java.math.RoundingMode.FLOOR; import static java.math.RoundingMode.UNNECESSARY; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link IntMath}. @@ -43,6 +47,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class IntMathTest extends TestCase { public void testMaxSignedPowerOfTwo() { assertTrue(IntMath.isPowerOfTwo(IntMath.MAX_SIGNED_POWER_OF_TWO)); @@ -58,11 +63,7 @@ public void testCeilingPowerOfTwo() { if (fitsInInt(expectedResult)) { assertEquals(expectedResult.intValue(), IntMath.ceilingPowerOfTwo(x)); } else { - try { - IntMath.ceilingPowerOfTwo(x); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> IntMath.ceilingPowerOfTwo(x)); } } } @@ -76,46 +77,30 @@ public void testFloorPowerOfTwo() { public void testCeilingPowerOfTwoNegative() { for (int x : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.ceilingPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.ceilingPowerOfTwo(x)); } } public void testFloorPowerOfTwoNegative() { for (int x : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.floorPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.floorPowerOfTwo(x)); } } public void testCeilingPowerOfTwoZero() { - try { - IntMath.ceilingPowerOfTwo(0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.ceilingPowerOfTwo(0)); } public void testFloorPowerOfTwoZero() { - try { - IntMath.floorPowerOfTwo(0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.floorPowerOfTwo(0)); } @GwtIncompatible // BigIntegerMath // TODO(cpovirk): GWT-enable BigIntegerMath public void testConstantMaxPowerOfSqrt2Unsigned() { assertEquals( - /*expected=*/ BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Integer.SIZE - 1), FLOOR) + /* expected= */ BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Integer.SIZE - 1), FLOOR) .intValue(), - /*actual=*/ IntMath.MAX_POWER_OF_SQRT2_UNSIGNED); + /* actual= */ IntMath.MAX_POWER_OF_SQRT2_UNSIGNED); } @GwtIncompatible // pow() @@ -139,7 +124,7 @@ public void testConstantsHalfPowersOf10() { for (int i = 0; i < IntMath.halfPowersOf10.length; i++) { assertEquals( IntMath.halfPowersOf10[i], - Math.min( + min( Integer.MAX_VALUE, BigIntegerMath.sqrt(BigInteger.TEN.pow(2 * i + 1), FLOOR).longValue())); } @@ -163,8 +148,8 @@ public void testConstantsBiggestBinomials() { @GwtIncompatible // sqrt public void testPowersSqrtMaxInt() { assertEquals( - /*expected=*/ IntMath.sqrt(Integer.MAX_VALUE, FLOOR), - /*actual=*/ IntMath.FLOOR_SQRT_MAX_INT); + /* expected= */ IntMath.sqrt(Integer.MAX_VALUE, FLOOR), + /* actual= */ IntMath.FLOOR_SQRT_MAX_INT); } @AndroidIncompatible // presumably slow @@ -192,27 +177,19 @@ public void testIsPowerOfTwo() { public void testLog2ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - IntMath.log2(0, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.log2(0, mode)); } } public void testLog2NegativeAlwaysThrows() { for (int x : NEGATIVE_INTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - IntMath.log2(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.log2(x, mode)); } } } - // Relies on the correctness of BigIntegrerMath.log2 for all modes except UNNECESSARY. + // Relies on the correctness of BigIntegerMath.log2 for all modes except UNNECESSARY. public void testLog2MatchesBigInteger() { for (int x : POSITIVE_INTEGER_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { @@ -238,11 +215,7 @@ public void testLog2Exact() { @GwtIncompatible // log10 public void testLog10ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - IntMath.log10(0, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.log10(0, mode)); } } @@ -250,11 +223,7 @@ public void testLog10ZeroAlwaysThrows() { public void testLog10NegativeAlwaysThrows() { for (int x : NEGATIVE_INTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - IntMath.log10(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.log10(x, mode)); } } } @@ -305,11 +274,7 @@ public void testSqrtZeroAlwaysZero() { public void testSqrtNegativeAlwaysThrows() { for (int x : NEGATIVE_INTEGER_CANDIDATES) { for (RoundingMode mode : RoundingMode.values()) { - try { - IntMath.sqrt(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.sqrt(x, mode)); } } } @@ -400,11 +365,7 @@ public void testZeroDivIsAlwaysZero() { public void testDivByZeroAlwaysFails() { for (int p : ALL_INTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - IntMath.divide(p, 0, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> IntMath.divide(p, 0, mode)); } } } @@ -420,22 +381,14 @@ public void testMod() { public void testModNegativeModulusFails() { for (int x : POSITIVE_INTEGER_CANDIDATES) { for (int m : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.mod(x, m); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> IntMath.mod(x, m)); } } } public void testModZeroModulusFails() { for (int x : ALL_INTEGER_CANDIDATES) { - try { - IntMath.mod(x, 0); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> IntMath.mod(x, 0)); } } @@ -457,31 +410,15 @@ public void testGCDZero() { public void testGCDNegativePositiveThrows() { for (int a : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.gcd(a, 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - IntMath.gcd(3, a); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.gcd(a, 3)); + assertThrows(IllegalArgumentException.class, () -> IntMath.gcd(3, a)); } } public void testGCDNegativeZeroThrows() { for (int a : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.gcd(a, 0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - IntMath.gcd(0, a); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.gcd(a, 0)); + assertThrows(IllegalArgumentException.class, () -> IntMath.gcd(0, a)); } } @@ -629,11 +566,7 @@ public void testFactorial() { public void testFactorialNegative() { for (int n : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.factorial(n); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> IntMath.factorial(n)); } } @@ -649,27 +582,16 @@ public void testBinomial() { } public void testBinomialOutside() { - for (int n = 0; n <= 50; n++) { - try { - IntMath.binomial(n, -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - IntMath.binomial(n, n + 1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + for (int i = 0; i <= 50; i++) { + final int n = i; + assertThrows(IllegalArgumentException.class, () -> IntMath.binomial(n, -1)); + assertThrows(IllegalArgumentException.class, () -> IntMath.binomial(n, n + 1)); } } public void testBinomialNegative() { - for (int n : NEGATIVE_INTEGER_CANDIDATES) { - try { - IntMath.binomial(n, 0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + for (final int n : NEGATIVE_INTEGER_CANDIDATES) { + assertThrows(IllegalArgumentException.class, () -> IntMath.binomial(n, 0)); } } @@ -745,6 +667,7 @@ private static boolean fitsInInt(BigInteger big) { return big.bitLength() <= 31; } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/math/LinearTransformationTest.java b/android/guava-tests/test/com/google/common/math/LinearTransformationTest.java index 36d5e84329e3..c18b7d6360a5 100644 --- a/android/guava-tests/test/com/google/common/math/LinearTransformationTest.java +++ b/android/guava-tests/test/com/google/common/math/LinearTransformationTest.java @@ -21,14 +21,17 @@ import static com.google.common.math.StatsTesting.assertLinearTransformationNaN; import static com.google.common.math.StatsTesting.assertVerticalLinearTransformation; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link LinearTransformation}. * * @author Pete Gillin */ +@NullUnmarked public class LinearTransformationTest extends TestCase { private static final double ALLOWED_ERROR = 1e-10; @@ -60,79 +63,59 @@ public void testMappingAnd_vertical() { } public void testMapping_infiniteX1() { - try { - LinearTransformation.mapping(Double.POSITIVE_INFINITY, 3.4); - fail("Expected IllegalArgumentException from mapping(x, y) with infinite x"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(Double.POSITIVE_INFINITY, 3.4)); } public void testMapping_infiniteY1() { - try { - LinearTransformation.mapping(1.2, Double.NEGATIVE_INFINITY); - fail("Expected IllegalArgumentException from mapping(x, y) with infinite y"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, Double.NEGATIVE_INFINITY)); } public void testMappingAnd_infiniteX2() { - try { - LinearTransformation.mapping(1.2, 3.4).and(Double.NEGATIVE_INFINITY, 7.8); - fail("Expected IllegalArgumentException from and(x, y) with infinite x"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, 3.4).and(Double.NEGATIVE_INFINITY, 7.8)); } public void testMappingAnd_infiniteY2() { - try { - LinearTransformation.mapping(1.2, 3.4).and(5.6, Double.POSITIVE_INFINITY); - fail("Expected IllegalArgumentException from and(x, y) with infinite y"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, 3.4).and(5.6, Double.POSITIVE_INFINITY)); } public void testMapping_nanX1() { - try { - LinearTransformation.mapping(Double.NaN, 3.4); - fail("Expected IllegalArgumentException from mapping(x, y) with NaN x"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> LinearTransformation.mapping(Double.NaN, 3.4)); } public void testMapping_nanY1() { - try { - LinearTransformation.mapping(1.2, Double.NaN); - fail("Expected IllegalArgumentException from mapping(x, y) with NaN y"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> LinearTransformation.mapping(1.2, Double.NaN)); } public void testMappingAnd_nanX2() { - try { - LinearTransformation.mapping(1.2, 3.4).and(Double.NaN, 7.8); - fail("Expected IllegalArgumentException from and(x, y) with NaN x"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, 3.4).and(Double.NaN, 7.8)); } public void testMappingAnd_nanY2() { - try { - LinearTransformation.mapping(1.2, 3.4).and(5.6, Double.NaN); - fail("Expected IllegalArgumentException from and(x, y) with NaN y"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, 3.4).and(5.6, Double.NaN)); } public void testMappingAnd_samePointTwice() { - try { - double x = 1.2; - double y = 3.4; - LinearTransformation.mapping(x, y).and(x, y); - fail( - "Expected IllegalArgumentException from mapping(x1, y1).and(x2, y2) with" - + " (x1 == x2) && (y1 == y2)"); - } catch (IllegalArgumentException expected) { - } + double x = 1.2; + double y = 3.4; + assertThrows( + IllegalArgumentException.class, + () -> { + LinearTransformation.mapping(x, y).and(x, y); + }); } public void testMappingWithSlope_regular() { @@ -184,11 +167,9 @@ public void testMappingWithSlope_maximalSlope() { } public void testMappingWithSlope_nanSlope() { - try { - LinearTransformation.mapping(1.2, 3.4).withSlope(Double.NaN); - fail("Expected IllegalArgumentException from withSlope(slope) with NaN slope"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.mapping(1.2, 3.4).withSlope(Double.NaN)); } public void testVertical_regular() { @@ -198,19 +179,13 @@ public void testVertical_regular() { } public void testVertical_infiniteX() { - try { - LinearTransformation.vertical(Double.NEGATIVE_INFINITY); - fail("Expected IllegalArgumentException from vertical(x) with infinite x"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.vertical(Double.NEGATIVE_INFINITY)); } public void testVertical_nanX() { - try { - LinearTransformation.vertical(Double.NaN); - fail("Expected IllegalArgumentException from vertical(x) with NaN x"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LinearTransformation.vertical(Double.NaN)); } public void testHorizontal_regular() { @@ -220,19 +195,13 @@ public void testHorizontal_regular() { } public void testHorizontal_infiniteY() { - try { - LinearTransformation.horizontal(Double.POSITIVE_INFINITY); - fail("Expected IllegalArgumentException from horizontal(y) with infinite y"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> LinearTransformation.horizontal(Double.POSITIVE_INFINITY)); } public void testHorizontal_nanY() { - try { - LinearTransformation.horizontal(Double.NaN); - fail("Expected IllegalArgumentException from horizontal(y) with NaN y"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LinearTransformation.horizontal(Double.NaN)); } public void testForNaN() { diff --git a/android/guava-tests/test/com/google/common/math/LongMathTest.java b/android/guava-tests/test/com/google/common/math/LongMathTest.java index 40c2ac9efa28..e15ee30c5b18 100644 --- a/android/guava-tests/test/com/google/common/math/LongMathTest.java +++ b/android/guava-tests/test/com/google/common/math/LongMathTest.java @@ -25,6 +25,7 @@ import static com.google.common.math.MathTesting.NONZERO_LONG_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_INTEGER_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_LONG_CANDIDATES; +import static com.google.common.math.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.math.BigInteger.valueOf; @@ -33,6 +34,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import java.math.BigDecimal; import java.math.BigInteger; @@ -40,6 +42,7 @@ import java.util.EnumSet; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for LongMath. @@ -47,6 +50,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class LongMathTest extends TestCase { @SuppressWarnings("ConstantOverflow") public void testMaxSignedPowerOfTwo() { @@ -60,11 +64,7 @@ public void testCeilingPowerOfTwo() { if (fitsInLong(expectedResult)) { assertEquals(expectedResult.longValue(), LongMath.ceilingPowerOfTwo(x)); } else { - try { - LongMath.ceilingPowerOfTwo(x); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> LongMath.ceilingPowerOfTwo(x)); } } } @@ -78,46 +78,30 @@ public void testFloorPowerOfTwo() { public void testCeilingPowerOfTwoNegative() { for (long x : NEGATIVE_LONG_CANDIDATES) { - try { - LongMath.ceilingPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.ceilingPowerOfTwo(x)); } } public void testFloorPowerOfTwoNegative() { for (long x : NEGATIVE_LONG_CANDIDATES) { - try { - LongMath.floorPowerOfTwo(x); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.floorPowerOfTwo(x)); } } public void testCeilingPowerOfTwoZero() { - try { - LongMath.ceilingPowerOfTwo(0L); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.ceilingPowerOfTwo(0L)); } public void testFloorPowerOfTwoZero() { - try { - LongMath.floorPowerOfTwo(0L); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.floorPowerOfTwo(0L)); } @GwtIncompatible // TODO public void testConstantMaxPowerOfSqrt2Unsigned() { assertEquals( - /*expected=*/ BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Long.SIZE - 1), FLOOR) + /* expected= */ BigIntegerMath.sqrt(BigInteger.ZERO.setBit(2 * Long.SIZE - 1), FLOOR) .longValue(), - /*actual=*/ LongMath.MAX_POWER_OF_SQRT2_UNSIGNED); + /* actual= */ LongMath.MAX_POWER_OF_SQRT2_UNSIGNED); } @GwtIncompatible // BigIntegerMath // TODO(cpovirk): GWT-enable BigIntegerMath @@ -134,11 +118,8 @@ public void testConstantsPowersOf10() { for (int i = 0; i < LongMath.powersOf10.length; i++) { assertEquals(LongMath.checkedPow(10, i), LongMath.powersOf10[i]); } - try { - LongMath.checkedPow(10, LongMath.powersOf10.length); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, () -> LongMath.checkedPow(10, LongMath.powersOf10.length)); } @GwtIncompatible // TODO @@ -156,8 +137,8 @@ public void testConstantsHalfPowersOf10() { @GwtIncompatible // TODO public void testConstantsSqrtMaxLong() { assertEquals( - /*expected=*/ LongMath.sqrt(Long.MAX_VALUE, FLOOR), - /*actual=*/ LongMath.FLOOR_SQRT_MAX_LONG); + /* expected= */ LongMath.sqrt(Long.MAX_VALUE, FLOOR), + /* actual= */ LongMath.FLOOR_SQRT_MAX_LONG); } @GwtIncompatible // TODO @@ -166,12 +147,11 @@ public void testConstantsFactorials() { for (int i = 0; i < LongMath.factorials.length; i++, expected *= i) { assertEquals(expected, LongMath.factorials[i]); } - try { - LongMath.checkedMultiply( - LongMath.factorials[LongMath.factorials.length - 1], LongMath.factorials.length); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expect) { - } + assertThrows( + ArithmeticException.class, + () -> + LongMath.checkedMultiply( + LongMath.factorials[LongMath.factorials.length - 1], LongMath.factorials.length)); } @GwtIncompatible // TODO @@ -191,25 +171,19 @@ public void testConstantsBiggestBinomials() { @GwtIncompatible // TODO public void testConstantsBiggestSimpleBinomials() { - for (int k = 0; k < LongMath.biggestSimpleBinomials.length; k++) { + for (int i = 0; i < LongMath.biggestSimpleBinomials.length; i++) { + final int k = i; assertTrue(LongMath.biggestSimpleBinomials[k] <= LongMath.biggestBinomials[k]); long unused = simpleBinomial(LongMath.biggestSimpleBinomials[k], k); // mustn't throw if (LongMath.biggestSimpleBinomials[k] < Integer.MAX_VALUE) { // unless all n are fair game with this k - try { - simpleBinomial(LongMath.biggestSimpleBinomials[k] + 1, k); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, + () -> simpleBinomial(LongMath.biggestSimpleBinomials[k] + 1, k)); } } - try { - int k = LongMath.biggestSimpleBinomials.length; - simpleBinomial(2 * k, k); - // 2 * k is the smallest value for which we don't replace k with (n-k). - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + int k = LongMath.biggestSimpleBinomials.length; + assertThrows(ArithmeticException.class, () -> simpleBinomial(2 * k, k)); } @AndroidIncompatible // slow @@ -249,22 +223,14 @@ public void testIsPowerOfTwo() { public void testLog2ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.log2(0L, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.log2(0L, mode)); } } public void testLog2NegativeAlwaysThrows() { for (long x : NEGATIVE_LONG_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.log2(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.log2(x, mode)); } } } @@ -296,11 +262,7 @@ public void testLog2Exact() { @GwtIncompatible // TODO public void testLog10ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.log10(0L, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.log10(0L, mode)); } } @@ -308,11 +270,7 @@ public void testLog10ZeroAlwaysThrows() { public void testLog10NegativeAlwaysThrows() { for (long x : NEGATIVE_LONG_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.log10(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.log10(x, mode)); } } } @@ -356,11 +314,7 @@ public void testLog10TrivialOnPowerOf10() { public void testSqrtNegativeAlwaysThrows() { for (long x : NEGATIVE_LONG_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.sqrt(x, mode); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.sqrt(x, mode)); } } } @@ -402,6 +356,7 @@ public void testPow() { } } + @J2ktIncompatible // J2kt BigDecimal.divide also has the rounding bug @GwtIncompatible // TODO @AndroidIncompatible // TODO(cpovirk): File BigDecimal.divide() rounding bug. public void testDivNonZero() { @@ -452,11 +407,7 @@ public void testZeroDivIsAlwaysZero() { public void testDivByZeroAlwaysFails() { for (long p : ALL_LONG_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { - try { - LongMath.divide(p, 0L, mode); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> LongMath.divide(p, 0L, mode)); } } } @@ -474,11 +425,7 @@ public void testIntMod() { public void testIntModNegativeModulusFails() { for (long x : ALL_LONG_CANDIDATES) { for (int m : NEGATIVE_INTEGER_CANDIDATES) { - try { - LongMath.mod(x, m); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> LongMath.mod(x, m)); } } } @@ -486,11 +433,7 @@ public void testIntModNegativeModulusFails() { @GwtIncompatible // TODO public void testIntModZeroModulusFails() { for (long x : ALL_LONG_CANDIDATES) { - try { - LongMath.mod(x, 0); - fail("Expected AE"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> LongMath.mod(x, 0)); } } @@ -508,11 +451,7 @@ public void testMod() { public void testModNegativeModulusFails() { for (long x : ALL_LONG_CANDIDATES) { for (long m : NEGATIVE_LONG_CANDIDATES) { - try { - LongMath.mod(x, m); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows(ArithmeticException.class, () -> LongMath.mod(x, m)); } } } @@ -537,37 +476,20 @@ public void testGCDZero() { @GwtIncompatible // TODO public void testGCDNegativePositiveThrows() { for (long a : NEGATIVE_LONG_CANDIDATES) { - try { - LongMath.gcd(a, 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - LongMath.gcd(3, a); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.gcd(a, 3)); + assertThrows(IllegalArgumentException.class, () -> LongMath.gcd(3, a)); } } @GwtIncompatible // TODO public void testGCDNegativeZeroThrows() { for (long a : NEGATIVE_LONG_CANDIDATES) { - try { - LongMath.gcd(a, 0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - LongMath.gcd(0, a); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.gcd(a, 0)); + assertThrows(IllegalArgumentException.class, () -> LongMath.gcd(0, a)); } } @AndroidIncompatible // slow - @GwtIncompatible // TODO public void testCheckedAdd() { for (long a : ALL_LONG_CANDIDATES) { for (long b : ALL_LONG_CANDIDATES) { @@ -586,7 +508,6 @@ public void testCheckedAdd() { } } - @GwtIncompatible // TODO @AndroidIncompatible // slow public void testCheckedSubtract() { for (long a : ALL_LONG_CANDIDATES) { @@ -728,11 +649,7 @@ public void testFactorial() { @GwtIncompatible // TODO public void testFactorialNegative() { for (int n : NEGATIVE_INTEGER_CANDIDATES) { - try { - LongMath.factorial(n); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.factorial(n)); } } @@ -760,31 +677,21 @@ public void testBinomial_exhaustiveNotOverflowing() { } public void testBinomialOutside() { - for (int n = 0; n <= 50; n++) { - try { - LongMath.binomial(n, -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - LongMath.binomial(n, n + 1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + for (int i = 0; i <= 50; i++) { + final int n = i; + assertThrows(IllegalArgumentException.class, () -> LongMath.binomial(n, -1)); + assertThrows(IllegalArgumentException.class, () -> LongMath.binomial(n, n + 1)); } } public void testBinomialNegative() { - for (int n : NEGATIVE_INTEGER_CANDIDATES) { - try { - LongMath.binomial(n, 0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + for (final int n : NEGATIVE_INTEGER_CANDIDATES) { + assertThrows(IllegalArgumentException.class, () -> LongMath.binomial(n, 0)); } } + @J2ktIncompatible // slow enough to cause flakiness @GwtIncompatible // far too slow public void testSqrtOfPerfectSquareAsDoubleIsPerfect() { // This takes just over a minute on my machine. @@ -884,6 +791,7 @@ private static long saturatedCast(BigInteger big) { return big.longValue(); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); @@ -946,11 +854,7 @@ public void testIsPrimeOnRandomComposites() { @GwtIncompatible // isPrime is GWT-incompatible public void testIsPrimeThrowsOnNegative() { - try { - LongMath.isPrime(-1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> LongMath.isPrime(-1)); } private static final long[] roundToDoubleTestCandidates = { @@ -989,6 +893,7 @@ public void testIsPrimeThrowsOnNegative() { Long.MIN_VALUE }; + @J2ktIncompatible // EnumSet.complementOf @GwtIncompatible public void testRoundToDoubleAgainstBigInteger() { for (RoundingMode roundingMode : EnumSet.complementOf(EnumSet.of(UNNECESSARY))) { @@ -1012,12 +917,8 @@ public void testRoundToDoubleAgainstBigIntegerUnnecessary() { if (expectedDouble != null) { assertThat(LongMath.roundToDouble(candidate, UNNECESSARY)).isEqualTo(expectedDouble); } else { - try { - LongMath.roundToDouble(candidate, UNNECESSARY); - fail("Expected ArithmeticException on roundToDouble(" + candidate + ", UNNECESSARY)"); - } catch (ArithmeticException expected) { - // success - } + assertThrows( + ArithmeticException.class, () -> LongMath.roundToDouble(candidate, UNNECESSARY)); } } } diff --git a/android/guava-tests/test/com/google/common/math/MathBenchmarking.java b/android/guava-tests/test/com/google/common/math/MathBenchmarking.java index 0c2aecfbba33..6f46dfe6c2a8 100644 --- a/android/guava-tests/test/com/google/common/math/MathBenchmarking.java +++ b/android/guava-tests/test/com/google/common/math/MathBenchmarking.java @@ -18,6 +18,7 @@ import java.math.BigInteger; import java.util.Random; +import org.jspecify.annotations.NullUnmarked; /** * Utilities for benchmarks. @@ -28,6 +29,7 @@ * * @author Louis Wasserman */ +@NullUnmarked final class MathBenchmarking { static final int ARRAY_SIZE = 0x10000; static final int ARRAY_MASK = 0x0ffff; diff --git a/android/guava-tests/test/com/google/common/math/MathPreconditionsTest.java b/android/guava-tests/test/com/google/common/math/MathPreconditionsTest.java index 5dbe017c142d..8f7b396d2c2c 100644 --- a/android/guava-tests/test/com/google/common/math/MathPreconditionsTest.java +++ b/android/guava-tests/test/com/google/common/math/MathPreconditionsTest.java @@ -16,12 +16,14 @@ package com.google.common.math; +import static com.google.common.math.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import java.math.BigInteger; import java.math.RoundingMode; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link MathPreconditions}. @@ -29,14 +31,11 @@ * @author Ben Yu */ @GwtCompatible +@NullUnmarked public class MathPreconditionsTest extends TestCase { public void testCheckPositive_zeroInt() { - try { - MathPreconditions.checkPositive("int", 0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MathPreconditions.checkPositive("int", 0)); } public void testCheckPositive_maxInt() { @@ -44,11 +43,9 @@ public void testCheckPositive_maxInt() { } public void testCheckPositive_minInt() { - try { - MathPreconditions.checkPositive("int", Integer.MIN_VALUE); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkPositive("int", Integer.MIN_VALUE)); } public void testCheckPositive_positiveInt() { @@ -56,19 +53,11 @@ public void testCheckPositive_positiveInt() { } public void testCheckPositive_negativeInt() { - try { - MathPreconditions.checkPositive("int", -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MathPreconditions.checkPositive("int", -1)); } public void testCheckPositive_zeroLong() { - try { - MathPreconditions.checkPositive("long", 0L); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MathPreconditions.checkPositive("long", 0L)); } public void testCheckPositive_maxLong() { @@ -76,11 +65,9 @@ public void testCheckPositive_maxLong() { } public void testCheckPositive_minLong() { - try { - MathPreconditions.checkPositive("long", Long.MIN_VALUE); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkPositive("long", Long.MIN_VALUE)); } public void testCheckPositive_positiveLong() { @@ -88,31 +75,24 @@ public void testCheckPositive_positiveLong() { } public void testCheckPositive_negativeLong() { - try { - MathPreconditions.checkPositive("long", -1L); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> MathPreconditions.checkPositive("long", -1L)); } public void testCheckPositive_zeroBigInteger() { - try { - MathPreconditions.checkPositive("BigInteger", BigInteger.ZERO); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkPositive("BigInteger", BigInteger.ZERO)); } - public void testCheckPositive_postiveBigInteger() { + public void testCheckPositive_positiveBigInteger() { MathPreconditions.checkPositive("BigInteger", BigInteger.ONE); } public void testCheckPositive_negativeBigInteger() { - try { - MathPreconditions.checkPositive("BigInteger", BigInteger.ZERO.negate()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkPositive("BigInteger", BigInteger.ZERO.negate())); } public void testCheckNonNegative_zeroInt() { @@ -124,11 +104,9 @@ public void testCheckNonNegative_maxInt() { } public void testCheckNonNegative_minInt() { - try { - MathPreconditions.checkNonNegative("int", Integer.MIN_VALUE); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkNonNegative("int", Integer.MIN_VALUE)); } public void testCheckNonNegative_positiveInt() { @@ -136,11 +114,8 @@ public void testCheckNonNegative_positiveInt() { } public void testCheckNonNegative_negativeInt() { - try { - MathPreconditions.checkNonNegative("int", -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> MathPreconditions.checkNonNegative("int", -1)); } public void testCheckNonNegative_zeroLong() { @@ -152,11 +127,9 @@ public void testCheckNonNegative_maxLong() { } public void testCheckNonNegative_minLong() { - try { - MathPreconditions.checkNonNegative("long", Long.MIN_VALUE); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkNonNegative("long", Long.MIN_VALUE)); } public void testCheckNonNegative_positiveLong() { @@ -164,11 +137,8 @@ public void testCheckNonNegative_positiveLong() { } public void testCheckNonNegative_negativeLong() { - try { - MathPreconditions.checkNonNegative("int", -1L); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> MathPreconditions.checkNonNegative("int", -1L)); } public void testCheckNonNegative_zeroBigInteger() { @@ -180,11 +150,9 @@ public void testCheckNonNegative_positiveBigInteger() { } public void testCheckNonNegative_negativeBigInteger() { - try { - MathPreconditions.checkNonNegative("int", BigInteger.ONE.negate()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkNonNegative("int", BigInteger.ONE.negate())); } public void testCheckNonNegative_zeroFloat() { @@ -204,19 +172,14 @@ public void testCheckNonNegative_positiveFloat() { } public void testCheckNonNegative_negativeFloat() { - try { - MathPreconditions.checkNonNegative("float", -1f); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> MathPreconditions.checkNonNegative("float", -1f)); } public void testCheckNonNegative_nanFloat() { - try { - MathPreconditions.checkNonNegative("float", Float.NaN); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkNonNegative("float", Float.NaN)); } public void testCheckNonNegative_zeroDouble() { @@ -236,31 +199,23 @@ public void testCheckNonNegative_positiveDouble() { } public void testCheckNonNegative_negativeDouble() { - try { - MathPreconditions.checkNonNegative("double", -1d); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> MathPreconditions.checkNonNegative("double", -1d)); } public void testCheckNonNegative_nanDouble() { - try { - MathPreconditions.checkNonNegative("double", Double.NaN); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> MathPreconditions.checkNonNegative("double", Double.NaN)); } - public void testCheckRoundingUnnnecessary_success() { + public void testCheckRoundingUnnecessary_success() { MathPreconditions.checkRoundingUnnecessary(true); } public void testCheckRoundingUnnecessary_failure() { - try { - MathPreconditions.checkRoundingUnnecessary(false); - fail(); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, () -> MathPreconditions.checkRoundingUnnecessary(false)); } public void testCheckInRange_success() { @@ -268,13 +223,12 @@ public void testCheckInRange_success() { } public void testCheckInRange_failure() { - try { - MathPreconditions.checkInRangeForRoundingInputs(false, 1.0, RoundingMode.UP); - fail(); - } catch (ArithmeticException expected) { - assertThat(expected).hasMessageThat().contains("1.0"); - assertThat(expected).hasMessageThat().contains("UP"); - } + ArithmeticException expected = + assertThrows( + ArithmeticException.class, + () -> MathPreconditions.checkInRangeForRoundingInputs(false, 1.0, RoundingMode.UP)); + assertThat(expected).hasMessageThat().contains("1.0"); + assertThat(expected).hasMessageThat().contains("UP"); } public void testCheckNoOverflow_success() { @@ -282,12 +236,11 @@ public void testCheckNoOverflow_success() { } public void testCheckNoOverflow_failure() { - try { - MathPreconditions.checkNoOverflow(false, "testCheckNoOverflow_failure", 0, 0); - fail(); - } catch (ArithmeticException expected) { - assertThat(expected).hasMessageThat().contains("testCheckNoOverflow_failure(0, 0)"); - } + ArithmeticException expected = + assertThrows( + ArithmeticException.class, + () -> MathPreconditions.checkNoOverflow(false, "testCheckNoOverflow_failure", 0, 0)); + assertThat(expected).hasMessageThat().contains("testCheckNoOverflow_failure(0, 0)"); } public void testNulls() { diff --git a/android/guava-tests/test/com/google/common/math/MathTesting.java b/android/guava-tests/test/com/google/common/math/MathTesting.java index 6b74f11ee89e..7e2fdc4a5737 100644 --- a/android/guava-tests/test/com/google/common/math/MathTesting.java +++ b/android/guava-tests/test/com/google/common/math/MathTesting.java @@ -36,6 +36,7 @@ import com.google.common.primitives.Doubles; import java.math.BigInteger; import java.math.RoundingMode; +import org.jspecify.annotations.NullUnmarked; /** * Exhaustive input sets for every integral type. @@ -43,6 +44,7 @@ * @author Louis Wasserman */ @GwtCompatible +@NullUnmarked public class MathTesting { static final ImmutableSet ALL_ROUNDING_MODES = ImmutableSet.copyOf(RoundingMode.values()); @@ -150,7 +152,7 @@ public BigInteger apply(BigInteger x) { static { ImmutableSet.Builder longValues = ImmutableSet.builder(); - // First of all add all the integer candidate values. + // First add all the integer candidate values. longValues.addAll(Iterables.transform(POSITIVE_INTEGER_CANDIDATES, TO_LONG)); // Add boundary values manually to avoid over/under flow (this covers 2^N for 31 and 63). longValues.add(Integer.MAX_VALUE + 1L, Long.MAX_VALUE - 1L, Long.MAX_VALUE); @@ -185,7 +187,7 @@ public BigInteger apply(BigInteger x) { static { ImmutableSet.Builder bigValues = ImmutableSet.builder(); - // First of all add all the long candidate values. + // First add all the long candidate values. bigValues.addAll(Iterables.transform(POSITIVE_LONG_CANDIDATES, TO_BIGINTEGER)); // Add boundary values manually to avoid over/under flow. bigValues.add(BigInteger.valueOf(Long.MAX_VALUE).add(ONE)); diff --git a/android/guava-tests/test/com/google/common/math/PackageSanityTests.java b/android/guava-tests/test/com/google/common/math/PackageSanityTests.java index 1fc7cc7c23bf..a46527b6c2c2 100644 --- a/android/guava-tests/test/com/google/common/math/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/math/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.math; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -24,6 +25,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { publicApiOnly(); diff --git a/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java b/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java index 34f82e9076ce..326ea0c70ea1 100644 --- a/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java +++ b/android/guava-tests/test/com/google/common/math/PairedStatsAccumulatorTest.java @@ -44,10 +44,12 @@ import static com.google.common.math.StatsTesting.createPartitionedFilledPairedStatsAccumulator; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertThrows; import com.google.common.math.StatsTesting.ManyValues; import java.util.Collections; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link PairedStatsAccumulator}. This tests the stats methods for instances built with @@ -57,6 +59,7 @@ * * @author Pete Gillin */ +@NullUnmarked public class PairedStatsAccumulatorTest extends TestCase { private PairedStatsAccumulator emptyAccumulator; @@ -174,20 +177,12 @@ public void testYStats() { } public void testPopulationCovariance() { - try { - emptyAccumulator.populationCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyPairedStats.populationCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(oneValueAccumulator.populationCovariance()).isWithin(0.0).of(0.0); - assertThat(oneValueAccumulatorByAddAllEmptyPairedStats.populationCovariance()) - .isWithin(0.0) - .of(0.0); + assertThrows(IllegalStateException.class, () -> emptyAccumulator.populationCovariance()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyPairedStats.populationCovariance()); + assertThat(oneValueAccumulator.populationCovariance()).isEqualTo(0.0); + assertThat(oneValueAccumulatorByAddAllEmptyPairedStats.populationCovariance()).isEqualTo(0.0); assertThat(twoValuesAccumulator.populationCovariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_PRODUCTS_OF_DELTAS / 2); @@ -241,26 +236,14 @@ public void testPopulationCovariance() { } public void testSampleCovariance() { - try { - emptyAccumulator.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyPairedStats.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulator.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulatorByAddAllEmptyPairedStats.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.sampleCovariance()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyPairedStats.sampleCovariance()); + assertThrows(IllegalStateException.class, () -> oneValueAccumulator.sampleCovariance()); + assertThrows( + IllegalStateException.class, + () -> oneValueAccumulatorByAddAllEmptyPairedStats.sampleCovariance()); assertThat(twoValuesAccumulator.sampleCovariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_PRODUCTS_OF_DELTAS); @@ -288,26 +271,16 @@ public void testSampleCovariance() { } public void testPearsonsCorrelationCoefficient() { - try { - emptyAccumulator.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyPairedStats.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulator.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulatorByAddAllEmptyPairedStats.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, () -> emptyAccumulator.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyPairedStats.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, () -> oneValueAccumulator.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> oneValueAccumulatorByAddAllEmptyPairedStats.pearsonsCorrelationCoefficient()); assertThat(twoValuesAccumulator.pearsonsCorrelationCoefficient()) .isWithin(ALLOWED_ERROR) .of( @@ -368,59 +341,41 @@ public void testPearsonsCorrelationCoefficient() { .populationStandardDeviation())); } } - try { - horizontalValuesAccumulator.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - horizontalValuesAccumulatorByAddAllPartitionedPairedStats.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - verticalValuesAccumulator.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - verticalValuesAccumulatorByAddAllPartitionedPairedStats.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - constantValuesAccumulator.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - constantValuesAccumulatorByAddAllPartitionedPairedStats.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, + () -> horizontalValuesAccumulator.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> + horizontalValuesAccumulatorByAddAllPartitionedPairedStats + .pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> verticalValuesAccumulator.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> + verticalValuesAccumulatorByAddAllPartitionedPairedStats + .pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> constantValuesAccumulator.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> + constantValuesAccumulatorByAddAllPartitionedPairedStats + .pearsonsCorrelationCoefficient()); } public void testLeastSquaresFit() { - try { - emptyAccumulator.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyPairedStats.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulator.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulatorByAddAllEmptyPairedStats.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.leastSquaresFit()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyPairedStats.leastSquaresFit()); + assertThrows(IllegalStateException.class, () -> oneValueAccumulator.leastSquaresFit()); + assertThrows( + IllegalStateException.class, + () -> oneValueAccumulatorByAddAllEmptyPairedStats.leastSquaresFit()); assertDiagonalLinearTransformation( twoValuesAccumulator.leastSquaresFit(), twoValuesAccumulator.xStats().mean(), @@ -483,15 +438,9 @@ public void testLeastSquaresFit() { assertVerticalLinearTransformation( verticalValuesAccumulatorByAddAllPartitionedPairedStats.leastSquaresFit(), verticalValuesAccumulatorByAddAllPartitionedPairedStats.xStats().mean()); - try { - constantValuesAccumulator.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - constantValuesAccumulatorByAddAllPartitionedPairedStats.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> constantValuesAccumulator.leastSquaresFit()); + assertThrows( + IllegalStateException.class, + () -> constantValuesAccumulatorByAddAllPartitionedPairedStats.leastSquaresFit()); } } diff --git a/android/guava-tests/test/com/google/common/math/PairedStatsTest.java b/android/guava-tests/test/com/google/common/math/PairedStatsTest.java index 7dd9e94d68d7..307ed451fefc 100644 --- a/android/guava-tests/test/com/google/common/math/PairedStatsTest.java +++ b/android/guava-tests/test/com/google/common/math/PairedStatsTest.java @@ -48,6 +48,7 @@ import static com.google.common.math.StatsTesting.createPairedStatsOf; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.math.StatsTesting.ManyValues; @@ -56,6 +57,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link PairedStats}. This tests instances created by {@link @@ -63,6 +65,7 @@ * * @author Pete Gillin */ +@NullUnmarked public class PairedStatsTest extends TestCase { public void testCount() { @@ -87,12 +90,8 @@ public void testYStats() { } public void testPopulationCovariance() { - try { - EMPTY_PAIRED_STATS.populationCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(ONE_VALUE_PAIRED_STATS.populationCovariance()).isWithin(0.0).of(0.0); + assertThrows(IllegalStateException.class, () -> EMPTY_PAIRED_STATS.populationCovariance()); + assertThat(ONE_VALUE_PAIRED_STATS.populationCovariance()).isEqualTo(0.0); assertThat(createSingleStats(Double.POSITIVE_INFINITY, 1.23).populationCovariance()).isNaN(); assertThat(createSingleStats(Double.NEGATIVE_INFINITY, 1.23).populationCovariance()).isNaN(); assertThat(createSingleStats(Double.NaN, 1.23).populationCovariance()).isNaN(); @@ -121,16 +120,8 @@ public void testPopulationCovariance() { } public void testSampleCovariance() { - try { - EMPTY_PAIRED_STATS.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - ONE_VALUE_PAIRED_STATS.sampleCovariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_PAIRED_STATS.sampleCovariance()); + assertThrows(IllegalStateException.class, () -> ONE_VALUE_PAIRED_STATS.sampleCovariance()); assertThat(TWO_VALUES_PAIRED_STATS.sampleCovariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_PRODUCTS_OF_DELTAS); @@ -143,21 +134,13 @@ public void testSampleCovariance() { } public void testPearsonsCorrelationCoefficient() { - try { - EMPTY_PAIRED_STATS.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - ONE_VALUE_PAIRED_STATS.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - createSingleStats(Double.POSITIVE_INFINITY, 1.23).pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, () -> EMPTY_PAIRED_STATS.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, () -> ONE_VALUE_PAIRED_STATS.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> createSingleStats(Double.POSITIVE_INFINITY, 1.23).pearsonsCorrelationCoefficient()); assertThat(TWO_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient()) .isWithin(ALLOWED_ERROR) .of( @@ -183,39 +166,23 @@ public void testPearsonsCorrelationCoefficient() { * stats.yStats().populationStandardDeviation())); } } - try { - HORIZONTAL_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - VERTICAL_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - CONSTANT_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, + () -> HORIZONTAL_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> VERTICAL_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient()); + assertThrows( + IllegalStateException.class, + () -> CONSTANT_VALUES_PAIRED_STATS.pearsonsCorrelationCoefficient()); } public void testLeastSquaresFit() { - try { - EMPTY_PAIRED_STATS.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - ONE_VALUE_PAIRED_STATS.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - createSingleStats(Double.POSITIVE_INFINITY, 1.23).leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_PAIRED_STATS.leastSquaresFit()); + assertThrows(IllegalStateException.class, () -> ONE_VALUE_PAIRED_STATS.leastSquaresFit()); + assertThrows( + IllegalStateException.class, + () -> createSingleStats(Double.POSITIVE_INFINITY, 1.23).leastSquaresFit()); assertDiagonalLinearTransformation( TWO_VALUES_PAIRED_STATS.leastSquaresFit(), TWO_VALUES_PAIRED_STATS.xStats().mean(), @@ -244,11 +211,7 @@ public void testLeastSquaresFit() { assertVerticalLinearTransformation( VERTICAL_VALUES_PAIRED_STATS.leastSquaresFit(), VERTICAL_VALUES_PAIRED_STATS.xStats().mean()); - try { - CONSTANT_VALUES_PAIRED_STATS.leastSquaresFit(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> CONSTANT_VALUES_PAIRED_STATS.leastSquaresFit()); } public void testEqualsAndHashCode() { @@ -303,19 +266,11 @@ public void testToByteArrayAndFromByteArrayRoundTrip() { } public void testFromByteArray_withNullInputThrowsNullPointerException() { - try { - PairedStats.fromByteArray(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> PairedStats.fromByteArray(null)); } public void testFromByteArray_withEmptyArrayInputThrowsIllegalArgumentException() { - try { - PairedStats.fromByteArray(new byte[0]); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> PairedStats.fromByteArray(new byte[0])); } public void testFromByteArray_withTooLongArrayInputThrowsIllegalArgumentException() { @@ -326,11 +281,7 @@ public void testFromByteArray_withTooLongArrayInputThrowsIllegalArgumentExceptio .put(buffer) .putChar('.') .array(); - try { - PairedStats.fromByteArray(tooLongByteArray); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> PairedStats.fromByteArray(tooLongByteArray)); } public void testFromByteArrayWithTooShortArrayInputThrowsIllegalArgumentException() { @@ -340,10 +291,7 @@ public void testFromByteArrayWithTooShortArrayInputThrowsIllegalArgumentExceptio .order(ByteOrder.LITTLE_ENDIAN) .put(buffer, 0, buffer.length - 1) .array(); - try { - PairedStats.fromByteArray(tooShortByteArray); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> PairedStats.fromByteArray(tooShortByteArray)); } } diff --git a/android/guava-tests/test/com/google/common/math/QuantilesAlgorithm.java b/android/guava-tests/test/com/google/common/math/QuantilesAlgorithm.java index 54d310f0d2d6..9a6fe1d39cd4 100644 --- a/android/guava-tests/test/com/google/common/math/QuantilesAlgorithm.java +++ b/android/guava-tests/test/com/google/common/math/QuantilesAlgorithm.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.NullUnmarked; /** * Enumerates several algorithms providing equivalent functionality to {@link Quantiles}, for use in @@ -31,6 +32,7 @@ * @author Pete Gillin * @since 20.0 */ +@NullUnmarked enum QuantilesAlgorithm { /** @@ -53,7 +55,7 @@ Map multipleQuantiles( for (int index : indexes) { builder.put(index, singleQuantileFromSorted(index, scale, dataset)); } - return builder.build(); + return builder.buildOrThrow(); } private double singleQuantileFromSorted(int index, int scale, double[] dataset) { @@ -97,7 +99,7 @@ Map multipleQuantiles( for (int index : indexes) { builder.put(index, singleQuantile(index, scale, dataset)); } - return builder.build(); + return builder.buildOrThrow(); } }, diff --git a/android/guava-tests/test/com/google/common/math/QuantilesAlgorithmTest.java b/android/guava-tests/test/com/google/common/math/QuantilesAlgorithmTest.java index 87a962a61299..6a913ace1e68 100644 --- a/android/guava-tests/test/com/google/common/math/QuantilesAlgorithmTest.java +++ b/android/guava-tests/test/com/google/common/math/QuantilesAlgorithmTest.java @@ -23,22 +23,25 @@ import com.google.common.collect.Sets; import java.util.Map; import java.util.Random; -import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests that the different algorithms benchmarked in {@link QuantilesBenchmark} are actually all * returning more-or-less the same answers. */ +@NullUnmarked public class QuantilesAlgorithmTest extends TestCase { - private static final Random RNG = new Random(82674067L); + private static final Random rng = new Random(82674067L); private static final int DATASET_SIZE = 1000; private static final double ALLOWED_ERROR = 1.0e-10; private static final QuantilesAlgorithm REFERENCE_ALGORITHM = QuantilesAlgorithm.SORTING; - private static final Set NON_REFERENCE_ALGORITHMS = + private static final ImmutableSet NON_REFERENCE_ALGORITHMS = Sets.difference( - ImmutableSet.copyOf(QuantilesAlgorithm.values()), ImmutableSet.of(REFERENCE_ALGORITHM)); + ImmutableSet.copyOf(QuantilesAlgorithm.values()), + ImmutableSet.of(REFERENCE_ALGORITHM)) + .immutableCopy(); private double[] dataset; @@ -46,7 +49,7 @@ public class QuantilesAlgorithmTest extends TestCase { protected void setUp() { dataset = new double[DATASET_SIZE]; for (int i = 0; i < DATASET_SIZE; i++) { - dataset[i] = RNG.nextDouble(); + dataset[i] = rng.nextDouble(); } } diff --git a/android/guava-tests/test/com/google/common/math/QuantilesTest.java b/android/guava-tests/test/com/google/common/math/QuantilesTest.java index 7cbc96a7fc1c..64f74e486c45 100644 --- a/android/guava-tests/test/com/google/common/math/QuantilesTest.java +++ b/android/guava-tests/test/com/google/common/math/QuantilesTest.java @@ -27,6 +27,7 @@ import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.FLOOR; import static java.math.RoundingMode.UNNECESSARY; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -41,14 +42,16 @@ import java.util.Collections; import java.util.List; import java.util.Random; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link Quantiles}. * * @author Pete Gillin */ +@NullUnmarked public class QuantilesTest extends TestCase { /* @@ -92,7 +95,7 @@ public class QuantilesTest extends TestCase { Correspondence.from( new BinaryPredicate() { @Override - public boolean apply(@CheckForNull Double actual, @CheckForNull Double expected) { + public boolean apply(@Nullable Double actual, @Nullable Double expected) { // Test for equality to allow non-finite values to match; otherwise, use the finite // test. return actual.equals(expected) @@ -424,12 +427,12 @@ public void testScale_indexes_varargs_compute_doubleCollection_positiveInfinity( 1, 1.5, 2, 2.0, 8, 5.0, - 9, POSITIVE_INFINITY, // interpolating between 5.0 and POSITIVE_INFNINITY + 9, POSITIVE_INFINITY, // interpolating between 5.0 and POSITIVE_INFINITY 10, POSITIVE_INFINITY); } public void testScale_index_compute_doubleCollection_positiveInfinity() { - // interpolating between 5.0 and POSITIVE_INFNINITY + // interpolating between 5.0 and POSITIVE_INFINITY assertThat(Quantiles.scale(10).index(9).compute(ONE_TO_FIVE_AND_POSITIVE_INFINITY)) .isPositiveInfinity(); } @@ -442,7 +445,7 @@ public void testScale_indexes_varargs_compute_doubleCollection_negativeInfinity( .comparingValuesUsing(QUANTILE_CORRESPONDENCE) .containsExactly( 0, NEGATIVE_INFINITY, - 1, NEGATIVE_INFINITY, // interpolating between NEGATIVE_INFNINITY and 1.0 + 1, NEGATIVE_INFINITY, // interpolating between NEGATIVE_INFINITY and 1.0 2, 1.0, 8, 4.0, 9, 4.5, @@ -450,7 +453,7 @@ public void testScale_indexes_varargs_compute_doubleCollection_negativeInfinity( } public void testScale_index_compute_doubleCollection_negativeInfinity() { - // interpolating between NEGATIVE_INFNINITY and 1.0 + // interpolating between NEGATIVE_INFINITY and 1.0 assertThat(Quantiles.scale(10).index(1).compute(ONE_TO_FIVE_AND_NEGATIVE_INFINITY)) .isNegativeInfinity(); } @@ -540,7 +543,7 @@ public void testPercentiles_index_computeInPlace() { // Assert that the dataset contains the same elements after the in-place computation (although // they may be reordered). We only do this for one index rather than for all indexes, as it is - // quite expensives (quadratic in the size of PSEUDORANDOM_DATASET). + // quite expensive (quadratic in the size of PSEUDORANDOM_DATASET). double[] dataset = Doubles.toArray(PSEUDORANDOM_DATASET); @SuppressWarnings("unused") double actual = percentiles().index(33).computeInPlace(dataset); @@ -557,7 +560,7 @@ public void testPercentiles_indexes_varargsPairs_compute_doubleCollection() { } assertThat(percentiles().indexes(index1, index2).compute(PSEUDORANDOM_DATASET)) .comparingValuesUsing(QUANTILE_CORRESPONDENCE) - .containsExactlyEntriesIn(expectedBuilder.build()); + .containsExactlyEntriesIn(expectedBuilder.buildOrThrow()); } } } @@ -573,7 +576,7 @@ public void testPercentiles_indexes_varargsAll_compute_doubleCollection() { Collections.shuffle(indexes, random); assertThat(percentiles().indexes(Ints.toArray(indexes)).compute(PSEUDORANDOM_DATASET)) .comparingValuesUsing(QUANTILE_CORRESPONDENCE) - .containsExactlyEntriesIn(expectedBuilder.build()); + .containsExactlyEntriesIn(expectedBuilder.buildOrThrow()); } @AndroidIncompatible // slow @@ -589,7 +592,7 @@ public void testPercentiles_indexes_varargsAll_computeInPlace() { Collections.shuffle(indexes, random); assertThat(percentiles().indexes(Ints.toArray(indexes)).computeInPlace(dataset)) .comparingValuesUsing(QUANTILE_CORRESPONDENCE) - .containsExactlyEntriesIn(expectedBuilder.build()); + .containsExactlyEntriesIn(expectedBuilder.buildOrThrow()); assertThat(dataset).usingExactEquality().containsExactlyElementsIn(PSEUDORANDOM_DATASET); } @@ -598,171 +601,103 @@ public void testPercentiles_indexes_varargsAll_computeInPlace() { private static final ImmutableList EMPTY_DATASET = ImmutableList.of(); public void testScale_zero() { - try { - Quantiles.scale(0); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Quantiles.scale(0)); } public void testScale_negative() { - try { - Quantiles.scale(-4); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Quantiles.scale(-4)); } public void testScale_index_negative() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.index(-1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.index(-1)); } public void testScale_index_tooHigh() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.index(11); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.index(11)); } public void testScale_indexes_varargs_negative() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.indexes(1, -1, 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.indexes(1, -1, 3)); } public void testScale_indexes_varargs_tooHigh() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.indexes(1, 11, 3); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.indexes(1, 11, 3)); } public void testScale_indexes_collection_negative() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.indexes(ImmutableList.of(1, -1, 3)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> intermediate.indexes(ImmutableList.of(1, -1, 3))); } public void testScale_indexes_collection_tooHigh() { Quantiles.Scale intermediate = Quantiles.scale(10); - try { - intermediate.indexes(ImmutableList.of(1, 11, 3)); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> intermediate.indexes(ImmutableList.of(1, 11, 3))); } public void testScale_index_compute_doubleCollection_empty() { Quantiles.ScaleAndIndex intermediate = Quantiles.scale(10).index(3); - try { - intermediate.compute(EMPTY_DATASET); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(EMPTY_DATASET)); } public void testScale_index_compute_doubleVarargs_empty() { Quantiles.ScaleAndIndex intermediate = Quantiles.scale(10).index(3); - try { - intermediate.compute(new double[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new double[] {})); } public void testScale_index_compute_longVarargs_empty() { Quantiles.ScaleAndIndex intermediate = Quantiles.scale(10).index(3); - try { - intermediate.compute(new long[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new long[] {})); } public void testScale_index_compute_intVarargs_empty() { Quantiles.ScaleAndIndex intermediate = Quantiles.scale(10).index(3); - try { - intermediate.compute(new int[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new int[] {})); } public void testScale_index_computeInPlace_empty() { Quantiles.ScaleAndIndex intermediate = Quantiles.scale(10).index(3); - try { - intermediate.computeInPlace(new double[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> intermediate.computeInPlace(new double[] {})); } public void testScale_indexes_varargs_compute_doubleCollection_empty() { Quantiles.ScaleAndIndexes intermediate = Quantiles.scale(10).indexes(1, 3, 5); - try { - intermediate.compute(EMPTY_DATASET); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(EMPTY_DATASET)); } public void testScale_indexes_varargs_compute_doubleVarargs_empty() { Quantiles.ScaleAndIndexes intermediate = Quantiles.scale(10).indexes(1, 3, 5); - try { - intermediate.compute(new double[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new double[] {})); } public void testScale_indexes_varargs_compute_longVarargs_empty() { Quantiles.ScaleAndIndexes intermediate = Quantiles.scale(10).indexes(1, 3, 5); - try { - intermediate.compute(new long[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new long[] {})); } public void testScale_indexes_varargs_compute_intVarargs_empty() { Quantiles.ScaleAndIndexes intermediate = Quantiles.scale(10).indexes(1, 3, 5); - try { - intermediate.compute(new int[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> intermediate.compute(new int[] {})); } public void testScale_indexes_varargs_computeInPlace_empty() { Quantiles.ScaleAndIndexes intermediate = Quantiles.scale(10).indexes(1, 3, 5); - try { - intermediate.computeInPlace(new double[] {}); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> intermediate.computeInPlace(new double[] {})); } public void testScale_indexes_indexes_computeInPlace_empty() { int[] emptyIndexes = {}; - try { - Quantiles.ScaleAndIndexes unused = Quantiles.scale(10).indexes(emptyIndexes); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> { + Quantiles.ScaleAndIndexes unused = Quantiles.scale(10).indexes(emptyIndexes); + }); } } diff --git a/android/guava-tests/test/com/google/common/math/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/math/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..28dff1009f28 --- /dev/null +++ b/android/guava-tests/test/com/google/common/math/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.math; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java b/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java index 6926a69c6cf3..7a160315b710 100644 --- a/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java +++ b/android/guava-tests/test/com/google/common/math/StatsAccumulatorTest.java @@ -36,6 +36,11 @@ import static com.google.common.math.StatsTesting.MANY_VALUES_MEAN; import static com.google.common.math.StatsTesting.MANY_VALUES_MIN; import static com.google.common.math.StatsTesting.MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS; +import static com.google.common.math.StatsTesting.MEGA_STREAM_COUNT; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MAX; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MEAN; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MIN; +import static com.google.common.math.StatsTesting.MEGA_STREAM_POPULATION_VARIANCE; import static com.google.common.math.StatsTesting.ONE_VALUE; import static com.google.common.math.StatsTesting.OTHER_ONE_VALUE; import static com.google.common.math.StatsTesting.TWO_VALUES; @@ -43,15 +48,21 @@ import static com.google.common.math.StatsTesting.TWO_VALUES_MEAN; import static com.google.common.math.StatsTesting.TWO_VALUES_MIN; import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS; +import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStream; +import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStreamPart1; +import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStreamPart2; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.lang.Math.sqrt; +import static java.util.stream.DoubleStream.concat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.math.StatsTesting.ManyValues; import com.google.common.primitives.Doubles; import com.google.common.primitives.Longs; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link StatsAccumulator}. This tests the stats methods for instances built with {@link @@ -61,6 +72,7 @@ * * @author Pete Gillin */ +@NullUnmarked public class StatsAccumulatorTest extends TestCase { private StatsAccumulator emptyAccumulator; @@ -188,21 +200,9 @@ public void testCountOverflow_doesNotThrow() { } public void testMean() { - try { - emptyAccumulator.mean(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.mean(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.mean(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.mean()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyIterable.mean()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyStats.mean()); assertThat(oneValueAccumulator.mean()).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(oneValueAccumulatorByAddAllEmptyStats.mean()).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(twoValuesAccumulator.mean()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MEAN); @@ -280,9 +280,9 @@ public void testMean() { } public void testSum() { - assertThat(emptyAccumulator.sum()).isWithin(0.0).of(0.0); - assertThat(emptyAccumulatorByAddAllEmptyIterable.sum()).isWithin(0.0).of(0.0); - assertThat(emptyAccumulatorByAddAllEmptyStats.sum()).isWithin(0.0).of(0.0); + assertThat(emptyAccumulator.sum()).isEqualTo(0.0); + assertThat(emptyAccumulatorByAddAllEmptyIterable.sum()).isEqualTo(0.0); + assertThat(emptyAccumulatorByAddAllEmptyStats.sum()).isEqualTo(0.0); assertThat(oneValueAccumulator.sum()).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(oneValueAccumulatorByAddAllEmptyStats.sum()).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(twoValuesAccumulator.sum()).isWithin(ALLOWED_ERROR).of(TWO_VALUES_MEAN * 2); @@ -322,23 +322,14 @@ public void testSum() { } public void testPopulationVariance() { - try { - emptyAccumulator.populationVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.populationVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.populationVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(oneValueAccumulator.populationVariance()).isWithin(0.0).of(0.0); - assertThat(oneValueAccumulatorByAddAllEmptyStats.populationVariance()).isWithin(0.0).of(0.0); + assertThrows(IllegalStateException.class, () -> emptyAccumulator.populationVariance()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyIterable.populationVariance()); + assertThrows( + IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyStats.populationVariance()); + assertThat(oneValueAccumulator.populationVariance()).isEqualTo(0.0); + assertThat(oneValueAccumulatorByAddAllEmptyStats.populationVariance()).isEqualTo(0.0); assertThat(twoValuesAccumulator.populationVariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS / 2); @@ -405,25 +396,15 @@ public void testPopulationVariance() { } public void testPopulationStandardDeviation() { - try { - emptyAccumulator.populationStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.populationStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.populationStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(oneValueAccumulator.populationStandardDeviation()).isWithin(0.0).of(0.0); - assertThat(oneValueAccumulatorByAddAllEmptyStats.populationStandardDeviation()) - .isWithin(0.0) - .of(0.0); + assertThrows(IllegalStateException.class, () -> emptyAccumulator.populationStandardDeviation()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyIterable.populationStandardDeviation()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyStats.populationStandardDeviation()); + assertThat(oneValueAccumulator.populationStandardDeviation()).isEqualTo(0.0); + assertThat(oneValueAccumulatorByAddAllEmptyStats.populationStandardDeviation()).isEqualTo(0.0); assertThat(twoValuesAccumulator.populationStandardDeviation()) .isWithin(ALLOWED_ERROR) .of(sqrt(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS / 2)); @@ -463,31 +444,14 @@ public void testPopulationStandardDeviation() { } public void testSampleVariance() { - try { - emptyAccumulator.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulator.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulatorByAddAllEmptyStats.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.sampleVariance()); + assertThrows( + IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyIterable.sampleVariance()); + assertThrows( + IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyStats.sampleVariance()); + assertThrows(IllegalStateException.class, () -> oneValueAccumulator.sampleVariance()); + assertThrows( + IllegalStateException.class, () -> oneValueAccumulatorByAddAllEmptyStats.sampleVariance()); assertThat(twoValuesAccumulator.sampleVariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS); @@ -527,31 +491,17 @@ public void testSampleVariance() { } public void testSampleStandardDeviation() { - try { - emptyAccumulator.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulator.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - oneValueAccumulatorByAddAllEmptyStats.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.sampleStandardDeviation()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyIterable.sampleStandardDeviation()); + assertThrows( + IllegalStateException.class, + () -> emptyAccumulatorByAddAllEmptyStats.sampleStandardDeviation()); + assertThrows(IllegalStateException.class, () -> oneValueAccumulator.sampleStandardDeviation()); + assertThrows( + IllegalStateException.class, + () -> oneValueAccumulatorByAddAllEmptyStats.sampleStandardDeviation()); assertThat(twoValuesAccumulator.sampleStandardDeviation()) .isWithin(ALLOWED_ERROR) .of(sqrt(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS)); @@ -591,21 +541,9 @@ public void testSampleStandardDeviation() { } public void testMax() { - try { - emptyAccumulator.max(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.max(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.max(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.max()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyIterable.max()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyStats.max()); assertThat(oneValueAccumulator.max()).isEqualTo(ONE_VALUE); assertThat(oneValueAccumulatorByAddAllEmptyStats.max()).isEqualTo(ONE_VALUE); assertThat(twoValuesAccumulator.max()).isEqualTo(TWO_VALUES_MAX); @@ -650,21 +588,9 @@ public void testMax() { } public void testMin() { - try { - emptyAccumulator.min(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyIterable.min(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - emptyAccumulatorByAddAllEmptyStats.min(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> emptyAccumulator.min()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyIterable.min()); + assertThrows(IllegalStateException.class, () -> emptyAccumulatorByAddAllEmptyStats.min()); assertThat(oneValueAccumulator.min()).isEqualTo(ONE_VALUE); assertThat(oneValueAccumulatorByAddAllEmptyStats.min()).isEqualTo(ONE_VALUE); assertThat(twoValuesAccumulator.min()).isEqualTo(TWO_VALUES_MIN); @@ -707,4 +633,51 @@ public void testMin() { assertThat(longManyValuesAccumulatorByAddAllIterator.min()).isEqualTo(LONG_MANY_VALUES_MIN); assertThat(longManyValuesAccumulatorByAddAllVarargs.min()).isEqualTo(LONG_MANY_VALUES_MIN); } + + public void testVerifyMegaStreamHalves() { + assertThat( + concat(megaPrimitiveDoubleStreamPart1(), megaPrimitiveDoubleStreamPart2()) + .sorted() + .toArray()) + .isEqualTo(megaPrimitiveDoubleStream().toArray()); + } + + public void testAddAllPrimitiveDoubleStream() { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(megaPrimitiveDoubleStreamPart1()); + accumulator.addAll(megaPrimitiveDoubleStreamPart2()); + assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(accumulator.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX); + } + + public void testAddAllPrimitiveIntStream() { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(megaPrimitiveDoubleStreamPart1().mapToInt(x -> (int) x)); + accumulator.addAll(megaPrimitiveDoubleStreamPart2().mapToInt(x -> (int) x)); + assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(accumulator.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX); + } + + public void testAddAllPrimitiveLongStream() { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(megaPrimitiveDoubleStreamPart1().mapToLong(x -> (long) x)); + accumulator.addAll(megaPrimitiveDoubleStreamPart2().mapToLong(x -> (long) x)); + assertThat(accumulator.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(accumulator.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(accumulator.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(accumulator.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(accumulator.max()).isEqualTo(MEGA_STREAM_MAX); + } } diff --git a/android/guava-tests/test/com/google/common/math/StatsTest.java b/android/guava-tests/test/com/google/common/math/StatsTest.java index 76de5b55c739..252066589ce1 100644 --- a/android/guava-tests/test/com/google/common/math/StatsTest.java +++ b/android/guava-tests/test/com/google/common/math/StatsTest.java @@ -16,6 +16,7 @@ package com.google.common.math; +import static com.google.common.math.Stats.toStats; import static com.google.common.math.StatsTesting.ALLOWED_ERROR; import static com.google.common.math.StatsTesting.ALL_MANY_VALUES; import static com.google.common.math.StatsTesting.ALL_STATS; @@ -55,6 +56,11 @@ import static com.google.common.math.StatsTesting.MANY_VALUES_STATS_SNAPSHOT; import static com.google.common.math.StatsTesting.MANY_VALUES_STATS_VARARGS; import static com.google.common.math.StatsTesting.MANY_VALUES_SUM_OF_SQUARES_OF_DELTAS; +import static com.google.common.math.StatsTesting.MEGA_STREAM_COUNT; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MAX; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MEAN; +import static com.google.common.math.StatsTesting.MEGA_STREAM_MIN; +import static com.google.common.math.StatsTesting.MEGA_STREAM_POPULATION_VARIANCE; import static com.google.common.math.StatsTesting.ONE_VALUE; import static com.google.common.math.StatsTesting.ONE_VALUE_STATS; import static com.google.common.math.StatsTesting.TWO_VALUES; @@ -63,12 +69,15 @@ import static com.google.common.math.StatsTesting.TWO_VALUES_MIN; import static com.google.common.math.StatsTesting.TWO_VALUES_STATS; import static com.google.common.math.StatsTesting.TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS; +import static com.google.common.math.StatsTesting.megaPrimitiveDoubleStream; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; import static java.lang.Double.POSITIVE_INFINITY; import static java.lang.Math.sqrt; +import static java.util.Arrays.stream; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.math.StatsTesting.ManyValues; @@ -76,9 +85,12 @@ import com.google.common.primitives.Longs; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; +import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.DoubleSummaryStatistics; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Stats}. This tests instances created by both {@link Stats#of} and {@link @@ -86,6 +98,7 @@ * * @author Pete Gillin */ +@NullUnmarked public class StatsTest extends TestCase { public void testCount() { @@ -104,16 +117,8 @@ public void testCount() { } public void testMean() { - try { - EMPTY_STATS_VARARGS.mean(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.mean(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.mean()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.mean()); assertThat(ONE_VALUE_STATS.mean()).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(Stats.of(POSITIVE_INFINITY).mean()).isPositiveInfinity(); assertThat(Stats.of(NEGATIVE_INFINITY).mean()).isNegativeInfinity(); @@ -196,17 +201,9 @@ public void testSum() { } public void testPopulationVariance() { - try { - EMPTY_STATS_VARARGS.populationVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.populationVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(ONE_VALUE_STATS.populationVariance()).isWithin(0.0).of(0.0); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.populationVariance()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.populationVariance()); + assertThat(ONE_VALUE_STATS.populationVariance()).isEqualTo(0.0); assertThat(Stats.of(POSITIVE_INFINITY).populationVariance()).isNaN(); assertThat(Stats.of(NEGATIVE_INFINITY).populationVariance()).isNaN(); assertThat(Stats.of(NaN).populationVariance()).isNaN(); @@ -256,17 +253,11 @@ public void testPopulationVariance() { } public void testPopulationStandardDeviation() { - try { - EMPTY_STATS_VARARGS.populationStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.populationStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - assertThat(ONE_VALUE_STATS.populationStandardDeviation()).isWithin(0.0).of(0.0); + assertThrows( + IllegalStateException.class, () -> EMPTY_STATS_VARARGS.populationStandardDeviation()); + assertThrows( + IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.populationStandardDeviation()); + assertThat(ONE_VALUE_STATS.populationStandardDeviation()).isEqualTo(0.0); assertThat(TWO_VALUES_STATS.populationStandardDeviation()) .isWithin(ALLOWED_ERROR) .of(sqrt(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS / 2)); @@ -297,21 +288,9 @@ public void testPopulationStandardDeviation() { } public void testSampleVariance() { - try { - EMPTY_STATS_VARARGS.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - ONE_VALUE_STATS.sampleVariance(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.sampleVariance()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.sampleVariance()); + assertThrows(IllegalStateException.class, () -> ONE_VALUE_STATS.sampleVariance()); assertThat(TWO_VALUES_STATS.sampleVariance()) .isWithin(ALLOWED_ERROR) .of(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS); @@ -342,21 +321,9 @@ public void testSampleVariance() { } public void testSampleStandardDeviation() { - try { - EMPTY_STATS_VARARGS.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - ONE_VALUE_STATS.sampleStandardDeviation(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.sampleStandardDeviation()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.sampleStandardDeviation()); + assertThrows(IllegalStateException.class, () -> ONE_VALUE_STATS.sampleStandardDeviation()); assertThat(TWO_VALUES_STATS.sampleStandardDeviation()) .isWithin(ALLOWED_ERROR) .of(sqrt(TWO_VALUES_SUM_OF_SQUARES_OF_DELTAS)); @@ -387,16 +354,8 @@ public void testSampleStandardDeviation() { } public void testMax() { - try { - EMPTY_STATS_VARARGS.max(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.max(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.max()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.max()); assertThat(ONE_VALUE_STATS.max()).isEqualTo(ONE_VALUE); assertThat(Stats.of(POSITIVE_INFINITY).max()).isPositiveInfinity(); assertThat(Stats.of(NEGATIVE_INFINITY).max()).isNegativeInfinity(); @@ -424,16 +383,8 @@ public void testMax() { } public void testMin() { - try { - EMPTY_STATS_VARARGS.min(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } - try { - EMPTY_STATS_ITERABLE.min(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_VARARGS.min()); + assertThrows(IllegalStateException.class, () -> EMPTY_STATS_ITERABLE.min()); assertThat(ONE_VALUE_STATS.min()).isEqualTo(ONE_VALUE); assertThat(Stats.of(POSITIVE_INFINITY).min()).isPositiveInfinity(); assertThat(Stats.of(NEGATIVE_INFINITY).min()).isNegativeInfinity(); @@ -462,6 +413,39 @@ public void testMin() { assertThat(LONG_MANY_VALUES_STATS_SNAPSHOT.min()).isEqualTo(LONG_MANY_VALUES_MIN); } + public void testOfPrimitiveDoubleStream() { + Stats stats = Stats.of(megaPrimitiveDoubleStream()); + assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(stats.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX); + } + + public void testOfPrimitiveIntStream() { + Stats stats = Stats.of(megaPrimitiveDoubleStream().mapToInt(x -> (int) x)); + assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(stats.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX); + } + + public void testOfPrimitiveLongStream() { + Stats stats = Stats.of(megaPrimitiveDoubleStream().mapToLong(x -> (long) x)); + assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(stats.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX); + } + public void testEqualsAndHashCode() { new EqualsTester() .addEqualityGroup( @@ -505,16 +489,8 @@ public void testToString() { } public void testMeanOf() { - try { - Stats.meanOf(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - try { - Stats.meanOf(ImmutableList.of()); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Stats.meanOf()); + assertThrows(IllegalArgumentException.class, () -> Stats.meanOf(ImmutableList.of())); assertThat(Stats.meanOf(ONE_VALUE)).isWithin(ALLOWED_ERROR).of(ONE_VALUE); assertThat(Stats.meanOf(POSITIVE_INFINITY)).isPositiveInfinity(); assertThat(Stats.meanOf(NEGATIVE_INFINITY)).isNegativeInfinity(); @@ -565,19 +541,11 @@ public void testToByteArrayAndFromByteArrayRoundTrip() { } public void testFromByteArray_withNullInputThrowsNullPointerException() { - try { - Stats.fromByteArray(null); - fail("Expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Stats.fromByteArray(null)); } public void testFromByteArray_withEmptyArrayInputThrowsIllegalArgumentException() { - try { - Stats.fromByteArray(new byte[0]); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Stats.fromByteArray(new byte[0])); } public void testFromByteArray_withTooLongArrayInputThrowsIllegalArgumentException() { @@ -588,11 +556,7 @@ public void testFromByteArray_withTooLongArrayInputThrowsIllegalArgumentExceptio .put(buffer) .putChar('.') .array(); - try { - Stats.fromByteArray(tooLongByteArray); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Stats.fromByteArray(tooLongByteArray)); } public void testFromByteArrayWithTooShortArrayInputThrowsIllegalArgumentException() { @@ -602,10 +566,64 @@ public void testFromByteArrayWithTooShortArrayInputThrowsIllegalArgumentExceptio .order(ByteOrder.LITTLE_ENDIAN) .put(buffer, 0, Stats.BYTES - 1) .array(); - try { - Stats.fromByteArray(tooShortByteArray); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { + assertThrows(IllegalArgumentException.class, () -> Stats.fromByteArray(tooShortByteArray)); + } + + public void testEquivalentStreams() { + // For datasets of many double values created from an array, we test many combinations of finite + // and non-finite values: + for (ManyValues values : ALL_MANY_VALUES) { + double[] array = values.asArray(); + Stats stats = Stats.of(array); + // instance methods on Stats vs on instance methods on DoubleStream + assertThat(stats.count()).isEqualTo(stream(array).count()); + assertEquivalent(stats.mean(), stream(array).average().getAsDouble()); + assertEquivalent(stats.sum(), stream(array).sum()); + assertEquivalent(stats.max(), stream(array).max().getAsDouble()); + assertEquivalent(stats.min(), stream(array).min().getAsDouble()); + // static method on Stats vs on instance method on DoubleStream + assertEquivalent(Stats.meanOf(array), stream(array).average().getAsDouble()); + // instance methods on Stats vs instance methods on DoubleSummaryStatistics + DoubleSummaryStatistics streamStats = stream(array).summaryStatistics(); + assertThat(stats.count()).isEqualTo(streamStats.getCount()); + assertEquivalent(stats.mean(), streamStats.getAverage()); + assertEquivalent(stats.sum(), streamStats.getSum()); + assertEquivalent(stats.max(), streamStats.getMax()); + assertEquivalent(stats.min(), streamStats.getMin()); + } + } + + private static void assertEquivalent(double actual, double expected) { + if (expected == POSITIVE_INFINITY) { + assertThat(actual).isPositiveInfinity(); + } else if (expected == NEGATIVE_INFINITY) { + assertThat(actual).isNegativeInfinity(); + } else if (Double.isNaN(expected)) { + assertThat(actual).isNaN(); + } else { + assertThat(actual).isWithin(ALLOWED_ERROR).of(expected); } } + + public void testBoxedDoubleStreamToStats() { + Stats stats = megaPrimitiveDoubleStream().boxed().collect(toStats()); + assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(stats.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX); + } + + public void testBoxedBigDecimalStreamToStats() { + Stats stats = megaPrimitiveDoubleStream().mapToObj(BigDecimal::valueOf).collect(toStats()); + assertThat(stats.count()).isEqualTo(MEGA_STREAM_COUNT); + assertThat(stats.mean()).isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT).of(MEGA_STREAM_MEAN); + assertThat(stats.populationVariance()) + .isWithin(ALLOWED_ERROR * MEGA_STREAM_COUNT) + .of(MEGA_STREAM_POPULATION_VARIANCE); + assertThat(stats.min()).isEqualTo(MEGA_STREAM_MIN); + assertThat(stats.max()).isEqualTo(MEGA_STREAM_MAX); + } } diff --git a/android/guava-tests/test/com/google/common/math/StatsTesting.java b/android/guava-tests/test/com/google/common/math/StatsTesting.java index 12689d3e345e..8203efc81cfd 100644 --- a/android/guava-tests/test/com/google/common/math/StatsTesting.java +++ b/android/guava-tests/test/com/google/common/math/StatsTesting.java @@ -17,6 +17,7 @@ package com.google.common.math; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; @@ -31,6 +32,8 @@ import com.google.common.primitives.Ints; import java.math.BigInteger; import java.util.List; +import java.util.stream.DoubleStream; +import org.jspecify.annotations.NullUnmarked; /** * Inputs, expected outputs, and helper methods for tests of {@link StatsAccumulator}, {@link @@ -38,9 +41,10 @@ * * @author Pete Gillin */ +@NullUnmarked class StatsTesting { - - static final double ALLOWED_ERROR = 1e-10; + // TODO(cpovirk): Convince myself that this larger error makes sense. + static final double ALLOWED_ERROR = isAndroid() ? .25 : 1e-10; // Inputs and their statistics: @@ -63,7 +67,7 @@ class StatsTesting { + (-56.78 - TWO_VALUES_MEAN) * (-789.012 - OTHER_TWO_VALUES_MEAN); /** - * Helper class for testing with non-finite values. {@link #ALL_MANY_VALUES} gives a number + * Helper class for testing with non-finite values. {@link #ALL_MANY_VALUES} gives a number of * instances with many combinations of finite and non-finite values. All have {@link * #MANY_VALUES_COUNT} values. If all the values are finite then the mean is {@link * #MANY_VALUES_MEAN} and the sum-of-squares-of-deltas is {@link @@ -211,6 +215,37 @@ private static ImmutableList createAll() { .divide(BigInteger.valueOf(16L)) .doubleValue(); + /** + * Returns a stream of a million primitive doubles. The stream is parallel, which should cause + * {@code collect} calls to run in multithreaded mode, so testing the combiner as well as the + * supplier and accumulator. + */ + static DoubleStream megaPrimitiveDoubleStream() { + return DoubleStream.iterate(0.0, x -> x + 1.0).limit(MEGA_STREAM_COUNT).parallel(); + } + + /** Returns a stream containing half the values from {@link #megaPrimitiveDoubleStream}. */ + static DoubleStream megaPrimitiveDoubleStreamPart1() { + return DoubleStream.iterate(0.0, x -> x + 2.0).limit(MEGA_STREAM_COUNT / 2).parallel(); + } + + /** + * Returns a stream containing the values from {@link #megaPrimitiveDoubleStream} not in {@link + * #megaPrimitiveDoubleStreamPart1()}. + */ + static DoubleStream megaPrimitiveDoubleStreamPart2() { + return DoubleStream.iterate(MEGA_STREAM_COUNT - 1.0, x -> x - 2.0) + .limit(MEGA_STREAM_COUNT / 2) + .parallel(); + } + + static final long MEGA_STREAM_COUNT = isAndroid() ? 100 : 1_000_000; + static final double MEGA_STREAM_MIN = 0.0; + static final double MEGA_STREAM_MAX = MEGA_STREAM_COUNT - 1; + static final double MEGA_STREAM_MEAN = MEGA_STREAM_MAX / 2; + static final double MEGA_STREAM_POPULATION_VARIANCE = + (MEGA_STREAM_COUNT - 1) * (MEGA_STREAM_COUNT + 1) / 12.0; + // Stats instances: static final Stats EMPTY_STATS_VARARGS = Stats.of(); @@ -353,7 +388,7 @@ static void assertStatsApproxEqual(Stats expectedStats, Stats actualStats) { } } else if (expectedStats.count() == 1) { assertThat(actualStats.mean()).isWithin(ALLOWED_ERROR).of(expectedStats.mean()); - assertThat(actualStats.populationVariance()).isWithin(0.0).of(0.0); + assertThat(actualStats.populationVariance()).isEqualTo(0.0); assertThat(actualStats.min()).isWithin(ALLOWED_ERROR).of(expectedStats.min()); assertThat(actualStats.max()).isWithin(ALLOWED_ERROR).of(expectedStats.max()); } else { @@ -367,7 +402,7 @@ static void assertStatsApproxEqual(Stats expectedStats, Stats actualStats) { } /** - * Asserts that {@code transformation} is diagonal (i.e. neither horizontal or vertical) and + * Asserts that {@code transformation} is diagonal (i.e. neither horizontal nor vertical) and * passes through both {@code (x1, y1)} and {@code (x1 + xDelta, y1 + yDelta)}. Includes * assertions about all the public instance methods of {@link LinearTransformation} (on both * {@code transformation} and its inverse). Since the transformation is expected to be diagonal, @@ -501,5 +536,9 @@ static PairedStatsAccumulator createPartitionedFilledPairedStatsAccumulator( return accumulator; } + private static boolean isAndroid() { + return checkNotNull(System.getProperty("java.runtime.name", "")).contains("Android"); + } + private StatsTesting() {} } diff --git a/android/guava-tests/test/com/google/common/math/TestPlatform.java b/android/guava-tests/test/com/google/common/math/TestPlatform.java index 03eb2ec0da3c..ba05ce7294bf 100644 --- a/android/guava-tests/test/com/google/common/math/TestPlatform.java +++ b/android/guava-tests/test/com/google/common/math/TestPlatform.java @@ -17,15 +17,19 @@ package com.google.common.math; import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullUnmarked; -/** @author Chris Povirk */ +/** + * @author Chris Povirk + */ @GwtCompatible(emulated = true) +@NullUnmarked class TestPlatform { static boolean intsCanGoOutOfRange() { return false; } static boolean isAndroid() { - return System.getProperty("java.runtime.name").contains("Android"); + return System.getProperty("java.runtime.name", "").contains("Android"); } } diff --git a/android/guava-tests/test/com/google/common/net/HostAndPortTest.java b/android/guava-tests/test/com/google/common/net/HostAndPortTest.java index 65e80962bafa..58a735fc28b4 100644 --- a/android/guava-tests/test/com/google/common/net/HostAndPortTest.java +++ b/android/guava-tests/test/com/google/common/net/HostAndPortTest.java @@ -16,10 +16,14 @@ package com.google.common.net; +import static com.google.common.net.ReflectionFreeAssertThrows.assertThrows; + import com.google.common.annotations.GwtCompatible; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link HostAndPort} @@ -27,6 +31,7 @@ * @author Paul Marks */ @GwtCompatible +@NullUnmarked public class HostAndPortTest extends TestCase { public void testFromStringWellFormed() { @@ -102,7 +107,7 @@ public void testFromStringParseableNonsense() { private static void checkFromStringCase( String hpString, int defaultPort, - String expectHost, + @Nullable String expectHost, int expectPort, boolean expectHasExplicitPort) { HostAndPort hp; @@ -159,17 +164,9 @@ public void testFromParts() { assertTrue(hp.hasPort()); assertEquals(81, hp.getPort()); - try { - HostAndPort.fromParts("gmail.com:80", 81); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HostAndPort.fromParts("gmail.com:80", 81)); - try { - HostAndPort.fromParts("gmail.com", -1); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HostAndPort.fromParts("gmail.com", -1)); } public void testFromHost() { @@ -181,17 +178,9 @@ public void testFromHost() { assertEquals("::1", hp.getHost()); assertFalse(hp.hasPort()); - try { - HostAndPort.fromHost("gmail.com:80"); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HostAndPort.fromHost("gmail.com:80")); - try { - HostAndPort.fromHost("[gmail.com]"); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> HostAndPort.fromHost("[gmail.com]")); } public void testGetPortOrDefault() { @@ -225,11 +214,9 @@ public void testRequireBracketsForIPv6() { assertEquals("x", HostAndPort.fromString("x:80").requireBracketsForIPv6().getHost()); // Non-bracketed IPv6 fails. - try { - HostAndPort.fromString("::1").requireBracketsForIPv6(); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> HostAndPort.fromString("::1").requireBracketsForIPv6()); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/net/HostSpecifierTest.java b/android/guava-tests/test/com/google/common/net/HostSpecifierTest.java index fadeff7aadf4..6b7ca2f39902 100644 --- a/android/guava-tests/test/com/google/common/net/HostSpecifierTest.java +++ b/android/guava-tests/test/com/google/common/net/HostSpecifierTest.java @@ -23,6 +23,7 @@ import com.google.common.testing.NullPointerTester; import java.text.ParseException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * {@link TestCase} for {@link HostSpecifier}. This is a relatively cursory test, as HostSpecifier @@ -32,6 +33,7 @@ * * @author Craig Berry */ +@NullUnmarked public final class HostSpecifierTest extends TestCase { private static final ImmutableList GOOD_IPS = @@ -92,8 +94,9 @@ public void testNulls() { } private void assertGood(String spec) throws ParseException { - HostSpecifier.fromValid(spec); // Throws exception if not working correctly - HostSpecifier.from(spec); + // Throws exception if not working correctly + HostSpecifier unused = HostSpecifier.fromValid(spec); + unused = HostSpecifier.from(spec); assertTrue(HostSpecifier.isValid(spec)); } diff --git a/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java b/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java index 5361b3fcea61..b89202ec9347 100644 --- a/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java +++ b/android/guava-tests/test/com/google/common/net/HttpHeadersTest.java @@ -25,12 +25,14 @@ import java.lang.reflect.Field; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the HttpHeaders class. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class HttpHeadersTest extends TestCase { public void testConstantNameMatchesString() throws Exception { @@ -40,6 +42,7 @@ public void testConstantNameMatchesString() throws Exception { .put("CDN_LOOP", "CDN-Loop") .put("ETAG", "ETag") .put("SOURCE_MAP", "SourceMap") + .put("SEC_CH_UA_WOW64", "Sec-CH-UA-WoW64") .put("SEC_WEBSOCKET_ACCEPT", "Sec-WebSocket-Accept") .put("SEC_WEBSOCKET_EXTENSIONS", "Sec-WebSocket-Extensions") .put("SEC_WEBSOCKET_KEY", "Sec-WebSocket-Key") @@ -47,11 +50,11 @@ public void testConstantNameMatchesString() throws Exception { .put("SEC_WEBSOCKET_VERSION", "Sec-WebSocket-Version") .put("X_WEBKIT_CSP", "X-WebKit-CSP") .put("X_WEBKIT_CSP_REPORT_ONLY", "X-WebKit-CSP-Report-Only") - .build(); + .buildOrThrow(); ImmutableSet uppercaseAcronyms = ImmutableSet.of( - "CH", "ID", "DNT", "DNS", "ECT", "HTTP2", "IP", "MD5", "P3P", "RTT", "TE", "UA", "UID", - "URL", "WWW", "XSS"); + "CH", "ID", "DNT", "DNS", "DPR", "ECT", "GPC", "HTTP2", "IP", "MD5", "P3P", "RTT", "TE", + "UA", "UID", "URL", "WWW", "XSS"); assertConstantNameMatchesString(HttpHeaders.class, specialCases, uppercaseAcronyms); } diff --git a/android/guava-tests/test/com/google/common/net/InetAddressesTest.java b/android/guava-tests/test/com/google/common/net/InetAddressesTest.java index be77e7b17f08..03425f0abebf 100644 --- a/android/guava-tests/test/com/google/common/net/InetAddressesTest.java +++ b/android/guava-tests/test/com/google/common/net/InetAddressesTest.java @@ -17,6 +17,7 @@ package com.google.common.net; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableSet; import com.google.common.testing.NullPointerTester; @@ -24,14 +25,19 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; import java.net.UnknownHostException; +import java.util.Enumeration; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link InetAddresses}. * * @author Erik Kline */ +@NullUnmarked public class InetAddressesTest extends TestCase { public void testNulls() { @@ -109,21 +115,16 @@ public void testForStringBogusInput() { ":1:2:3:4:5:6:"); for (String bogusInput : bogusInputs) { - try { - InetAddresses.forString(bogusInput); - fail("IllegalArgumentException expected for '" + bogusInput + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "IllegalArgumentException expected for '" + bogusInput + "'", + IllegalArgumentException.class, + () -> InetAddresses.forString(bogusInput)); assertFalse(InetAddresses.isInetAddress(bogusInput)); } } public void test3ff31() { - try { - InetAddresses.forString("3ffe:::1"); - fail("IllegalArgumentException expected"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forString("3ffe:::1")); assertFalse(InetAddresses.isInetAddress("016.016.016.016")); } @@ -195,14 +196,10 @@ public void testConvertDottedQuadToHex() throws UnknownHostException { } } - // see https://github.com/google/guava/issues/2587 - private static final ImmutableSet SCOPE_IDS = - ImmutableSet.of("eno1", "en1", "eth0", "X", "1", "2", "14", "20"); - - public void testIPv4AddressWithScopeId() { + public void testIPv4AddressWithScopeId() throws SocketException { ImmutableSet ipStrings = ImmutableSet.of("1.2.3.4", "192.168.0.1"); for (String ipString : ipStrings) { - for (String scopeId : SCOPE_IDS) { + for (String scopeId : getMachineScopesAndInterfaces()) { String withScopeId = ipString + "%" + scopeId; assertFalse( "InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true", @@ -211,11 +208,11 @@ public void testIPv4AddressWithScopeId() { } } - public void testDottedQuadAddressWithScopeId() { + public void testDottedQuadAddressWithScopeId() throws SocketException { ImmutableSet ipStrings = ImmutableSet.of("7::0.128.0.127", "7::0.128.0.128", "7::128.128.0.127", "7::0.128.128.127"); for (String ipString : ipStrings) { - for (String scopeId : SCOPE_IDS) { + for (String scopeId : getMachineScopesAndInterfaces()) { String withScopeId = ipString + "%" + scopeId; assertFalse( "InetAddresses.isInetAddress(" + withScopeId + ") should be false but was true", @@ -224,27 +221,106 @@ public void testDottedQuadAddressWithScopeId() { } } - public void testIPv6AddressWithScopeId() { + public void testIPv6AddressWithScopeId() throws SocketException, UnknownHostException { ImmutableSet ipStrings = ImmutableSet.of( - "0:0:0:0:0:0:0:1", - "fe80::a", - "fe80::1", - "fe80::2", - "fe80::42", - "fe80::3dd0:7f8e:57b7:34d5", - "fe80::71a3:2b00:ddd3:753f", - "fe80::8b2:d61e:e5c:b333", - "fe80::b059:65f4:e877:c40"); + "::1", + "1180::a", + "1180::1", + "1180::2", + "1180::42", + "1180::3dd0:7f8e:57b7:34d5", + "1180::71a3:2b00:ddd3:753f", + "1180::8b2:d61e:e5c:b333", + "1180::b059:65f4:e877:c40", + "fe80::34", + "fec0::34"); + boolean processedNamedInterface = false; for (String ipString : ipStrings) { - for (String scopeId : SCOPE_IDS) { + for (String scopeId : getMachineScopesAndInterfaces()) { String withScopeId = ipString + "%" + scopeId; assertTrue( "InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false", InetAddresses.isInetAddress(withScopeId)); - assertEquals(InetAddresses.forString(withScopeId), InetAddresses.forString(ipString)); + Inet6Address parsed; + boolean isNumeric = scopeId.matches("\\d+"); + try { + parsed = (Inet6Address) InetAddresses.forString(withScopeId); + } catch (IllegalArgumentException e) { + if (!isNumeric) { + // Android doesn't recognize %interface as valid + continue; + } + throw e; + } + processedNamedInterface |= !isNumeric; + assertThat(InetAddresses.toAddrString(parsed)).contains("%"); + if (isNumeric) { + assertEquals(Integer.parseInt(scopeId), parsed.getScopeId()); + } else { + assertEquals(scopeId, parsed.getScopedInterface().getName()); + } + Inet6Address reparsed = + (Inet6Address) InetAddresses.forString(InetAddresses.toAddrString(parsed)); + assertEquals(reparsed, parsed); + assertEquals(reparsed.getScopeId(), parsed.getScopeId()); } } + assertTrue(processedNamedInterface); + } + + public void testIPv6AddressWithScopeId_platformEquivalence() + throws SocketException, UnknownHostException { + ImmutableSet ipStrings = + ImmutableSet.of( + "::1", + "1180::a", + "1180::1", + "1180::2", + "1180::42", + "1180::3dd0:7f8e:57b7:34d5", + "1180::71a3:2b00:ddd3:753f", + "1180::8b2:d61e:e5c:b333", + "1180::b059:65f4:e877:c40", + "fe80::34", + "fec0::34"); + for (String ipString : ipStrings) { + for (String scopeId : getMachineScopesAndInterfaces()) { + String withScopeId = ipString + "%" + scopeId; + assertTrue( + "InetAddresses.isInetAddress(" + withScopeId + ") should be true but was false", + InetAddresses.isInetAddress(withScopeId)); + Inet6Address parsed; + boolean isNumeric = scopeId.matches("\\d+"); + try { + parsed = (Inet6Address) InetAddresses.forString(withScopeId); + } catch (IllegalArgumentException e) { + if (!isNumeric) { + // Android doesn't recognize %interface as valid + continue; + } + throw e; + } + Inet6Address platformValue; + try { + platformValue = (Inet6Address) InetAddress.getByName(withScopeId); + } catch (UnknownHostException e) { + // Android doesn't recognize %interface as valid + if (!isNumeric) { + continue; + } + throw e; + } + assertEquals(platformValue, parsed); + assertEquals(platformValue.getScopeId(), parsed.getScopeId()); + } + } + } + + public void testIPv6AddressWithBadScopeId() throws SocketException, UnknownHostException { + assertThrows( + IllegalArgumentException.class, + () -> InetAddresses.forString("1180::b059:65f4:e877:c40%eth9")); } public void testToAddrStringIPv4() { @@ -327,71 +403,33 @@ public void testIsUriInetAddress() { } public void testForUriStringBad() { - try { - InetAddresses.forUriString(""); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forUriString("")); - try { - InetAddresses.forUriString("192.168.999.888"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("192.168.999.888")); - try { - InetAddresses.forUriString("www.google.com"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("www.google.com")); - try { - InetAddresses.forUriString("[1:2e]"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forUriString("[1:2e]")); - try { - InetAddresses.forUriString("[192.168.1.1]"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forUriString("[192.168.1.1]")); - try { - InetAddresses.forUriString("192.168.1.1]"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forUriString("192.168.1.1]")); - try { - InetAddresses.forUriString("[192.168.1.1"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.forUriString("[192.168.1.1")); - try { - InetAddresses.forUriString("[3ffe:0:0:0:0:0:0:1"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("[3ffe:0:0:0:0:0:0:1")); - try { - InetAddresses.forUriString("3ffe:0:0:0:0:0:0:1]"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("3ffe:0:0:0:0:0:0:1]")); - try { - InetAddresses.forUriString("3ffe:0:0:0:0:0:0:1"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("3ffe:0:0:0:0:0:0:1")); - try { - InetAddresses.forUriString("::ffff:192.0.2.1"); - fail("expected IllegalArgumentException"); // COV_NF_LINE - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> InetAddresses.forUriString("::ffff:192.0.2.1")); } public void testCompatIPv4Addresses() { @@ -400,11 +438,10 @@ public void testCompatIPv4Addresses() { for (String nonCompatAddress : nonCompatAddresses) { InetAddress ip = InetAddresses.forString(nonCompatAddress); assertFalse(InetAddresses.isCompatIPv4Address((Inet6Address) ip)); - try { - InetAddresses.getCompatIPv4Address((Inet6Address) ip); - fail("IllegalArgumentException expected for '" + nonCompatAddress + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "IllegalArgumentException expected for '" + nonCompatAddress + "'", + IllegalArgumentException.class, + () -> InetAddresses.getCompatIPv4Address((Inet6Address) ip)); } ImmutableSet validCompatAddresses = ImmutableSet.of("::1.2.3.4", "::102:304"); @@ -470,11 +507,10 @@ public void test6to4Addresses() { for (String non6to4Address : non6to4Addresses) { InetAddress ip = InetAddresses.forString(non6to4Address); assertFalse(InetAddresses.is6to4Address((Inet6Address) ip)); - try { - InetAddresses.get6to4IPv4Address((Inet6Address) ip); - fail("IllegalArgumentException expected for '" + non6to4Address + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "IllegalArgumentException expected for '" + non6to4Address + "'", + IllegalArgumentException.class, + () -> InetAddresses.get6to4IPv4Address((Inet6Address) ip)); } String valid6to4Address = "2002:0102:0304::1"; @@ -492,11 +528,10 @@ public void testTeredoAddresses() { for (String nonTeredoAddress : nonTeredoAddresses) { InetAddress ip = InetAddresses.forString(nonTeredoAddress); assertFalse(InetAddresses.isTeredoAddress((Inet6Address) ip)); - try { - InetAddresses.getTeredoInfo((Inet6Address) ip); - fail("IllegalArgumentException expected for '" + nonTeredoAddress + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "IllegalArgumentException expected for '" + nonTeredoAddress + "'", + IllegalArgumentException.class, + () -> InetAddresses.getTeredoInfo((Inet6Address) ip)); } String validTeredoAddress = "2001:0000:4136:e378:8000:63bf:3fff:fdd2"; @@ -559,11 +594,10 @@ public void testIsatapAddresses() { for (String nonIsatapAddress : nonIsatapAddresses) { InetAddress ip = InetAddresses.forString(nonIsatapAddress); assertFalse(InetAddresses.isIsatapAddress((Inet6Address) ip)); - try { - InetAddresses.getIsatapIPv4Address((Inet6Address) ip); - fail("IllegalArgumentException expected for '" + nonIsatapAddress + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows( + "IllegalArgumentException expected for '" + nonIsatapAddress + "'", + IllegalArgumentException.class, + () -> InetAddresses.getIsatapIPv4Address((Inet6Address) ip)); } } @@ -645,7 +679,7 @@ public void testGetCoercedIPv4Address() { InetAddresses.getCoercedIPv4Address( InetAddresses.forString("2001:0000:4136:e378:8000:63bf:3fff:fdd3"))); - // 2 Teredo addresses NOT differing in the their embedded IPv4 addresses should hash to the same + // 2 Teredo addresses NOT differing in their embedded IPv4 addresses should hash to the same // value. assertThat( InetAddresses.getCoercedIPv4Address( @@ -683,12 +717,8 @@ public void testFromLittleEndianByteArray() throws UnknownHostException { InetAddress.getByAddress( new byte[] {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1})); - try { - InetAddresses.fromLittleEndianByteArray(new byte[3]); - fail("expected exception"); - } catch (UnknownHostException expected) { - // success - } + assertThrows( + UnknownHostException.class, () -> InetAddresses.fromLittleEndianByteArray(new byte[3])); } public void testIsMaximum() throws UnknownHostException { @@ -705,6 +735,7 @@ public void testIsMaximum() throws UnknownHostException { assertTrue(InetAddresses.isMaximum(address)); } + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public void testIncrementIPv4() throws UnknownHostException { InetAddress address_66_0 = InetAddress.getByName("172.24.66.0"); InetAddress address_66_255 = InetAddress.getByName("172.24.66.255"); @@ -720,14 +751,10 @@ public void testIncrementIPv4() throws UnknownHostException { assertEquals(address_67_0, address); InetAddress address_ffffff = InetAddress.getByName("255.255.255.255"); - address = address_ffffff; - try { - address = InetAddresses.increment(address); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.increment(address_ffffff)); } + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public void testIncrementIPv6() throws UnknownHostException { InetAddress addressV6_66_0 = InetAddress.getByName("2001:db8::6600"); InetAddress addressV6_66_ff = InetAddress.getByName("2001:db8::66ff"); @@ -743,12 +770,7 @@ public void testIncrementIPv6() throws UnknownHostException { assertEquals(addressV6_67_0, address); InetAddress addressV6_ffffff = InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); - address = addressV6_ffffff; - try { - address = InetAddresses.increment(address); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.increment(addressV6_ffffff)); } public void testDecrementIPv4() throws UnknownHostException { @@ -767,12 +789,7 @@ public void testDecrementIPv4() throws UnknownHostException { assertEquals(address660, address); InetAddress address0000 = InetAddress.getByName("0.0.0.0"); - address = address0000; - try { - address = InetAddresses.decrement(address); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.decrement(address0000)); } public void testDecrementIPv6() throws UnknownHostException { @@ -791,30 +808,27 @@ public void testDecrementIPv6() throws UnknownHostException { assertEquals(addressV6660, address); InetAddress addressV6000000 = InetAddress.getByName("0:0:0:0:0:0:0:0"); - address = addressV6000000; - try { - address = InetAddresses.decrement(address); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InetAddresses.decrement(addressV6000000)); } public void testFromIpv4BigIntegerThrowsLessThanZero() { - try { - InetAddresses.fromIPv4BigInteger(BigInteger.valueOf(-1L)); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals("BigInteger must be greater than or equal to 0", expected.getMessage()); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> InetAddresses.fromIPv4BigInteger(BigInteger.valueOf(-1L))); + assertThat(expected) + .hasMessageThat() + .isEqualTo("BigInteger must be greater than or equal to 0"); } public void testFromIpv6BigIntegerThrowsLessThanZero() { - try { - InetAddresses.fromIPv6BigInteger(BigInteger.valueOf(-1L)); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals("BigInteger must be greater than or equal to 0", expected.getMessage()); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> InetAddresses.fromIPv6BigInteger(BigInteger.valueOf(-1L))); + assertThat(expected) + .hasMessageThat() + .isEqualTo("BigInteger must be greater than or equal to 0"); } public void testFromIpv4BigIntegerValid() { @@ -840,27 +854,42 @@ public void testFromIpv6BigIntegerValid() { } public void testFromIpv4BigIntegerInputTooLarge() { - try { - InetAddresses.fromIPv4BigInteger(BigInteger.ONE.shiftLeft(32).add(BigInteger.ONE)); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals( - "BigInteger cannot be converted to InetAddress because it has more than 4 bytes:" - + " 4294967297", - expected.getMessage()); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> + InetAddresses.fromIPv4BigInteger(BigInteger.ONE.shiftLeft(32).add(BigInteger.ONE))); + assertThat(expected) + .hasMessageThat() + .isEqualTo( + "BigInteger cannot be converted to InetAddress because it has more than 4 bytes:" + + " 4294967297"); } public void testFromIpv6BigIntegerInputTooLarge() { - try { - InetAddresses.fromIPv6BigInteger(BigInteger.ONE.shiftLeft(128).add(BigInteger.ONE)); - fail(); - } catch (IllegalArgumentException expected) { - assertEquals( - "BigInteger cannot be converted to InetAddress because it has more than 16 bytes:" - + " 340282366920938463463374607431768211457", - expected.getMessage()); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> + InetAddresses.fromIPv6BigInteger( + BigInteger.ONE.shiftLeft(128).add(BigInteger.ONE))); + assertThat(expected) + .hasMessageThat() + .isEqualTo( + "BigInteger cannot be converted to InetAddress because it has more than 16 bytes:" + + " 340282366920938463463374607431768211457"); + } + + // see https://github.com/google/guava/issues/2587 + private static ImmutableSet getMachineScopesAndInterfaces() throws SocketException { + ImmutableSet.Builder builder = ImmutableSet.builder(); + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + assertTrue(interfaces.hasMoreElements()); + while (interfaces.hasMoreElements()) { + NetworkInterface i = interfaces.nextElement(); + builder.add(i.getName()).add(String.valueOf(i.getIndex())); + } + return builder.build(); } /** Checks that the IP converts to the big integer and the big integer converts to the IP. */ diff --git a/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java b/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java index 7113fb45741a..213ebfbf5ef8 100644 --- a/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java +++ b/android/guava-tests/test/com/google/common/net/InternetDomainNameTest.java @@ -16,9 +16,11 @@ package com.google.common.net; +import static com.google.common.net.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; @@ -26,6 +28,7 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * {@link TestCase} for {@link InternetDomainName}. @@ -33,6 +36,7 @@ * @author Craig Berry */ @GwtCompatible(emulated = true) +@NullUnmarked public final class InternetDomainNameTest extends TestCase { private static final InternetDomainName UNICODE_EXAMPLE = InternetDomainName.from("j\u00f8rpeland.no"); @@ -43,74 +47,85 @@ public final class InternetDomainNameTest extends TestCase { private static final String DELTA = "\u0394"; /** A domain part which is valid under lenient validation, but invalid under strict validation. */ + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 static final String LOTS_OF_DELTAS = Strings.repeat(DELTA, 62); + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 private static final String ALMOST_TOO_MANY_LEVELS = Strings.repeat("a.", 127); + @SuppressWarnings("InlineMeInliner") // String.repeat unavailable under Java 8 private static final String ALMOST_TOO_LONG = Strings.repeat("aaaaa.", 40) + "1234567890.c"; private static final ImmutableSet VALID_NAME = ImmutableSet.of( - "foo.com", - "f-_-o.cOM", - "f--1.com", - "f11-1.com", - "www", + // keep-sorted start + "123.cn", + "8server.shop", + "a" + DELTA + "b.com", "abc.a23", "biz.com.ua", - "x", - "fOo", + "f--1.com", "f--o", + "f-_-o.cOM", + "f11-1.com", + "fOo", "f_a", + "foo.com", "foo.net.us\uFF61ocm", "woo.com.", - "8server.shop", - "123.cn", - "a" + DELTA + "b.com", - ALMOST_TOO_MANY_LEVELS, - ALMOST_TOO_LONG); + "www", + "x", + ALMOST_TOO_LONG, + ALMOST_TOO_MANY_LEVELS + // keep-sorted end + ); private static final ImmutableSet INVALID_NAME = ImmutableSet.of( - "", + // keep-sorted start " ", + "", + ".", + "..", + "...", + "..bar.com", + "..quiffle.com", + ".foo.com", "127.0.0.1", - "::1", "13", - "abc.12c", - "foo-.com", + "::1", "_bar.quux", - "foo+bar.com", - "foo!bar.com", - ".foo.com", - "..bar.com", + "a" + DELTA + " .com", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", + "abc.12c", "baz..com", - "..quiffle.com", "fleeb.com..", - ".", - "..", - "...", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", - "a" + DELTA + " .com", - ALMOST_TOO_MANY_LEVELS + "com", - ALMOST_TOO_LONG + ".c"); + "foo!bar.com", + "foo+bar.com", + "foo-.com", + ALMOST_TOO_LONG + ".c", + ALMOST_TOO_MANY_LEVELS + "com" + // keep-sorted end + ); private static final ImmutableSet RS = ImmutableSet.of( - "com", + // keep-sorted start + "\u7f51\u7edc.Cn", // "网络.Cn" "co.uk", + "co.uk.", // Trailing dot + "co\uFF61uk", // Alternate dot character + "com", "foo.bd", - "xxxxxx.bd", "org.mK", "us", - "co.uk.", // Trailing dot - "co\uFF61uk", // Alternate dot character - "\u7f51\u7edc.Cn", // "网络.Cn" + "xxxxxx.bd", + // keep-sorted end "j\u00f8rpeland.no", // "jorpeland.no" (first o slashed) - "xn--jrpeland-54a.no"); // IDNA (punycode) encoding of above + "xn--jrpeland-54a.no" // IDNA (punycode) encoding of above + ); - private static final ImmutableSet PS_NOT_RS = - ImmutableSet.of("blogspot.com", "blogspot.co.uk", "uk.com"); + private static final ImmutableSet PS_NOT_RS = ImmutableSet.of("blogspot.com", "uk.com"); private static final ImmutableSet PS = ImmutableSet.builder().addAll(RS).addAll(PS_NOT_RS).build(); @@ -126,23 +141,26 @@ public final class InternetDomainNameTest extends TestCase { private static final ImmutableSet NON_PS = ImmutableSet.of( - "foo.bar.com", - "foo.ca", + // keep-sorted start + "dominio.com.co", "foo.bar.ca", - "foo.blogspot.com", + "foo.bar.co.il", + "foo.bar.com", "foo.blogspot.co.uk", + "foo.blogspot.com", + "foo.ca", + "foo.eDu.au", "foo.uk.com", - "foo.bar.co.il", - "state.CA.us", - "www.state.pa.us", - "pvt.k12.ca.us", - "www.google.com", - "www4.yahoo.co.uk", "home.netscape.com", - "web.MIT.edu", - "foo.eDu.au", + "pvt.k12.ca.us", + "state.CA.us", "utenti.blah.IT", - "dominio.com.co"); + "web.MIT.edu", + "www.google.com", + "www.state.pa.us", + "www4.yahoo.co.uk" + // keep-sorted end + ); private static final ImmutableSet NON_RS = ImmutableSet.builder().addAll(NON_PS).addAll(PS_NOT_RS).build(); @@ -168,63 +186,64 @@ public final class InternetDomainNameTest extends TestCase { private static final ImmutableSet SOMEWHERE_UNDER_PS = ImmutableSet.of( - "foo.bar.google.com", + // keep-sorted start + "1.fm", "a.b.c.1.2.3.ca.us", - "site.jp", - "uomi-online.kir.jp", + "a\u7f51\u7edcA.\u7f51\u7edc.Cn", // "a网络A.网络.Cn" + "cnn.ca", + "cool.co.uk", + "cool.de", + "cool.dk", + "cool.es", + "cool.nl", + "cool.se", + "cool\uFF61fr", // Alternate dot character + "foo.bar.google.com", + "google.Co.uK", + "google.com", + "home.netscape.com", + "it-trace.ch", + "jobs.kt.com.", "jprs.co.jp", - "site.quick.jp", - "site.tenki.jp", - "site.or.jp", - "site.gr.jp", - "site.ne.jp", + "kt.co", + "ledger-enquirer.com", + "members.blah.nl.", + "pvt.k12.ca.us", "site.ac.jp", "site.ad.jp", - "site.ed.jp", - "site.geo.jp", - "site.go.jp", - "site.lg.jp", - "1.fm", "site.cc", + "site.ed.jp", "site.ee", "site.fi", "site.fm", + "site.geo.jp", + "site.go.jp", "site.gr", - "www.leguide.ma", + "site.gr.jp", + "site.jp", + "site.lg.jp", "site.ma", - "some.org.mk", "site.mk", + "site.ne.jp", + "site.or.jp", + "site.quick.jp", + "site.tenki.jp", "site.tv", "site.us", - "www.odev.us", - "www.GOOGLE.com", - "www.com", - "google.com", - "www7.google.co.uk", - "google.Co.uK", - "jobs.kt.com.", - "home.netscape.com", - "web.stanford.edu", + "some.org.mk", "stanford.edu", "state.ca.us", - "www.state.ca.us", - "state.ca.us", - "pvt.k12.ca.us", - "www.rave.ca.", - "cnn.ca", - "ledger-enquirer.com", - "it-trace.ch", - "cool.dk", - "cool.co.uk", - "cool.de", - "cool.es", - "cool\uFF61fr", // Alternate dot character - "cool.nl", - "members.blah.nl.", - "cool.se", + "uomi-online.kir.jp", "utenti.blah.it", - "kt.co", - "a\u7f51\u7edcA.\u7f51\u7edc.Cn" // "a网络A.网络.Cn" + "web.stanford.edu", + "www.GOOGLE.com", + "www.com", + "www.leguide.ma", + "www.odev.us", + "www.rave.ca.", + "www.state.ca.us", + "www7.google.co.uk" + // keep-sorted end ); private static final ImmutableSet SOMEWHERE_UNDER_RS = @@ -232,17 +251,13 @@ public final class InternetDomainNameTest extends TestCase { public void testValid() { for (String name : VALID_NAME) { - InternetDomainName.from(name); + InternetDomainName unused = InternetDomainName.from(name); } } public void testInvalid() { for (String name : INVALID_NAME) { - try { - InternetDomainName.from(name); - fail("Should have been invalid: '" + name + "'"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> InternetDomainName.from(name)); } } @@ -365,11 +380,7 @@ public void testParent() { assertEquals("uk", InternetDomainName.from("co.uk").parent().toString()); assertEquals("google.com", InternetDomainName.from("www.google.com").parent().toString()); - try { - InternetDomainName.from("com").parent(); - fail("'com' should throw ISE on .parent() call"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> InternetDomainName.from("com").parent()); } public void testChild() { @@ -377,11 +388,7 @@ public void testChild() { assertEquals("www.foo.com", domain.child("www").toString()); - try { - domain.child("www."); - fail("www..google.com should have been invalid"); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> domain.child("www.")); } public void testParentChild() { @@ -392,7 +399,7 @@ public void testParentChild() { // These would throw an exception if leniency were not preserved during parent() and child() // calls. InternetDomainName child = parent.child(LOTS_OF_DELTAS); - child.child(LOTS_OF_DELTAS); + InternetDomainName unused = child.child(LOTS_OF_DELTAS); } public void testValidTopPrivateDomain() { @@ -407,11 +414,8 @@ public void testInvalidTopPrivateDomain() { ImmutableSet badCookieDomains = ImmutableSet.of("co.uk", "foo", "com"); for (String domain : badCookieDomains) { - try { - InternetDomainName.from(domain).topPrivateDomain(); - fail(domain); - } catch (IllegalStateException expected) { - } + assertThrows( + IllegalStateException.class, () -> InternetDomainName.from(domain).topPrivateDomain()); } } @@ -461,7 +465,7 @@ public void testPublicSuffixExclusion() { public void testPublicSuffixMultipleUnders() { // PSL has both *.uk and *.sch.uk; the latter should win. - // See http://code.google.com/p/guava-libraries/issues/detail?id=1176 + // See https://github.com/google/guava/issues/1176 InternetDomainName domain = InternetDomainName.from("www.essex.sch.uk"); assertTrue(domain.hasPublicSuffix()); @@ -480,7 +484,7 @@ public void testRegistrySuffixExclusion() { public void testRegistrySuffixMultipleUnders() { // PSL has both *.uk and *.sch.uk; the latter should win. - // See http://code.google.com/p/guava-libraries/issues/detail?id=1176 + // See https://github.com/google/guava/issues/1176 InternetDomainName domain = InternetDomainName.from("www.essex.sch.uk"); assertTrue(domain.hasRegistrySuffix()); @@ -501,6 +505,7 @@ private static InternetDomainName idn(String domain) { return InternetDomainName.from(domain); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { final NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/net/MediaTypeTest.java b/android/guava-tests/test/com/google/common/net/MediaTypeTest.java index 574db68e80b3..da6c4914b0b6 100644 --- a/android/guava-tests/test/com/google/common/net/MediaTypeTest.java +++ b/android/guava-tests/test/com/google/common/net/MediaTypeTest.java @@ -16,8 +16,6 @@ package com.google.common.net; -import static com.google.common.base.Charsets.UTF_16; -import static com.google.common.base.Charsets.UTF_8; import static com.google.common.net.MediaType.ANY_APPLICATION_TYPE; import static com.google.common.net.MediaType.ANY_AUDIO_TYPE; import static com.google.common.net.MediaType.ANY_IMAGE_TYPE; @@ -27,14 +25,18 @@ import static com.google.common.net.MediaType.HTML_UTF_8; import static com.google.common.net.MediaType.JPEG; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; +import static com.google.common.net.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import static java.lang.reflect.Modifier.isFinal; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; +import static java.nio.charset.StandardCharsets.UTF_16; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; @@ -49,6 +51,7 @@ import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link MediaType}. @@ -56,7 +59,9 @@ * @author Gregory Kick */ @GwtCompatible(emulated = true) +@NullUnmarked public class MediaTypeTest extends TestCase { + @J2ktIncompatible @GwtIncompatible // reflection public void testParse_useConstants() throws Exception { for (MediaType constant : getConstants()) { @@ -64,6 +69,7 @@ public void testParse_useConstants() throws Exception { } } + @J2ktIncompatible @GwtIncompatible // reflection public void testCreate_useConstants() throws Exception { for (MediaType constant : getConstants()) { @@ -74,6 +80,7 @@ public void testCreate_useConstants() throws Exception { } } + @J2ktIncompatible @GwtIncompatible // reflection public void testConstants_charset() throws Exception { for (Field field : getConstantFields()) { @@ -86,11 +93,13 @@ public void testConstants_charset() throws Exception { } } + @J2ktIncompatible @GwtIncompatible // reflection public void testConstants_areUnique() { assertThat(getConstants()).containsNoDuplicates(); } + @J2ktIncompatible @GwtIncompatible // reflection private static FluentIterable getConstantFields() { return FluentIterable.from(asList(MediaType.class.getDeclaredFields())) @@ -107,6 +116,7 @@ && isFinal(modifiers) }); } + @J2ktIncompatible @GwtIncompatible // reflection private static FluentIterable getConstants() { return getConstantFields() @@ -124,59 +134,31 @@ public MediaType apply(Field input) { } public void testCreate_invalidType() { - try { - MediaType.create("te> MediaType.create("te> MediaType.create("text", "pl@intext")); } public void testCreate_wildcardTypeDeclaredSubtype() { - try { - MediaType.create("*", "text"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.create("*", "text")); } public void testCreate_nonAsciiType() { - try { - MediaType.create("…", "a"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.create("…", "a")); } public void testCreate_nonAsciiSubtype() { - try { - MediaType.create("a", "…"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.create("a", "…")); } public void testCreate_emptyType() { - try { - MediaType.create("", "a"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.create("", "a")); } public void testCreate_emptySubtype() { - try { - MediaType.create("a", ""); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.create("a", "")); } public void testCreateApplicationType() { @@ -255,31 +237,19 @@ public void testWithParameters_invalidAttribute() { MediaType mediaType = MediaType.parse("text/plain"); ImmutableListMultimap parameters = ImmutableListMultimap.of("a", "1", "@", "2", "b", "3"); - try { - mediaType.withParameters(parameters); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameters(parameters)); } public void testWithParameters_nonAsciiParameter() { MediaType mediaType = MediaType.parse("text/plain"); ImmutableListMultimap parameters = ImmutableListMultimap.of("…", "a"); - try { - mediaType.withParameters(parameters); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameters(parameters)); } public void testWithParameters_nonAsciiParameterValue() { MediaType mediaType = MediaType.parse("text/plain"); ImmutableListMultimap parameters = ImmutableListMultimap.of("a", "…"); - try { - mediaType.withParameters(parameters); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameters(parameters)); } public void testWithParameter() { @@ -298,38 +268,22 @@ public void testWithParameter() { public void testWithParameter_invalidAttribute() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameter("@", "2"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("@", "2")); } public void testWithParameter_nonAsciiParameter() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameter("…", "a"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("…", "a")); } public void testWithParameter_nonAsciiParameterValue() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameter("a", "…"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("a", "…")); } public void testWithParameter_emptyParameter() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameter("", "a"); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> mediaType.withParameter("", "a")); } public void testWithParametersIterable() { @@ -352,38 +306,27 @@ public void testWithParametersIterable() { public void testWithParametersIterable_invalidAttribute() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameters("@", ImmutableSet.of("2")); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> mediaType.withParameters("@", ImmutableSet.of("2"))); } public void testWithParametersIterable_nonAsciiParameter() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameters("…", ImmutableSet.of("a")); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> mediaType.withParameters("…", ImmutableSet.of("a"))); } public void testWithParametersIterable_nonAsciiParameterValue() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameters("a", ImmutableSet.of("…")); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> mediaType.withParameters("a", ImmutableSet.of("…"))); } public void testWithParametersIterable_nullValue() { MediaType mediaType = MediaType.parse("text/plain"); - try { - mediaType.withParameters("a", Arrays.asList((String) null)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> mediaType.withParameters("a", Arrays.asList((String) null))); } public void testWithCharset() { @@ -422,94 +365,34 @@ public void testIs() { } public void testParse_empty() { - try { - MediaType.parse(""); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("")); } public void testParse_badInput() { - try { - MediaType.parse("/"); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - MediaType.parse("text"); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - MediaType.parse("text/"); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - MediaType.parse("te MediaType.parse("/")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("te MediaType.parse("text/pl@in")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain;")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; ")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=@")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\"@")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1;")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; ")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=1; b=")); + assertThrows(IllegalArgumentException.class, () -> MediaType.parse("text/plain; a=\u2025")); + } + + // https://github.com/google/guava/issues/6663 + public void testParse_spaceInParameterSeparator() { + assertThat(MediaType.parse("text/plain; charset =utf-8").charset()).hasValue(UTF_8); + assertThat(MediaType.parse("text/plain; charset= utf-8").charset()).hasValue(UTF_8); + assertThat(MediaType.parse("text/plain; charset = utf-8").charset()).hasValue(UTF_8); + assertThat(MediaType.parse("text/plain;charset =utf-8").charset()).hasValue(UTF_8); } public void testGetCharset() { @@ -517,6 +400,7 @@ public void testGetCharset() { assertThat(MediaType.parse("text/plain; charset=utf-8").charset()).hasValue(UTF_8); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testGetCharset_utf16() { assertThat(MediaType.parse("text/plain; charset=utf-16").charset()).hasValue(UTF_16); @@ -524,29 +408,17 @@ public void testGetCharset_utf16() { public void testGetCharset_tooMany() { MediaType mediaType = MediaType.parse("text/plain; charset=utf-8; charset=utf-16"); - try { - mediaType.charset(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, mediaType::charset); } public void testGetCharset_illegalCharset() { MediaType mediaType = MediaType.parse("text/plain; charset=\"!@#$%^&*()\""); - try { - mediaType.charset(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, mediaType::charset); } public void testGetCharset_unsupportedCharset() { MediaType mediaType = MediaType.parse("text/plain; charset=utf-wtf"); - try { - mediaType.charset(); - fail(); - } catch (UnsupportedCharsetException expected) { - } + assertThrows(UnsupportedCharsetException.class, mediaType::charset); } public void testEquals() { @@ -556,6 +428,9 @@ public void testEquals() { MediaType.create("TEXT", "PLAIN"), MediaType.parse("text/plain"), MediaType.parse("TEXT/PLAIN"), + MediaType.parse("text /plain"), + MediaType.parse("TEXT/ plain"), + MediaType.parse("text / plain"), MediaType.create("text", "plain").withParameter("a", "1").withoutParameters()) .addEqualityGroup( MediaType.create("text", "plain").withCharset(UTF_8), @@ -571,7 +446,11 @@ public void testEquals() { MediaType.parse("text/plain; charset=\"utf-8\""), MediaType.parse("text/plain; charset=\"\\u\\tf-\\8\""), MediaType.parse("text/plain; charset=UTF-8"), - MediaType.parse("text/plain ; charset=utf-8")) + MediaType.parse("text/plain ; charset=utf-8"), + MediaType.parse("text/plain; charset =UTF-8"), + MediaType.parse("text/plain; charset= UTF-8"), + MediaType.parse("text/plain; charset = UTF-8"), + MediaType.parse("text/plain; charset=\tUTF-8")) .addEqualityGroup(MediaType.parse("text/plain; charset=utf-8; charset=utf-8")) .addEqualityGroup( MediaType.create("text", "plain").withParameter("a", "value"), @@ -589,6 +468,7 @@ public void testEquals() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // Non-UTF-8 Charset public void testEquals_nonUtf8Charsets() { new EqualsTester() @@ -598,6 +478,7 @@ public void testEquals_nonUtf8Charsets() { .testEquals(); } + @J2ktIncompatible @GwtIncompatible // com.google.common.testing.NullPointerTester public void testNullPointer() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/net/PackageSanityTests.java b/android/guava-tests/test/com/google/common/net/PackageSanityTests.java index 3d18ad6dee6b..1a0a4c452f7a 100644 --- a/android/guava-tests/test/com/google/common/net/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/net/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.net; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Basic sanity tests for the entire package. @@ -24,6 +25,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { setDefault(InternetDomainName.class, InternetDomainName.from("google.com")); diff --git a/android/guava-tests/test/com/google/common/net/PercentEscaperTest.java b/android/guava-tests/test/com/google/common/net/PercentEscaperTest.java index 8443680e7f11..6f33ba1078be 100644 --- a/android/guava-tests/test/com/google/common/net/PercentEscaperTest.java +++ b/android/guava-tests/test/com/google/common/net/PercentEscaperTest.java @@ -19,12 +19,14 @@ import static com.google.common.escape.testing.EscaperAsserts.assertEscaping; import static com.google.common.escape.testing.EscaperAsserts.assertUnescaped; import static com.google.common.escape.testing.EscaperAsserts.assertUnicodeEscaping; +import static com.google.common.net.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import com.google.common.escape.UnicodeEscaper; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link PercentEscaper}. @@ -32,6 +34,7 @@ * @author David Beaumont */ @GwtCompatible +@NullUnmarked public class PercentEscaperTest extends TestCase { /** Tests that the simple escaper treats 0-9, a-z and A-Z as safe */ @@ -45,7 +48,7 @@ public void testSimpleEscaper() { } } - // Testing mutlibyte escape sequences + // Testing multibyte escape sequences assertEscaping(e, "%00", '\u0000'); // nul assertEscaping(e, "%7F", '\u007f'); // del assertEscaping(e, "%C2%80", '\u0080'); // xx-00010,x-000000 @@ -97,12 +100,7 @@ public void testCustomEscaper_withpercent() { /** Test that giving a null 'safeChars' string causes a {@link NullPointerException}. */ public void testBadArguments_null() { - try { - new PercentEscaper(null, false); - fail("Expected null pointer exception for null parameter"); - } catch (NullPointerException expected) { - // pass - } + assertThrows(NullPointerException.class, () -> new PercentEscaper(null, false)); } /** @@ -112,31 +110,20 @@ public void testBadArguments_null() { public void testBadArguments_badchars() { String msg = "Alphanumeric characters are always 'safe' " + "and should not be explicitly specified"; - try { - new PercentEscaper("-+#abc.!", false); - fail(msg); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo(msg); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> new PercentEscaper("-+#abc.!", false)); + assertThat(expected).hasMessageThat().isEqualTo(msg); } - /** - * Tests that if space is a safe character you cannot also specify 'plusForSpace' (throws {@link - * IllegalArgumentException}). - */ public void testBadArguments_plusforspace() { - try { - new PercentEscaper(" ", false); - } catch (IllegalArgumentException e) { - fail("Space can be a 'safe' character if plusForSpace is false"); - } + // space can be a safe char if plusForSpace is false + PercentEscaper unused = new PercentEscaper(" ", false); + + // space cannot be a safe char is plusForSpace is true String msg = "plusForSpace cannot be specified when space is a 'safe' character"; - try { - new PercentEscaper(" ", true); - fail(msg); - } catch (IllegalArgumentException expected) { - assertThat(expected).hasMessageThat().isEqualTo(msg); - } + IllegalArgumentException expected = + assertThrows(IllegalArgumentException.class, () -> new PercentEscaper(" ", true)); + assertThat(expected).hasMessageThat().isEqualTo(msg); } /** Helper to manually escape a 7-bit ascii character */ diff --git a/android/guava-tests/test/com/google/common/net/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/net/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..806c2a1b133b --- /dev/null +++ b/android/guava-tests/test/com/google/common/net/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.net; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/net/UrlEscaperTesting.java b/android/guava-tests/test/com/google/common/net/UrlEscaperTesting.java new file mode 100644 index 000000000000..9992a1898ce1 --- /dev/null +++ b/android/guava-tests/test/com/google/common/net/UrlEscaperTesting.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.net; + +import static com.google.common.escape.testing.EscaperAsserts.assertEscaping; +import static com.google.common.escape.testing.EscaperAsserts.assertUnescaped; +import static com.google.common.escape.testing.EscaperAsserts.assertUnicodeEscaping; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.escape.UnicodeEscaper; +import org.jspecify.annotations.NullUnmarked; + +/** + * Testing utilities for {@link UrlEscapers} and {@link LegacyUrlEscapersTest}. + * + * @author David Beaumont + */ +@GwtCompatible +@NullUnmarked +final class UrlEscaperTesting { + /** + * Helper to assert common expected behaviour of uri escapers. You should call + * assertBasicUrlEscaper() unless the escaper explicitly does not escape '%'. + */ + static void assertBasicUrlEscaperExceptPercent(UnicodeEscaper e) { + // URL escapers should throw null pointer exceptions for null input + try { + e.escape((String) null); + fail("Escaping null string should throw exception"); + } catch (NullPointerException x) { + // pass + } + + // All URL escapers should leave 0-9, A-Z, a-z unescaped + assertUnescaped(e, 'a'); + assertUnescaped(e, 'z'); + assertUnescaped(e, 'A'); + assertUnescaped(e, 'Z'); + assertUnescaped(e, '0'); + assertUnescaped(e, '9'); + + // Unreserved characters used in java.net.URLEncoder + assertUnescaped(e, '-'); + assertUnescaped(e, '_'); + assertUnescaped(e, '.'); + assertUnescaped(e, '*'); + + assertEscaping(e, "%00", '\u0000'); // nul + assertEscaping(e, "%7F", '\u007f'); // del + assertEscaping(e, "%C2%80", '\u0080'); // xx-00010,x-000000 + assertEscaping(e, "%DF%BF", '\u07ff'); // xx-11111,x-111111 + assertEscaping(e, "%E0%A0%80", '\u0800'); // xxx-0000,x-100000,x-00,0000 + assertEscaping(e, "%EF%BF%BF", '\uffff'); // xxx-1111,x-111111,x-11,1111 + assertUnicodeEscaping(e, "%F0%90%80%80", '\uD800', '\uDC00'); + assertUnicodeEscaping(e, "%F4%8F%BF%BF", '\uDBFF', '\uDFFF'); + + assertEquals("", e.escape("")); + assertEquals("safestring", e.escape("safestring")); + assertEquals("embedded%00null", e.escape("embedded\0null")); + assertEquals("max%EF%BF%BFchar", e.escape("max\uffffchar")); + } + + // Helper to assert common expected behaviour of uri escapers. + static void assertBasicUrlEscaper(UnicodeEscaper e) { + assertBasicUrlEscaperExceptPercent(e); + // The escape character must always be escaped + assertEscaping(e, "%25", '%'); + } + + static void assertPathEscaper(UnicodeEscaper e) { + assertBasicUrlEscaper(e); + + assertUnescaped(e, '!'); + assertUnescaped(e, '\''); + assertUnescaped(e, '('); + assertUnescaped(e, ')'); + assertUnescaped(e, '~'); + assertUnescaped(e, ':'); + assertUnescaped(e, '@'); + + // Don't use plus for spaces + assertEscaping(e, "%20", ' '); + + assertEquals("safe%20with%20spaces", e.escape("safe with spaces")); + assertEquals("foo@bar.com", e.escape("foo@bar.com")); + } + + private UrlEscaperTesting() {} +} diff --git a/android/guava-tests/test/com/google/common/net/UrlEscapersTest.java b/android/guava-tests/test/com/google/common/net/UrlEscapersTest.java index 9a67a95327d2..e9c0cd0a85d2 100644 --- a/android/guava-tests/test/com/google/common/net/UrlEscapersTest.java +++ b/android/guava-tests/test/com/google/common/net/UrlEscapersTest.java @@ -18,7 +18,8 @@ import static com.google.common.escape.testing.EscaperAsserts.assertEscaping; import static com.google.common.escape.testing.EscaperAsserts.assertUnescaped; -import static com.google.common.escape.testing.EscaperAsserts.assertUnicodeEscaping; +import static com.google.common.net.UrlEscaperTesting.assertBasicUrlEscaper; +import static com.google.common.net.UrlEscaperTesting.assertPathEscaper; import static com.google.common.net.UrlEscapers.urlFormParameterEscaper; import static com.google.common.net.UrlEscapers.urlFragmentEscaper; import static com.google.common.net.UrlEscapers.urlPathSegmentEscaper; @@ -26,6 +27,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.escape.UnicodeEscaper; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the {@link UrlEscapers} class. @@ -33,56 +35,8 @@ * @author David Beaumont */ @GwtCompatible +@NullUnmarked public class UrlEscapersTest extends TestCase { - /** - * Helper to assert common expected behaviour of uri escapers. You should call - * assertBasicUrlEscaper() unless the escaper explicitly does not escape '%'. - */ - static void assertBasicUrlEscaperExceptPercent(UnicodeEscaper e) { - // URL escapers should throw null pointer exceptions for null input - try { - e.escape((String) null); - fail("Escaping null string should throw exception"); - } catch (NullPointerException x) { - // pass - } - - // All URL escapers should leave 0-9, A-Z, a-z unescaped - assertUnescaped(e, 'a'); - assertUnescaped(e, 'z'); - assertUnescaped(e, 'A'); - assertUnescaped(e, 'Z'); - assertUnescaped(e, '0'); - assertUnescaped(e, '9'); - - // Unreserved characters used in java.net.URLEncoder - assertUnescaped(e, '-'); - assertUnescaped(e, '_'); - assertUnescaped(e, '.'); - assertUnescaped(e, '*'); - - assertEscaping(e, "%00", '\u0000'); // nul - assertEscaping(e, "%7F", '\u007f'); // del - assertEscaping(e, "%C2%80", '\u0080'); // xx-00010,x-000000 - assertEscaping(e, "%DF%BF", '\u07ff'); // xx-11111,x-111111 - assertEscaping(e, "%E0%A0%80", '\u0800'); // xxx-0000,x-100000,x-00,0000 - assertEscaping(e, "%EF%BF%BF", '\uffff'); // xxx-1111,x-111111,x-11,1111 - assertUnicodeEscaping(e, "%F0%90%80%80", '\uD800', '\uDC00'); - assertUnicodeEscaping(e, "%F4%8F%BF%BF", '\uDBFF', '\uDFFF'); - - assertEquals("", e.escape("")); - assertEquals("safestring", e.escape("safestring")); - assertEquals("embedded%00null", e.escape("embedded\0null")); - assertEquals("max%EF%BF%BFchar", e.escape("max\uffffchar")); - } - - // Helper to assert common expected behaviour of uri escapers. - static void assertBasicUrlEscaper(UnicodeEscaper e) { - assertBasicUrlEscaperExceptPercent(e); - // The escape character must always be escaped - assertEscaping(e, "%25", '%'); - } - public void testUrlFormParameterEscaper() { UnicodeEscaper e = (UnicodeEscaper) urlFormParameterEscaper(); // Verify that these are the same escaper (as documented) @@ -114,24 +68,6 @@ public void testUrlPathSegmentEscaper() { assertUnescaped(e, '+'); } - static void assertPathEscaper(UnicodeEscaper e) { - assertBasicUrlEscaper(e); - - assertUnescaped(e, '!'); - assertUnescaped(e, '\''); - assertUnescaped(e, '('); - assertUnescaped(e, ')'); - assertUnescaped(e, '~'); - assertUnescaped(e, ':'); - assertUnescaped(e, '@'); - - // Don't use plus for spaces - assertEscaping(e, "%20", ' '); - - assertEquals("safe%20with%20spaces", e.escape("safe with spaces")); - assertEquals("foo@bar.com", e.escape("foo@bar.com")); - } - public void testUrlFragmentEscaper() { UnicodeEscaper e = (UnicodeEscaper) urlFragmentEscaper(); assertUnescaped(e, '+'); diff --git a/android/guava-tests/test/com/google/common/primitives/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/primitives/AndroidIncompatible.java index 2cf4a28d003b..ad1a2ab4e987 100644 --- a/android/guava-tests/test/com/google/common/primitives/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/primitives/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java index 43ec04c4a4a4..e6e907062059 100644 --- a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java @@ -16,8 +16,13 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; @@ -27,6 +32,8 @@ import java.util.Comparator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Booleans}. @@ -34,6 +41,7 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked public class BooleansTest extends TestCase { private static final boolean[] EMPTY = {}; private static final boolean[] ARRAY_FALSE = {false}; @@ -44,116 +52,137 @@ public class BooleansTest extends TestCase { private static final boolean[] VALUES = {false, true}; public void testHashCode() { - assertEquals(Boolean.TRUE.hashCode(), Booleans.hashCode(true)); - assertEquals(Boolean.FALSE.hashCode(), Booleans.hashCode(false)); + assertThat(Booleans.hashCode(true)).isEqualTo(Boolean.TRUE.hashCode()); + assertThat(Booleans.hashCode(false)).isEqualTo(Boolean.FALSE.hashCode()); } public void testTrueFirst() { - assertEquals(0, Booleans.trueFirst().compare(true, true)); - assertEquals(0, Booleans.trueFirst().compare(false, false)); - assertTrue(Booleans.trueFirst().compare(true, false) < 0); - assertTrue(Booleans.trueFirst().compare(false, true) > 0); + assertThat(Booleans.trueFirst().compare(true, true)).isEqualTo(0); + assertThat(Booleans.trueFirst().compare(false, false)).isEqualTo(0); + assertThat(Booleans.trueFirst().compare(true, false)).isLessThan(0); + assertThat(Booleans.trueFirst().compare(false, true)).isGreaterThan(0); } public void testFalseFirst() { - assertEquals(0, Booleans.falseFirst().compare(true, true)); - assertEquals(0, Booleans.falseFirst().compare(false, false)); - assertTrue(Booleans.falseFirst().compare(false, true) < 0); - assertTrue(Booleans.falseFirst().compare(true, false) > 0); + assertThat(Booleans.falseFirst().compare(true, true)).isEqualTo(0); + assertThat(Booleans.falseFirst().compare(false, false)).isEqualTo(0); + assertThat(Booleans.falseFirst().compare(false, true)).isLessThan(0); + assertThat(Booleans.falseFirst().compare(true, false)).isGreaterThan(0); } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (boolean x : VALUES) { for (boolean y : VALUES) { // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Boolean.valueOf(x).compareTo(y), Booleans.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Booleans.compare(x, y)) + .isEqualTo(Boolean.valueOf(x).compareTo(y)); } } } public void testContains() { - assertFalse(Booleans.contains(EMPTY, false)); - assertFalse(Booleans.contains(ARRAY_FALSE, true)); - assertTrue(Booleans.contains(ARRAY_FALSE, false)); - assertTrue(Booleans.contains(ARRAY_FALSE_TRUE, false)); - assertTrue(Booleans.contains(ARRAY_FALSE_TRUE, true)); + assertThat(Booleans.contains(EMPTY, false)).isFalse(); + assertThat(Booleans.contains(ARRAY_FALSE, true)).isFalse(); + assertThat(Booleans.contains(ARRAY_FALSE, false)).isTrue(); + assertThat(Booleans.contains(ARRAY_FALSE_TRUE, false)).isTrue(); + assertThat(Booleans.contains(ARRAY_FALSE_TRUE, true)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Booleans.indexOf(EMPTY, ARRAY_FALSE)); - assertEquals(-1, Booleans.indexOf(ARRAY_FALSE, ARRAY_FALSE_TRUE)); - assertEquals(0, Booleans.indexOf(ARRAY_FALSE_FALSE, ARRAY_FALSE)); - assertEquals(0, Booleans.indexOf(ARRAY_FALSE, ARRAY_FALSE)); - assertEquals(0, Booleans.indexOf(ARRAY_FALSE_TRUE, ARRAY_FALSE)); - assertEquals(1, Booleans.indexOf(ARRAY_FALSE_TRUE, ARRAY_TRUE)); - assertEquals(0, Booleans.indexOf(ARRAY_TRUE, new boolean[0])); + assertThat(Booleans.indexOf(EMPTY, ARRAY_FALSE)).isEqualTo(-1); + assertThat(Booleans.indexOf(ARRAY_FALSE, ARRAY_FALSE_TRUE)).isEqualTo(-1); + assertThat(Booleans.indexOf(ARRAY_FALSE_FALSE, ARRAY_FALSE)).isEqualTo(0); + assertThat(Booleans.indexOf(ARRAY_FALSE, ARRAY_FALSE)).isEqualTo(0); + assertThat(Booleans.indexOf(ARRAY_FALSE_TRUE, ARRAY_FALSE)).isEqualTo(0); + assertThat(Booleans.indexOf(ARRAY_FALSE_TRUE, ARRAY_TRUE)).isEqualTo(1); + assertThat(Booleans.indexOf(ARRAY_TRUE, new boolean[0])).isEqualTo(0); } public void testIndexOf_arrays() { - assertEquals(-1, Booleans.indexOf(EMPTY, false)); - assertEquals(-1, Booleans.indexOf(ARRAY_FALSE, true)); - assertEquals(-1, Booleans.indexOf(ARRAY_FALSE_FALSE, true)); - assertEquals(0, Booleans.indexOf(ARRAY_FALSE, false)); - assertEquals(0, Booleans.indexOf(ARRAY_FALSE_TRUE, false)); - assertEquals(1, Booleans.indexOf(ARRAY_FALSE_TRUE, true)); - assertEquals(2, Booleans.indexOf(new boolean[] {false, false, true}, true)); + assertThat(Booleans.indexOf(EMPTY, false)).isEqualTo(-1); + assertThat(Booleans.indexOf(ARRAY_FALSE, true)).isEqualTo(-1); + assertThat(Booleans.indexOf(ARRAY_FALSE_FALSE, true)).isEqualTo(-1); + assertThat(Booleans.indexOf(ARRAY_FALSE, false)).isEqualTo(0); + assertThat(Booleans.indexOf(ARRAY_FALSE_TRUE, false)).isEqualTo(0); + assertThat(Booleans.indexOf(ARRAY_FALSE_TRUE, true)).isEqualTo(1); + assertThat(Booleans.indexOf(new boolean[] {false, false, true}, true)).isEqualTo(2); } public void testLastIndexOf() { - assertEquals(-1, Booleans.lastIndexOf(EMPTY, false)); - assertEquals(-1, Booleans.lastIndexOf(ARRAY_FALSE, true)); - assertEquals(-1, Booleans.lastIndexOf(ARRAY_FALSE_FALSE, true)); - assertEquals(0, Booleans.lastIndexOf(ARRAY_FALSE, false)); - assertEquals(0, Booleans.lastIndexOf(ARRAY_FALSE_TRUE, false)); - assertEquals(1, Booleans.lastIndexOf(ARRAY_FALSE_TRUE, true)); - assertEquals(2, Booleans.lastIndexOf(new boolean[] {false, true, true}, true)); + assertThat(Booleans.lastIndexOf(EMPTY, false)).isEqualTo(-1); + assertThat(Booleans.lastIndexOf(ARRAY_FALSE, true)).isEqualTo(-1); + assertThat(Booleans.lastIndexOf(ARRAY_FALSE_FALSE, true)).isEqualTo(-1); + assertThat(Booleans.lastIndexOf(ARRAY_FALSE, false)).isEqualTo(0); + assertThat(Booleans.lastIndexOf(ARRAY_FALSE_TRUE, false)).isEqualTo(0); + assertThat(Booleans.lastIndexOf(ARRAY_FALSE_TRUE, true)).isEqualTo(1); + assertThat(Booleans.lastIndexOf(new boolean[] {false, true, true}, true)).isEqualTo(2); } public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Booleans.concat())); - assertTrue(Arrays.equals(EMPTY, Booleans.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Booleans.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY_FALSE, Booleans.concat(ARRAY_FALSE))); - assertNotSame(ARRAY_FALSE, Booleans.concat(ARRAY_FALSE)); - assertTrue(Arrays.equals(ARRAY_FALSE, Booleans.concat(EMPTY, ARRAY_FALSE, EMPTY))); - assertTrue( - Arrays.equals( - new boolean[] {false, false, false}, - Booleans.concat(ARRAY_FALSE, ARRAY_FALSE, ARRAY_FALSE))); - assertTrue( - Arrays.equals( - new boolean[] {false, false, true}, Booleans.concat(ARRAY_FALSE, ARRAY_FALSE_TRUE))); + assertThat(Booleans.concat()).isEqualTo(EMPTY); + assertThat(Booleans.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Booleans.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Booleans.concat(ARRAY_FALSE)).isEqualTo(ARRAY_FALSE); + assertThat(Booleans.concat(ARRAY_FALSE)).isNotSameInstanceAs(ARRAY_FALSE); + assertThat(Booleans.concat(EMPTY, ARRAY_FALSE, EMPTY)).isEqualTo(ARRAY_FALSE); + assertThat(Booleans.concat(ARRAY_FALSE, ARRAY_FALSE, ARRAY_FALSE)) + .isEqualTo(new boolean[] {false, false, false}); + assertThat(Booleans.concat(ARRAY_FALSE, ARRAY_FALSE_TRUE)) + .isEqualTo(new boolean[] {false, false, true}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + boolean[][] arrays = new boolean[arraysDim1][]; + // it's shared to avoid using too much memory in tests + boolean[] sharedArray = new boolean[arraysDim2]; + Arrays.fill(arrays, sharedArray); + + try { + Booleans.concat(arrays); + fail(); + } catch (IllegalArgumentException expected) { + } } public void testEnsureCapacity() { - assertSame(EMPTY, Booleans.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY_FALSE, Booleans.ensureCapacity(ARRAY_FALSE, 0, 1)); - assertSame(ARRAY_FALSE, Booleans.ensureCapacity(ARRAY_FALSE, 1, 1)); - assertTrue( - Arrays.equals( - new boolean[] {true, false, false}, - Booleans.ensureCapacity(new boolean[] {true}, 2, 1))); + assertThat(Booleans.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Booleans.ensureCapacity(ARRAY_FALSE, 0, 1)).isSameInstanceAs(ARRAY_FALSE); + assertThat(Booleans.ensureCapacity(ARRAY_FALSE, 1, 1)).isSameInstanceAs(ARRAY_FALSE); + assertThat(Booleans.ensureCapacity(new boolean[] {true}, 2, 1)) + .isEqualTo(new boolean[] {true, false, false}); } public void testEnsureCapacity_fail() { - try { - Booleans.ensureCapacity(ARRAY_FALSE, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Booleans.ensureCapacity(ARRAY_FALSE, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Booleans.ensureCapacity(ARRAY_FALSE, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Booleans.ensureCapacity(ARRAY_FALSE, 1, -1)); } public void testJoin() { - assertEquals("", Booleans.join(",", EMPTY)); - assertEquals("false", Booleans.join(",", ARRAY_FALSE)); - assertEquals("false,true", Booleans.join(",", false, true)); - assertEquals("falsetruefalse", Booleans.join("", false, true, false)); + assertThat(Booleans.join(",", EMPTY)).isEmpty(); + assertThat(Booleans.join(",", ARRAY_FALSE)).isEqualTo("false"); + assertThat(Booleans.join(",", false, true)).isEqualTo("false,true"); + assertThat(Booleans.join("", false, true, false)).isEqualTo("falsetruefalse"); } public void testLexicographicalComparator() { @@ -172,10 +201,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Booleans.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -189,14 +219,14 @@ public void testReverse() { private static void testReverse(boolean[] input, boolean[] expectedOutput) { input = Arrays.copyOf(input, input.length); Booleans.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse( boolean[] input, int fromIndex, int toIndex, boolean[] expectedOutput) { input = Arrays.copyOf(input, input.length); Booleans.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -209,20 +239,251 @@ public void testReverseIndexed() { new boolean[] {true, true, false, false}, 1, 3, new boolean[] {true, false, true, false}); } + private static void testRotate(boolean[] input, int distance, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + boolean[] input, int distance, int fromIndex, int toIndex, boolean[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Booleans.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new boolean[] {}, -1, new boolean[] {}); + testRotate(new boolean[] {}, 0, new boolean[] {}); + testRotate(new boolean[] {}, 1, new boolean[] {}); + + testRotate(new boolean[] {true}, -2, new boolean[] {true}); + testRotate(new boolean[] {true}, -1, new boolean[] {true}); + testRotate(new boolean[] {true}, 0, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 2, new boolean[] {true}); + + testRotate(new boolean[] {true, false}, -3, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, -2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 0, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 1, new boolean[] {false, true}); + testRotate(new boolean[] {true, false}, 2, new boolean[] {true, false}); + testRotate(new boolean[] {true, false}, 3, new boolean[] {false, true}); + + testRotate(new boolean[] {true, false, true}, -5, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -4, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, -3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, -2, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, -1, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 0, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 1, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 2, new boolean[] {false, true, true}); + testRotate(new boolean[] {true, false, true}, 3, new boolean[] {true, false, true}); + testRotate(new boolean[] {true, false, true}, 4, new boolean[] {true, true, false}); + testRotate(new boolean[] {true, false, true}, 5, new boolean[] {false, true, true}); + + testRotate( + new boolean[] {true, false, true, false}, -9, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, -1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 0, new boolean[] {true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false}, 1, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 5, new boolean[] {false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false}, 9, new boolean[] {false, true, false, true}); + + testRotate( + new boolean[] {true, false, true, false, true}, + -6, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -4, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + -3, + new boolean[] {false, true, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + -1, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 0, + new boolean[] {true, false, true, false, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 1, + new boolean[] {true, true, false, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 3, + new boolean[] {true, false, true, true, false}); + testRotate( + new boolean[] {true, false, true, false, true}, + 4, + new boolean[] {false, true, false, true, true}); + testRotate( + new boolean[] {true, false, true, false, true}, + 6, + new boolean[] {true, true, false, true, false}); + } + + public void testRotateIndexed() { + testRotate(new boolean[] {}, 0, 0, 0, new boolean[] {}); + + testRotate(new boolean[] {true}, 0, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 0, 1, new boolean[] {true}); + testRotate(new boolean[] {true}, 1, 1, 1, new boolean[] {true}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 5, + 1, + 6, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 14, + 1, + 6, + new boolean[] {false, false, true, false, true, true, false}); + + // Rotate the first three elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 0, + 3, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 0, + 3, + new boolean[] {false, false, true, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 0, + 3, + new boolean[] {true, false, false, true, false, true, false}); + + // Rotate the last four elements + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -6, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -5, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -4, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + -1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 0, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 1, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 2, + 3, + 7, + new boolean[] {false, true, false, true, false, true, false}); + testRotate( + new boolean[] {false, true, false, true, false, true, false}, + 3, + 3, + 7, + new boolean[] {false, true, false, false, true, false, true}); + } + public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Booleans.toArray(none))); + assertThat(Booleans.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList(false); - assertTrue(Arrays.equals(ARRAY_FALSE, Booleans.toArray(one))); + assertThat(Booleans.toArray(one)).isEqualTo(ARRAY_FALSE); boolean[] array = {false, false, true}; List three = Arrays.asList(false, false, true); - assertTrue(Arrays.equals(array, Booleans.toArray(three))); + assertThat(Booleans.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Booleans.toArray(Booleans.asList(array)))); + assertThat(Booleans.toArray(Booleans.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -236,109 +497,120 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); boolean[] arr = Booleans.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList(false, true, null); - try { - Booleans.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Boolean> list = Arrays.asList(false, true, null); + assertThrows(NullPointerException.class, () -> Booleans.toArray(list)); } + @SuppressWarnings({"CollectionIsEmptyTruth", "CollectionIsNotEmptyTruth"}) public void testAsListIsEmpty() { - assertTrue(Booleans.asList(EMPTY).isEmpty()); - assertFalse(Booleans.asList(ARRAY_FALSE).isEmpty()); + assertThat(Booleans.asList(EMPTY).isEmpty()).isTrue(); + assertThat(Booleans.asList(ARRAY_FALSE).isEmpty()).isFalse(); } + @SuppressWarnings("CollectionSizeTruth") public void testAsListSize() { - assertEquals(0, Booleans.asList(EMPTY).size()); - assertEquals(1, Booleans.asList(ARRAY_FALSE).size()); - assertEquals(2, Booleans.asList(ARRAY_FALSE_TRUE).size()); + assertThat(Booleans.asList(EMPTY).size()).isEqualTo(0); + assertThat(Booleans.asList(ARRAY_FALSE).size()).isEqualTo(1); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).size()).isEqualTo(2); } + @SuppressWarnings("BooleanArrayIndexOfBoolean") public void testAsListIndexOf() { - assertEquals(-1, Booleans.asList(EMPTY).indexOf((Object) "wrong type")); - assertEquals(-1, Booleans.asList(EMPTY).indexOf(true)); - assertEquals(-1, Booleans.asList(ARRAY_FALSE).indexOf(true)); - assertEquals(0, Booleans.asList(ARRAY_FALSE).indexOf(false)); - assertEquals(1, Booleans.asList(ARRAY_FALSE_TRUE).indexOf(true)); + assertThat(Booleans.asList(EMPTY).indexOf((Object) "wrong type")).isEqualTo(-1); + assertThat(Booleans.asList(EMPTY).indexOf(true)).isEqualTo(-1); + assertThat(Booleans.asList(ARRAY_FALSE).indexOf(true)).isEqualTo(-1); + assertThat(Booleans.asList(ARRAY_FALSE).indexOf(false)).isEqualTo(0); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).indexOf(true)).isEqualTo(1); } public void testAsListLastIndexOf() { - assertEquals(-1, Booleans.asList(EMPTY).lastIndexOf((Object) "wrong type")); - assertEquals(-1, Booleans.asList(EMPTY).lastIndexOf(true)); - assertEquals(-1, Booleans.asList(ARRAY_FALSE).lastIndexOf(true)); - assertEquals(1, Booleans.asList(ARRAY_FALSE_TRUE).lastIndexOf(true)); - assertEquals(1, Booleans.asList(ARRAY_FALSE_FALSE).lastIndexOf(false)); + assertThat(Booleans.asList(EMPTY).lastIndexOf((Object) "wrong type")).isEqualTo(-1); + assertThat(Booleans.asList(EMPTY).lastIndexOf(true)).isEqualTo(-1); + assertThat(Booleans.asList(ARRAY_FALSE).lastIndexOf(true)).isEqualTo(-1); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).lastIndexOf(true)).isEqualTo(1); + assertThat(Booleans.asList(ARRAY_FALSE_FALSE).lastIndexOf(false)).isEqualTo(1); } + @SuppressWarnings({"BooleanArrayContainsBoolean", "CollectionDoesNotContainTruth"}) public void testAsListContains() { - assertFalse(Booleans.asList(EMPTY).contains((Object) "wrong type")); - assertFalse(Booleans.asList(EMPTY).contains(true)); - assertFalse(Booleans.asList(ARRAY_FALSE).contains(true)); - assertTrue(Booleans.asList(ARRAY_TRUE).contains(true)); - assertTrue(Booleans.asList(ARRAY_FALSE_TRUE).contains(false)); - assertTrue(Booleans.asList(ARRAY_FALSE_TRUE).contains(true)); + assertThat(Booleans.asList(EMPTY).contains((Object) "wrong type")).isFalse(); + assertThat(Booleans.asList(EMPTY).contains(true)).isFalse(); + assertThat(Booleans.asList(ARRAY_FALSE).contains(true)).isFalse(); + assertThat(Booleans.asList(ARRAY_TRUE).contains(true)).isTrue(); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).contains(false)).isTrue(); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).contains(true)).isTrue(); } public void testAsListEquals() { - assertEquals(Booleans.asList(EMPTY), Collections.emptyList()); - assertEquals(Booleans.asList(ARRAY_FALSE), Booleans.asList(ARRAY_FALSE)); - assertFalse(Booleans.asList(ARRAY_FALSE).equals(ARRAY_FALSE)); - assertFalse(Booleans.asList(ARRAY_FALSE).equals(null)); - assertFalse(Booleans.asList(ARRAY_FALSE).equals(Booleans.asList(ARRAY_FALSE_TRUE))); - assertFalse(Booleans.asList(ARRAY_FALSE_FALSE).equals(Booleans.asList(ARRAY_FALSE_TRUE))); + assertThat(Booleans.asList(EMPTY).equals(Collections.emptyList())).isTrue(); + assertThat(Booleans.asList(ARRAY_FALSE).equals(Booleans.asList(ARRAY_FALSE))).isTrue(); + @SuppressWarnings("EqualsIncompatibleType") + boolean listEqualsArray = Booleans.asList(ARRAY_FALSE).equals(ARRAY_FALSE); + assertThat(listEqualsArray).isFalse(); + assertThat(Booleans.asList(ARRAY_FALSE).equals(null)).isFalse(); + assertThat(Booleans.asList(ARRAY_FALSE).equals(Booleans.asList(ARRAY_FALSE_TRUE))).isFalse(); + assertThat(Booleans.asList(ARRAY_FALSE_FALSE).equals(Booleans.asList(ARRAY_FALSE_TRUE))) + .isFalse(); assertEquals(1, Booleans.asList(ARRAY_FALSE_TRUE).lastIndexOf(true)); List reference = Booleans.asList(ARRAY_FALSE); assertEquals(Booleans.asList(ARRAY_FALSE), reference); // Explicitly call `equals`; `assertEquals` might return fast - assertTrue(reference.equals(reference)); + assertThat(reference.equals(reference)).isTrue(); } public void testAsListHashcode() { - assertEquals(1, Booleans.asList(EMPTY).hashCode()); - assertEquals(Booleans.asList(ARRAY_FALSE).hashCode(), Booleans.asList(ARRAY_FALSE).hashCode()); + assertThat(Booleans.asList(EMPTY).hashCode()).isEqualTo(1); + assertThat(Booleans.asList(ARRAY_FALSE).hashCode()) + .isEqualTo(Booleans.asList(ARRAY_FALSE).hashCode()); List reference = Booleans.asList(ARRAY_FALSE); - assertEquals(Booleans.asList(ARRAY_FALSE).hashCode(), reference.hashCode()); + assertThat(reference.hashCode()).isEqualTo(Booleans.asList(ARRAY_FALSE).hashCode()); } public void testAsListToString() { - assertEquals("[false]", Booleans.asList(ARRAY_FALSE).toString()); - assertEquals("[false, true]", Booleans.asList(ARRAY_FALSE_TRUE).toString()); + assertThat(Booleans.asList(ARRAY_FALSE).toString()).isEqualTo("[false]"); + assertThat(Booleans.asList(ARRAY_FALSE_TRUE).toString()).isEqualTo("[false, true]"); } public void testAsListSet() { List list = Booleans.asList(ARRAY_FALSE); - assertFalse(list.set(0, true)); - assertTrue(list.set(0, false)); - try { - list.set(0, null); - fail(); - } catch (NullPointerException expected) { - } - try { - list.set(1, true); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThat(list.set(0, true)).isFalse(); + assertThat(list.set(0, false)).isTrue(); + assertThrows(NullPointerException.class, () -> list.set(0, null)); + assertThrows(IndexOutOfBoundsException.class, () -> list.set(1, true)); + } + + public void testAsListCanonicalValues() { + List list = Booleans.asList(true, false); + assertThat(list.get(0)).isSameInstanceAs(true); + assertThat(list.get(1)).isSameInstanceAs(false); + @SuppressWarnings("deprecation") + Boolean anotherTrue = new Boolean(true); + @SuppressWarnings("deprecation") + Boolean anotherFalse = new Boolean(false); + list.set(0, anotherTrue); + assertThat(list.get(0)).isSameInstanceAs(true); + list.set(1, anotherFalse); + assertThat(list.get(1)).isSameInstanceAs(false); } public void testCountTrue() { - assertEquals(0, Booleans.countTrue()); - assertEquals(0, Booleans.countTrue(false)); - assertEquals(1, Booleans.countTrue(true)); - assertEquals(3, Booleans.countTrue(false, true, false, true, false, true)); - assertEquals(1, Booleans.countTrue(false, false, true, false, false)); + assertThat(Booleans.countTrue()).isEqualTo(0); + assertThat(Booleans.countTrue(false)).isEqualTo(0); + assertThat(Booleans.countTrue(true)).isEqualTo(1); + assertThat(Booleans.countTrue(false, true, false, true, false, true)).isEqualTo(3); + assertThat(Booleans.countTrue(false, false, true, false, false)).isEqualTo(1); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Booleans.class); diff --git a/android/guava-tests/test/com/google/common/primitives/ByteArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/ByteArrayAsListTest.java index c3d0be1ab76c..b4676078bada 100644 --- a/android/guava-tests/test/com/google/common/primitives/ByteArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ByteArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Bytes#asList(byte[])}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class ByteArrayAsListTest extends TestCase { private static List asList(Byte[] values) { @@ -48,6 +52,7 @@ private static List asList(Byte[] values) { return Bytes.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/BytesTest.java b/android/guava-tests/test/com/google/common/primitives/BytesTest.java index 233a0150cedb..3aa23f5f7b2e 100644 --- a/android/guava-tests/test/com/google/common/primitives/BytesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BytesTest.java @@ -16,8 +16,12 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import java.util.Arrays; @@ -25,12 +29,15 @@ import java.util.Collections; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Bytes}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) public class BytesTest extends TestCase { private static final byte[] EMPTY = {}; @@ -41,127 +48,149 @@ public class BytesTest extends TestCase { public void testHashCode() { for (byte value : VALUES) { - assertEquals(((Byte) value).hashCode(), Bytes.hashCode(value)); + assertThat(Bytes.hashCode(value)).isEqualTo(((Byte) value).hashCode()); } } public void testContains() { - assertFalse(Bytes.contains(EMPTY, (byte) 1)); - assertFalse(Bytes.contains(ARRAY1, (byte) 2)); - assertFalse(Bytes.contains(ARRAY234, (byte) 1)); - assertTrue(Bytes.contains(new byte[] {(byte) -1}, (byte) -1)); - assertTrue(Bytes.contains(ARRAY234, (byte) 2)); - assertTrue(Bytes.contains(ARRAY234, (byte) 3)); - assertTrue(Bytes.contains(ARRAY234, (byte) 4)); + assertThat(Bytes.contains(EMPTY, (byte) 1)).isFalse(); + assertThat(Bytes.contains(ARRAY1, (byte) 2)).isFalse(); + assertThat(Bytes.contains(ARRAY234, (byte) 1)).isFalse(); + assertThat(Bytes.contains(new byte[] {(byte) -1}, (byte) -1)).isTrue(); + assertThat(Bytes.contains(ARRAY234, (byte) 2)).isTrue(); + assertThat(Bytes.contains(ARRAY234, (byte) 3)).isTrue(); + assertThat(Bytes.contains(ARRAY234, (byte) 4)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Bytes.indexOf(EMPTY, (byte) 1)); - assertEquals(-1, Bytes.indexOf(ARRAY1, (byte) 2)); - assertEquals(-1, Bytes.indexOf(ARRAY234, (byte) 1)); - assertEquals(0, Bytes.indexOf(new byte[] {(byte) -1}, (byte) -1)); - assertEquals(0, Bytes.indexOf(ARRAY234, (byte) 2)); - assertEquals(1, Bytes.indexOf(ARRAY234, (byte) 3)); - assertEquals(2, Bytes.indexOf(ARRAY234, (byte) 4)); - assertEquals(1, Bytes.indexOf(new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3}, (byte) 3)); + assertThat(Bytes.indexOf(EMPTY, (byte) 1)).isEqualTo(-1); + assertThat(Bytes.indexOf(ARRAY1, (byte) 2)).isEqualTo(-1); + assertThat(Bytes.indexOf(ARRAY234, (byte) 1)).isEqualTo(-1); + assertThat(Bytes.indexOf(new byte[] {(byte) -1}, (byte) -1)).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, (byte) 2)).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, (byte) 3)).isEqualTo(1); + assertThat(Bytes.indexOf(ARRAY234, (byte) 4)).isEqualTo(2); + assertThat(Bytes.indexOf(new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3}, (byte) 3)) + .isEqualTo(1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Bytes.indexOf(EMPTY, EMPTY)); - assertEquals(0, Bytes.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Bytes.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Bytes.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Bytes.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Bytes.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Bytes.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Bytes.indexOf(ARRAY234, new byte[] {(byte) 2, (byte) 3})); - assertEquals(1, Bytes.indexOf(ARRAY234, new byte[] {(byte) 3, (byte) 4})); - assertEquals(1, Bytes.indexOf(ARRAY234, new byte[] {(byte) 3})); - assertEquals(2, Bytes.indexOf(ARRAY234, new byte[] {(byte) 4})); - assertEquals( - 1, - Bytes.indexOf( - new byte[] {(byte) 2, (byte) 3, (byte) 3, (byte) 3, (byte) 3}, new byte[] {(byte) 3})); - assertEquals( - 2, - Bytes.indexOf( - new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3, (byte) 4, (byte) 2, (byte) 3}, - new byte[] {(byte) 2, (byte) 3, (byte) 4})); - assertEquals( - 1, - Bytes.indexOf( - new byte[] {(byte) 2, (byte) 2, (byte) 3, (byte) 4, (byte) 2, (byte) 3, (byte) 4}, - new byte[] {(byte) 2, (byte) 3, (byte) 4})); - assertEquals( - -1, - Bytes.indexOf( - new byte[] {(byte) 4, (byte) 3, (byte) 2}, new byte[] {(byte) 2, (byte) 3, (byte) 4})); + assertThat(Bytes.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Bytes.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Bytes.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Bytes.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Bytes.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, new byte[] {(byte) 2, (byte) 3})).isEqualTo(0); + assertThat(Bytes.indexOf(ARRAY234, new byte[] {(byte) 3, (byte) 4})).isEqualTo(1); + assertThat(Bytes.indexOf(ARRAY234, new byte[] {(byte) 3})).isEqualTo(1); + assertThat(Bytes.indexOf(ARRAY234, new byte[] {(byte) 4})).isEqualTo(2); + assertThat( + Bytes.indexOf( + new byte[] {(byte) 2, (byte) 3, (byte) 3, (byte) 3, (byte) 3}, + new byte[] {(byte) 3})) + .isEqualTo(1); + assertThat( + Bytes.indexOf( + new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3, (byte) 4, (byte) 2, (byte) 3}, + new byte[] {(byte) 2, (byte) 3, (byte) 4})) + .isEqualTo(2); + assertThat( + Bytes.indexOf( + new byte[] {(byte) 2, (byte) 2, (byte) 3, (byte) 4, (byte) 2, (byte) 3, (byte) 4}, + new byte[] {(byte) 2, (byte) 3, (byte) 4})) + .isEqualTo(1); + assertThat( + Bytes.indexOf( + new byte[] {(byte) 4, (byte) 3, (byte) 2}, + new byte[] {(byte) 2, (byte) 3, (byte) 4})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Bytes.lastIndexOf(EMPTY, (byte) 1)); - assertEquals(-1, Bytes.lastIndexOf(ARRAY1, (byte) 2)); - assertEquals(-1, Bytes.lastIndexOf(ARRAY234, (byte) 1)); - assertEquals(0, Bytes.lastIndexOf(new byte[] {(byte) -1}, (byte) -1)); - assertEquals(0, Bytes.lastIndexOf(ARRAY234, (byte) 2)); - assertEquals(1, Bytes.lastIndexOf(ARRAY234, (byte) 3)); - assertEquals(2, Bytes.lastIndexOf(ARRAY234, (byte) 4)); - assertEquals( - 3, Bytes.lastIndexOf(new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3}, (byte) 3)); + assertThat(Bytes.lastIndexOf(EMPTY, (byte) 1)).isEqualTo(-1); + assertThat(Bytes.lastIndexOf(ARRAY1, (byte) 2)).isEqualTo(-1); + assertThat(Bytes.lastIndexOf(ARRAY234, (byte) 1)).isEqualTo(-1); + assertThat(Bytes.lastIndexOf(new byte[] {(byte) -1}, (byte) -1)).isEqualTo(0); + assertThat(Bytes.lastIndexOf(ARRAY234, (byte) 2)).isEqualTo(0); + assertThat(Bytes.lastIndexOf(ARRAY234, (byte) 3)).isEqualTo(1); + assertThat(Bytes.lastIndexOf(ARRAY234, (byte) 4)).isEqualTo(2); + assertThat(Bytes.lastIndexOf(new byte[] {(byte) 2, (byte) 3, (byte) 2, (byte) 3}, (byte) 3)) + .isEqualTo(3); } public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Bytes.concat())); - assertTrue(Arrays.equals(EMPTY, Bytes.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Bytes.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Bytes.concat(ARRAY1))); - assertNotSame(ARRAY1, Bytes.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Bytes.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new byte[] {(byte) 1, (byte) 1, (byte) 1}, Bytes.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new byte[] {(byte) 1, (byte) 2, (byte) 3, (byte) 4}, Bytes.concat(ARRAY1, ARRAY234))); + assertThat(Bytes.concat()).isEqualTo(EMPTY); + assertThat(Bytes.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Bytes.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Bytes.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Bytes.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Bytes.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Bytes.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new byte[] {(byte) 1, (byte) 1, (byte) 1}); + assertThat(Bytes.concat(ARRAY1, ARRAY234)) + .isEqualTo(new byte[] {(byte) 1, (byte) 2, (byte) 3, (byte) 4}); } - public void testEnsureCapacity() { - assertSame(EMPTY, Bytes.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Bytes.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Bytes.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new byte[] {(byte) 1, (byte) 0, (byte) 0}, Bytes.ensureCapacity(ARRAY1, 2, 1))); + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); } - public void testEnsureCapacity_fail() { - try { - Bytes.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + byte[][] arrays = new byte[arraysDim1][]; + // it's shared to avoid using too much memory in tests + byte[] sharedArray = new byte[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - // notice that this should even fail when no growth was needed - Bytes.ensureCapacity(ARRAY1, 1, -1); + Bytes.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } + public void testEnsureCapacity() { + assertThat(Bytes.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Bytes.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Bytes.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat(Bytes.ensureCapacity(ARRAY1, 2, 1)) + .isEqualTo(new byte[] {(byte) 1, (byte) 0, (byte) 0}); + } + + public void testEnsureCapacity_fail() { + assertThrows(IllegalArgumentException.class, () -> Bytes.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Bytes.ensureCapacity(ARRAY1, 1, -1)); + } + public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Bytes.toArray(none))); + assertThat(Bytes.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((byte) 1); - assertTrue(Arrays.equals(ARRAY1, Bytes.toArray(one))); + assertThat(Bytes.toArray(one)).isEqualTo(ARRAY1); byte[] array = {(byte) 0, (byte) 1, (byte) 0x55}; List three = Arrays.asList((byte) 0, (byte) 1, (byte) 0x55); - assertTrue(Arrays.equals(array, Bytes.toArray(three))); + assertThat(Bytes.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Bytes.toArray(Bytes.asList(array)))); + assertThat(Bytes.toArray(Bytes.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -171,21 +200,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); byte[] arr = Bytes.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((byte) 0, (byte) 1, null); - try { - Bytes.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Byte> list = Arrays.asList((byte) 0, (byte) 1, null); + assertThrows(NullPointerException.class, () -> Bytes.toArray(list)); } public void testToArray_withConversion() { @@ -198,21 +223,22 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Bytes.toArray(bytes))); - assertTrue(Arrays.equals(array, Bytes.toArray(shorts))); - assertTrue(Arrays.equals(array, Bytes.toArray(ints))); - assertTrue(Arrays.equals(array, Bytes.toArray(floats))); - assertTrue(Arrays.equals(array, Bytes.toArray(longs))); - assertTrue(Arrays.equals(array, Bytes.toArray(doubles))); + assertThat(Bytes.toArray(bytes)).isEqualTo(array); + assertThat(Bytes.toArray(shorts)).isEqualTo(array); + assertThat(Bytes.toArray(ints)).isEqualTo(array); + assertThat(Bytes.toArray(floats)).isEqualTo(array); + assertThat(Bytes.toArray(longs)).isEqualTo(array); + assertThat(Bytes.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { byte[] array = {(byte) 0, (byte) 1}; List list = Bytes.asList(array); list.set(0, (byte) 2); - assertTrue(Arrays.equals(new byte[] {(byte) 2, (byte) 1}, array)); + assertThat(array).isEqualTo(new byte[] {(byte) 2, (byte) 1}); array[1] = (byte) 3; - assertEquals(Arrays.asList((byte) 2, (byte) 3), list); + assertThat(list).containsExactly((byte) 2, (byte) 3).inOrder(); } public void testAsList_toArray_roundTrip() { @@ -222,21 +248,21 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (byte) 4); - assertTrue(Arrays.equals(new byte[] {(byte) 0, (byte) 1, (byte) 2}, newArray)); + assertThat(newArray).isEqualTo(new byte[] {(byte) 0, (byte) 1, (byte) 2}); newArray[1] = (byte) 5; - assertEquals((byte) 1, (byte) list.get(1)); + assertThat((byte) list.get(1)).isEqualTo((byte) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { byte[] array = {(byte) 0, (byte) 1, (byte) 2, (byte) 3}; List list = Bytes.asList(array); - assertTrue(Arrays.equals(new byte[] {(byte) 1, (byte) 2}, Bytes.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new byte[] {}, Bytes.toArray(list.subList(2, 2)))); + assertThat(Bytes.toArray(list.subList(1, 3))).isEqualTo(new byte[] {(byte) 1, (byte) 2}); + assertThat(Bytes.toArray(list.subList(2, 2))).isEqualTo(new byte[] {}); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Bytes.asList(EMPTY)); + assertThat(Bytes.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } public void testReverse() { @@ -250,13 +276,13 @@ public void testReverse() { private static void testReverse(byte[] input, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); Bytes.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse(byte[] input, int fromIndex, int toIndex, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); Bytes.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -268,6 +294,104 @@ public void testReverseIndexed() { testReverse(new byte[] {-1, 1, -2, 2}, 1, 3, new byte[] {-1, -2, 1, 2}); } + private static void testRotate(byte[] input, int distance, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + byte[] input, int distance, int fromIndex, int toIndex, byte[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Bytes.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new byte[] {}, -1, new byte[] {}); + testRotate(new byte[] {}, 0, new byte[] {}); + testRotate(new byte[] {}, 1, new byte[] {}); + + testRotate(new byte[] {1}, -2, new byte[] {1}); + testRotate(new byte[] {1}, -1, new byte[] {1}); + testRotate(new byte[] {1}, 0, new byte[] {1}); + testRotate(new byte[] {1}, 1, new byte[] {1}); + testRotate(new byte[] {1}, 2, new byte[] {1}); + + testRotate(new byte[] {1, 2}, -3, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, -2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 0, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 1, new byte[] {2, 1}); + testRotate(new byte[] {1, 2}, 2, new byte[] {1, 2}); + testRotate(new byte[] {1, 2}, 3, new byte[] {2, 1}); + + testRotate(new byte[] {1, 2, 3}, -5, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -4, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, -3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, -2, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, -1, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 0, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 1, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 2, new byte[] {2, 3, 1}); + testRotate(new byte[] {1, 2, 3}, 3, new byte[] {1, 2, 3}); + testRotate(new byte[] {1, 2, 3}, 4, new byte[] {3, 1, 2}); + testRotate(new byte[] {1, 2, 3}, 5, new byte[] {2, 3, 1}); + + testRotate(new byte[] {1, 2, 3, 4}, -9, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -5, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, -1, new byte[] {2, 3, 4, 1}); + testRotate(new byte[] {1, 2, 3, 4}, 0, new byte[] {1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4}, 1, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 5, new byte[] {4, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4}, 9, new byte[] {4, 1, 2, 3}); + + testRotate(new byte[] {1, 2, 3, 4, 5}, -6, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -4, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -3, new byte[] {4, 5, 1, 2, 3}); + testRotate(new byte[] {1, 2, 3, 4, 5}, -1, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 0, new byte[] {1, 2, 3, 4, 5}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 1, new byte[] {5, 1, 2, 3, 4}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 3, new byte[] {3, 4, 5, 1, 2}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 4, new byte[] {2, 3, 4, 5, 1}); + testRotate(new byte[] {1, 2, 3, 4, 5}, 6, new byte[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new byte[] {}, 0, 0, 0, new byte[] {}); + + testRotate(new byte[] {1}, 0, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 0, 1, new byte[] {1}); + testRotate(new byte[] {1}, 1, 1, 1, new byte[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new byte[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new byte[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new byte[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new byte[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new byte[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new byte[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new byte[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new byte[] {0, 1, 2, 4, 5, 6, 3}); + } + + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Bytes.class); diff --git a/android/guava-tests/test/com/google/common/primitives/CharArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/CharArrayAsListTest.java index fa2a53dabc1a..fd1dafaf1b3b 100644 --- a/android/guava-tests/test/com/google/common/primitives/CharArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/CharArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Chars#asList(char[])}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class CharArrayAsListTest extends TestCase { private static List asList(Character[] values) { @@ -48,6 +52,7 @@ private static List asList(Character[] values) { return Chars.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/CharsTest.java b/android/guava-tests/test/com/google/common/primitives/CharsTest.java index f1da7fd8e67a..30e1c3990395 100644 --- a/android/guava-tests/test/com/google/common/primitives/CharsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/CharsTest.java @@ -16,8 +16,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.Chars.max; +import static com.google.common.primitives.Chars.min; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; @@ -28,6 +35,8 @@ import java.util.List; import java.util.Locale; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Chars}. @@ -35,7 +44,7 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless +@NullMarked public class CharsTest extends TestCase { private static final char[] EMPTY = {}; private static final char[] ARRAY1 = {(char) 1}; @@ -48,13 +57,13 @@ public class CharsTest extends TestCase { public void testHashCode() { for (char value : VALUES) { - assertEquals(((Character) value).hashCode(), Chars.hashCode(value)); + assertThat(Chars.hashCode(value)).isEqualTo(((Character) value).hashCode()); } } public void testCheckedCast() { for (char value : VALUES) { - assertEquals(value, Chars.checkedCast((long) value)); + assertThat(Chars.checkedCast((long) value)).isEqualTo(value); } assertCastFails(GREATEST + 1L); assertCastFails(LEAST - 1L); @@ -64,12 +73,12 @@ public void testCheckedCast() { public void testSaturatedCast() { for (char value : VALUES) { - assertEquals(value, Chars.saturatedCast((long) value)); + assertThat(Chars.saturatedCast((long) value)).isEqualTo(value); } - assertEquals(GREATEST, Chars.saturatedCast(GREATEST + 1L)); - assertEquals(LEAST, Chars.saturatedCast(LEAST - 1L)); - assertEquals(GREATEST, Chars.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, Chars.saturatedCast(Long.MIN_VALUE)); + assertThat(Chars.saturatedCast(GREATEST + 1L)).isEqualTo(GREATEST); + assertThat(Chars.saturatedCast(LEAST - 1L)).isEqualTo(LEAST); + assertThat(Chars.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(Chars.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } private void assertCastFails(long value) { @@ -77,163 +86,184 @@ private void assertCastFails(long value) { Chars.checkedCast(value); fail("Cast to char should have failed: " + value); } catch (IllegalArgumentException ex) { - assertTrue( - value + " not found in exception text: " + ex.getMessage(), - ex.getMessage().contains(String.valueOf(value))); + assertWithMessage(value + " not found in exception text: " + ex.getMessage()) + .that(ex.getMessage().contains(String.valueOf(value))) + .isTrue(); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (char x : VALUES) { for (char y : VALUES) { - // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Character.valueOf(x).compareTo(y), Chars.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Math.signum(Chars.compare(x, y))) + .isEqualTo(Math.signum(Character.valueOf(x).compareTo(y))); } } } public void testContains() { - assertFalse(Chars.contains(EMPTY, (char) 1)); - assertFalse(Chars.contains(ARRAY1, (char) 2)); - assertFalse(Chars.contains(ARRAY234, (char) 1)); - assertTrue(Chars.contains(new char[] {(char) -1}, (char) -1)); - assertTrue(Chars.contains(ARRAY234, (char) 2)); - assertTrue(Chars.contains(ARRAY234, (char) 3)); - assertTrue(Chars.contains(ARRAY234, (char) 4)); + assertThat(Chars.contains(EMPTY, (char) 1)).isFalse(); + assertThat(Chars.contains(ARRAY1, (char) 2)).isFalse(); + assertThat(Chars.contains(ARRAY234, (char) 1)).isFalse(); + assertThat(Chars.contains(new char[] {(char) -1}, (char) -1)).isTrue(); + assertThat(Chars.contains(ARRAY234, (char) 2)).isTrue(); + assertThat(Chars.contains(ARRAY234, (char) 3)).isTrue(); + assertThat(Chars.contains(ARRAY234, (char) 4)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Chars.indexOf(EMPTY, (char) 1)); - assertEquals(-1, Chars.indexOf(ARRAY1, (char) 2)); - assertEquals(-1, Chars.indexOf(ARRAY234, (char) 1)); - assertEquals(0, Chars.indexOf(new char[] {(char) -1}, (char) -1)); - assertEquals(0, Chars.indexOf(ARRAY234, (char) 2)); - assertEquals(1, Chars.indexOf(ARRAY234, (char) 3)); - assertEquals(2, Chars.indexOf(ARRAY234, (char) 4)); - assertEquals(1, Chars.indexOf(new char[] {(char) 2, (char) 3, (char) 2, (char) 3}, (char) 3)); + assertThat(Chars.indexOf(EMPTY, (char) 1)).isEqualTo(-1); + assertThat(Chars.indexOf(ARRAY1, (char) 2)).isEqualTo(-1); + assertThat(Chars.indexOf(ARRAY234, (char) 1)).isEqualTo(-1); + assertThat(Chars.indexOf(new char[] {(char) -1}, (char) -1)).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, (char) 2)).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, (char) 3)).isEqualTo(1); + assertThat(Chars.indexOf(ARRAY234, (char) 4)).isEqualTo(2); + assertThat(Chars.indexOf(new char[] {(char) 2, (char) 3, (char) 2, (char) 3}, (char) 3)) + .isEqualTo(1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Chars.indexOf(EMPTY, EMPTY)); - assertEquals(0, Chars.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Chars.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Chars.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Chars.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Chars.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Chars.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Chars.indexOf(ARRAY234, new char[] {(char) 2, (char) 3})); - assertEquals(1, Chars.indexOf(ARRAY234, new char[] {(char) 3, (char) 4})); - assertEquals(1, Chars.indexOf(ARRAY234, new char[] {(char) 3})); - assertEquals(2, Chars.indexOf(ARRAY234, new char[] {(char) 4})); - assertEquals( - 1, - Chars.indexOf( - new char[] {(char) 2, (char) 3, (char) 3, (char) 3, (char) 3}, new char[] {(char) 3})); - assertEquals( - 2, - Chars.indexOf( - new char[] {(char) 2, (char) 3, (char) 2, (char) 3, (char) 4, (char) 2, (char) 3}, - new char[] {(char) 2, (char) 3, (char) 4})); - assertEquals( - 1, - Chars.indexOf( - new char[] {(char) 2, (char) 2, (char) 3, (char) 4, (char) 2, (char) 3, (char) 4}, - new char[] {(char) 2, (char) 3, (char) 4})); - assertEquals( - -1, - Chars.indexOf( - new char[] {(char) 4, (char) 3, (char) 2}, new char[] {(char) 2, (char) 3, (char) 4})); + assertThat(Chars.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Chars.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Chars.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Chars.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Chars.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, new char[] {(char) 2, (char) 3})).isEqualTo(0); + assertThat(Chars.indexOf(ARRAY234, new char[] {(char) 3, (char) 4})).isEqualTo(1); + assertThat(Chars.indexOf(ARRAY234, new char[] {(char) 3})).isEqualTo(1); + assertThat(Chars.indexOf(ARRAY234, new char[] {(char) 4})).isEqualTo(2); + assertThat( + Chars.indexOf( + new char[] {(char) 2, (char) 3, (char) 3, (char) 3, (char) 3}, + new char[] {(char) 3})) + .isEqualTo(1); + assertThat( + Chars.indexOf( + new char[] {(char) 2, (char) 3, (char) 2, (char) 3, (char) 4, (char) 2, (char) 3}, + new char[] {(char) 2, (char) 3, (char) 4})) + .isEqualTo(2); + assertThat( + Chars.indexOf( + new char[] {(char) 2, (char) 2, (char) 3, (char) 4, (char) 2, (char) 3, (char) 4}, + new char[] {(char) 2, (char) 3, (char) 4})) + .isEqualTo(1); + assertThat( + Chars.indexOf( + new char[] {(char) 4, (char) 3, (char) 2}, + new char[] {(char) 2, (char) 3, (char) 4})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Chars.lastIndexOf(EMPTY, (char) 1)); - assertEquals(-1, Chars.lastIndexOf(ARRAY1, (char) 2)); - assertEquals(-1, Chars.lastIndexOf(ARRAY234, (char) 1)); - assertEquals(0, Chars.lastIndexOf(new char[] {(char) -1}, (char) -1)); - assertEquals(0, Chars.lastIndexOf(ARRAY234, (char) 2)); - assertEquals(1, Chars.lastIndexOf(ARRAY234, (char) 3)); - assertEquals(2, Chars.lastIndexOf(ARRAY234, (char) 4)); - assertEquals( - 3, Chars.lastIndexOf(new char[] {(char) 2, (char) 3, (char) 2, (char) 3}, (char) 3)); + assertThat(Chars.lastIndexOf(EMPTY, (char) 1)).isEqualTo(-1); + assertThat(Chars.lastIndexOf(ARRAY1, (char) 2)).isEqualTo(-1); + assertThat(Chars.lastIndexOf(ARRAY234, (char) 1)).isEqualTo(-1); + assertThat(Chars.lastIndexOf(new char[] {(char) -1}, (char) -1)).isEqualTo(0); + assertThat(Chars.lastIndexOf(ARRAY234, (char) 2)).isEqualTo(0); + assertThat(Chars.lastIndexOf(ARRAY234, (char) 3)).isEqualTo(1); + assertThat(Chars.lastIndexOf(ARRAY234, (char) 4)).isEqualTo(2); + assertThat(Chars.lastIndexOf(new char[] {(char) 2, (char) 3, (char) 2, (char) 3}, (char) 3)) + .isEqualTo(3); } public void testMax_noArgs() { - try { - Chars.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, Chars.max(LEAST)); - assertEquals(GREATEST, Chars.max(GREATEST)); - assertEquals( - (char) 9, Chars.max((char) 8, (char) 6, (char) 7, (char) 5, (char) 3, (char) 0, (char) 9)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max((char) 8, (char) 6, (char) 7, (char) 5, (char) 3, (char) 0, (char) 9)) + .isEqualTo((char) 9); } public void testMin_noArgs() { - try { - Chars.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, Chars.min(LEAST)); - assertEquals(GREATEST, Chars.min(GREATEST)); - assertEquals( - (char) 0, Chars.min((char) 8, (char) 6, (char) 7, (char) 5, (char) 3, (char) 0, (char) 9)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((char) 8, (char) 6, (char) 7, (char) 5, (char) 3, (char) 0, (char) 9)) + .isEqualTo((char) 0); } public void testConstrainToRange() { - assertEquals((char) 1, Chars.constrainToRange((char) 1, (char) 0, (char) 5)); - assertEquals((char) 1, Chars.constrainToRange((char) 1, (char) 1, (char) 5)); - assertEquals((char) 3, Chars.constrainToRange((char) 1, (char) 3, (char) 5)); - assertEquals((char) 254, Chars.constrainToRange((char) 255, (char) 250, (char) 254)); - assertEquals((char) 2, Chars.constrainToRange((char) 5, (char) 2, (char) 2)); + assertThat(Chars.constrainToRange((char) 1, (char) 0, (char) 5)).isEqualTo((char) 1); + assertThat(Chars.constrainToRange((char) 1, (char) 1, (char) 5)).isEqualTo((char) 1); + assertThat(Chars.constrainToRange((char) 1, (char) 3, (char) 5)).isEqualTo((char) 3); + assertThat(Chars.constrainToRange((char) 255, (char) 250, (char) 254)).isEqualTo((char) 254); + assertThat(Chars.constrainToRange((char) 5, (char) 2, (char) 2)).isEqualTo((char) 2); + assertThrows( + IllegalArgumentException.class, () -> Chars.constrainToRange((char) 1, (char) 3, (char) 2)); + } + + public void testConcat() { + assertThat(Chars.concat()).isEqualTo(EMPTY); + assertThat(Chars.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Chars.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Chars.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Chars.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Chars.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Chars.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new char[] {(char) 1, (char) 1, (char) 1}); + assertThat(Chars.concat(ARRAY1, ARRAY234)) + .isEqualTo(new char[] {(char) 1, (char) 2, (char) 3, (char) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + char[][] arrays = new char[arraysDim1][]; + // it's shared to avoid using too much memory in tests + char[] sharedArray = new char[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Chars.constrainToRange((char) 1, (char) 3, (char) 2); + Chars.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Chars.concat())); - assertTrue(Arrays.equals(EMPTY, Chars.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Chars.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Chars.concat(ARRAY1))); - assertNotSame(ARRAY1, Chars.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Chars.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new char[] {(char) 1, (char) 1, (char) 1}, Chars.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new char[] {(char) 1, (char) 2, (char) 3, (char) 4}, Chars.concat(ARRAY1, ARRAY234))); - } - @GwtIncompatible // Chars.fromByteArray public void testFromByteArray() { - assertEquals('\u2345', Chars.fromByteArray(new byte[] {0x23, 0x45, (byte) 0xDC})); - assertEquals('\uFEDC', Chars.fromByteArray(new byte[] {(byte) 0xFE, (byte) 0xDC})); + assertThat(Chars.fromByteArray(new byte[] {0x23, 0x45, (byte) 0xDC})).isEqualTo('\u2345'); + assertThat(Chars.fromByteArray(new byte[] {(byte) 0xFE, (byte) 0xDC})).isEqualTo('\uFEDC'); } @GwtIncompatible // Chars.fromByteArray public void testFromByteArrayFails() { - try { - Chars.fromByteArray(new byte[Chars.BYTES - 1]); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Chars.fromByteArray(new byte[Chars.BYTES - 1])); } @GwtIncompatible // Chars.fromBytes public void testFromBytes() { - assertEquals('\u2345', Chars.fromBytes((byte) 0x23, (byte) 0x45)); - assertEquals('\uFEDC', Chars.fromBytes((byte) 0xFE, (byte) 0xDC)); + assertThat(Chars.fromBytes((byte) 0x23, (byte) 0x45)).isEqualTo('\u2345'); + assertThat(Chars.fromBytes((byte) 0xFE, (byte) 0xDC)).isEqualTo('\uFEDC'); } @GwtIncompatible // Chars.fromByteArray, Chars.toByteArray @@ -242,59 +272,50 @@ public void testByteArrayRoundTrips() { for (int hi = 0; hi < 256; hi++) { for (int lo = 0; lo < 256; lo++) { char result = Chars.fromByteArray(new byte[] {(byte) hi, (byte) lo}); - assertEquals( - String.format( - Locale.ROOT, "hi=%s, lo=%s, expected=%s, result=%s", hi, lo, (int) c, (int) result), - c, - result); + assertWithMessage( + String.format( + Locale.ROOT, + "hi=%s, lo=%s, expected=%s, result=%s", + hi, + lo, + (int) c, + (int) result)) + .that(result) + .isEqualTo(c); byte[] bytes = Chars.toByteArray(c); - assertEquals((byte) hi, bytes[0]); - assertEquals((byte) lo, bytes[1]); + assertThat(bytes[0]).isEqualTo((byte) hi); + assertThat(bytes[1]).isEqualTo((byte) lo); c++; } } - assertEquals((char) 0, c); // sanity check + assertThat(c).isEqualTo((char) 0); // sanity check } @GwtIncompatible // Chars.fromByteArray, Chars.toByteArray public void testByteArrayRoundTripsFails() { - try { - Chars.fromByteArray(new byte[] {0x11}); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Chars.fromByteArray(new byte[] {0x11})); } public void testEnsureCapacity() { - assertSame(EMPTY, Chars.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Chars.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Chars.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new char[] {(char) 1, (char) 0, (char) 0}, Chars.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Chars.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Chars.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Chars.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat(Chars.ensureCapacity(ARRAY1, 2, 1)) + .isEqualTo(new char[] {(char) 1, (char) 0, (char) 0}); } public void testEnsureCapacity_fail() { - try { - Chars.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Chars.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Chars.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Chars.ensureCapacity(ARRAY1, 1, -1)); } public void testJoin() { - assertEquals("", Chars.join(",", EMPTY)); - assertEquals("1", Chars.join(",", '1')); - assertEquals("1,2", Chars.join(",", '1', '2')); - assertEquals("123", Chars.join("", '1', '2', '3')); + assertThat(Chars.join(",", EMPTY)).isEmpty(); + assertThat(Chars.join(",", '1')).isEqualTo("1"); + assertThat(Chars.join(",", '1', '2')).isEqualTo("1,2"); + assertThat(Chars.join("", '1', '2', '3')).isEqualTo("123"); } public void testLexicographicalComparator() { @@ -314,10 +335,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Chars.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -331,13 +353,13 @@ public void testReverse() { private static void testReverse(char[] input, char[] expectedOutput) { input = Arrays.copyOf(input, input.length); Chars.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse(char[] input, int fromIndex, int toIndex, char[] expectedOutput) { input = Arrays.copyOf(input, input.length); Chars.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -349,6 +371,203 @@ public void testReverseIndexed() { testReverse(new char[] {'A', '1', 'B', '2'}, 1, 3, new char[] {'A', 'B', '1', '2'}); } + private static void testRotate(char[] input, int distance, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + char[] input, int distance, int fromIndex, int toIndex, char[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Chars.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new char[] {}, -1, new char[] {}); + testRotate(new char[] {}, 0, new char[] {}); + testRotate(new char[] {}, 1, new char[] {}); + + testRotate(new char[] {'1'}, -2, new char[] {'1'}); + testRotate(new char[] {'1'}, -1, new char[] {'1'}); + testRotate(new char[] {'1'}, 0, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 2, new char[] {'1'}); + + testRotate(new char[] {'1', '2'}, -3, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, -2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 0, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 1, new char[] {'2', '1'}); + testRotate(new char[] {'1', '2'}, 2, new char[] {'1', '2'}); + testRotate(new char[] {'1', '2'}, 3, new char[] {'2', '1'}); + + testRotate(new char[] {'1', '2', '3'}, -5, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -4, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, -3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, -2, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, -1, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 0, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 1, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 2, new char[] {'2', '3', '1'}); + testRotate(new char[] {'1', '2', '3'}, 3, new char[] {'1', '2', '3'}); + testRotate(new char[] {'1', '2', '3'}, 4, new char[] {'3', '1', '2'}); + testRotate(new char[] {'1', '2', '3'}, 5, new char[] {'2', '3', '1'}); + + testRotate(new char[] {'1', '2', '3', '4'}, -9, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -5, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, -1, new char[] {'2', '3', '4', '1'}); + testRotate(new char[] {'1', '2', '3', '4'}, 0, new char[] {'1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4'}, 1, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 5, new char[] {'4', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4'}, 9, new char[] {'4', '1', '2', '3'}); + + testRotate(new char[] {'1', '2', '3', '4', '5'}, -6, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -4, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -3, new char[] {'4', '5', '1', '2', '3'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, -1, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 0, new char[] {'1', '2', '3', '4', '5'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 1, new char[] {'5', '1', '2', '3', '4'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 3, new char[] {'3', '4', '5', '1', '2'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 4, new char[] {'2', '3', '4', '5', '1'}); + testRotate(new char[] {'1', '2', '3', '4', '5'}, 6, new char[] {'5', '1', '2', '3', '4'}); + } + + public void testRotateIndexed() { + testRotate(new char[] {}, 0, 0, 0, new char[] {}); + + testRotate(new char[] {'1'}, 0, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 0, 1, new char[] {'1'}); + testRotate(new char[] {'1'}, 1, 1, 1, new char[] {'1'}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 5, + 1, + 6, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 14, + 1, + 6, + new char[] {'0', '2', '3', '4', '5', '1', '6'}); + + // Rotate the first three elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 0, + 3, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 0, + 3, + new char[] {'2', '0', '1', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 0, + 3, + new char[] {'1', '2', '0', '3', '4', '5', '6'}); + + // Rotate the last four elements + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -6, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -5, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -4, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -3, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + -1, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 0, + 3, + 7, + new char[] {'0', '1', '2', '3', '4', '5', '6'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 1, + 3, + 7, + new char[] {'0', '1', '2', '6', '3', '4', '5'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 2, + 3, + 7, + new char[] {'0', '1', '2', '5', '6', '3', '4'}); + testRotate( + new char[] {'0', '1', '2', '3', '4', '5', '6'}, + 3, + 3, + 7, + new char[] {'0', '1', '2', '4', '5', '6', '3'}); + } + public void testSortDescending() { testSortDescending(new char[] {}, new char[] {}); testSortDescending(new char[] {'1'}, new char[] {'1'}); @@ -360,14 +579,14 @@ public void testSortDescending() { private static void testSortDescending(char[] input, char[] expectedOutput) { input = Arrays.copyOf(input, input.length); Chars.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( char[] input, int fromIndex, int toIndex, char[] expectedOutput) { input = Arrays.copyOf(input, input.length); Chars.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -382,17 +601,17 @@ public void testSortDescendingIndexed() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Chars.toArray(none))); + assertThat(Chars.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((char) 1); - assertTrue(Arrays.equals(ARRAY1, Chars.toArray(one))); + assertThat(Chars.toArray(one)).isEqualTo(ARRAY1); char[] array = {(char) 0, (char) 1, 'A'}; List three = Arrays.asList((char) 0, (char) 1, 'A'); - assertTrue(Arrays.equals(array, Chars.toArray(three))); + assertThat(Chars.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Chars.toArray(Chars.asList(array)))); + assertThat(Chars.toArray(Chars.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -402,30 +621,27 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); char[] arr = Chars.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((char) 0, (char) 1, null); - try { - Chars.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Character> list = Arrays.asList((char) 0, (char) 1, null); + assertThrows(NullPointerException.class, () -> Chars.toArray(list)); } + @J2ktIncompatible // b/285319375 public void testAsList_isAView() { char[] array = {(char) 0, (char) 1}; List list = Chars.asList(array); list.set(0, (char) 2); - assertTrue(Arrays.equals(new char[] {(char) 2, (char) 1}, array)); + assertThat(array).isEqualTo(new char[] {(char) 2, (char) 1}); array[1] = (char) 3; - assertEquals(Arrays.asList((char) 2, (char) 3), list); + assertThat(list).containsExactly((char) 2, (char) 3).inOrder(); } public void testAsList_toArray_roundTrip() { @@ -435,23 +651,24 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (char) 4); - assertTrue(Arrays.equals(new char[] {(char) 0, (char) 1, (char) 2}, newArray)); + assertThat(newArray).isEqualTo(new char[] {(char) 0, (char) 1, (char) 2}); newArray[1] = (char) 5; - assertEquals((char) 1, (char) list.get(1)); + assertThat((char) list.get(1)).isEqualTo((char) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { char[] array = {(char) 0, (char) 1, (char) 2, (char) 3}; List list = Chars.asList(array); - assertTrue(Arrays.equals(new char[] {(char) 1, (char) 2}, Chars.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new char[] {}, Chars.toArray(list.subList(2, 2)))); + assertThat(Chars.toArray(list.subList(1, 3))).isEqualTo(new char[] {(char) 1, (char) 2}); + assertThat(Chars.toArray(list.subList(2, 2))).isEqualTo(new char[] {}); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Chars.asList(EMPTY)); + assertThat(Chars.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Chars.class); diff --git a/android/guava-tests/test/com/google/common/primitives/DoubleArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/DoubleArrayAsListTest.java index 23a7ca14083b..f1b054d69d18 100644 --- a/android/guava-tests/test/com/google/common/primitives/DoubleArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/DoubleArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Doubles#asList(double[])}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class DoubleArrayAsListTest extends TestCase { private static List asList(Double[] values) { @@ -48,12 +52,13 @@ private static List asList(Double[] values) { return Doubles.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = ImmutableList.of( ListTestSuiteBuilder.using(new DoublesAsListGenerator()).named("Doubles.asList"), - ListTestSuiteBuilder.using(new DoublsAsListHeadSubListGenerator()) + ListTestSuiteBuilder.using(new DoublesAsListHeadSubListGenerator()) .named("Doubles.asList, head subList"), ListTestSuiteBuilder.using(new DoublesAsListTailSubListGenerator()) .named("Doubles.asList, tail subList"), @@ -84,7 +89,7 @@ protected List create(Double[] elements) { } } - public static final class DoublsAsListHeadSubListGenerator extends TestDoubleListGenerator { + public static final class DoublesAsListHeadSubListGenerator extends TestDoubleListGenerator { @Override protected List create(Double[] elements) { Double[] suffix = {Double.MIN_VALUE, Double.MAX_VALUE}; diff --git a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java index 871b84c286ff..e0bb4a8736b3 100644 --- a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java @@ -16,11 +16,16 @@ package com.google.common.primitives; +import static com.google.common.primitives.Doubles.max; +import static com.google.common.primitives.Doubles.min; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static java.lang.Double.NaN; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.Helpers; @@ -33,14 +38,16 @@ import java.util.List; import java.util.regex.Pattern; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Doubles}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless public class DoublesTest extends TestCase { private static final double[] EMPTY = {}; private static final double[] ARRAY1 = {(double) 1}; @@ -77,242 +84,256 @@ public class DoublesTest extends TestCase { public void testHashCode() { for (double value : VALUES) { - assertEquals(((Double) value).hashCode(), Doubles.hashCode(value)); + assertThat(Doubles.hashCode(value)).isEqualTo(((Double) value).hashCode()); } } public void testIsFinite() { for (double value : NUMBERS) { - assertEquals(!(Double.isNaN(value) || Double.isInfinite(value)), Doubles.isFinite(value)); + assertThat(Doubles.isFinite(value)) + .isEqualTo(!(Double.isNaN(value) || Double.isInfinite(value))); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (double x : VALUES) { for (double y : VALUES) { // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Double.valueOf(x).compareTo(y), Doubles.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Doubles.compare(x, y)) + .isEqualTo(Double.valueOf(x).compareTo(y)); } } } public void testContains() { - assertFalse(Doubles.contains(EMPTY, (double) 1)); - assertFalse(Doubles.contains(ARRAY1, (double) 2)); - assertFalse(Doubles.contains(ARRAY234, (double) 1)); - assertTrue(Doubles.contains(new double[] {(double) -1}, (double) -1)); - assertTrue(Doubles.contains(ARRAY234, (double) 2)); - assertTrue(Doubles.contains(ARRAY234, (double) 3)); - assertTrue(Doubles.contains(ARRAY234, (double) 4)); + assertThat(Doubles.contains(EMPTY, (double) 1)).isFalse(); + assertThat(Doubles.contains(ARRAY1, (double) 2)).isFalse(); + assertThat(Doubles.contains(ARRAY234, (double) 1)).isFalse(); + assertThat(Doubles.contains(new double[] {(double) -1}, (double) -1)).isTrue(); + assertThat(Doubles.contains(ARRAY234, (double) 2)).isTrue(); + assertThat(Doubles.contains(ARRAY234, (double) 3)).isTrue(); + assertThat(Doubles.contains(ARRAY234, (double) 4)).isTrue(); for (double value : NUMBERS) { - assertTrue("" + value, Doubles.contains(new double[] {5.0, value}, value)); + assertWithMessage("" + value) + .that(Doubles.contains(new double[] {5.0, value}, value)) + .isTrue(); } - assertFalse(Doubles.contains(new double[] {5.0, NaN}, NaN)); + assertThat(Doubles.contains(new double[] {5.0, NaN}, NaN)).isFalse(); } public void testIndexOf() { - assertEquals(-1, Doubles.indexOf(EMPTY, (double) 1)); - assertEquals(-1, Doubles.indexOf(ARRAY1, (double) 2)); - assertEquals(-1, Doubles.indexOf(ARRAY234, (double) 1)); - assertEquals(0, Doubles.indexOf(new double[] {(double) -1}, (double) -1)); - assertEquals(0, Doubles.indexOf(ARRAY234, (double) 2)); - assertEquals(1, Doubles.indexOf(ARRAY234, (double) 3)); - assertEquals(2, Doubles.indexOf(ARRAY234, (double) 4)); - assertEquals( - 1, - Doubles.indexOf(new double[] {(double) 2, (double) 3, (double) 2, (double) 3}, (double) 3)); + assertThat(Doubles.indexOf(EMPTY, (double) 1)).isEqualTo(-1); + assertThat(Doubles.indexOf(ARRAY1, (double) 2)).isEqualTo(-1); + assertThat(Doubles.indexOf(ARRAY234, (double) 1)).isEqualTo(-1); + assertThat(Doubles.indexOf(new double[] {(double) -1}, (double) -1)).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, (double) 2)).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, (double) 3)).isEqualTo(1); + assertThat(Doubles.indexOf(ARRAY234, (double) 4)).isEqualTo(2); + assertThat( + Doubles.indexOf( + new double[] {(double) 2, (double) 3, (double) 2, (double) 3}, (double) 3)) + .isEqualTo(1); for (double value : NUMBERS) { - assertEquals("" + value, 1, Doubles.indexOf(new double[] {5.0, value}, value)); + assertWithMessage("" + value) + .that(Doubles.indexOf(new double[] {5.0, value}, value)) + .isEqualTo(1); } - assertEquals(-1, Doubles.indexOf(new double[] {5.0, NaN}, NaN)); + assertThat(Doubles.indexOf(new double[] {5.0, NaN}, NaN)).isEqualTo(-1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Doubles.indexOf(EMPTY, EMPTY)); - assertEquals(0, Doubles.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Doubles.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Doubles.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Doubles.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Doubles.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Doubles.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Doubles.indexOf(ARRAY234, new double[] {(double) 2, (double) 3})); - assertEquals(1, Doubles.indexOf(ARRAY234, new double[] {(double) 3, (double) 4})); - assertEquals(1, Doubles.indexOf(ARRAY234, new double[] {(double) 3})); - assertEquals(2, Doubles.indexOf(ARRAY234, new double[] {(double) 4})); - assertEquals( - 1, - Doubles.indexOf( - new double[] {(double) 2, (double) 3, (double) 3, (double) 3, (double) 3}, - new double[] {(double) 3})); - assertEquals( - 2, - Doubles.indexOf( - new double[] { - (double) 2, (double) 3, (double) 2, (double) 3, (double) 4, (double) 2, (double) 3 - }, - new double[] {(double) 2, (double) 3, (double) 4})); - assertEquals( - 1, - Doubles.indexOf( - new double[] { - (double) 2, (double) 2, (double) 3, (double) 4, (double) 2, (double) 3, (double) 4 - }, - new double[] {(double) 2, (double) 3, (double) 4})); - assertEquals( - -1, - Doubles.indexOf( - new double[] {(double) 4, (double) 3, (double) 2}, - new double[] {(double) 2, (double) 3, (double) 4})); + assertThat(Doubles.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Doubles.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Doubles.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Doubles.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Doubles.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, new double[] {(double) 2, (double) 3})).isEqualTo(0); + assertThat(Doubles.indexOf(ARRAY234, new double[] {(double) 3, (double) 4})).isEqualTo(1); + assertThat(Doubles.indexOf(ARRAY234, new double[] {(double) 3})).isEqualTo(1); + assertThat(Doubles.indexOf(ARRAY234, new double[] {(double) 4})).isEqualTo(2); + assertThat( + Doubles.indexOf( + new double[] {(double) 2, (double) 3, (double) 3, (double) 3, (double) 3}, + new double[] {(double) 3})) + .isEqualTo(1); + assertThat( + Doubles.indexOf( + new double[] { + (double) 2, (double) 3, (double) 2, (double) 3, (double) 4, (double) 2, (double) 3 + }, + new double[] {(double) 2, (double) 3, (double) 4})) + .isEqualTo(2); + assertThat( + Doubles.indexOf( + new double[] { + (double) 2, (double) 2, (double) 3, (double) 4, (double) 2, (double) 3, (double) 4 + }, + new double[] {(double) 2, (double) 3, (double) 4})) + .isEqualTo(1); + assertThat( + Doubles.indexOf( + new double[] {(double) 4, (double) 3, (double) 2}, + new double[] {(double) 2, (double) 3, (double) 4})) + .isEqualTo(-1); for (double value : NUMBERS) { - assertEquals( - "" + value, - 1, - Doubles.indexOf(new double[] {5.0, value, value, 5.0}, new double[] {value, value})); + assertWithMessage("" + value) + .that(Doubles.indexOf(new double[] {5.0, value, value, 5.0}, new double[] {value, value})) + .isEqualTo(1); } - assertEquals(-1, Doubles.indexOf(new double[] {5.0, NaN, NaN, 5.0}, new double[] {NaN, NaN})); + assertThat(Doubles.indexOf(new double[] {5.0, NaN, NaN, 5.0}, new double[] {NaN, NaN})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Doubles.lastIndexOf(EMPTY, (double) 1)); - assertEquals(-1, Doubles.lastIndexOf(ARRAY1, (double) 2)); - assertEquals(-1, Doubles.lastIndexOf(ARRAY234, (double) 1)); - assertEquals(0, Doubles.lastIndexOf(new double[] {(double) -1}, (double) -1)); - assertEquals(0, Doubles.lastIndexOf(ARRAY234, (double) 2)); - assertEquals(1, Doubles.lastIndexOf(ARRAY234, (double) 3)); - assertEquals(2, Doubles.lastIndexOf(ARRAY234, (double) 4)); - assertEquals( - 3, - Doubles.lastIndexOf( - new double[] {(double) 2, (double) 3, (double) 2, (double) 3}, (double) 3)); + assertThat(Doubles.lastIndexOf(EMPTY, (double) 1)).isEqualTo(-1); + assertThat(Doubles.lastIndexOf(ARRAY1, (double) 2)).isEqualTo(-1); + assertThat(Doubles.lastIndexOf(ARRAY234, (double) 1)).isEqualTo(-1); + assertThat(Doubles.lastIndexOf(new double[] {(double) -1}, (double) -1)).isEqualTo(0); + assertThat(Doubles.lastIndexOf(ARRAY234, (double) 2)).isEqualTo(0); + assertThat(Doubles.lastIndexOf(ARRAY234, (double) 3)).isEqualTo(1); + assertThat(Doubles.lastIndexOf(ARRAY234, (double) 4)).isEqualTo(2); + assertThat( + Doubles.lastIndexOf( + new double[] {(double) 2, (double) 3, (double) 2, (double) 3}, (double) 3)) + .isEqualTo(3); for (double value : NUMBERS) { - assertEquals("" + value, 0, Doubles.lastIndexOf(new double[] {value, 5.0}, value)); + assertWithMessage("" + value) + .that(Doubles.lastIndexOf(new double[] {value, 5.0}, value)) + .isEqualTo(0); } - assertEquals(-1, Doubles.lastIndexOf(new double[] {NaN, 5.0}, NaN)); + assertThat(Doubles.lastIndexOf(new double[] {NaN, 5.0}, NaN)).isEqualTo(-1); } @GwtIncompatible public void testMax_noArgs() { - try { - Doubles.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, Doubles.max(LEAST)); - assertEquals(GREATEST, Doubles.max(GREATEST)); - assertEquals( - (double) 9, - Doubles.max( - (double) 8, (double) 6, (double) 7, (double) 5, (double) 3, (double) 0, (double) 9)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat( + max((double) 8, (double) 6, (double) 7, (double) 5, (double) 3, (double) 0, (double) 9)) + .isEqualTo((double) 9); - assertEquals(0.0, Doubles.max(-0.0, 0.0)); - assertEquals(0.0, Doubles.max(0.0, -0.0)); - assertEquals(GREATEST, Doubles.max(NUMBERS)); - assertTrue(Double.isNaN(Doubles.max(VALUES))); + assertThat(max(-0.0, 0.0)).isEqualTo(0.0); + assertThat(max(0.0, -0.0)).isEqualTo(0.0); + assertThat(max(NUMBERS)).isEqualTo(GREATEST); + assertThat(Double.isNaN(max(VALUES))).isTrue(); } @GwtIncompatible public void testMin_noArgs() { - try { - Doubles.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, Doubles.min(LEAST)); - assertEquals(GREATEST, Doubles.min(GREATEST)); - assertEquals( - (double) 0, - Doubles.min( - (double) 8, (double) 6, (double) 7, (double) 5, (double) 3, (double) 0, (double) 9)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat( + min((double) 8, (double) 6, (double) 7, (double) 5, (double) 3, (double) 0, (double) 9)) + .isEqualTo((double) 0); - assertEquals(-0.0, Doubles.min(-0.0, 0.0)); - assertEquals(-0.0, Doubles.min(0.0, -0.0)); - assertEquals(LEAST, Doubles.min(NUMBERS)); - assertTrue(Double.isNaN(Doubles.min(VALUES))); + assertThat(min(-0.0, 0.0)).isEqualTo(-0.0); + assertThat(min(0.0, -0.0)).isEqualTo(-0.0); + assertThat(min(NUMBERS)).isEqualTo(LEAST); + assertThat(Double.isNaN(min(VALUES))).isTrue(); } public void testConstrainToRange() { - double tolerance = 1e-10; - assertEquals( - (double) 1, Doubles.constrainToRange((double) 1, (double) 0, (double) 5), tolerance); - assertEquals( - (double) 1, Doubles.constrainToRange((double) 1, (double) 1, (double) 5), tolerance); - assertEquals( - (double) 3, Doubles.constrainToRange((double) 1, (double) 3, (double) 5), tolerance); - assertEquals( - (double) -1, Doubles.constrainToRange((double) 0, (double) -5, (double) -1), tolerance); - assertEquals( - (double) 2, Doubles.constrainToRange((double) 5, (double) 2, (double) 2), tolerance); + assertThat(Doubles.constrainToRange((double) 1, (double) 0, (double) 5)).isEqualTo((double) 1); + assertThat(Doubles.constrainToRange((double) 1, (double) 1, (double) 5)).isEqualTo((double) 1); + assertThat(Doubles.constrainToRange((double) 1, (double) 3, (double) 5)).isEqualTo((double) 3); + assertThat(Doubles.constrainToRange((double) 0, (double) -5, (double) -1)) + .isEqualTo((double) -1); + assertThat(Doubles.constrainToRange((double) 5, (double) 2, (double) 2)).isEqualTo((double) 2); + assertThrows( + IllegalArgumentException.class, + () -> Doubles.constrainToRange((double) 1, (double) 3, (double) 2)); + } + + public void testConcat() { + assertThat(Doubles.concat()).isEqualTo(EMPTY); + assertThat(Doubles.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Doubles.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Doubles.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Doubles.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Doubles.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Doubles.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new double[] {(double) 1, (double) 1, (double) 1}); + assertThat(Doubles.concat(ARRAY1, ARRAY234)) + .isEqualTo(new double[] {(double) 1, (double) 2, (double) 3, (double) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + double[][] arrays = new double[arraysDim1][]; + // it's shared to avoid using too much memory in tests + double[] sharedArray = new double[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Doubles.constrainToRange((double) 1, (double) 3, (double) 2); + Doubles.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Doubles.concat())); - assertTrue(Arrays.equals(EMPTY, Doubles.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Doubles.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Doubles.concat(ARRAY1))); - assertNotSame(ARRAY1, Doubles.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Doubles.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new double[] {(double) 1, (double) 1, (double) 1}, - Doubles.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new double[] {(double) 1, (double) 2, (double) 3, (double) 4}, - Doubles.concat(ARRAY1, ARRAY234))); - } - public void testEnsureCapacity() { - assertSame(EMPTY, Doubles.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Doubles.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Doubles.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new double[] {(double) 1, (double) 0, (double) 0}, - Doubles.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Doubles.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Doubles.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Doubles.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat( + Arrays.equals( + new double[] {(double) 1, (double) 0, (double) 0}, + Doubles.ensureCapacity(ARRAY1, 2, 1))) + .isTrue(); } public void testEnsureCapacity_fail() { - try { - Doubles.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Doubles.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Doubles.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Doubles.ensureCapacity(ARRAY1, 1, -1)); } @GwtIncompatible // Double.toString returns different value in GWT. public void testJoin() { - assertEquals("", Doubles.join(",", EMPTY)); - assertEquals("1.0", Doubles.join(",", ARRAY1)); - assertEquals("1.0,2.0", Doubles.join(",", (double) 1, (double) 2)); - assertEquals("1.02.03.0", Doubles.join("", (double) 1, (double) 2, (double) 3)); + assertThat(Doubles.join(",", EMPTY)).isEmpty(); + assertThat(Doubles.join(",", ARRAY1)).isEqualTo("1.0"); + assertThat(Doubles.join(",", (double) 1, (double) 2)).isEqualTo("1.0,2.0"); + assertThat(Doubles.join("", (double) 1, (double) 2, (double) 3)).isEqualTo("1.02.03.0"); } public void testJoinNonTrivialDoubles() { - assertEquals("", Doubles.join(",", EMPTY)); - assertEquals("1.2", Doubles.join(",", 1.2)); - assertEquals("1.3,2.4", Doubles.join(",", 1.3, 2.4)); - assertEquals("1.42.53.6", Doubles.join("", 1.4, 2.5, 3.6)); + assertThat(Doubles.join(",", EMPTY)).isEmpty(); + assertThat(Doubles.join(",", 1.2)).isEqualTo("1.2"); + assertThat(Doubles.join(",", 1.3, 2.4)).isEqualTo("1.3,2.4"); + assertThat(Doubles.join("", 1.4, 2.5, 3.6)).isEqualTo("1.42.53.6"); } public void testLexicographicalComparator() { @@ -343,14 +364,14 @@ public void testReverse() { private static void testReverse(double[] input, double[] expectedOutput) { input = Arrays.copyOf(input, input.length); Doubles.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse( double[] input, int fromIndex, int toIndex, double[] expectedOutput) { input = Arrays.copyOf(input, input.length); Doubles.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -362,6 +383,103 @@ public void testReverseIndexed() { testReverse(new double[] {-1, 1, -2, 2}, 1, 3, new double[] {-1, -2, 1, 2}); } + private static void testRotate(double[] input, int distance, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + double[] input, int distance, int fromIndex, int toIndex, double[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Doubles.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new double[] {}, -1, new double[] {}); + testRotate(new double[] {}, 0, new double[] {}); + testRotate(new double[] {}, 1, new double[] {}); + + testRotate(new double[] {1}, -2, new double[] {1}); + testRotate(new double[] {1}, -1, new double[] {1}); + testRotate(new double[] {1}, 0, new double[] {1}); + testRotate(new double[] {1}, 1, new double[] {1}); + testRotate(new double[] {1}, 2, new double[] {1}); + + testRotate(new double[] {1, 2}, -3, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, -2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 0, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 1, new double[] {2, 1}); + testRotate(new double[] {1, 2}, 2, new double[] {1, 2}); + testRotate(new double[] {1, 2}, 3, new double[] {2, 1}); + + testRotate(new double[] {1, 2, 3}, -5, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -4, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, -3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, -2, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, -1, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 0, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 1, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 2, new double[] {2, 3, 1}); + testRotate(new double[] {1, 2, 3}, 3, new double[] {1, 2, 3}); + testRotate(new double[] {1, 2, 3}, 4, new double[] {3, 1, 2}); + testRotate(new double[] {1, 2, 3}, 5, new double[] {2, 3, 1}); + + testRotate(new double[] {1, 2, 3, 4}, -9, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -5, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, -1, new double[] {2, 3, 4, 1}); + testRotate(new double[] {1, 2, 3, 4}, 0, new double[] {1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4}, 1, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 5, new double[] {4, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4}, 9, new double[] {4, 1, 2, 3}); + + testRotate(new double[] {1, 2, 3, 4, 5}, -6, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, -4, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, -3, new double[] {4, 5, 1, 2, 3}); + testRotate(new double[] {1, 2, 3, 4, 5}, -1, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 0, new double[] {1, 2, 3, 4, 5}); + testRotate(new double[] {1, 2, 3, 4, 5}, 1, new double[] {5, 1, 2, 3, 4}); + testRotate(new double[] {1, 2, 3, 4, 5}, 3, new double[] {3, 4, 5, 1, 2}); + testRotate(new double[] {1, 2, 3, 4, 5}, 4, new double[] {2, 3, 4, 5, 1}); + testRotate(new double[] {1, 2, 3, 4, 5}, 6, new double[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new double[] {}, 0, 0, 0, new double[] {}); + + testRotate(new double[] {1}, 0, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 0, 1, new double[] {1}); + testRotate(new double[] {1}, 1, 1, 1, new double[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new double[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new double[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new double[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new double[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new double[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new double[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new double[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new double[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new double[] {}, new double[] {}); testSortDescending(new double[] {1}, new double[] {1}); @@ -369,16 +487,15 @@ public void testSortDescending() { testSortDescending(new double[] {1, 3, 1}, new double[] {3, 1, 1}); testSortDescending(new double[] {-1, 1, -2, 2}, new double[] {2, 1, -1, -2}); testSortDescending( - new double[] {-1, 1, Double.NaN, -2, -0, 0, 2}, - new double[] {Double.NaN, 2, 1, 0, -0, -1, -2}); + new double[] {-1, 1, Double.NaN, -2, -0.0, 0, 2}, + new double[] {Double.NaN, 2, 1, 0, -0.0, -1, -2}); } private static void testSortDescending(double[] input, double[] expectedOutput) { input = Arrays.copyOf(input, input.length); Doubles.sortDescending(input); - // GWT's Arrays.equals doesn't appear to handle NaN correctly, so test each element individually for (int i = 0; i < input.length; i++) { - assertEquals(0, Double.compare(expectedOutput[i], input[i])); + assertThat(input[i]).isEqualTo(expectedOutput[i]); } } @@ -386,9 +503,8 @@ private static void testSortDescending( double[] input, int fromIndex, int toIndex, double[] expectedOutput) { input = Arrays.copyOf(input, input.length); Doubles.sortDescending(input, fromIndex, toIndex); - // GWT's Arrays.equals doesn't appear to handle NaN correctly, so test each element individually for (int i = 0; i < input.length; i++) { - assertEquals(0, Double.compare(expectedOutput[i], input[i])); + assertThat(input[i]).isEqualTo(expectedOutput[i]); } } @@ -403,12 +519,14 @@ public void testSortDescendingIndexed() { new double[] {-1, 1, Double.NaN, -2, 2}, 1, 4, new double[] {-1, Double.NaN, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Doubles.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testStringConverterSerialization() { SerializableTester.reserializeAndAssert(Doubles.stringConverter()); @@ -417,17 +535,17 @@ public void testStringConverterSerialization() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Doubles.toArray(none))); + assertThat(Doubles.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((double) 1); - assertTrue(Arrays.equals(ARRAY1, Doubles.toArray(one))); + assertThat(Doubles.toArray(one)).isEqualTo(ARRAY1); double[] array = {(double) 0, (double) 1, Math.PI}; List three = Arrays.asList((double) 0, (double) 1, Math.PI); - assertTrue(Arrays.equals(array, Doubles.toArray(three))); + assertThat(Doubles.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Doubles.toArray(Doubles.asList(array)))); + assertThat(Doubles.toArray(Doubles.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -437,21 +555,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); double[] arr = Doubles.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr.length).isEqualTo(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((double) 0, (double) 1, null); - try { - Doubles.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Double> list = Arrays.asList((double) 0, (double) 1, null); + assertThrows(NullPointerException.class, () -> Doubles.toArray(list)); } public void testToArray_withConversion() { @@ -464,19 +578,20 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Doubles.toArray(bytes))); - assertTrue(Arrays.equals(array, Doubles.toArray(shorts))); - assertTrue(Arrays.equals(array, Doubles.toArray(ints))); - assertTrue(Arrays.equals(array, Doubles.toArray(floats))); - assertTrue(Arrays.equals(array, Doubles.toArray(longs))); - assertTrue(Arrays.equals(array, Doubles.toArray(doubles))); + assertThat(Doubles.toArray(bytes)).isEqualTo(array); + assertThat(Doubles.toArray(shorts)).isEqualTo(array); + assertThat(Doubles.toArray(ints)).isEqualTo(array); + assertThat(Doubles.toArray(floats)).isEqualTo(array); + assertThat(Doubles.toArray(longs)).isEqualTo(array); + assertThat(Doubles.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { double[] array = {(double) 0, (double) 1}; List list = Doubles.asList(array); list.set(0, (double) 2); - assertTrue(Arrays.equals(new double[] {(double) 2, (double) 1}, array)); + assertThat(array).isEqualTo(new double[] {(double) 2, (double) 1}); array[1] = (double) 3; assertThat(list).containsExactly((double) 2, (double) 3).inOrder(); } @@ -488,29 +603,29 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (double) 4); - assertTrue(Arrays.equals(new double[] {(double) 0, (double) 1, (double) 2}, newArray)); + assertThat(newArray).isEqualTo(new double[] {(double) 0, (double) 1, (double) 2}); newArray[1] = (double) 5; - assertEquals((double) 1, (double) list.get(1)); + assertThat((double) list.get(1)).isEqualTo((double) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { double[] array = {(double) 0, (double) 1, (double) 2, (double) 3}; List list = Doubles.asList(array); - assertTrue( - Arrays.equals(new double[] {(double) 1, (double) 2}, Doubles.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new double[] {}, Doubles.toArray(list.subList(2, 2)))); + assertThat(Doubles.toArray(list.subList(1, 3))) + .isEqualTo(new double[] {(double) 1, (double) 2}); + assertThat(Doubles.toArray(list.subList(2, 2))).isEmpty(); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Doubles.asList(EMPTY)); + assertThat(Doubles.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } /** * A reference implementation for {@code tryParse} that just catches the exception from {@link * Double#valueOf}. */ - private static Double referenceTryParse(String input) { + private static @Nullable Double referenceTryParse(String input) { if (input.trim().length() < input.length()) { return null; } @@ -524,7 +639,7 @@ private static Double referenceTryParse(String input) { @GwtIncompatible // Doubles.tryParse private static void checkTryParse(String input) { Double expected = referenceTryParse(input); - assertEquals(expected, Doubles.tryParse(input)); + assertThat(Doubles.tryParse(input)).isEqualTo(expected); if (expected != null && !Doubles.FLOATING_POINT_PATTERN.matcher(input).matches()) { // TODO(cpovirk): Use SourceCodeEscapers if it is added to Guava. StringBuilder escapedInput = new StringBuilder(); @@ -541,7 +656,7 @@ private static void checkTryParse(String input) { @GwtIncompatible // Doubles.tryParse private static void checkTryParse(double expected, String input) { - assertEquals(Double.valueOf(expected), Doubles.tryParse(input)); + assertThat(Doubles.tryParse(input)).isEqualTo(Double.valueOf(expected)); assertThat(input) .matches( Pattern.compile( @@ -586,6 +701,7 @@ public void testTryParseOfToStringIsOriginal() { } } + @J2ktIncompatible // hexadecimal doubles @GwtIncompatible // Doubles.tryParse public void testTryParseOfToHexStringIsOriginal() { for (double d : NUMBERS) { @@ -632,11 +748,12 @@ public void testTryParseFailures() { Pattern.compile( Doubles.FLOATING_POINT_PATTERN.pattern(), Doubles.FLOATING_POINT_PATTERN.flags())); - assertEquals(referenceTryParse(badInput), Doubles.tryParse(badInput)); - assertNull(Doubles.tryParse(badInput)); + assertThat(Doubles.tryParse(badInput)).isEqualTo(referenceTryParse(badInput)); + assertThat(Doubles.tryParse(badInput)).isNull(); } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Doubles.class); @@ -644,39 +761,37 @@ public void testNulls() { public void testStringConverter_convert() { Converter converter = Doubles.stringConverter(); - assertEquals((Double) 1.0, converter.convert("1.0")); - assertEquals((Double) 0.0, converter.convert("0.0")); - assertEquals((Double) (-1.0), converter.convert("-1.0")); - assertEquals((Double) 1.0, converter.convert("1")); - assertEquals((Double) 0.0, converter.convert("0")); - assertEquals((Double) (-1.0), converter.convert("-1")); - assertEquals((Double) 1e6, converter.convert("1e6")); - assertEquals((Double) 1e-6, converter.convert("1e-6")); + assertThat(converter.convert("1.0")).isEqualTo((Double) 1.0); + assertThat(converter.convert("0.0")).isEqualTo((Double) 0.0); + assertThat(converter.convert("-1.0")).isEqualTo((Double) (-1.0)); + assertThat(converter.convert("1")).isEqualTo((Double) 1.0); + assertThat(converter.convert("0")).isEqualTo((Double) 0.0); + assertThat(converter.convert("-1")).isEqualTo((Double) (-1.0)); + assertThat(converter.convert("1e6")).isEqualTo((Double) 1e6); + assertThat(converter.convert("1e-6")).isEqualTo((Double) 1e-6); } public void testStringConverter_convertError() { - try { - Doubles.stringConverter().convert("notanumber"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, () -> Doubles.stringConverter().convert("notanumber")); } public void testStringConverter_nullConversions() { - assertNull(Doubles.stringConverter().convert(null)); - assertNull(Doubles.stringConverter().reverse().convert(null)); + assertThat(Doubles.stringConverter().convert(null)).isNull(); + assertThat(Doubles.stringConverter().reverse().convert(null)).isNull(); } @GwtIncompatible // Double.toString returns different value in GWT. public void testStringConverter_reverse() { Converter converter = Doubles.stringConverter(); - assertEquals("1.0", converter.reverse().convert(1.0)); - assertEquals("0.0", converter.reverse().convert(0.0)); - assertEquals("-1.0", converter.reverse().convert(-1.0)); - assertEquals("1000000.0", converter.reverse().convert(1e6)); - assertEquals("1.0E-6", converter.reverse().convert(1e-6)); + assertThat(converter.reverse().convert(1.0)).isEqualTo("1.0"); + assertThat(converter.reverse().convert(0.0)).isEqualTo("0.0"); + assertThat(converter.reverse().convert(-1.0)).isEqualTo("-1.0"); + assertThat(converter.reverse().convert(1e6)).isEqualTo("1000000.0"); + assertThat(converter.reverse().convert(1e-6)).isEqualTo("1.0E-6"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStringConverter_nullPointerTester() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -685,11 +800,7 @@ public void testStringConverter_nullPointerTester() throws Exception { @GwtIncompatible public void testTryParse_withNullNoGwt() { - assertNull(Doubles.tryParse("null")); - try { - Doubles.tryParse(null); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } + assertThat(Doubles.tryParse("null")).isNull(); + assertThrows(NullPointerException.class, () -> Doubles.tryParse(null)); } } diff --git a/android/guava-tests/test/com/google/common/primitives/FloatArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/FloatArrayAsListTest.java index 233a0211b03b..8d88e79b4a73 100644 --- a/android/guava-tests/test/com/google/common/primitives/FloatArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/FloatArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Floats#asList(float[])})}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class FloatArrayAsListTest extends TestCase { private static List asList(Float[] values) { @@ -48,6 +52,7 @@ private static List asList(Float[] values) { return Floats.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java index fd59ad4d82a6..0423cf46599e 100644 --- a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java @@ -16,11 +16,16 @@ package com.google.common.primitives; +import static com.google.common.primitives.Floats.max; +import static com.google.common.primitives.Floats.min; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static java.lang.Float.NaN; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.Helpers; @@ -32,14 +37,16 @@ import java.util.Comparator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Floats}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless public class FloatsTest extends TestCase { private static final float[] EMPTY = {}; private static final float[] ARRAY1 = {(float) 1}; @@ -72,223 +79,240 @@ public class FloatsTest extends TestCase { public void testHashCode() { for (float value : VALUES) { - assertEquals(((Float) value).hashCode(), Floats.hashCode(value)); + assertThat(Floats.hashCode(value)).isEqualTo(((Float) value).hashCode()); } } public void testIsFinite() { for (float value : NUMBERS) { - assertEquals(!(Float.isInfinite(value) || Float.isNaN(value)), Floats.isFinite(value)); + assertThat(Floats.isFinite(value)) + .isEqualTo(!(Float.isInfinite(value) || Float.isNaN(value))); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (float x : VALUES) { for (float y : VALUES) { // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Float.valueOf(x).compareTo(y), Floats.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Floats.compare(x, y)) + .isEqualTo(Float.valueOf(x).compareTo(y)); } } } public void testContains() { - assertFalse(Floats.contains(EMPTY, (float) 1)); - assertFalse(Floats.contains(ARRAY1, (float) 2)); - assertFalse(Floats.contains(ARRAY234, (float) 1)); - assertTrue(Floats.contains(new float[] {(float) -1}, (float) -1)); - assertTrue(Floats.contains(ARRAY234, (float) 2)); - assertTrue(Floats.contains(ARRAY234, (float) 3)); - assertTrue(Floats.contains(ARRAY234, (float) 4)); + assertThat(Floats.contains(EMPTY, (float) 1)).isFalse(); + assertThat(Floats.contains(ARRAY1, (float) 2)).isFalse(); + assertThat(Floats.contains(ARRAY234, (float) 1)).isFalse(); + assertThat(Floats.contains(new float[] {(float) -1}, (float) -1)).isTrue(); + assertThat(Floats.contains(ARRAY234, (float) 2)).isTrue(); + assertThat(Floats.contains(ARRAY234, (float) 3)).isTrue(); + assertThat(Floats.contains(ARRAY234, (float) 4)).isTrue(); for (float value : NUMBERS) { - assertTrue("" + value, Floats.contains(new float[] {5f, value}, value)); + assertWithMessage("" + value).that(Floats.contains(new float[] {5f, value}, value)).isTrue(); } - assertFalse(Floats.contains(new float[] {5f, NaN}, NaN)); + assertThat(Floats.contains(new float[] {5f, NaN}, NaN)).isFalse(); } public void testIndexOf() { - assertEquals(-1, Floats.indexOf(EMPTY, (float) 1)); - assertEquals(-1, Floats.indexOf(ARRAY1, (float) 2)); - assertEquals(-1, Floats.indexOf(ARRAY234, (float) 1)); - assertEquals(0, Floats.indexOf(new float[] {(float) -1}, (float) -1)); - assertEquals(0, Floats.indexOf(ARRAY234, (float) 2)); - assertEquals(1, Floats.indexOf(ARRAY234, (float) 3)); - assertEquals(2, Floats.indexOf(ARRAY234, (float) 4)); - assertEquals( - 1, Floats.indexOf(new float[] {(float) 2, (float) 3, (float) 2, (float) 3}, (float) 3)); + assertThat(Floats.indexOf(EMPTY, (float) 1)).isEqualTo(-1); + assertThat(Floats.indexOf(ARRAY1, (float) 2)).isEqualTo(-1); + assertThat(Floats.indexOf(ARRAY234, (float) 1)).isEqualTo(-1); + assertThat(Floats.indexOf(new float[] {(float) -1}, (float) -1)).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, (float) 2)).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, (float) 3)).isEqualTo(1); + assertThat(Floats.indexOf(ARRAY234, (float) 4)).isEqualTo(2); + assertThat(Floats.indexOf(new float[] {(float) 2, (float) 3, (float) 2, (float) 3}, (float) 3)) + .isEqualTo(1); for (float value : NUMBERS) { - assertEquals("" + value, 1, Floats.indexOf(new float[] {5f, value}, value)); + assertWithMessage("" + value) + .that(Floats.indexOf(new float[] {5f, value}, value)) + .isEqualTo(1); } - assertEquals(-1, Floats.indexOf(new float[] {5f, NaN}, NaN)); + assertThat(Floats.indexOf(new float[] {5f, NaN}, NaN)).isEqualTo(-1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Floats.indexOf(EMPTY, EMPTY)); - assertEquals(0, Floats.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Floats.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Floats.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Floats.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Floats.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Floats.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Floats.indexOf(ARRAY234, new float[] {(float) 2, (float) 3})); - assertEquals(1, Floats.indexOf(ARRAY234, new float[] {(float) 3, (float) 4})); - assertEquals(1, Floats.indexOf(ARRAY234, new float[] {(float) 3})); - assertEquals(2, Floats.indexOf(ARRAY234, new float[] {(float) 4})); - assertEquals( - 1, - Floats.indexOf( - new float[] {(float) 2, (float) 3, (float) 3, (float) 3, (float) 3}, - new float[] {(float) 3})); - assertEquals( - 2, - Floats.indexOf( - new float[] { - (float) 2, (float) 3, (float) 2, (float) 3, (float) 4, (float) 2, (float) 3 - }, - new float[] {(float) 2, (float) 3, (float) 4})); - assertEquals( - 1, - Floats.indexOf( - new float[] { - (float) 2, (float) 2, (float) 3, (float) 4, (float) 2, (float) 3, (float) 4 - }, - new float[] {(float) 2, (float) 3, (float) 4})); - assertEquals( - -1, - Floats.indexOf( - new float[] {(float) 4, (float) 3, (float) 2}, - new float[] {(float) 2, (float) 3, (float) 4})); + assertThat(Floats.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Floats.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Floats.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Floats.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Floats.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, new float[] {(float) 2, (float) 3})).isEqualTo(0); + assertThat(Floats.indexOf(ARRAY234, new float[] {(float) 3, (float) 4})).isEqualTo(1); + assertThat(Floats.indexOf(ARRAY234, new float[] {(float) 3})).isEqualTo(1); + assertThat(Floats.indexOf(ARRAY234, new float[] {(float) 4})).isEqualTo(2); + assertThat( + Floats.indexOf( + new float[] {(float) 2, (float) 3, (float) 3, (float) 3, (float) 3}, + new float[] {(float) 3})) + .isEqualTo(1); + assertThat( + Floats.indexOf( + new float[] { + (float) 2, (float) 3, (float) 2, (float) 3, (float) 4, (float) 2, (float) 3 + }, + new float[] {(float) 2, (float) 3, (float) 4})) + .isEqualTo(2); + assertThat( + Floats.indexOf( + new float[] { + (float) 2, (float) 2, (float) 3, (float) 4, (float) 2, (float) 3, (float) 4 + }, + new float[] {(float) 2, (float) 3, (float) 4})) + .isEqualTo(1); + assertThat( + Floats.indexOf( + new float[] {(float) 4, (float) 3, (float) 2}, + new float[] {(float) 2, (float) 3, (float) 4})) + .isEqualTo(-1); for (float value : NUMBERS) { - assertEquals( - "" + value, - 1, - Floats.indexOf(new float[] {5f, value, value, 5f}, new float[] {value, value})); + assertWithMessage("" + value) + .that(Floats.indexOf(new float[] {5f, value, value, 5f}, new float[] {value, value})) + .isEqualTo(1); } - assertEquals(-1, Floats.indexOf(new float[] {5f, NaN, NaN, 5f}, new float[] {NaN, NaN})); + assertThat(Floats.indexOf(new float[] {5f, NaN, NaN, 5f}, new float[] {NaN, NaN})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Floats.lastIndexOf(EMPTY, (float) 1)); - assertEquals(-1, Floats.lastIndexOf(ARRAY1, (float) 2)); - assertEquals(-1, Floats.lastIndexOf(ARRAY234, (float) 1)); - assertEquals(0, Floats.lastIndexOf(new float[] {(float) -1}, (float) -1)); - assertEquals(0, Floats.lastIndexOf(ARRAY234, (float) 2)); - assertEquals(1, Floats.lastIndexOf(ARRAY234, (float) 3)); - assertEquals(2, Floats.lastIndexOf(ARRAY234, (float) 4)); - assertEquals( - 3, Floats.lastIndexOf(new float[] {(float) 2, (float) 3, (float) 2, (float) 3}, (float) 3)); + assertThat(Floats.lastIndexOf(EMPTY, (float) 1)).isEqualTo(-1); + assertThat(Floats.lastIndexOf(ARRAY1, (float) 2)).isEqualTo(-1); + assertThat(Floats.lastIndexOf(ARRAY234, (float) 1)).isEqualTo(-1); + assertThat(Floats.lastIndexOf(new float[] {(float) -1}, (float) -1)).isEqualTo(0); + assertThat(Floats.lastIndexOf(ARRAY234, (float) 2)).isEqualTo(0); + assertThat(Floats.lastIndexOf(ARRAY234, (float) 3)).isEqualTo(1); + assertThat(Floats.lastIndexOf(ARRAY234, (float) 4)).isEqualTo(2); + assertThat( + Floats.lastIndexOf(new float[] {(float) 2, (float) 3, (float) 2, (float) 3}, (float) 3)) + .isEqualTo(3); for (float value : NUMBERS) { - assertEquals("" + value, 0, Floats.lastIndexOf(new float[] {value, 5f}, value)); + assertWithMessage("" + value) + .that(Floats.lastIndexOf(new float[] {value, 5f}, value)) + .isEqualTo(0); } - assertEquals(-1, Floats.lastIndexOf(new float[] {NaN, 5f}, NaN)); + assertThat(Floats.lastIndexOf(new float[] {NaN, 5f}, NaN)).isEqualTo(-1); } @GwtIncompatible public void testMax_noArgs() { - try { - Floats.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(GREATEST, Floats.max(GREATEST)); - assertEquals(LEAST, Floats.max(LEAST)); - assertEquals( - (float) 9, - Floats.max((float) 8, (float) 6, (float) 7, (float) 5, (float) 3, (float) 0, (float) 9)); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max((float) 8, (float) 6, (float) 7, (float) 5, (float) 3, (float) 0, (float) 9)) + .isEqualTo((float) 9); - assertEquals(0f, Floats.max(-0f, 0f)); - assertEquals(0f, Floats.max(0f, -0f)); - assertEquals(GREATEST, Floats.max(NUMBERS)); - assertTrue(Float.isNaN(Floats.max(VALUES))); + assertThat(max(-0f, 0f)).isEqualTo(0f); + assertThat(max(0f, -0f)).isEqualTo(0f); + assertThat(max(NUMBERS)).isEqualTo(GREATEST); + assertThat(Float.isNaN(max(VALUES))).isTrue(); } @GwtIncompatible public void testMin_noArgs() { - try { - Floats.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, Floats.min(LEAST)); - assertEquals(GREATEST, Floats.min(GREATEST)); - assertEquals( - (float) 0, - Floats.min((float) 8, (float) 6, (float) 7, (float) 5, (float) 3, (float) 0, (float) 9)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((float) 8, (float) 6, (float) 7, (float) 5, (float) 3, (float) 0, (float) 9)) + .isEqualTo((float) 0); - assertEquals(-0f, Floats.min(-0f, 0f)); - assertEquals(-0f, Floats.min(0f, -0f)); - assertEquals(LEAST, Floats.min(NUMBERS)); - assertTrue(Float.isNaN(Floats.min(VALUES))); + assertThat(min(-0f, 0f)).isEqualTo(-0f); + assertThat(min(0f, -0f)).isEqualTo(-0f); + assertThat(min(NUMBERS)).isEqualTo(LEAST); + assertThat(Float.isNaN(min(VALUES))).isTrue(); } public void testConstrainToRange() { - float tolerance = 1e-10f; - assertEquals((float) 1, Floats.constrainToRange((float) 1, (float) 0, (float) 5), tolerance); - assertEquals((float) 1, Floats.constrainToRange((float) 1, (float) 1, (float) 5), tolerance); - assertEquals((float) 3, Floats.constrainToRange((float) 1, (float) 3, (float) 5), tolerance); - assertEquals((float) -1, Floats.constrainToRange((float) 0, (float) -5, (float) -1), tolerance); - assertEquals((float) 2, Floats.constrainToRange((float) 5, (float) 2, (float) 2), tolerance); + assertThat(Floats.constrainToRange((float) 1, (float) 0, (float) 5)).isEqualTo((float) 1); + assertThat(Floats.constrainToRange((float) 1, (float) 1, (float) 5)).isEqualTo((float) 1); + assertThat(Floats.constrainToRange((float) 1, (float) 3, (float) 5)).isEqualTo((float) 3); + assertThat(Floats.constrainToRange((float) 0, (float) -5, (float) -1)).isEqualTo((float) -1); + assertThat(Floats.constrainToRange((float) 5, (float) 2, (float) 2)).isEqualTo((float) 2); + assertThrows( + IllegalArgumentException.class, + () -> Floats.constrainToRange((float) 1, (float) 3, (float) 2)); + } + + public void testConcat() { + assertThat(Floats.concat()).isEqualTo(EMPTY); + assertThat(Floats.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Floats.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Floats.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Floats.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Floats.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Floats.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new float[] {(float) 1, (float) 1, (float) 1}); + assertThat(Floats.concat(ARRAY1, ARRAY234)) + .isEqualTo(new float[] {(float) 1, (float) 2, (float) 3, (float) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + float[][] arrays = new float[arraysDim1][]; + // it's shared to avoid using too much memory in tests + float[] sharedArray = new float[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Floats.constrainToRange((float) 1, (float) 3, (float) 2); + Floats.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Floats.concat())); - assertTrue(Arrays.equals(EMPTY, Floats.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Floats.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Floats.concat(ARRAY1))); - assertNotSame(ARRAY1, Floats.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Floats.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new float[] {(float) 1, (float) 1, (float) 1}, Floats.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new float[] {(float) 1, (float) 2, (float) 3, (float) 4}, - Floats.concat(ARRAY1, ARRAY234))); - } - public void testEnsureCapacity() { - assertSame(EMPTY, Floats.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Floats.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Floats.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new float[] {(float) 1, (float) 0, (float) 0}, Floats.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Floats.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Floats.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Floats.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat( + Arrays.equals( + new float[] {(float) 1, (float) 0, (float) 0}, Floats.ensureCapacity(ARRAY1, 2, 1))) + .isTrue(); } public void testEnsureCapacity_fail() { - try { - Floats.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Floats.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Floats.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Floats.ensureCapacity(ARRAY1, 1, -1)); } @GwtIncompatible // Float.toString returns different value in GWT. public void testJoin() { - assertEquals("", Floats.join(",", EMPTY)); - assertEquals("1.0", Floats.join(",", ARRAY1)); - assertEquals("1.0,2.0", Floats.join(",", (float) 1, (float) 2)); - assertEquals("1.02.03.0", Floats.join("", (float) 1, (float) 2, (float) 3)); + assertThat(Floats.join(",", EMPTY)).isEmpty(); + assertThat(Floats.join(",", ARRAY1)).isEqualTo("1.0"); + assertThat(Floats.join(",", (float) 1, (float) 2)).isEqualTo("1.0,2.0"); + assertThat(Floats.join("", (float) 1, (float) 2, (float) 3)).isEqualTo("1.02.03.0"); } public void testLexicographicalComparator() { @@ -308,10 +332,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Floats.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -325,14 +350,14 @@ public void testReverse() { private static void testReverse(float[] input, float[] expectedOutput) { input = Arrays.copyOf(input, input.length); Floats.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse( float[] input, int fromIndex, int toIndex, float[] expectedOutput) { input = Arrays.copyOf(input, input.length); Floats.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -344,6 +369,103 @@ public void testReverseIndexed() { testReverse(new float[] {-1, 1, -2, 2}, 1, 3, new float[] {-1, -2, 1, 2}); } + private static void testRotate(float[] input, int distance, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + float[] input, int distance, int fromIndex, int toIndex, float[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Floats.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new float[] {}, -1, new float[] {}); + testRotate(new float[] {}, 0, new float[] {}); + testRotate(new float[] {}, 1, new float[] {}); + + testRotate(new float[] {1}, -2, new float[] {1}); + testRotate(new float[] {1}, -1, new float[] {1}); + testRotate(new float[] {1}, 0, new float[] {1}); + testRotate(new float[] {1}, 1, new float[] {1}); + testRotate(new float[] {1}, 2, new float[] {1}); + + testRotate(new float[] {1, 2}, -3, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, -2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 0, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 1, new float[] {2, 1}); + testRotate(new float[] {1, 2}, 2, new float[] {1, 2}); + testRotate(new float[] {1, 2}, 3, new float[] {2, 1}); + + testRotate(new float[] {1, 2, 3}, -5, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -4, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, -3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, -2, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, -1, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 0, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 1, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 2, new float[] {2, 3, 1}); + testRotate(new float[] {1, 2, 3}, 3, new float[] {1, 2, 3}); + testRotate(new float[] {1, 2, 3}, 4, new float[] {3, 1, 2}); + testRotate(new float[] {1, 2, 3}, 5, new float[] {2, 3, 1}); + + testRotate(new float[] {1, 2, 3, 4}, -9, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -5, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, -1, new float[] {2, 3, 4, 1}); + testRotate(new float[] {1, 2, 3, 4}, 0, new float[] {1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4}, 1, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 5, new float[] {4, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4}, 9, new float[] {4, 1, 2, 3}); + + testRotate(new float[] {1, 2, 3, 4, 5}, -6, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, -4, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, -3, new float[] {4, 5, 1, 2, 3}); + testRotate(new float[] {1, 2, 3, 4, 5}, -1, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 0, new float[] {1, 2, 3, 4, 5}); + testRotate(new float[] {1, 2, 3, 4, 5}, 1, new float[] {5, 1, 2, 3, 4}); + testRotate(new float[] {1, 2, 3, 4, 5}, 3, new float[] {3, 4, 5, 1, 2}); + testRotate(new float[] {1, 2, 3, 4, 5}, 4, new float[] {2, 3, 4, 5, 1}); + testRotate(new float[] {1, 2, 3, 4, 5}, 6, new float[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new float[] {}, 0, 0, 0, new float[] {}); + + testRotate(new float[] {1}, 0, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 0, 1, new float[] {1}); + testRotate(new float[] {1}, 1, 1, 1, new float[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new float[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new float[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new float[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new float[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new float[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new float[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new float[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new float[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new float[] {}, new float[] {}); testSortDescending(new float[] {1}, new float[] {1}); @@ -351,15 +473,15 @@ public void testSortDescending() { testSortDescending(new float[] {1, 3, 1}, new float[] {3, 1, 1}); testSortDescending(new float[] {-1, 1, -2, 2}, new float[] {2, 1, -1, -2}); testSortDescending( - new float[] {-1, 1, Float.NaN, -2, -0, 0, 2}, new float[] {Float.NaN, 2, 1, 0, -0, -1, -2}); + new float[] {-1, 1, Float.NaN, -2, -0f, 0, 2}, + new float[] {Float.NaN, 2, 1, 0, -0f, -1, -2}); } private static void testSortDescending(float[] input, float[] expectedOutput) { input = Arrays.copyOf(input, input.length); Floats.sortDescending(input); - // GWT's Arrays.equals doesn't appear to handle NaN correctly, so test each element individually for (int i = 0; i < input.length; i++) { - assertEquals(0, Float.compare(expectedOutput[i], input[i])); + assertThat(input[i]).isEqualTo(expectedOutput[i]); } } @@ -367,9 +489,8 @@ private static void testSortDescending( float[] input, int fromIndex, int toIndex, float[] expectedOutput) { input = Arrays.copyOf(input, input.length); Floats.sortDescending(input, fromIndex, toIndex); - // GWT's Arrays.equals doesn't appear to handle NaN correctly, so test each element individually for (int i = 0; i < input.length; i++) { - assertEquals(0, Float.compare(expectedOutput[i], input[i])); + assertThat(input[i]).isEqualTo(expectedOutput[i]); } } @@ -384,6 +505,7 @@ public void testSortDescendingIndexed() { new float[] {-1, 1, Float.NaN, -2, 2}, 1, 4, new float[] {-1, Float.NaN, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testStringConverterSerialization() { SerializableTester.reserializeAndAssert(Floats.stringConverter()); @@ -392,17 +514,17 @@ public void testStringConverterSerialization() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Floats.toArray(none))); + assertThat(Floats.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((float) 1); - assertTrue(Arrays.equals(ARRAY1, Floats.toArray(one))); + assertThat(Floats.toArray(one)).isEqualTo(ARRAY1); float[] array = {(float) 0, (float) 1, (float) 3}; List three = Arrays.asList((float) 0, (float) 1, (float) 3); - assertTrue(Arrays.equals(array, Floats.toArray(three))); + assertThat(Floats.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Floats.toArray(Floats.asList(array)))); + assertThat(Floats.toArray(Floats.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -412,21 +534,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); float[] arr = Floats.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr.length).isEqualTo(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((float) 0, (float) 1, null); - try { - Floats.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Float> list = Arrays.asList((float) 0, (float) 1, null); + assertThrows(NullPointerException.class, () -> Floats.toArray(list)); } public void testToArray_withConversion() { @@ -439,19 +557,20 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Floats.toArray(bytes))); - assertTrue(Arrays.equals(array, Floats.toArray(shorts))); - assertTrue(Arrays.equals(array, Floats.toArray(ints))); - assertTrue(Arrays.equals(array, Floats.toArray(floats))); - assertTrue(Arrays.equals(array, Floats.toArray(longs))); - assertTrue(Arrays.equals(array, Floats.toArray(doubles))); + assertThat(Floats.toArray(bytes)).isEqualTo(array); + assertThat(Floats.toArray(shorts)).isEqualTo(array); + assertThat(Floats.toArray(ints)).isEqualTo(array); + assertThat(Floats.toArray(floats)).isEqualTo(array); + assertThat(Floats.toArray(longs)).isEqualTo(array); + assertThat(Floats.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { float[] array = {(float) 0, (float) 1}; List list = Floats.asList(array); list.set(0, (float) 2); - assertTrue(Arrays.equals(new float[] {(float) 2, (float) 1}, array)); + assertThat(array).isEqualTo(new float[] {(float) 2, (float) 1}); array[1] = (float) 3; assertThat(list).containsExactly((float) 2, (float) 3).inOrder(); } @@ -463,29 +582,28 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (float) 4); - assertTrue(Arrays.equals(new float[] {(float) 0, (float) 1, (float) 2}, newArray)); + assertThat(newArray).isEqualTo(new float[] {(float) 0, (float) 1, (float) 2}); newArray[1] = (float) 5; - assertEquals((float) 1, (float) list.get(1)); + assertThat((float) list.get(1)).isEqualTo((float) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { float[] array = {(float) 0, (float) 1, (float) 2, (float) 3}; List list = Floats.asList(array); - assertTrue( - Arrays.equals(new float[] {(float) 1, (float) 2}, Floats.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new float[] {}, Floats.toArray(list.subList(2, 2)))); + assertThat(Floats.toArray(list.subList(1, 3))).isEqualTo(new float[] {(float) 1, (float) 2}); + assertThat(Floats.toArray(list.subList(2, 2))).isEmpty(); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Floats.asList(EMPTY)); + assertThat(Floats.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } /** * A reference implementation for {@code tryParse} that just catches the exception from {@link * Float#valueOf}. */ - private static Float referenceTryParse(String input) { + private static @Nullable Float referenceTryParse(String input) { if (input.trim().length() < input.length()) { return null; } @@ -498,12 +616,12 @@ private static Float referenceTryParse(String input) { @GwtIncompatible // Floats.tryParse private static void checkTryParse(String input) { - assertEquals(referenceTryParse(input), Floats.tryParse(input)); + assertThat(Floats.tryParse(input)).isEqualTo(referenceTryParse(input)); } @GwtIncompatible // Floats.tryParse private static void checkTryParse(float expected, String input) { - assertEquals(Float.valueOf(expected), Floats.tryParse(input)); + assertThat(Floats.tryParse(input)).isEqualTo(Float.valueOf(expected)); } @GwtIncompatible // Floats.tryParse @@ -544,6 +662,7 @@ public void testTryParseOfToStringIsOriginal() { } } + @J2ktIncompatible // hexadecimal floats @GwtIncompatible // Floats.tryParse public void testTryParseOfToHexStringIsOriginal() { for (float f : NUMBERS) { @@ -585,11 +704,12 @@ public void testTryParseInfinity() { @GwtIncompatible // Floats.tryParse public void testTryParseFailures() { for (String badInput : BAD_TRY_PARSE_INPUTS) { - assertEquals(referenceTryParse(badInput), Floats.tryParse(badInput)); - assertNull(Floats.tryParse(badInput)); + assertThat(Floats.tryParse(badInput)).isEqualTo(referenceTryParse(badInput)); + assertThat(Floats.tryParse(badInput)).isNull(); } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Floats.class); @@ -598,39 +718,37 @@ public void testNulls() { @GwtIncompatible // Float.toString returns different value in GWT. public void testStringConverter_convert() { Converter converter = Floats.stringConverter(); - assertEquals((Float) 1.0f, converter.convert("1.0")); - assertEquals((Float) 0.0f, converter.convert("0.0")); - assertEquals((Float) (-1.0f), converter.convert("-1.0")); - assertEquals((Float) 1.0f, converter.convert("1")); - assertEquals((Float) 0.0f, converter.convert("0")); - assertEquals((Float) (-1.0f), converter.convert("-1")); - assertEquals((Float) 1e6f, converter.convert("1e6")); - assertEquals((Float) 1e-6f, converter.convert("1e-6")); + assertThat(converter.convert("1.0")).isEqualTo((Float) 1.0f); + assertThat(converter.convert("0.0")).isEqualTo((Float) 0.0f); + assertThat(converter.convert("-1.0")).isEqualTo((Float) (-1.0f)); + assertThat(converter.convert("1")).isEqualTo((Float) 1.0f); + assertThat(converter.convert("0")).isEqualTo((Float) 0.0f); + assertThat(converter.convert("-1")).isEqualTo((Float) (-1.0f)); + assertThat(converter.convert("1e6")).isEqualTo((Float) 1e6f); + assertThat(converter.convert("1e-6")).isEqualTo((Float) 1e-6f); } public void testStringConverter_convertError() { - try { - Floats.stringConverter().convert("notanumber"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> Floats.stringConverter().convert("notanumber")); } public void testStringConverter_nullConversions() { - assertNull(Floats.stringConverter().convert(null)); - assertNull(Floats.stringConverter().reverse().convert(null)); + assertThat(Floats.stringConverter().convert(null)).isNull(); + assertThat(Floats.stringConverter().reverse().convert(null)).isNull(); } + @J2ktIncompatible @GwtIncompatible // Float.toString returns different value in GWT. public void testStringConverter_reverse() { Converter converter = Floats.stringConverter(); - assertEquals("1.0", converter.reverse().convert(1.0f)); - assertEquals("0.0", converter.reverse().convert(0.0f)); - assertEquals("-1.0", converter.reverse().convert(-1.0f)); - assertEquals("1000000.0", converter.reverse().convert(1e6f)); - assertEquals("1.0E-6", converter.reverse().convert(1e-6f)); + assertThat(converter.reverse().convert(1.0f)).isEqualTo("1.0"); + assertThat(converter.reverse().convert(0.0f)).isEqualTo("0.0"); + assertThat(converter.reverse().convert(-1.0f)).isEqualTo("-1.0"); + assertThat(converter.reverse().convert(1e6f)).isEqualTo("1000000.0"); + assertThat(converter.reverse().convert(1e-6f)).isEqualTo("1.0E-6"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStringConverter_nullPointerTester() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -639,11 +757,7 @@ public void testStringConverter_nullPointerTester() throws Exception { @GwtIncompatible public void testTryParse_withNullNoGwt() { - assertNull(Floats.tryParse("null")); - try { - Floats.tryParse(null); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } + assertThat(Floats.tryParse("null")).isNull(); + assertThrows(NullPointerException.class, () -> Floats.tryParse(null)); } } diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java index 4d988365fadd..c0f7af4de727 100644 --- a/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ImmutableDoubleArrayTest.java @@ -14,12 +14,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.primitives.TestPlatform.reduceIterationsIfGwt; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.stream; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ObjectArrays; import com.google.common.collect.testing.ListTestSuiteBuilder; @@ -36,12 +39,17 @@ import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.DoubleStream; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; -/** @author Kevin Bourrillion */ +/** + * @author Kevin Bourrillion + */ @GwtCompatible(emulated = true) +@NullUnmarked public class ImmutableDoubleArrayTest extends TestCase { // Test all creation paths very lazily: by assuming asList() works @@ -137,6 +145,14 @@ public void testCopyOf_collection_nonempty() { assertThat(iia.asList()).containsExactly(0.0, 1.0, 3.0).inOrder(); } + public void testCopyOf_stream() { + assertThat(ImmutableDoubleArray.copyOf(DoubleStream.empty())) + .isSameInstanceAs(ImmutableDoubleArray.of()); + assertThat(ImmutableDoubleArray.copyOf(DoubleStream.of(0, 1, 3)).asList()) + .containsExactly(0.0, 1.0, 3.0) + .inOrder(); + } + public void testBuilder_presize_zero() { ImmutableDoubleArray.Builder builder = ImmutableDoubleArray.builder(0); builder.add(5.0); @@ -145,11 +161,7 @@ public void testBuilder_presize_zero() { } public void testBuilder_presize_negative() { - try { - ImmutableDoubleArray.builder(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ImmutableDoubleArray.builder(-1)); } /** @@ -158,7 +170,7 @@ public void testBuilder_presize_negative() { */ public void testBuilder_bruteForce() { for (int i = 0; i < reduceIterationsIfGwt(100); i++) { - ImmutableDoubleArray.Builder builder = ImmutableDoubleArray.builder(RANDOM.nextInt(20)); + ImmutableDoubleArray.Builder builder = ImmutableDoubleArray.builder(random.nextInt(20)); AtomicInteger counter = new AtomicInteger(0); while (counter.get() < 1000) { BuilderOp op = BuilderOp.randomOp(); @@ -181,7 +193,7 @@ void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { ADD_ARRAY { @Override void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { - double[] array = new double[RANDOM.nextInt(10)]; + double[] array = new double[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -192,7 +204,7 @@ void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { @Override void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { List list = new ArrayList<>(); - double num = RANDOM.nextInt(10); + double num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add((double) counter.getAndIncrement()); } @@ -203,17 +215,27 @@ void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { @Override void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { List list = new ArrayList<>(); - double num = RANDOM.nextInt(10); + double num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add((double) counter.getAndIncrement()); } builder.addAll(iterable(list)); } }, + ADD_STREAM { + @Override + void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { + double[] array = new double[random.nextInt(10)]; + for (int i = 0; i < array.length; i++) { + array[i] = counter.getAndIncrement(); + } + builder.addAll(stream(array)); + } + }, ADD_IIA { @Override void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { - double[] array = new double[RANDOM.nextInt(10)]; + double[] array = new double[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -223,7 +245,7 @@ void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { ADD_LARGER_ARRAY { @Override void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { - double[] array = new double[RANDOM.nextInt(200) + 200]; + double[] array = new double[random.nextInt(200) + 200]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -235,13 +257,13 @@ void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter) { static final BuilderOp[] values = values(); static BuilderOp randomOp() { - return values[RANDOM.nextInt(values.length)]; + return values[random.nextInt(values.length)]; } abstract void doIt(ImmutableDoubleArray.Builder builder, AtomicInteger counter); } - private static final Random RANDOM = new Random(42); + private static final Random random = new Random(42); public void testLength() { assertThat(ImmutableDoubleArray.of().length()).isEqualTo(0); @@ -268,23 +290,11 @@ public void testGet_good() { public void testGet_bad() { ImmutableDoubleArray iia = ImmutableDoubleArray.of(0, 1, 3); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia.get(3); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(3)); - iia = iia.subArray(1, 2); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + ImmutableDoubleArray sub = iia.subArray(1, 2); + assertThrows(IndexOutOfBoundsException.class, () -> sub.get(-1)); } public void testIndexOf() { @@ -327,6 +337,23 @@ public void testContains() { assertThat(iia.subArray(1, 5).contains(1)).isTrue(); } + public void testForEach() { + ImmutableDoubleArray.of().forEach(i -> fail()); + ImmutableDoubleArray.of(0, 1, 3).subArray(1, 1).forEach(i -> fail()); + + AtomicInteger count = new AtomicInteger(0); + ImmutableDoubleArray.of(0, 1, 2, 3) + .forEach(i -> assertThat(i).isEqualTo((double) count.getAndIncrement())); + assertThat(count.get()).isEqualTo(4); + } + + public void testStream() { + ImmutableDoubleArray.of().stream().forEach(i -> fail()); + ImmutableDoubleArray.of(0, 1, 3).subArray(1, 1).stream().forEach(i -> fail()); + assertThat(ImmutableDoubleArray.of(0, 1, 3).stream().toArray()) + .isEqualTo(new double[] {0, 1, 3}); + } + public void testSubArray() { ImmutableDoubleArray iia0 = ImmutableDoubleArray.of(); ImmutableDoubleArray iia1 = ImmutableDoubleArray.of(5); @@ -339,16 +366,8 @@ public void testSubArray() { assertThat(iia3.subArray(0, 2).asList()).containsExactly(5.0, 25.0).inOrder(); assertThat(iia3.subArray(1, 3).asList()).containsExactly(25.0, 125.0).inOrder(); - try { - iia3.subArray(-1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia3.subArray(1, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(-1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(1, 4)); } /* @@ -399,6 +418,7 @@ public void testTrimmed() { assertActuallyTrims(underSized); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { assertThat(reserialize(ImmutableDoubleArray.of())).isSameInstanceAs(ImmutableDoubleArray.of()); @@ -423,7 +443,9 @@ private static void assertDoesntActuallyTrim(ImmutableDoubleArray iia) { assertThat(iia.trimmed()).isSameInstanceAs(iia); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { List> builders = ImmutableList.of( @@ -454,7 +476,9 @@ public static Test suite() { return suite; } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible private static ImmutableDoubleArray makeArray(Double[] values) { return ImmutableDoubleArray.copyOf(Arrays.asList(values)); } @@ -462,7 +486,9 @@ private static ImmutableDoubleArray makeArray(Double[] values) { // Test generators. To let the GWT test suite generator access them, they need to be public named // classes with a public default constructor (not that we run these suites under GWT yet). + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableDoubleArrayAsListGenerator extends TestDoubleListGenerator { @Override protected List create(Double[] elements) { @@ -470,7 +496,9 @@ protected List create(Double[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableDoubleArrayHeadSubListAsListGenerator extends TestDoubleListGenerator { @Override @@ -481,7 +509,9 @@ protected List create(Double[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableDoubleArrayTailSubListAsListGenerator extends TestDoubleListGenerator { @Override @@ -492,7 +522,9 @@ protected List create(Double[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableDoubleArrayMiddleSubListAsListGenerator extends TestDoubleListGenerator { @Override @@ -504,12 +536,16 @@ protected List create(Double[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible private static Double[] concat(Double[] a, Double[] b) { return ObjectArrays.concat(a, b, Double.class); } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public abstract static class TestDoubleListGenerator implements TestListGenerator { @Override public SampleElements samples() { @@ -544,7 +580,9 @@ public List order(List insertionOrder) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static class SampleDoubles extends SampleElements { public SampleDoubles() { super(-0.0, Long.MAX_VALUE * 3.0, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN); diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java index 86274d4531cf..6051eead8fec 100644 --- a/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ImmutableIntArrayTest.java @@ -14,12 +14,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.primitives.TestPlatform.reduceIterationsIfGwt; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.stream; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ObjectArrays; import com.google.common.collect.testing.ListTestSuiteBuilder; @@ -36,12 +39,17 @@ import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; -/** @author Kevin Bourrillion */ +/** + * @author Kevin Bourrillion + */ @GwtCompatible(emulated = true) +@NullUnmarked public class ImmutableIntArrayTest extends TestCase { // Test all creation paths very lazily: by assuming asList() works @@ -134,6 +142,14 @@ public void testCopyOf_collection_nonempty() { assertThat(iia.asList()).containsExactly(0, 1, 3).inOrder(); } + public void testCopyOf_stream() { + assertThat(ImmutableIntArray.copyOf(IntStream.empty())) + .isSameInstanceAs(ImmutableIntArray.of()); + assertThat(ImmutableIntArray.copyOf(IntStream.of(0, 1, 3)).asList()) + .containsExactly(0, 1, 3) + .inOrder(); + } + public void testBuilder_presize_zero() { ImmutableIntArray.Builder builder = ImmutableIntArray.builder(0); builder.add(5); @@ -142,11 +158,7 @@ public void testBuilder_presize_zero() { } public void testBuilder_presize_negative() { - try { - ImmutableIntArray.builder(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ImmutableIntArray.builder(-1)); } /** @@ -155,7 +167,7 @@ public void testBuilder_presize_negative() { */ public void testBuilder_bruteForce() { for (int i = 0; i < reduceIterationsIfGwt(100); i++) { - ImmutableIntArray.Builder builder = ImmutableIntArray.builder(RANDOM.nextInt(20)); + ImmutableIntArray.Builder builder = ImmutableIntArray.builder(random.nextInt(20)); AtomicInteger counter = new AtomicInteger(0); while (counter.get() < 1000) { BuilderOp op = BuilderOp.randomOp(); @@ -178,7 +190,7 @@ void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { ADD_ARRAY { @Override void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { - int[] array = new int[RANDOM.nextInt(10)]; + int[] array = new int[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -189,7 +201,7 @@ void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { @Override void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { List list = new ArrayList<>(); - int num = RANDOM.nextInt(10); + int num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add(counter.getAndIncrement()); } @@ -200,17 +212,27 @@ void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { @Override void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { List list = new ArrayList<>(); - int num = RANDOM.nextInt(10); + int num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add(counter.getAndIncrement()); } builder.addAll(iterable(list)); } }, + ADD_STREAM { + @Override + void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { + int[] array = new int[random.nextInt(10)]; + for (int i = 0; i < array.length; i++) { + array[i] = counter.getAndIncrement(); + } + builder.addAll(stream(array)); + } + }, ADD_IIA { @Override void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { - int[] array = new int[RANDOM.nextInt(10)]; + int[] array = new int[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -220,7 +242,7 @@ void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { ADD_LARGER_ARRAY { @Override void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { - int[] array = new int[RANDOM.nextInt(200) + 200]; + int[] array = new int[random.nextInt(200) + 200]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -232,13 +254,13 @@ void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter) { static final BuilderOp[] values = values(); static BuilderOp randomOp() { - return values[RANDOM.nextInt(values.length)]; + return values[random.nextInt(values.length)]; } abstract void doIt(ImmutableIntArray.Builder builder, AtomicInteger counter); } - private static final Random RANDOM = new Random(42); + private static final Random random = new Random(42); public void testLength() { assertThat(ImmutableIntArray.of().length()).isEqualTo(0); @@ -265,23 +287,11 @@ public void testGet_good() { public void testGet_bad() { ImmutableIntArray iia = ImmutableIntArray.of(0, 1, 3); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia.get(3); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(3)); - iia = iia.subArray(1, 2); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + ImmutableIntArray sub = iia.subArray(1, 2); + assertThrows(IndexOutOfBoundsException.class, () -> sub.get(-1)); } public void testIndexOf() { @@ -314,6 +324,21 @@ public void testContains() { assertThat(iia.subArray(1, 5).contains(1)).isTrue(); } + public void testForEach() { + ImmutableIntArray.of().forEach(i -> fail()); + ImmutableIntArray.of(0, 1, 3).subArray(1, 1).forEach(i -> fail()); + + AtomicInteger count = new AtomicInteger(0); + ImmutableIntArray.of(0, 1, 2, 3).forEach(i -> assertThat(i).isEqualTo(count.getAndIncrement())); + assertThat(count.get()).isEqualTo(4); + } + + public void testStream() { + ImmutableIntArray.of().stream().forEach(i -> fail()); + ImmutableIntArray.of(0, 1, 3).subArray(1, 1).stream().forEach(i -> fail()); + assertThat(ImmutableIntArray.of(0, 1, 3).stream().toArray()).isEqualTo(new int[] {0, 1, 3}); + } + public void testSubArray() { ImmutableIntArray iia0 = ImmutableIntArray.of(); ImmutableIntArray iia1 = ImmutableIntArray.of(5); @@ -326,16 +351,8 @@ public void testSubArray() { assertThat(iia3.subArray(0, 2).asList()).containsExactly(5, 25).inOrder(); assertThat(iia3.subArray(1, 3).asList()).containsExactly(25, 125).inOrder(); - try { - iia3.subArray(-1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia3.subArray(1, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(-1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(1, 4)); } /* @@ -386,6 +403,7 @@ public void testTrimmed() { assertActuallyTrims(underSized); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { assertThat(reserialize(ImmutableIntArray.of())).isSameInstanceAs(ImmutableIntArray.of()); @@ -410,7 +428,9 @@ private static void assertDoesntActuallyTrim(ImmutableIntArray iia) { assertThat(iia.trimmed()).isSameInstanceAs(iia); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { List> builders = ImmutableList.of( @@ -441,6 +461,7 @@ public static Test suite() { return suite; } + @J2ktIncompatible @GwtIncompatible // used only from suite private static ImmutableIntArray makeArray(Integer[] values) { return ImmutableIntArray.copyOf(Arrays.asList(values)); @@ -449,6 +470,7 @@ private static ImmutableIntArray makeArray(Integer[] values) { // Test generators. To let the GWT test suite generator access them, they need to be public named // classes with a public default constructor (not that we run these suites under GWT yet). + @J2ktIncompatible @GwtIncompatible // used only from suite public static final class ImmutableIntArrayAsListGenerator extends TestIntegerListGenerator { @Override @@ -457,6 +479,7 @@ protected List create(Integer[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite public static final class ImmutableIntArrayHeadSubListAsListGenerator extends TestIntegerListGenerator { @@ -468,6 +491,7 @@ protected List create(Integer[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite public static final class ImmutableIntArrayTailSubListAsListGenerator extends TestIntegerListGenerator { @@ -479,6 +503,7 @@ protected List create(Integer[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite public static final class ImmutableIntArrayMiddleSubListAsListGenerator extends TestIntegerListGenerator { @@ -491,11 +516,13 @@ protected List create(Integer[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite private static Integer[] concat(Integer[] a, Integer[] b) { return ObjectArrays.concat(a, b, Integer.class); } + @J2ktIncompatible @GwtIncompatible // used only from suite public abstract static class TestIntegerListGenerator implements TestListGenerator { @Override @@ -531,6 +558,7 @@ public List order(List insertionOrder) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite public static class SampleIntegers extends SampleElements { public SampleIntegers() { diff --git a/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java b/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java index ff879ec11f09..ec506695b6cf 100644 --- a/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ImmutableLongArrayTest.java @@ -14,12 +14,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.primitives.TestPlatform.reduceIterationsIfGwt; import static com.google.common.testing.SerializableTester.reserialize; import static com.google.common.truth.Truth.assertThat; +import static java.util.Arrays.stream; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ObjectArrays; import com.google.common.collect.testing.ListTestSuiteBuilder; @@ -36,12 +39,17 @@ import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.LongStream; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; -/** @author Kevin Bourrillion */ +/** + * @author Kevin Bourrillion + */ @GwtCompatible(emulated = true) +@NullUnmarked public class ImmutableLongArrayTest extends TestCase { // Test all creation paths very lazily: by assuming asList() works @@ -136,6 +144,14 @@ public void testCopyOf_collection_nonempty() { assertThat(iia.asList()).containsExactly(0L, 1L, 3L).inOrder(); } + public void testCopyOf_stream() { + assertThat(ImmutableLongArray.copyOf(LongStream.empty())) + .isSameInstanceAs(ImmutableLongArray.of()); + assertThat(ImmutableLongArray.copyOf(LongStream.of(0, 1, 3)).asList()) + .containsExactly(0L, 1L, 3L) + .inOrder(); + } + public void testBuilder_presize_zero() { ImmutableLongArray.Builder builder = ImmutableLongArray.builder(0); builder.add(5L); @@ -144,11 +160,7 @@ public void testBuilder_presize_zero() { } public void testBuilder_presize_negative() { - try { - ImmutableLongArray.builder(-1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> ImmutableLongArray.builder(-1)); } /** @@ -157,7 +169,7 @@ public void testBuilder_presize_negative() { */ public void testBuilder_bruteForce() { for (int i = 0; i < reduceIterationsIfGwt(100); i++) { - ImmutableLongArray.Builder builder = ImmutableLongArray.builder(RANDOM.nextInt(20)); + ImmutableLongArray.Builder builder = ImmutableLongArray.builder(random.nextInt(20)); AtomicLong counter = new AtomicLong(0); while (counter.get() < 1000) { BuilderOp op = BuilderOp.randomOp(); @@ -180,7 +192,7 @@ void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { ADD_ARRAY { @Override void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { - long[] array = new long[RANDOM.nextInt(10)]; + long[] array = new long[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -191,7 +203,7 @@ void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { @Override void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { List list = new ArrayList<>(); - long num = RANDOM.nextInt(10); + long num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add(counter.getAndIncrement()); } @@ -202,17 +214,27 @@ void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { @Override void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { List list = new ArrayList<>(); - long num = RANDOM.nextInt(10); + long num = random.nextInt(10); for (int i = 0; i < num; i++) { list.add(counter.getAndIncrement()); } builder.addAll(iterable(list)); } }, + ADD_STREAM { + @Override + void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { + long[] array = new long[random.nextInt(10)]; + for (int i = 0; i < array.length; i++) { + array[i] = counter.getAndIncrement(); + } + builder.addAll(stream(array)); + } + }, ADD_IIA { @Override void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { - long[] array = new long[RANDOM.nextInt(10)]; + long[] array = new long[random.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -222,7 +244,7 @@ void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { ADD_LARGER_ARRAY { @Override void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { - long[] array = new long[RANDOM.nextInt(200) + 200]; + long[] array = new long[random.nextInt(200) + 200]; for (int i = 0; i < array.length; i++) { array[i] = counter.getAndIncrement(); } @@ -234,13 +256,13 @@ void doIt(ImmutableLongArray.Builder builder, AtomicLong counter) { static final BuilderOp[] values = values(); static BuilderOp randomOp() { - return values[RANDOM.nextInt(values.length)]; + return values[random.nextInt(values.length)]; } abstract void doIt(ImmutableLongArray.Builder builder, AtomicLong counter); } - private static final Random RANDOM = new Random(42); + private static final Random random = new Random(42); public void testLength() { assertThat(ImmutableLongArray.of().length()).isEqualTo(0); @@ -267,23 +289,11 @@ public void testGet_good() { public void testGet_bad() { ImmutableLongArray iia = ImmutableLongArray.of(0, 1, 3); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia.get(3); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia.get(3)); - iia = iia.subArray(1, 2); - try { - iia.get(-1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + ImmutableLongArray sub = iia.subArray(1, 2); + assertThrows(IndexOutOfBoundsException.class, () -> sub.get(-1)); } public void testIndexOf() { @@ -316,6 +326,22 @@ public void testContains() { assertThat(iia.subArray(1, 5).contains(1)).isTrue(); } + public void testForEach() { + ImmutableLongArray.of().forEach(i -> fail()); + ImmutableLongArray.of(0, 1, 3).subArray(1, 1).forEach(i -> fail()); + + AtomicLong count = new AtomicLong(0); + ImmutableLongArray.of(0, 1, 2, 3) + .forEach(i -> assertThat(i).isEqualTo(count.getAndIncrement())); + assertThat(count.get()).isEqualTo(4); + } + + public void testStream() { + ImmutableLongArray.of().stream().forEach(i -> fail()); + ImmutableLongArray.of(0, 1, 3).subArray(1, 1).stream().forEach(i -> fail()); + assertThat(ImmutableLongArray.of(0, 1, 3).stream().toArray()).isEqualTo(new long[] {0, 1, 3}); + } + public void testSubArray() { ImmutableLongArray iia0 = ImmutableLongArray.of(); ImmutableLongArray iia1 = ImmutableLongArray.of(5); @@ -328,16 +354,8 @@ public void testSubArray() { assertThat(iia3.subArray(0, 2).asList()).containsExactly(5L, 25L).inOrder(); assertThat(iia3.subArray(1, 3).asList()).containsExactly(25L, 125L).inOrder(); - try { - iia3.subArray(-1, 1); - fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - iia3.subArray(1, 4); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(-1, 1)); + assertThrows(IndexOutOfBoundsException.class, () -> iia3.subArray(1, 4)); } /* @@ -388,6 +406,7 @@ public void testTrimmed() { assertActuallyTrims(underSized); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testSerialization() { assertThat(reserialize(ImmutableLongArray.of())).isSameInstanceAs(ImmutableLongArray.of()); @@ -412,7 +431,9 @@ private static void assertDoesntActuallyTrim(ImmutableLongArray iia) { assertThat(iia.trimmed()).isSameInstanceAs(iia); } + @J2ktIncompatible @GwtIncompatible // suite + @AndroidIncompatible // test-suite builders public static Test suite() { List> builders = ImmutableList.of( @@ -443,7 +464,9 @@ public static Test suite() { return suite; } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible private static ImmutableLongArray makeArray(Long[] values) { return ImmutableLongArray.copyOf(Arrays.asList(values)); } @@ -451,7 +474,9 @@ private static ImmutableLongArray makeArray(Long[] values) { // Test generators. To let the GWT test suite generator access them, they need to be public named // classes with a public default constructor (not that we run these suites under GWT yet). + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableLongArrayAsListGenerator extends TestLongListGenerator { @Override protected List create(Long[] elements) { @@ -459,7 +484,9 @@ protected List create(Long[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableLongArrayHeadSubListAsListGenerator extends TestLongListGenerator { @Override @@ -470,7 +497,9 @@ protected List create(Long[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableLongArrayTailSubListAsListGenerator extends TestLongListGenerator { @Override @@ -481,7 +510,9 @@ protected List create(Long[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static final class ImmutableLongArrayMiddleSubListAsListGenerator extends TestLongListGenerator { @Override @@ -493,12 +524,16 @@ protected List create(Long[] elements) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible private static Long[] concat(Long[] a, Long[] b) { return ObjectArrays.concat(a, b, Long.class); } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public abstract static class TestLongListGenerator implements TestListGenerator { @Override public SampleElements samples() { @@ -533,7 +568,9 @@ public List order(List insertionOrder) { } } + @J2ktIncompatible @GwtIncompatible // used only from suite + @AndroidIncompatible public static class SampleLongs extends SampleElements { public SampleLongs() { super(1L << 31, 1L << 33, 1L << 36, 1L << 40, 1L << 45); diff --git a/android/guava-tests/test/com/google/common/primitives/IntArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/IntArrayAsListTest.java index e02d1aa5a52f..14ab5667bce3 100644 --- a/android/guava-tests/test/com/google/common/primitives/IntArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/IntArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Ints#asList(int[])}. @@ -39,6 +41,8 @@ */ @GwtCompatible(emulated = true) @SuppressWarnings("cast") // redundant casts are intentional and harmless +@NullUnmarked +@AndroidIncompatible // test-suite builders public class IntArrayAsListTest extends TestCase { private static List asList(Integer[] values) { @@ -49,6 +53,7 @@ private static List asList(Integer[] values) { return Ints.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/IntsTest.java b/android/guava-tests/test/com/google/common/primitives/IntsTest.java index 72154224c7f4..f5a2b557c6d7 100644 --- a/android/guava-tests/test/com/google/common/primitives/IntsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/IntsTest.java @@ -16,8 +16,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.Ints.max; +import static com.google.common.primitives.Ints.min; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; @@ -29,6 +36,8 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Ints}. @@ -36,6 +45,7 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullMarked @SuppressWarnings("cast") // redundant casts are intentional and harmless public class IntsTest extends TestCase { private static final int[] EMPTY = {}; @@ -49,13 +59,13 @@ public class IntsTest extends TestCase { public void testHashCode() { for (int value : VALUES) { - assertEquals(((Integer) value).hashCode(), Ints.hashCode(value)); + assertThat(Ints.hashCode(value)).isEqualTo(((Integer) value).hashCode()); } } public void testCheckedCast() { for (int value : VALUES) { - assertEquals(value, Ints.checkedCast((long) value)); + assertThat(Ints.checkedCast((long) value)).isEqualTo(value); } assertCastFails(GREATEST + 1L); assertCastFails(LEAST - 1L); @@ -65,12 +75,12 @@ public void testCheckedCast() { public void testSaturatedCast() { for (int value : VALUES) { - assertEquals(value, Ints.saturatedCast((long) value)); + assertThat(Ints.saturatedCast((long) value)).isEqualTo(value); } - assertEquals(GREATEST, Ints.saturatedCast(GREATEST + 1L)); - assertEquals(LEAST, Ints.saturatedCast(LEAST - 1L)); - assertEquals(GREATEST, Ints.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, Ints.saturatedCast(Long.MIN_VALUE)); + assertThat(Ints.saturatedCast(GREATEST + 1L)).isEqualTo(GREATEST); + assertThat(Ints.saturatedCast(LEAST - 1L)).isEqualTo(LEAST); + assertThat(Ints.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(Ints.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } private static void assertCastFails(long value) { @@ -78,166 +88,190 @@ private static void assertCastFails(long value) { Ints.checkedCast(value); fail("Cast to int should have failed: " + value); } catch (IllegalArgumentException ex) { - assertTrue( - value + " not found in exception text: " + ex.getMessage(), - ex.getMessage().contains(String.valueOf(value))); + assertWithMessage(value + " not found in exception text: " + ex.getMessage()) + .that(ex.getMessage().contains(String.valueOf(value))) + .isTrue(); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (int x : VALUES) { for (int y : VALUES) { // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Integer.valueOf(x).compareTo(y), Ints.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Ints.compare(x, y)) + .isEqualTo(Integer.valueOf(x).compareTo(y)); } } } public void testContains() { - assertFalse(Ints.contains(EMPTY, (int) 1)); - assertFalse(Ints.contains(ARRAY1, (int) 2)); - assertFalse(Ints.contains(ARRAY234, (int) 1)); - assertTrue(Ints.contains(new int[] {(int) -1}, (int) -1)); - assertTrue(Ints.contains(ARRAY234, (int) 2)); - assertTrue(Ints.contains(ARRAY234, (int) 3)); - assertTrue(Ints.contains(ARRAY234, (int) 4)); + assertThat(Ints.contains(EMPTY, (int) 1)).isFalse(); + assertThat(Ints.contains(ARRAY1, (int) 2)).isFalse(); + assertThat(Ints.contains(ARRAY234, (int) 1)).isFalse(); + assertThat(Ints.contains(new int[] {(int) -1}, (int) -1)).isTrue(); + assertThat(Ints.contains(ARRAY234, (int) 2)).isTrue(); + assertThat(Ints.contains(ARRAY234, (int) 3)).isTrue(); + assertThat(Ints.contains(ARRAY234, (int) 4)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Ints.indexOf(EMPTY, (int) 1)); - assertEquals(-1, Ints.indexOf(ARRAY1, (int) 2)); - assertEquals(-1, Ints.indexOf(ARRAY234, (int) 1)); - assertEquals(0, Ints.indexOf(new int[] {(int) -1}, (int) -1)); - assertEquals(0, Ints.indexOf(ARRAY234, (int) 2)); - assertEquals(1, Ints.indexOf(ARRAY234, (int) 3)); - assertEquals(2, Ints.indexOf(ARRAY234, (int) 4)); - assertEquals(1, Ints.indexOf(new int[] {(int) 2, (int) 3, (int) 2, (int) 3}, (int) 3)); + assertThat(Ints.indexOf(EMPTY, (int) 1)).isEqualTo(-1); + assertThat(Ints.indexOf(ARRAY1, (int) 2)).isEqualTo(-1); + assertThat(Ints.indexOf(ARRAY234, (int) 1)).isEqualTo(-1); + assertThat(Ints.indexOf(new int[] {(int) -1}, (int) -1)).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, (int) 2)).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, (int) 3)).isEqualTo(1); + assertThat(Ints.indexOf(ARRAY234, (int) 4)).isEqualTo(2); + assertThat(Ints.indexOf(new int[] {(int) 2, (int) 3, (int) 2, (int) 3}, (int) 3)).isEqualTo(1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Ints.indexOf(EMPTY, EMPTY)); - assertEquals(0, Ints.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Ints.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Ints.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Ints.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Ints.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Ints.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Ints.indexOf(ARRAY234, new int[] {(int) 2, (int) 3})); - assertEquals(1, Ints.indexOf(ARRAY234, new int[] {(int) 3, (int) 4})); - assertEquals(1, Ints.indexOf(ARRAY234, new int[] {(int) 3})); - assertEquals(2, Ints.indexOf(ARRAY234, new int[] {(int) 4})); - assertEquals( - 1, - Ints.indexOf(new int[] {(int) 2, (int) 3, (int) 3, (int) 3, (int) 3}, new int[] {(int) 3})); - assertEquals( - 2, - Ints.indexOf( - new int[] {(int) 2, (int) 3, (int) 2, (int) 3, (int) 4, (int) 2, (int) 3}, - new int[] {(int) 2, (int) 3, (int) 4})); - assertEquals( - 1, - Ints.indexOf( - new int[] {(int) 2, (int) 2, (int) 3, (int) 4, (int) 2, (int) 3, (int) 4}, - new int[] {(int) 2, (int) 3, (int) 4})); - assertEquals( - -1, - Ints.indexOf(new int[] {(int) 4, (int) 3, (int) 2}, new int[] {(int) 2, (int) 3, (int) 4})); + assertThat(Ints.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Ints.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Ints.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Ints.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Ints.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, new int[] {(int) 2, (int) 3})).isEqualTo(0); + assertThat(Ints.indexOf(ARRAY234, new int[] {(int) 3, (int) 4})).isEqualTo(1); + assertThat(Ints.indexOf(ARRAY234, new int[] {(int) 3})).isEqualTo(1); + assertThat(Ints.indexOf(ARRAY234, new int[] {(int) 4})).isEqualTo(2); + assertThat( + Ints.indexOf( + new int[] {(int) 2, (int) 3, (int) 3, (int) 3, (int) 3}, new int[] {(int) 3})) + .isEqualTo(1); + assertThat( + Ints.indexOf( + new int[] {(int) 2, (int) 3, (int) 2, (int) 3, (int) 4, (int) 2, (int) 3}, + new int[] {(int) 2, (int) 3, (int) 4})) + .isEqualTo(2); + assertThat( + Ints.indexOf( + new int[] {(int) 2, (int) 2, (int) 3, (int) 4, (int) 2, (int) 3, (int) 4}, + new int[] {(int) 2, (int) 3, (int) 4})) + .isEqualTo(1); + assertThat( + Ints.indexOf( + new int[] {(int) 4, (int) 3, (int) 2}, new int[] {(int) 2, (int) 3, (int) 4})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Ints.lastIndexOf(EMPTY, (int) 1)); - assertEquals(-1, Ints.lastIndexOf(ARRAY1, (int) 2)); - assertEquals(-1, Ints.lastIndexOf(ARRAY234, (int) 1)); - assertEquals(0, Ints.lastIndexOf(new int[] {(int) -1}, (int) -1)); - assertEquals(0, Ints.lastIndexOf(ARRAY234, (int) 2)); - assertEquals(1, Ints.lastIndexOf(ARRAY234, (int) 3)); - assertEquals(2, Ints.lastIndexOf(ARRAY234, (int) 4)); - assertEquals(3, Ints.lastIndexOf(new int[] {(int) 2, (int) 3, (int) 2, (int) 3}, (int) 3)); + assertThat(Ints.lastIndexOf(EMPTY, (int) 1)).isEqualTo(-1); + assertThat(Ints.lastIndexOf(ARRAY1, (int) 2)).isEqualTo(-1); + assertThat(Ints.lastIndexOf(ARRAY234, (int) 1)).isEqualTo(-1); + assertThat(Ints.lastIndexOf(new int[] {(int) -1}, (int) -1)).isEqualTo(0); + assertThat(Ints.lastIndexOf(ARRAY234, (int) 2)).isEqualTo(0); + assertThat(Ints.lastIndexOf(ARRAY234, (int) 3)).isEqualTo(1); + assertThat(Ints.lastIndexOf(ARRAY234, (int) 4)).isEqualTo(2); + assertThat(Ints.lastIndexOf(new int[] {(int) 2, (int) 3, (int) 2, (int) 3}, (int) 3)) + .isEqualTo(3); } @GwtIncompatible public void testMax_noArgs() { - try { - Ints.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, Ints.max(LEAST)); - assertEquals(GREATEST, Ints.max(GREATEST)); - assertEquals((int) 9, Ints.max((int) 8, (int) 6, (int) 7, (int) 5, (int) 3, (int) 0, (int) 9)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max((int) 8, (int) 6, (int) 7, (int) 5, (int) 3, (int) 0, (int) 9)) + .isEqualTo((int) 9); } @GwtIncompatible public void testMin_noArgs() { - try { - Ints.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, Ints.min(LEAST)); - assertEquals(GREATEST, Ints.min(GREATEST)); - assertEquals((int) 0, Ints.min((int) 8, (int) 6, (int) 7, (int) 5, (int) 3, (int) 0, (int) 9)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((int) 8, (int) 6, (int) 7, (int) 5, (int) 3, (int) 0, (int) 9)) + .isEqualTo((int) 0); } public void testConstrainToRange() { - assertEquals((int) 1, Ints.constrainToRange((int) 1, (int) 0, (int) 5)); - assertEquals((int) 1, Ints.constrainToRange((int) 1, (int) 1, (int) 5)); - assertEquals((int) 3, Ints.constrainToRange((int) 1, (int) 3, (int) 5)); - assertEquals((int) -1, Ints.constrainToRange((int) 0, (int) -5, (int) -1)); - assertEquals((int) 2, Ints.constrainToRange((int) 5, (int) 2, (int) 2)); + assertThat(Ints.constrainToRange((int) 1, (int) 0, (int) 5)).isEqualTo((int) 1); + assertThat(Ints.constrainToRange((int) 1, (int) 1, (int) 5)).isEqualTo((int) 1); + assertThat(Ints.constrainToRange((int) 1, (int) 3, (int) 5)).isEqualTo((int) 3); + assertThat(Ints.constrainToRange((int) 0, (int) -5, (int) -1)).isEqualTo((int) -1); + assertThat(Ints.constrainToRange((int) 5, (int) 2, (int) 2)).isEqualTo((int) 2); + assertThrows( + IllegalArgumentException.class, () -> Ints.constrainToRange((int) 1, (int) 3, (int) 2)); + } + + public void testConcat() { + assertThat(Ints.concat()).isEqualTo(EMPTY); + assertThat(Ints.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Ints.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Ints.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Ints.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Ints.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Ints.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new int[] {(int) 1, (int) 1, (int) 1}); + assertThat(Ints.concat(ARRAY1, ARRAY234)) + .isEqualTo(new int[] {(int) 1, (int) 2, (int) 3, (int) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + int[][] arrays = new int[arraysDim1][]; + // it's shared to avoid using too much memory in tests + int[] sharedArray = new int[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Ints.constrainToRange((int) 1, (int) 3, (int) 2); + Ints.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Ints.concat())); - assertTrue(Arrays.equals(EMPTY, Ints.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Ints.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Ints.concat(ARRAY1))); - assertNotSame(ARRAY1, Ints.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Ints.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals(new int[] {(int) 1, (int) 1, (int) 1}, Ints.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new int[] {(int) 1, (int) 2, (int) 3, (int) 4}, Ints.concat(ARRAY1, ARRAY234))); - } - public void testToByteArray() { - assertTrue(Arrays.equals(new byte[] {0x12, 0x13, 0x14, 0x15}, Ints.toByteArray(0x12131415))); - assertTrue( - Arrays.equals( - new byte[] {(byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC}, - Ints.toByteArray(0xFFEEDDCC))); + assertThat(Ints.toByteArray(0x12131415)).isEqualTo(new byte[] {0x12, 0x13, 0x14, 0x15}); + assertThat(Ints.toByteArray(0xFFEEDDCC)) + .isEqualTo(new byte[] {(byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC}); } public void testFromByteArray() { - assertEquals(0x12131415, Ints.fromByteArray(new byte[] {0x12, 0x13, 0x14, 0x15, 0x33})); - assertEquals( - 0xFFEEDDCC, - Ints.fromByteArray(new byte[] {(byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC})); + assertThat(Ints.fromByteArray(new byte[] {0x12, 0x13, 0x14, 0x15, 0x33})).isEqualTo(0x12131415); + assertThat(Ints.fromByteArray(new byte[] {(byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC})) + .isEqualTo(0xFFEEDDCC); } public void testFromByteArrayFails() { - try { - Ints.fromByteArray(new byte[Ints.BYTES - 1]); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Ints.fromByteArray(new byte[Ints.BYTES - 1])); } public void testFromBytes() { - assertEquals(0x12131415, Ints.fromBytes((byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15)); - assertEquals(0xFFEEDDCC, Ints.fromBytes((byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC)); + assertThat(Ints.fromBytes((byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15)) + .isEqualTo(0x12131415); + assertThat(Ints.fromBytes((byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC)) + .isEqualTo(0xFFEEDDCC); } public void testByteArrayRoundTrips() { @@ -247,40 +281,30 @@ public void testByteArrayRoundTrips() { // total overkill, but, it takes 0.1 sec so why not... for (int i = 0; i < 10000; i++) { int num = r.nextInt(); - assertEquals(num, Ints.fromByteArray(Ints.toByteArray(num))); + assertThat(Ints.fromByteArray(Ints.toByteArray(num))).isEqualTo(num); r.nextBytes(b); - assertTrue(Arrays.equals(b, Ints.toByteArray(Ints.fromByteArray(b)))); + assertThat(Ints.toByteArray(Ints.fromByteArray(b))).isEqualTo(b); } } public void testEnsureCapacity() { - assertSame(EMPTY, Ints.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Ints.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Ints.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals(new int[] {(int) 1, (int) 0, (int) 0}, Ints.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Ints.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Ints.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Ints.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat(Ints.ensureCapacity(ARRAY1, 2, 1)).isEqualTo(new int[] {(int) 1, (int) 0, (int) 0}); } public void testEnsureCapacity_fail() { - try { - Ints.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Ints.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ints.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Ints.ensureCapacity(ARRAY1, 1, -1)); } public void testJoin() { - assertEquals("", Ints.join(",", EMPTY)); - assertEquals("1", Ints.join(",", ARRAY1)); - assertEquals("1,2", Ints.join(",", (int) 1, (int) 2)); - assertEquals("123", Ints.join("", (int) 1, (int) 2, (int) 3)); + assertThat(Ints.join(",", EMPTY)).isEmpty(); + assertThat(Ints.join(",", ARRAY1)).isEqualTo("1"); + assertThat(Ints.join(",", (int) 1, (int) 2)).isEqualTo("1,2"); + assertThat(Ints.join("", (int) 1, (int) 2, (int) 3)).isEqualTo("123"); } public void testLexicographicalComparator() { @@ -300,10 +324,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Ints.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -317,13 +342,13 @@ public void testReverse() { private static void testReverse(int[] input, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); Ints.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse(int[] input, int fromIndex, int toIndex, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); Ints.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -335,6 +360,103 @@ public void testReverseIndexed() { testReverse(new int[] {-1, 1, -2, 2}, 1, 3, new int[] {-1, -2, 1, 2}); } + private static void testRotate(int[] input, int distance, int[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Ints.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + int[] input, int distance, int fromIndex, int toIndex, int[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Ints.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new int[] {}, -1, new int[] {}); + testRotate(new int[] {}, 0, new int[] {}); + testRotate(new int[] {}, 1, new int[] {}); + + testRotate(new int[] {1}, -2, new int[] {1}); + testRotate(new int[] {1}, -1, new int[] {1}); + testRotate(new int[] {1}, 0, new int[] {1}); + testRotate(new int[] {1}, 1, new int[] {1}); + testRotate(new int[] {1}, 2, new int[] {1}); + + testRotate(new int[] {1, 2}, -3, new int[] {2, 1}); + testRotate(new int[] {1, 2}, -1, new int[] {2, 1}); + testRotate(new int[] {1, 2}, -2, new int[] {1, 2}); + testRotate(new int[] {1, 2}, 0, new int[] {1, 2}); + testRotate(new int[] {1, 2}, 1, new int[] {2, 1}); + testRotate(new int[] {1, 2}, 2, new int[] {1, 2}); + testRotate(new int[] {1, 2}, 3, new int[] {2, 1}); + + testRotate(new int[] {1, 2, 3}, -5, new int[] {3, 1, 2}); + testRotate(new int[] {1, 2, 3}, -4, new int[] {2, 3, 1}); + testRotate(new int[] {1, 2, 3}, -3, new int[] {1, 2, 3}); + testRotate(new int[] {1, 2, 3}, -2, new int[] {3, 1, 2}); + testRotate(new int[] {1, 2, 3}, -1, new int[] {2, 3, 1}); + testRotate(new int[] {1, 2, 3}, 0, new int[] {1, 2, 3}); + testRotate(new int[] {1, 2, 3}, 1, new int[] {3, 1, 2}); + testRotate(new int[] {1, 2, 3}, 2, new int[] {2, 3, 1}); + testRotate(new int[] {1, 2, 3}, 3, new int[] {1, 2, 3}); + testRotate(new int[] {1, 2, 3}, 4, new int[] {3, 1, 2}); + testRotate(new int[] {1, 2, 3}, 5, new int[] {2, 3, 1}); + + testRotate(new int[] {1, 2, 3, 4}, -9, new int[] {2, 3, 4, 1}); + testRotate(new int[] {1, 2, 3, 4}, -5, new int[] {2, 3, 4, 1}); + testRotate(new int[] {1, 2, 3, 4}, -1, new int[] {2, 3, 4, 1}); + testRotate(new int[] {1, 2, 3, 4}, 0, new int[] {1, 2, 3, 4}); + testRotate(new int[] {1, 2, 3, 4}, 1, new int[] {4, 1, 2, 3}); + testRotate(new int[] {1, 2, 3, 4}, 5, new int[] {4, 1, 2, 3}); + testRotate(new int[] {1, 2, 3, 4}, 9, new int[] {4, 1, 2, 3}); + + testRotate(new int[] {1, 2, 3, 4, 5}, -6, new int[] {2, 3, 4, 5, 1}); + testRotate(new int[] {1, 2, 3, 4, 5}, -4, new int[] {5, 1, 2, 3, 4}); + testRotate(new int[] {1, 2, 3, 4, 5}, -3, new int[] {4, 5, 1, 2, 3}); + testRotate(new int[] {1, 2, 3, 4, 5}, -1, new int[] {2, 3, 4, 5, 1}); + testRotate(new int[] {1, 2, 3, 4, 5}, 0, new int[] {1, 2, 3, 4, 5}); + testRotate(new int[] {1, 2, 3, 4, 5}, 1, new int[] {5, 1, 2, 3, 4}); + testRotate(new int[] {1, 2, 3, 4, 5}, 3, new int[] {3, 4, 5, 1, 2}); + testRotate(new int[] {1, 2, 3, 4, 5}, 4, new int[] {2, 3, 4, 5, 1}); + testRotate(new int[] {1, 2, 3, 4, 5}, 6, new int[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new int[] {}, 0, 0, 0, new int[] {}); + + testRotate(new int[] {1}, 0, 0, 1, new int[] {1}); + testRotate(new int[] {1}, 1, 0, 1, new int[] {1}); + testRotate(new int[] {1}, 1, 1, 1, new int[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new int[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new int[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new int[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new int[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new int[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new int[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new int[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new int[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new int[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new int[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new int[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new int[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new int[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new int[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new int[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new int[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new int[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new int[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new int[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new int[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new int[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new int[] {}, new int[] {}); testSortDescending(new int[] {1}, new int[] {1}); @@ -346,14 +468,14 @@ public void testSortDescending() { private static void testSortDescending(int[] input, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); Ints.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( int[] input, int fromIndex, int toIndex, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); Ints.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -365,6 +487,7 @@ public void testSortDescendingIndexed() { testSortDescending(new int[] {-1, -2, 1, 2}, 1, 3, new int[] {-1, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testStringConverterSerialization() { SerializableTester.reserializeAndAssert(Ints.stringConverter()); @@ -373,17 +496,17 @@ public void testStringConverterSerialization() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Ints.toArray(none))); + assertThat(Ints.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((int) 1); - assertTrue(Arrays.equals(ARRAY1, Ints.toArray(one))); + assertThat(Ints.toArray(one)).isEqualTo(ARRAY1); int[] array = {(int) 0, (int) 1, (int) 0xdeadbeef}; List three = Arrays.asList((int) 0, (int) 1, (int) 0xdeadbeef); - assertTrue(Arrays.equals(array, Ints.toArray(three))); + assertThat(Ints.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Ints.toArray(Ints.asList(array)))); + assertThat(Ints.toArray(Ints.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -393,21 +516,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); int[] arr = Ints.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((int) 0, (int) 1, null); - try { - Ints.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Integer> list = Arrays.asList((int) 0, (int) 1, null); + assertThrows(NullPointerException.class, () -> Ints.toArray(list)); } public void testToArray_withConversion() { @@ -420,21 +539,22 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Ints.toArray(bytes))); - assertTrue(Arrays.equals(array, Ints.toArray(shorts))); - assertTrue(Arrays.equals(array, Ints.toArray(ints))); - assertTrue(Arrays.equals(array, Ints.toArray(floats))); - assertTrue(Arrays.equals(array, Ints.toArray(longs))); - assertTrue(Arrays.equals(array, Ints.toArray(doubles))); + assertThat(Ints.toArray(bytes)).isEqualTo(array); + assertThat(Ints.toArray(shorts)).isEqualTo(array); + assertThat(Ints.toArray(ints)).isEqualTo(array); + assertThat(Ints.toArray(floats)).isEqualTo(array); + assertThat(Ints.toArray(longs)).isEqualTo(array); + assertThat(Ints.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { int[] array = {(int) 0, (int) 1}; List list = Ints.asList(array); list.set(0, (int) 2); - assertTrue(Arrays.equals(new int[] {(int) 2, (int) 1}, array)); + assertThat(array).isEqualTo(new int[] {(int) 2, (int) 1}); array[1] = (int) 3; - assertEquals(Arrays.asList((int) 2, (int) 3), list); + assertThat(list).containsExactly((int) 2, (int) 3).inOrder(); } public void testAsList_toArray_roundTrip() { @@ -444,23 +564,24 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (int) 4); - assertTrue(Arrays.equals(new int[] {(int) 0, (int) 1, (int) 2}, newArray)); + assertThat(newArray).isEqualTo(new int[] {(int) 0, (int) 1, (int) 2}); newArray[1] = (int) 5; - assertEquals((int) 1, (int) list.get(1)); + assertThat((int) list.get(1)).isEqualTo((int) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { int[] array = {(int) 0, (int) 1, (int) 2, (int) 3}; List list = Ints.asList(array); - assertTrue(Arrays.equals(new int[] {(int) 1, (int) 2}, Ints.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new int[] {}, Ints.toArray(list.subList(2, 2)))); + assertThat(Ints.toArray(list.subList(1, 3))).isEqualTo(new int[] {(int) 1, (int) 2}); + assertThat(Ints.toArray(list.subList(2, 2))).isEqualTo(new int[] {}); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Ints.asList(EMPTY)); + assertThat(Ints.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Ints.class); @@ -468,40 +589,37 @@ public void testNulls() { public void testStringConverter_convert() { Converter converter = Ints.stringConverter(); - assertEquals((Integer) 1, converter.convert("1")); - assertEquals((Integer) 0, converter.convert("0")); - assertEquals((Integer) (-1), converter.convert("-1")); - assertEquals((Integer) 255, converter.convert("0xff")); - assertEquals((Integer) 255, converter.convert("0xFF")); - assertEquals((Integer) (-255), converter.convert("-0xFF")); - assertEquals((Integer) 255, converter.convert("#0000FF")); - assertEquals((Integer) 438, converter.convert("0666")); + assertThat(converter.convert("1")).isEqualTo((Integer) 1); + assertThat(converter.convert("0")).isEqualTo((Integer) 0); + assertThat(converter.convert("-1")).isEqualTo((Integer) (-1)); + assertThat(converter.convert("0xff")).isEqualTo((Integer) 255); + assertThat(converter.convert("0xFF")).isEqualTo((Integer) 255); + assertThat(converter.convert("-0xFF")).isEqualTo((Integer) (-255)); + assertThat(converter.convert("#0000FF")).isEqualTo((Integer) 255); + assertThat(converter.convert("0666")).isEqualTo((Integer) 438); } public void testStringConverter_convertError() { - try { - Ints.stringConverter().convert("notanumber"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> Ints.stringConverter().convert("notanumber")); } public void testStringConverter_nullConversions() { - assertNull(Ints.stringConverter().convert(null)); - assertNull(Ints.stringConverter().reverse().convert(null)); + assertThat(Ints.stringConverter().convert(null)).isNull(); + assertThat(Ints.stringConverter().reverse().convert(null)).isNull(); } public void testStringConverter_reverse() { Converter converter = Ints.stringConverter(); - assertEquals("1", converter.reverse().convert(1)); - assertEquals("0", converter.reverse().convert(0)); - assertEquals("-1", converter.reverse().convert(-1)); - assertEquals("255", converter.reverse().convert(0xff)); - assertEquals("255", converter.reverse().convert(0xFF)); - assertEquals("-255", converter.reverse().convert(-0xFF)); - assertEquals("438", converter.reverse().convert(0666)); + assertThat(converter.reverse().convert(1)).isEqualTo("1"); + assertThat(converter.reverse().convert(0)).isEqualTo("0"); + assertThat(converter.reverse().convert(-1)).isEqualTo("-1"); + assertThat(converter.reverse().convert(0xff)).isEqualTo("255"); + assertThat(converter.reverse().convert(0xFF)).isEqualTo("255"); + assertThat(converter.reverse().convert(-0xFF)).isEqualTo("-255"); + assertThat(converter.reverse().convert(0666)).isEqualTo("438"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStringConverter_nullPointerTester() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -517,17 +635,25 @@ public void testTryParse() { tryParseAndAssertEquals(-8900, "-8900"); tryParseAndAssertEquals(GREATEST, Integer.toString(GREATEST)); tryParseAndAssertEquals(LEAST, Integer.toString(LEAST)); - assertNull(Ints.tryParse("")); - assertNull(Ints.tryParse("-")); - assertNull(Ints.tryParse("+1")); - assertNull(Ints.tryParse("9999999999999999")); - assertNull("Max integer + 1", Ints.tryParse(Long.toString(((long) GREATEST) + 1))); - assertNull("Max integer * 10", Ints.tryParse(Long.toString(((long) GREATEST) * 10))); - assertNull("Min integer - 1", Ints.tryParse(Long.toString(((long) LEAST) - 1))); - assertNull("Min integer * 10", Ints.tryParse(Long.toString(((long) LEAST) * 10))); - assertNull("Max long", Ints.tryParse(Long.toString(Long.MAX_VALUE))); - assertNull("Min long", Ints.tryParse(Long.toString(Long.MIN_VALUE))); - assertNull(Ints.tryParse("\u0662\u06f3")); + assertThat(Ints.tryParse("")).isNull(); + assertThat(Ints.tryParse("-")).isNull(); + assertThat(Ints.tryParse("+1")).isNull(); + assertThat(Ints.tryParse("9999999999999999")).isNull(); + assertWithMessage("Max integer + 1") + .that(Ints.tryParse(Long.toString(((long) GREATEST) + 1))) + .isNull(); + assertWithMessage("Max integer * 10") + .that(Ints.tryParse(Long.toString(((long) GREATEST) * 10))) + .isNull(); + assertWithMessage("Min integer - 1") + .that(Ints.tryParse(Long.toString(((long) LEAST) - 1))) + .isNull(); + assertWithMessage("Min integer * 10") + .that(Ints.tryParse(Long.toString(((long) LEAST) * 10))) + .isNull(); + assertWithMessage("Max long").that(Ints.tryParse(Long.toString(Long.MAX_VALUE))).isNull(); + assertWithMessage("Min long").that(Ints.tryParse(Long.toString(Long.MIN_VALUE))).isNull(); + assertThat(Ints.tryParse("\u0662\u06f3")).isNull(); } /** @@ -535,7 +661,7 @@ public void testTryParse() { * expected. */ private static void tryParseAndAssertEquals(Integer expected, String value) { - assertEquals(expected, Ints.tryParse(value)); + assertThat(Ints.tryParse(value)).isEqualTo(expected); } public void testTryParse_radix() { @@ -545,45 +671,38 @@ public void testTryParse_radix() { radixEncodeParseAndAssertEquals(-8000, radix); radixEncodeParseAndAssertEquals(GREATEST, radix); radixEncodeParseAndAssertEquals(LEAST, radix); - assertNull("Radix: " + radix, Ints.tryParse("9999999999999999", radix)); - assertNull( - "Radix: " + radix, Ints.tryParse(Long.toString((long) GREATEST + 1, radix), radix)); - assertNull("Radix: " + radix, Ints.tryParse(Long.toString((long) LEAST - 1, radix), radix)); + assertWithMessage("Radix: " + radix).that(Ints.tryParse("9999999999999999", radix)).isNull(); + assertWithMessage("Radix: " + radix) + .that(Ints.tryParse(Long.toString((long) GREATEST + 1, radix), radix)) + .isNull(); + assertWithMessage("Radix: " + radix) + .that(Ints.tryParse(Long.toString((long) LEAST - 1, radix), radix)) + .isNull(); } - assertNull("Hex string and dec parm", Ints.tryParse("FFFF", 10)); - assertEquals("Mixed hex case", 65535, (int) Ints.tryParse("ffFF", 16)); + assertWithMessage("Hex string and dec parm").that(Ints.tryParse("FFFF", 10)).isNull(); + assertWithMessage("Mixed hex case").that((int) Ints.tryParse("ffFF", 16)).isEqualTo(65535); } /** - * Encodes the an integer as a string with given radix, then uses {@link Ints#tryParse(String, - * int)} to parse the result. Asserts the result is the same as what we started with. + * Encodes an integer as a string with given radix, then uses {@link Ints#tryParse(String, int)} + * to parse the result. Asserts the result is the same as what we started with. */ private static void radixEncodeParseAndAssertEquals(Integer value, int radix) { - assertEquals("Radix: " + radix, value, Ints.tryParse(Integer.toString(value, radix), radix)); + assertWithMessage("Radix: " + radix) + .that(Ints.tryParse(Integer.toString(value, radix), radix)) + .isEqualTo(value); } public void testTryParse_radixTooBig() { - try { - Ints.tryParse("0", Character.MAX_RADIX + 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ints.tryParse("0", Character.MAX_RADIX + 1)); } public void testTryParse_radixTooSmall() { - try { - Ints.tryParse("0", Character.MIN_RADIX - 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Ints.tryParse("0", Character.MIN_RADIX - 1)); } public void testTryParse_withNullGwt() { - assertNull(Ints.tryParse("null")); - try { - Ints.tryParse(null); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } + assertThat(Ints.tryParse("null")).isNull(); + assertThrows(NullPointerException.class, () -> Ints.tryParse(null)); } } diff --git a/android/guava-tests/test/com/google/common/primitives/LongArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/LongArrayAsListTest.java index 1c51e7ea05e5..b3657c437701 100644 --- a/android/guava-tests/test/com/google/common/primitives/LongArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/LongArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Longs#asList(long[])}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class LongArrayAsListTest extends TestCase { private static List asList(Long[] values) { @@ -48,6 +52,7 @@ private static List asList(Long[] values) { return Longs.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/LongsTest.java b/android/guava-tests/test/com/google/common/primitives/LongsTest.java index 236d46165c1b..d09ba263af89 100644 --- a/android/guava-tests/test/com/google/common/primitives/LongsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/LongsTest.java @@ -16,11 +16,17 @@ package com.google.common.primitives; +import static com.google.common.primitives.Longs.max; +import static com.google.common.primitives.Longs.min; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MIN_VALUE; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; @@ -33,14 +39,16 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Longs}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless public class LongsTest extends TestCase { private static final long[] EMPTY = {}; private static final long[] ARRAY1 = {(long) 1}; @@ -51,146 +59,174 @@ public class LongsTest extends TestCase { @GwtIncompatible // Long.hashCode returns different values in GWT. public void testHashCode() { for (long value : VALUES) { - assertEquals("hashCode for " + value, ((Long) value).hashCode(), Longs.hashCode(value)); + assertWithMessage("hashCode for " + value) + .that(Longs.hashCode(value)) + .isEqualTo(((Long) value).hashCode()); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (long x : VALUES) { for (long y : VALUES) { // note: spec requires only that the sign is the same - assertEquals(x + ", " + y, Long.valueOf(x).compareTo(y), Longs.compare(x, y)); + assertWithMessage(x + ", " + y) + .that(Longs.compare(x, y)) + .isEqualTo(Long.valueOf(x).compareTo(y)); } } } public void testContains() { - assertFalse(Longs.contains(EMPTY, (long) 1)); - assertFalse(Longs.contains(ARRAY1, (long) 2)); - assertFalse(Longs.contains(ARRAY234, (long) 1)); - assertTrue(Longs.contains(new long[] {(long) -1}, (long) -1)); - assertTrue(Longs.contains(ARRAY234, (long) 2)); - assertTrue(Longs.contains(ARRAY234, (long) 3)); - assertTrue(Longs.contains(ARRAY234, (long) 4)); + assertThat(Longs.contains(EMPTY, (long) 1)).isFalse(); + assertThat(Longs.contains(ARRAY1, (long) 2)).isFalse(); + assertThat(Longs.contains(ARRAY234, (long) 1)).isFalse(); + assertThat(Longs.contains(new long[] {(long) -1}, (long) -1)).isTrue(); + assertThat(Longs.contains(ARRAY234, (long) 2)).isTrue(); + assertThat(Longs.contains(ARRAY234, (long) 3)).isTrue(); + assertThat(Longs.contains(ARRAY234, (long) 4)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Longs.indexOf(EMPTY, (long) 1)); - assertEquals(-1, Longs.indexOf(ARRAY1, (long) 2)); - assertEquals(-1, Longs.indexOf(ARRAY234, (long) 1)); - assertEquals(0, Longs.indexOf(new long[] {(long) -1}, (long) -1)); - assertEquals(0, Longs.indexOf(ARRAY234, (long) 2)); - assertEquals(1, Longs.indexOf(ARRAY234, (long) 3)); - assertEquals(2, Longs.indexOf(ARRAY234, (long) 4)); - assertEquals(1, Longs.indexOf(new long[] {(long) 2, (long) 3, (long) 2, (long) 3}, (long) 3)); + assertThat(Longs.indexOf(EMPTY, (long) 1)).isEqualTo(-1); + assertThat(Longs.indexOf(ARRAY1, (long) 2)).isEqualTo(-1); + assertThat(Longs.indexOf(ARRAY234, (long) 1)).isEqualTo(-1); + assertThat(Longs.indexOf(new long[] {(long) -1}, (long) -1)).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, (long) 2)).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, (long) 3)).isEqualTo(1); + assertThat(Longs.indexOf(ARRAY234, (long) 4)).isEqualTo(2); + assertThat(Longs.indexOf(new long[] {(long) 2, (long) 3, (long) 2, (long) 3}, (long) 3)) + .isEqualTo(1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Longs.indexOf(EMPTY, EMPTY)); - assertEquals(0, Longs.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Longs.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Longs.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Longs.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Longs.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Longs.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Longs.indexOf(ARRAY234, new long[] {(long) 2, (long) 3})); - assertEquals(1, Longs.indexOf(ARRAY234, new long[] {(long) 3, (long) 4})); - assertEquals(1, Longs.indexOf(ARRAY234, new long[] {(long) 3})); - assertEquals(2, Longs.indexOf(ARRAY234, new long[] {(long) 4})); - assertEquals( - 1, - Longs.indexOf( - new long[] {(long) 2, (long) 3, (long) 3, (long) 3, (long) 3}, new long[] {(long) 3})); - assertEquals( - 2, - Longs.indexOf( - new long[] {(long) 2, (long) 3, (long) 2, (long) 3, (long) 4, (long) 2, (long) 3}, - new long[] {(long) 2, (long) 3, (long) 4})); - assertEquals( - 1, - Longs.indexOf( - new long[] {(long) 2, (long) 2, (long) 3, (long) 4, (long) 2, (long) 3, (long) 4}, - new long[] {(long) 2, (long) 3, (long) 4})); - assertEquals( - -1, - Longs.indexOf( - new long[] {(long) 4, (long) 3, (long) 2}, new long[] {(long) 2, (long) 3, (long) 4})); + assertThat(Longs.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Longs.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Longs.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Longs.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Longs.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, new long[] {(long) 2, (long) 3})).isEqualTo(0); + assertThat(Longs.indexOf(ARRAY234, new long[] {(long) 3, (long) 4})).isEqualTo(1); + assertThat(Longs.indexOf(ARRAY234, new long[] {(long) 3})).isEqualTo(1); + assertThat(Longs.indexOf(ARRAY234, new long[] {(long) 4})).isEqualTo(2); + assertThat( + Longs.indexOf( + new long[] {(long) 2, (long) 3, (long) 3, (long) 3, (long) 3}, + new long[] {(long) 3})) + .isEqualTo(1); + assertThat( + Longs.indexOf( + new long[] {(long) 2, (long) 3, (long) 2, (long) 3, (long) 4, (long) 2, (long) 3}, + new long[] {(long) 2, (long) 3, (long) 4})) + .isEqualTo(2); + assertThat( + Longs.indexOf( + new long[] {(long) 2, (long) 2, (long) 3, (long) 4, (long) 2, (long) 3, (long) 4}, + new long[] {(long) 2, (long) 3, (long) 4})) + .isEqualTo(1); + assertThat( + Longs.indexOf( + new long[] {(long) 4, (long) 3, (long) 2}, + new long[] {(long) 2, (long) 3, (long) 4})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Longs.lastIndexOf(EMPTY, (long) 1)); - assertEquals(-1, Longs.lastIndexOf(ARRAY1, (long) 2)); - assertEquals(-1, Longs.lastIndexOf(ARRAY234, (long) 1)); - assertEquals(0, Longs.lastIndexOf(new long[] {(long) -1}, (long) -1)); - assertEquals(0, Longs.lastIndexOf(ARRAY234, (long) 2)); - assertEquals(1, Longs.lastIndexOf(ARRAY234, (long) 3)); - assertEquals(2, Longs.lastIndexOf(ARRAY234, (long) 4)); - assertEquals( - 3, Longs.lastIndexOf(new long[] {(long) 2, (long) 3, (long) 2, (long) 3}, (long) 3)); + assertThat(Longs.lastIndexOf(EMPTY, (long) 1)).isEqualTo(-1); + assertThat(Longs.lastIndexOf(ARRAY1, (long) 2)).isEqualTo(-1); + assertThat(Longs.lastIndexOf(ARRAY234, (long) 1)).isEqualTo(-1); + assertThat(Longs.lastIndexOf(new long[] {(long) -1}, (long) -1)).isEqualTo(0); + assertThat(Longs.lastIndexOf(ARRAY234, (long) 2)).isEqualTo(0); + assertThat(Longs.lastIndexOf(ARRAY234, (long) 3)).isEqualTo(1); + assertThat(Longs.lastIndexOf(ARRAY234, (long) 4)).isEqualTo(2); + assertThat(Longs.lastIndexOf(new long[] {(long) 2, (long) 3, (long) 2, (long) 3}, (long) 3)) + .isEqualTo(3); } public void testMax_noArgs() { - try { - Longs.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(MIN_VALUE, Longs.max(MIN_VALUE)); - assertEquals(MAX_VALUE, Longs.max(MAX_VALUE)); - assertEquals( - (long) 9, Longs.max((long) 8, (long) 6, (long) 7, (long) 5, (long) 3, (long) 0, (long) 9)); + assertThat(max(MIN_VALUE)).isEqualTo(MIN_VALUE); + assertThat(max(MAX_VALUE)).isEqualTo(MAX_VALUE); + assertThat(max((long) 8, (long) 6, (long) 7, (long) 5, (long) 3, (long) 0, (long) 9)) + .isEqualTo((long) 9); } public void testMin_noArgs() { - try { - Longs.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(MIN_VALUE, Longs.min(MIN_VALUE)); - assertEquals(MAX_VALUE, Longs.min(MAX_VALUE)); - assertEquals( - (long) 0, Longs.min((long) 8, (long) 6, (long) 7, (long) 5, (long) 3, (long) 0, (long) 9)); + assertThat(min(MIN_VALUE)).isEqualTo(MIN_VALUE); + assertThat(min(MAX_VALUE)).isEqualTo(MAX_VALUE); + assertThat(min((long) 8, (long) 6, (long) 7, (long) 5, (long) 3, (long) 0, (long) 9)) + .isEqualTo((long) 0); } public void testConstrainToRange() { - assertEquals((long) 1, Longs.constrainToRange((long) 1, (long) 0, (long) 5)); - assertEquals((long) 1, Longs.constrainToRange((long) 1, (long) 1, (long) 5)); - assertEquals((long) 3, Longs.constrainToRange((long) 1, (long) 3, (long) 5)); - assertEquals((long) -1, Longs.constrainToRange((long) 0, (long) -5, (long) -1)); - assertEquals((long) 2, Longs.constrainToRange((long) 5, (long) 2, (long) 2)); + assertThat(Longs.constrainToRange((long) 1, (long) 0, (long) 5)).isEqualTo((long) 1); + assertThat(Longs.constrainToRange((long) 1, (long) 1, (long) 5)).isEqualTo((long) 1); + assertThat(Longs.constrainToRange((long) 1, (long) 3, (long) 5)).isEqualTo((long) 3); + assertThat(Longs.constrainToRange((long) 0, (long) -5, (long) -1)).isEqualTo((long) -1); + assertThat(Longs.constrainToRange((long) 5, (long) 2, (long) 2)).isEqualTo((long) 2); + assertThrows( + IllegalArgumentException.class, () -> Longs.constrainToRange((long) 1, (long) 3, (long) 2)); + } + + public void testConcat() { + assertThat(Longs.concat()).isEqualTo(EMPTY); + assertThat(Longs.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Longs.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Longs.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Longs.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Longs.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Longs.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new long[] {(long) 1, (long) 1, (long) 1}); + assertThat(Longs.concat(ARRAY1, ARRAY234)) + .isEqualTo(new long[] {(long) 1, (long) 2, (long) 3, (long) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + long[][] arrays = new long[arraysDim1][]; + // it's shared to avoid using too much memory in tests + long[] sharedArray = new long[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Longs.constrainToRange((long) 1, (long) 3, (long) 2); + Longs.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Longs.concat())); - assertTrue(Arrays.equals(EMPTY, Longs.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Longs.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Longs.concat(ARRAY1))); - assertNotSame(ARRAY1, Longs.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Longs.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new long[] {(long) 1, (long) 1, (long) 1}, Longs.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new long[] {(long) 1, (long) 2, (long) 3, (long) 4}, Longs.concat(ARRAY1, ARRAY234))); - } - private static void assertByteArrayEquals(byte[] expected, byte[] actual) { - assertTrue( - "Expected: " + Arrays.toString(expected) + ", but got: " + Arrays.toString(actual), - Arrays.equals(expected, actual)); + assertWithMessage( + "Expected: " + Arrays.toString(expected) + ", but got: " + Arrays.toString(actual)) + .that(Arrays.equals(expected, actual)) + .isTrue(); } public void testToByteArray() { @@ -206,49 +242,46 @@ public void testToByteArray() { } public void testFromByteArray() { - assertEquals( - 0x1213141516171819L, - Longs.fromByteArray(new byte[] {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x33})); - assertEquals( - 0xFFEEDDCCBBAA9988L, - Longs.fromByteArray( - new byte[] { - (byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC, - (byte) 0xBB, (byte) 0xAA, (byte) 0x99, (byte) 0x88 - })); + assertThat( + Longs.fromByteArray(new byte[] {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x33})) + .isEqualTo(0x1213141516171819L); + assertThat( + Longs.fromByteArray( + new byte[] { + (byte) 0xFF, (byte) 0xEE, (byte) 0xDD, (byte) 0xCC, + (byte) 0xBB, (byte) 0xAA, (byte) 0x99, (byte) 0x88 + })) + .isEqualTo(0xFFEEDDCCBBAA9988L); } public void testFromByteArrayFails() { - try { - Longs.fromByteArray(new byte[Longs.BYTES - 1]); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Longs.fromByteArray(new byte[Longs.BYTES - 1])); } public void testFromBytes() { - assertEquals( - 0x1213141516171819L, - Longs.fromBytes( - (byte) 0x12, - (byte) 0x13, - (byte) 0x14, - (byte) 0x15, - (byte) 0x16, - (byte) 0x17, - (byte) 0x18, - (byte) 0x19)); - assertEquals( - 0xFFEEDDCCBBAA9988L, - Longs.fromBytes( - (byte) 0xFF, - (byte) 0xEE, - (byte) 0xDD, - (byte) 0xCC, - (byte) 0xBB, - (byte) 0xAA, - (byte) 0x99, - (byte) 0x88)); + assertThat( + Longs.fromBytes( + (byte) 0x12, + (byte) 0x13, + (byte) 0x14, + (byte) 0x15, + (byte) 0x16, + (byte) 0x17, + (byte) 0x18, + (byte) 0x19)) + .isEqualTo(0x1213141516171819L); + assertThat( + Longs.fromBytes( + (byte) 0xFF, + (byte) 0xEE, + (byte) 0xDD, + (byte) 0xCC, + (byte) 0xBB, + (byte) 0xAA, + (byte) 0x99, + (byte) 0x88)) + .isEqualTo(0xFFEEDDCCBBAA9988L); } public void testByteArrayRoundTrips() { @@ -257,42 +290,32 @@ public void testByteArrayRoundTrips() { for (int i = 0; i < 1000; i++) { long num = r.nextLong(); - assertEquals(num, Longs.fromByteArray(Longs.toByteArray(num))); + assertThat(Longs.fromByteArray(Longs.toByteArray(num))).isEqualTo(num); r.nextBytes(b); long value = Longs.fromByteArray(b); - assertTrue("" + value, Arrays.equals(b, Longs.toByteArray(value))); + assertWithMessage("" + value).that(Arrays.equals(b, Longs.toByteArray(value))).isTrue(); } } public void testEnsureCapacity() { - assertSame(EMPTY, Longs.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Longs.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Longs.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new long[] {(long) 1, (long) 0, (long) 0}, Longs.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Longs.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Longs.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Longs.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat(Longs.ensureCapacity(ARRAY1, 2, 1)) + .isEqualTo(new long[] {(long) 1, (long) 0, (long) 0}); } public void testEnsureCapacity_fail() { - try { - Longs.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Longs.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Longs.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Longs.ensureCapacity(ARRAY1, 1, -1)); } public void testJoin() { - assertEquals("", Longs.join(",", EMPTY)); - assertEquals("1", Longs.join(",", ARRAY1)); - assertEquals("1,2", Longs.join(",", (long) 1, (long) 2)); - assertEquals("123", Longs.join("", (long) 1, (long) 2, (long) 3)); + assertThat(Longs.join(",", EMPTY)).isEmpty(); + assertThat(Longs.join(",", ARRAY1)).isEqualTo("1"); + assertThat(Longs.join(",", (long) 1, (long) 2)).isEqualTo("1,2"); + assertThat(Longs.join("", (long) 1, (long) 2, (long) 3)).isEqualTo("123"); } public void testLexicographicalComparator() { @@ -312,10 +335,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Longs.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -329,13 +353,13 @@ public void testReverse() { private static void testReverse(long[] input, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); Longs.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse(long[] input, int fromIndex, int toIndex, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); Longs.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -347,6 +371,103 @@ public void testReverseIndexed() { testReverse(new long[] {-1, 1, -2, 2}, 1, 3, new long[] {-1, -2, 1, 2}); } + private static void testRotate(long[] input, int distance, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + long[] input, int distance, int fromIndex, int toIndex, long[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Longs.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new long[] {}, -1, new long[] {}); + testRotate(new long[] {}, 0, new long[] {}); + testRotate(new long[] {}, 1, new long[] {}); + + testRotate(new long[] {1}, -2, new long[] {1}); + testRotate(new long[] {1}, -1, new long[] {1}); + testRotate(new long[] {1}, 0, new long[] {1}); + testRotate(new long[] {1}, 1, new long[] {1}); + testRotate(new long[] {1}, 2, new long[] {1}); + + testRotate(new long[] {1, 2}, -3, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, -2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 0, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 1, new long[] {2, 1}); + testRotate(new long[] {1, 2}, 2, new long[] {1, 2}); + testRotate(new long[] {1, 2}, 3, new long[] {2, 1}); + + testRotate(new long[] {1, 2, 3}, -5, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -4, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, -3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, -2, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, -1, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 0, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 1, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 2, new long[] {2, 3, 1}); + testRotate(new long[] {1, 2, 3}, 3, new long[] {1, 2, 3}); + testRotate(new long[] {1, 2, 3}, 4, new long[] {3, 1, 2}); + testRotate(new long[] {1, 2, 3}, 5, new long[] {2, 3, 1}); + + testRotate(new long[] {1, 2, 3, 4}, -9, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -5, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, -1, new long[] {2, 3, 4, 1}); + testRotate(new long[] {1, 2, 3, 4}, 0, new long[] {1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4}, 1, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 5, new long[] {4, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4}, 9, new long[] {4, 1, 2, 3}); + + testRotate(new long[] {1, 2, 3, 4, 5}, -6, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, -4, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, -3, new long[] {4, 5, 1, 2, 3}); + testRotate(new long[] {1, 2, 3, 4, 5}, -1, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 0, new long[] {1, 2, 3, 4, 5}); + testRotate(new long[] {1, 2, 3, 4, 5}, 1, new long[] {5, 1, 2, 3, 4}); + testRotate(new long[] {1, 2, 3, 4, 5}, 3, new long[] {3, 4, 5, 1, 2}); + testRotate(new long[] {1, 2, 3, 4, 5}, 4, new long[] {2, 3, 4, 5, 1}); + testRotate(new long[] {1, 2, 3, 4, 5}, 6, new long[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new long[] {}, 0, 0, 0, new long[] {}); + + testRotate(new long[] {1}, 0, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 0, 1, new long[] {1}); + testRotate(new long[] {1}, 1, 1, 1, new long[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new long[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new long[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new long[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new long[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new long[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new long[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new long[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new long[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new long[] {}, new long[] {}); testSortDescending(new long[] {1}, new long[] {1}); @@ -358,14 +479,14 @@ public void testSortDescending() { private static void testSortDescending(long[] input, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); Longs.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( long[] input, int fromIndex, int toIndex, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); Longs.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -377,6 +498,7 @@ public void testSortDescendingIndexed() { testSortDescending(new long[] {-1, -2, 1, 2}, 1, 3, new long[] {-1, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testStringConverterSerialization() { SerializableTester.reserializeAndAssert(Longs.stringConverter()); @@ -385,17 +507,17 @@ public void testStringConverterSerialization() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Longs.toArray(none))); + assertThat(Longs.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((long) 1); - assertTrue(Arrays.equals(ARRAY1, Longs.toArray(one))); + assertThat(Longs.toArray(one)).isEqualTo(ARRAY1); long[] array = {(long) 0, (long) 1, 0x0FF1C1AL}; List three = Arrays.asList((long) 0, (long) 1, 0x0FF1C1AL); - assertTrue(Arrays.equals(array, Longs.toArray(three))); + assertThat(Longs.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Longs.toArray(Longs.asList(array)))); + assertThat(Longs.toArray(Longs.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -405,21 +527,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); long[] arr = Longs.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((long) 0, (long) 1, null); - try { - Longs.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Long> list = Arrays.asList((long) 0, (long) 1, null); + assertThrows(NullPointerException.class, () -> Longs.toArray(list)); } public void testToArray_withConversion() { @@ -432,21 +550,22 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Longs.toArray(bytes))); - assertTrue(Arrays.equals(array, Longs.toArray(shorts))); - assertTrue(Arrays.equals(array, Longs.toArray(ints))); - assertTrue(Arrays.equals(array, Longs.toArray(floats))); - assertTrue(Arrays.equals(array, Longs.toArray(longs))); - assertTrue(Arrays.equals(array, Longs.toArray(doubles))); + assertThat(Longs.toArray(bytes)).isEqualTo(array); + assertThat(Longs.toArray(shorts)).isEqualTo(array); + assertThat(Longs.toArray(ints)).isEqualTo(array); + assertThat(Longs.toArray(floats)).isEqualTo(array); + assertThat(Longs.toArray(longs)).isEqualTo(array); + assertThat(Longs.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { long[] array = {(long) 0, (long) 1}; List list = Longs.asList(array); list.set(0, (long) 2); - assertTrue(Arrays.equals(new long[] {(long) 2, (long) 1}, array)); + assertThat(array).isEqualTo(new long[] {(long) 2, (long) 1}); array[1] = (long) 3; - assertEquals(Arrays.asList((long) 2, (long) 3), list); + assertThat(list).containsExactly((long) 2, (long) 3).inOrder(); } public void testAsList_toArray_roundTrip() { @@ -456,23 +575,24 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (long) 4); - assertTrue(Arrays.equals(new long[] {(long) 0, (long) 1, (long) 2}, newArray)); + assertThat(newArray).isEqualTo(new long[] {(long) 0, (long) 1, (long) 2}); newArray[1] = (long) 5; - assertEquals((long) 1, (long) list.get(1)); + assertThat((long) list.get(1)).isEqualTo((long) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { long[] array = {(long) 0, (long) 1, (long) 2, (long) 3}; List list = Longs.asList(array); - assertTrue(Arrays.equals(new long[] {(long) 1, (long) 2}, Longs.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new long[] {}, Longs.toArray(list.subList(2, 2)))); + assertThat(Longs.toArray(list.subList(1, 3))).isEqualTo(new long[] {(long) 1, (long) 2}); + assertThat(Longs.toArray(list.subList(2, 2))).isEqualTo(new long[] {}); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Longs.asList(EMPTY)); + assertThat(Longs.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Longs.class); @@ -480,40 +600,37 @@ public void testNulls() { public void testStringConverter_convert() { Converter converter = Longs.stringConverter(); - assertEquals((Long) 1L, converter.convert("1")); - assertEquals((Long) 0L, converter.convert("0")); - assertEquals((Long) (-1L), converter.convert("-1")); - assertEquals((Long) 255L, converter.convert("0xff")); - assertEquals((Long) 255L, converter.convert("0xFF")); - assertEquals((Long) (-255L), converter.convert("-0xFF")); - assertEquals((Long) 255L, converter.convert("#0000FF")); - assertEquals((Long) 438L, converter.convert("0666")); + assertThat(converter.convert("1")).isEqualTo((Long) 1L); + assertThat(converter.convert("0")).isEqualTo((Long) 0L); + assertThat(converter.convert("-1")).isEqualTo((Long) (-1L)); + assertThat(converter.convert("0xff")).isEqualTo((Long) 255L); + assertThat(converter.convert("0xFF")).isEqualTo((Long) 255L); + assertThat(converter.convert("-0xFF")).isEqualTo((Long) (-255L)); + assertThat(converter.convert("#0000FF")).isEqualTo((Long) 255L); + assertThat(converter.convert("0666")).isEqualTo((Long) 438L); } public void testStringConverter_convertError() { - try { - Longs.stringConverter().convert("notanumber"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> Longs.stringConverter().convert("notanumber")); } public void testStringConverter_nullConversions() { - assertNull(Longs.stringConverter().convert(null)); - assertNull(Longs.stringConverter().reverse().convert(null)); + assertThat(Longs.stringConverter().convert(null)).isNull(); + assertThat(Longs.stringConverter().reverse().convert(null)).isNull(); } public void testStringConverter_reverse() { Converter converter = Longs.stringConverter(); - assertEquals("1", converter.reverse().convert(1L)); - assertEquals("0", converter.reverse().convert(0L)); - assertEquals("-1", converter.reverse().convert(-1L)); - assertEquals("255", converter.reverse().convert(0xffL)); - assertEquals("255", converter.reverse().convert(0xFFL)); - assertEquals("-255", converter.reverse().convert(-0xFFL)); - assertEquals("438", converter.reverse().convert(0666L)); + assertThat(converter.reverse().convert(1L)).isEqualTo("1"); + assertThat(converter.reverse().convert(0L)).isEqualTo("0"); + assertThat(converter.reverse().convert(-1L)).isEqualTo("-1"); + assertThat(converter.reverse().convert(0xffL)).isEqualTo("255"); + assertThat(converter.reverse().convert(0xFFL)).isEqualTo("255"); + assertThat(converter.reverse().convert(-0xFFL)).isEqualTo("-255"); + assertThat(converter.reverse().convert(0666L)).isEqualTo("438"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStringConverter_nullPointerTester() throws Exception { NullPointerTester tester = new NullPointerTester(); @@ -529,23 +646,26 @@ public void testTryParse() { tryParseAndAssertEquals(-8900L, "-8900"); tryParseAndAssertEquals(MAX_VALUE, Long.toString(MAX_VALUE)); tryParseAndAssertEquals(MIN_VALUE, Long.toString(MIN_VALUE)); - assertNull(Longs.tryParse("")); - assertNull(Longs.tryParse("-")); - assertNull(Longs.tryParse("+1")); - assertNull(Longs.tryParse("999999999999999999999999")); - assertNull( - "Max long + 1", - Longs.tryParse(BigInteger.valueOf(MAX_VALUE).add(BigInteger.ONE).toString())); - assertNull( - "Max long * 10", - Longs.tryParse(BigInteger.valueOf(MAX_VALUE).multiply(BigInteger.TEN).toString())); - assertNull( - "Min long - 1", - Longs.tryParse(BigInteger.valueOf(MIN_VALUE).subtract(BigInteger.ONE).toString())); - assertNull( - "Min long * 10", - Longs.tryParse(BigInteger.valueOf(MIN_VALUE).multiply(BigInteger.TEN).toString())); - assertNull(Longs.tryParse("\u0662\u06f3")); + assertThat(Longs.tryParse("")).isNull(); + assertThat(Longs.tryParse("-")).isNull(); + assertThat(Longs.tryParse("+1")).isNull(); + assertThat(Longs.tryParse("999999999999999999999999")).isNull(); + assertThat(Longs.tryParse(" ")).isNull(); + assertThat(Longs.tryParse("1 ")).isNull(); + assertThat(Longs.tryParse(" 1")).isNull(); + assertWithMessage("Max long + 1") + .that(Longs.tryParse(BigInteger.valueOf(MAX_VALUE).add(BigInteger.ONE).toString())) + .isNull(); + assertWithMessage("Max long * 10") + .that(Longs.tryParse(BigInteger.valueOf(MAX_VALUE).multiply(BigInteger.TEN).toString())) + .isNull(); + assertWithMessage("Min long - 1") + .that(Longs.tryParse(BigInteger.valueOf(MIN_VALUE).subtract(BigInteger.ONE).toString())) + .isNull(); + assertWithMessage("Min long * 10") + .that(Longs.tryParse(BigInteger.valueOf(MIN_VALUE).multiply(BigInteger.TEN).toString())) + .isNull(); + assertThat(Longs.tryParse("\u0662\u06f3")).isNull(); } /** @@ -553,7 +673,7 @@ public void testTryParse() { * expected. */ private static void tryParseAndAssertEquals(Long expected, String value) { - assertEquals(expected, Longs.tryParse(value)); + assertThat(Longs.tryParse(value)).isEqualTo(expected); } public void testTryParse_radix() { @@ -563,16 +683,22 @@ public void testTryParse_radix() { radixEncodeParseAndAssertEquals((long) -8000, radix); radixEncodeParseAndAssertEquals(MAX_VALUE, radix); radixEncodeParseAndAssertEquals(MIN_VALUE, radix); - assertNull("Radix: " + radix, Longs.tryParse("999999999999999999999999", radix)); - assertNull( - "Radix: " + radix, - Longs.tryParse(BigInteger.valueOf(MAX_VALUE).add(BigInteger.ONE).toString(), radix)); - assertNull( - "Radix: " + radix, - Longs.tryParse(BigInteger.valueOf(MIN_VALUE).subtract(BigInteger.ONE).toString(), radix)); + assertWithMessage("Radix: " + radix) + .that(Longs.tryParse("999999999999999999999999", radix)) + .isNull(); + assertWithMessage("Radix: " + radix) + .that(Longs.tryParse(BigInteger.valueOf(MAX_VALUE).add(BigInteger.ONE).toString(), radix)) + .isNull(); + assertWithMessage("Radix: " + radix) + .that( + Longs.tryParse( + BigInteger.valueOf(MIN_VALUE).subtract(BigInteger.ONE).toString(), radix)) + .isNull(); } - assertNull("Hex string and dec parm", Longs.tryParse("FFFF", 10)); - assertEquals("Mixed hex case", 65535, Longs.tryParse("ffFF", 16).longValue()); + assertWithMessage("Hex string and dec parm").that(Longs.tryParse("FFFF", 10)).isNull(); + assertWithMessage("Mixed hex case") + .that(Longs.tryParse("ffFF", 16).longValue()) + .isEqualTo(65535); } /** @@ -580,31 +706,23 @@ public void testTryParse_radix() { * parse the result. Asserts the result is the same as what we started with. */ private static void radixEncodeParseAndAssertEquals(Long value, int radix) { - assertEquals("Radix: " + radix, value, Longs.tryParse(Long.toString(value, radix), radix)); + assertWithMessage("Radix: " + radix) + .that(Longs.tryParse(Long.toString(value, radix), radix)) + .isEqualTo(value); } public void testTryParse_radixTooBig() { - try { - Longs.tryParse("0", Character.MAX_RADIX + 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Longs.tryParse("0", Character.MAX_RADIX + 1)); } public void testTryParse_radixTooSmall() { - try { - Longs.tryParse("0", Character.MIN_RADIX - 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Longs.tryParse("0", Character.MIN_RADIX - 1)); } public void testTryParse_withNullGwt() { - assertNull(Longs.tryParse("null")); - try { - Longs.tryParse(null); - fail("Expected NPE"); - } catch (NullPointerException expected) { - } + assertThat(Longs.tryParse("null")).isNull(); + assertThrows(NullPointerException.class, () -> Longs.tryParse(null)); } } diff --git a/android/guava-tests/test/com/google/common/primitives/PackageSanityTests.java b/android/guava-tests/test/com/google/common/primitives/PackageSanityTests.java index 3f3e74539dac..eaa6ba09a51e 100644 --- a/android/guava-tests/test/com/google/common/primitives/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/primitives/PackageSanityTests.java @@ -17,6 +17,7 @@ package com.google.common.primitives; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** * Tests basic sanity for each class in the package. @@ -24,6 +25,7 @@ * @author Ben Yu */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests { public PackageSanityTests() { setDefault(String.class, "string"); diff --git a/android/guava-tests/test/com/google/common/primitives/PrimitivesTest.java b/android/guava-tests/test/com/google/common/primitives/PrimitivesTest.java index 1e09743654b5..d3bd1cc84241 100644 --- a/android/guava-tests/test/com/google/common/primitives/PrimitivesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/PrimitivesTest.java @@ -16,38 +16,46 @@ package com.google.common.primitives; -import com.google.common.collect.ImmutableSet; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.testing.NullPointerTester; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Primitives}. * * @author Kevin Bourrillion */ +@GwtCompatible(emulated = true) +@NullUnmarked public class PrimitivesTest extends TestCase { public void testIsWrapperType() { - assertTrue(Primitives.isWrapperType(Void.class)); - assertFalse(Primitives.isWrapperType(void.class)); + assertThat(Primitives.isWrapperType(Void.class)).isTrue(); + assertThat(Primitives.isWrapperType(void.class)).isFalse(); } public void testWrap() { - assertSame(Integer.class, Primitives.wrap(int.class)); - assertSame(Integer.class, Primitives.wrap(Integer.class)); - assertSame(String.class, Primitives.wrap(String.class)); + assertThat(Primitives.wrap(int.class)).isSameInstanceAs(Integer.class); + assertThat(Primitives.wrap(Integer.class)).isSameInstanceAs(Integer.class); + assertThat(Primitives.wrap(String.class)).isSameInstanceAs(String.class); } public void testUnwrap() { - assertSame(int.class, Primitives.unwrap(Integer.class)); - assertSame(int.class, Primitives.unwrap(int.class)); - assertSame(String.class, Primitives.unwrap(String.class)); + assertThat(Primitives.unwrap(Integer.class)).isSameInstanceAs(int.class); + assertThat(Primitives.unwrap(int.class)).isSameInstanceAs(int.class); + assertThat(Primitives.unwrap(String.class)).isSameInstanceAs(String.class); } public void testAllPrimitiveTypes() { Set> primitives = Primitives.allPrimitiveTypes(); - assertEquals( - ImmutableSet.of( + assertThat(primitives) + .containsExactly( boolean.class, byte.class, char.class, @@ -56,20 +64,15 @@ public void testAllPrimitiveTypes() { int.class, long.class, short.class, - void.class), - primitives); + void.class); - try { - primitives.remove(boolean.class); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> primitives.remove(boolean.class)); } public void testAllWrapperTypes() { Set> wrappers = Primitives.allWrapperTypes(); - assertEquals( - ImmutableSet.of( + assertThat(wrappers) + .containsExactly( Boolean.class, Byte.class, Character.class, @@ -78,16 +81,13 @@ public void testAllWrapperTypes() { Integer.class, Long.class, Short.class, - Void.class), - wrappers); + Void.class); - try { - wrappers.remove(Boolean.class); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> wrappers.remove(Boolean.class)); } + @GwtIncompatible + @J2ktIncompatible public void testNullPointerExceptions() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(Primitives.class); diff --git a/android/guava-tests/test/com/google/common/primitives/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/primitives/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..e86fd59bad3f --- /dev/null +++ b/android/guava-tests/test/com/google/common/primitives/ReflectionFreeAssertThrows.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.primitives; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/primitives/ShortArrayAsListTest.java b/android/guava-tests/test/com/google/common/primitives/ShortArrayAsListTest.java index 9a1fada728d6..33a01b8c22c1 100644 --- a/android/guava-tests/test/com/google/common/primitives/ShortArrayAsListTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ShortArrayAsListTest.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.ListTestSuiteBuilder; import com.google.common.collect.testing.SampleElements; @@ -31,6 +32,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Test suite covering {@link Shorts#asList(short[])}. @@ -38,6 +40,8 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) +@NullUnmarked +@AndroidIncompatible // test-suite builders public class ShortArrayAsListTest extends TestCase { private static List asList(Short[] values) { @@ -48,6 +52,7 @@ private static List asList(Short[] values) { return Shorts.asList(temp); } + @J2ktIncompatible @GwtIncompatible // suite public static Test suite() { List> builders = diff --git a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java index 0816c6973c0d..7fdc4d646920 100644 --- a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java @@ -16,8 +16,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.primitives.Shorts.max; +import static com.google.common.primitives.Shorts.min; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; @@ -29,14 +36,16 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link Shorts}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless public class ShortsTest extends TestCase { private static final short[] EMPTY = {}; private static final short[] ARRAY1 = {(short) 1}; @@ -49,13 +58,13 @@ public class ShortsTest extends TestCase { public void testHashCode() { for (short value : VALUES) { - assertEquals(((Short) value).hashCode(), Shorts.hashCode(value)); + assertThat(Shorts.hashCode(value)).isEqualTo(((Short) value).hashCode()); } } public void testCheckedCast() { for (short value : VALUES) { - assertEquals(value, Shorts.checkedCast((long) value)); + assertThat(Shorts.checkedCast((long) value)).isEqualTo(value); } assertCastFails(GREATEST + 1L); assertCastFails(LEAST - 1L); @@ -65,12 +74,12 @@ public void testCheckedCast() { public void testSaturatedCast() { for (short value : VALUES) { - assertEquals(value, Shorts.saturatedCast((long) value)); + assertThat(Shorts.saturatedCast((long) value)).isEqualTo(value); } - assertEquals(GREATEST, Shorts.saturatedCast(GREATEST + 1L)); - assertEquals(LEAST, Shorts.saturatedCast(LEAST - 1L)); - assertEquals(GREATEST, Shorts.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, Shorts.saturatedCast(Long.MIN_VALUE)); + assertThat(Shorts.saturatedCast(GREATEST + 1L)).isEqualTo(GREATEST); + assertThat(Shorts.saturatedCast(LEAST - 1L)).isEqualTo(LEAST); + assertThat(Shorts.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(Shorts.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } private static void assertCastFails(long value) { @@ -78,12 +87,14 @@ private static void assertCastFails(long value) { Shorts.checkedCast(value); fail("Cast to short should have failed: " + value); } catch (IllegalArgumentException ex) { - assertTrue( - value + " not found in exception text: " + ex.getMessage(), - ex.getMessage().contains(String.valueOf(value))); + assertWithMessage(value + " not found in exception text: " + ex.getMessage()) + .that(ex.getMessage().contains(String.valueOf(value))) + .isTrue(); } } + // We need to test that our method behaves like the JDK method. + @SuppressWarnings("InlineMeInliner") public void testCompare() { for (short x : VALUES) { for (short y : VALUES) { @@ -91,179 +102,194 @@ public void testCompare() { int expected = Short.valueOf(x).compareTo(y); int actual = Shorts.compare(x, y); if (expected == 0) { - assertEquals(x + ", " + y, expected, actual); + assertWithMessage(x + ", " + y).that(actual).isEqualTo(expected); } else if (expected < 0) { - assertTrue( - x + ", " + y + " (expected: " + expected + ", actual" + actual + ")", actual < 0); + assertWithMessage(x + ", " + y + " (expected: " + expected + ", actual" + actual + ")") + .that(actual < 0) + .isTrue(); } else { - assertTrue( - x + ", " + y + " (expected: " + expected + ", actual" + actual + ")", actual > 0); + assertWithMessage(x + ", " + y + " (expected: " + expected + ", actual" + actual + ")") + .that(actual > 0) + .isTrue(); } } } } public void testContains() { - assertFalse(Shorts.contains(EMPTY, (short) 1)); - assertFalse(Shorts.contains(ARRAY1, (short) 2)); - assertFalse(Shorts.contains(ARRAY234, (short) 1)); - assertTrue(Shorts.contains(new short[] {(short) -1}, (short) -1)); - assertTrue(Shorts.contains(ARRAY234, (short) 2)); - assertTrue(Shorts.contains(ARRAY234, (short) 3)); - assertTrue(Shorts.contains(ARRAY234, (short) 4)); + assertThat(Shorts.contains(EMPTY, (short) 1)).isFalse(); + assertThat(Shorts.contains(ARRAY1, (short) 2)).isFalse(); + assertThat(Shorts.contains(ARRAY234, (short) 1)).isFalse(); + assertThat(Shorts.contains(new short[] {(short) -1}, (short) -1)).isTrue(); + assertThat(Shorts.contains(ARRAY234, (short) 2)).isTrue(); + assertThat(Shorts.contains(ARRAY234, (short) 3)).isTrue(); + assertThat(Shorts.contains(ARRAY234, (short) 4)).isTrue(); } public void testIndexOf() { - assertEquals(-1, Shorts.indexOf(EMPTY, (short) 1)); - assertEquals(-1, Shorts.indexOf(ARRAY1, (short) 2)); - assertEquals(-1, Shorts.indexOf(ARRAY234, (short) 1)); - assertEquals(0, Shorts.indexOf(new short[] {(short) -1}, (short) -1)); - assertEquals(0, Shorts.indexOf(ARRAY234, (short) 2)); - assertEquals(1, Shorts.indexOf(ARRAY234, (short) 3)); - assertEquals(2, Shorts.indexOf(ARRAY234, (short) 4)); - assertEquals( - 1, Shorts.indexOf(new short[] {(short) 2, (short) 3, (short) 2, (short) 3}, (short) 3)); + assertThat(Shorts.indexOf(EMPTY, (short) 1)).isEqualTo(-1); + assertThat(Shorts.indexOf(ARRAY1, (short) 2)).isEqualTo(-1); + assertThat(Shorts.indexOf(ARRAY234, (short) 1)).isEqualTo(-1); + assertThat(Shorts.indexOf(new short[] {(short) -1}, (short) -1)).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, (short) 2)).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, (short) 3)).isEqualTo(1); + assertThat(Shorts.indexOf(ARRAY234, (short) 4)).isEqualTo(2); + assertThat(Shorts.indexOf(new short[] {(short) 2, (short) 3, (short) 2, (short) 3}, (short) 3)) + .isEqualTo(1); } public void testIndexOf_arrayTarget() { - assertEquals(0, Shorts.indexOf(EMPTY, EMPTY)); - assertEquals(0, Shorts.indexOf(ARRAY234, EMPTY)); - assertEquals(-1, Shorts.indexOf(EMPTY, ARRAY234)); - assertEquals(-1, Shorts.indexOf(ARRAY234, ARRAY1)); - assertEquals(-1, Shorts.indexOf(ARRAY1, ARRAY234)); - assertEquals(0, Shorts.indexOf(ARRAY1, ARRAY1)); - assertEquals(0, Shorts.indexOf(ARRAY234, ARRAY234)); - assertEquals(0, Shorts.indexOf(ARRAY234, new short[] {(short) 2, (short) 3})); - assertEquals(1, Shorts.indexOf(ARRAY234, new short[] {(short) 3, (short) 4})); - assertEquals(1, Shorts.indexOf(ARRAY234, new short[] {(short) 3})); - assertEquals(2, Shorts.indexOf(ARRAY234, new short[] {(short) 4})); - assertEquals( - 1, - Shorts.indexOf( - new short[] {(short) 2, (short) 3, (short) 3, (short) 3, (short) 3}, - new short[] {(short) 3})); - assertEquals( - 2, - Shorts.indexOf( - new short[] { - (short) 2, (short) 3, (short) 2, (short) 3, (short) 4, (short) 2, (short) 3 - }, - new short[] {(short) 2, (short) 3, (short) 4})); - assertEquals( - 1, - Shorts.indexOf( - new short[] { - (short) 2, (short) 2, (short) 3, (short) 4, (short) 2, (short) 3, (short) 4 - }, - new short[] {(short) 2, (short) 3, (short) 4})); - assertEquals( - -1, - Shorts.indexOf( - new short[] {(short) 4, (short) 3, (short) 2}, - new short[] {(short) 2, (short) 3, (short) 4})); + assertThat(Shorts.indexOf(EMPTY, EMPTY)).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, EMPTY)).isEqualTo(0); + assertThat(Shorts.indexOf(EMPTY, ARRAY234)).isEqualTo(-1); + assertThat(Shorts.indexOf(ARRAY234, ARRAY1)).isEqualTo(-1); + assertThat(Shorts.indexOf(ARRAY1, ARRAY234)).isEqualTo(-1); + assertThat(Shorts.indexOf(ARRAY1, ARRAY1)).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, ARRAY234)).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, new short[] {(short) 2, (short) 3})).isEqualTo(0); + assertThat(Shorts.indexOf(ARRAY234, new short[] {(short) 3, (short) 4})).isEqualTo(1); + assertThat(Shorts.indexOf(ARRAY234, new short[] {(short) 3})).isEqualTo(1); + assertThat(Shorts.indexOf(ARRAY234, new short[] {(short) 4})).isEqualTo(2); + assertThat( + Shorts.indexOf( + new short[] {(short) 2, (short) 3, (short) 3, (short) 3, (short) 3}, + new short[] {(short) 3})) + .isEqualTo(1); + assertThat( + Shorts.indexOf( + new short[] { + (short) 2, (short) 3, (short) 2, (short) 3, (short) 4, (short) 2, (short) 3 + }, + new short[] {(short) 2, (short) 3, (short) 4})) + .isEqualTo(2); + assertThat( + Shorts.indexOf( + new short[] { + (short) 2, (short) 2, (short) 3, (short) 4, (short) 2, (short) 3, (short) 4 + }, + new short[] {(short) 2, (short) 3, (short) 4})) + .isEqualTo(1); + assertThat( + Shorts.indexOf( + new short[] {(short) 4, (short) 3, (short) 2}, + new short[] {(short) 2, (short) 3, (short) 4})) + .isEqualTo(-1); } public void testLastIndexOf() { - assertEquals(-1, Shorts.lastIndexOf(EMPTY, (short) 1)); - assertEquals(-1, Shorts.lastIndexOf(ARRAY1, (short) 2)); - assertEquals(-1, Shorts.lastIndexOf(ARRAY234, (short) 1)); - assertEquals(0, Shorts.lastIndexOf(new short[] {(short) -1}, (short) -1)); - assertEquals(0, Shorts.lastIndexOf(ARRAY234, (short) 2)); - assertEquals(1, Shorts.lastIndexOf(ARRAY234, (short) 3)); - assertEquals(2, Shorts.lastIndexOf(ARRAY234, (short) 4)); - assertEquals( - 3, Shorts.lastIndexOf(new short[] {(short) 2, (short) 3, (short) 2, (short) 3}, (short) 3)); + assertThat(Shorts.lastIndexOf(EMPTY, (short) 1)).isEqualTo(-1); + assertThat(Shorts.lastIndexOf(ARRAY1, (short) 2)).isEqualTo(-1); + assertThat(Shorts.lastIndexOf(ARRAY234, (short) 1)).isEqualTo(-1); + assertThat(Shorts.lastIndexOf(new short[] {(short) -1}, (short) -1)).isEqualTo(0); + assertThat(Shorts.lastIndexOf(ARRAY234, (short) 2)).isEqualTo(0); + assertThat(Shorts.lastIndexOf(ARRAY234, (short) 3)).isEqualTo(1); + assertThat(Shorts.lastIndexOf(ARRAY234, (short) 4)).isEqualTo(2); + assertThat( + Shorts.lastIndexOf(new short[] {(short) 2, (short) 3, (short) 2, (short) 3}, (short) 3)) + .isEqualTo(3); } @GwtIncompatible public void testMax_noArgs() { - try { - Shorts.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, Shorts.max(LEAST)); - assertEquals(GREATEST, Shorts.max(GREATEST)); - assertEquals( - (short) 9, - Shorts.max((short) 8, (short) 6, (short) 7, (short) 5, (short) 3, (short) 0, (short) 9)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max((short) 8, (short) 6, (short) 7, (short) 5, (short) 3, (short) 0, (short) 9)) + .isEqualTo((short) 9); } @GwtIncompatible public void testMin_noArgs() { - try { - Shorts.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, Shorts.min(LEAST)); - assertEquals(GREATEST, Shorts.min(GREATEST)); - assertEquals( - (short) 0, - Shorts.min((short) 8, (short) 6, (short) 7, (short) 5, (short) 3, (short) 0, (short) 9)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((short) 8, (short) 6, (short) 7, (short) 5, (short) 3, (short) 0, (short) 9)) + .isEqualTo((short) 0); } public void testConstrainToRange() { - assertEquals((short) 1, Shorts.constrainToRange((short) 1, (short) 0, (short) 5)); - assertEquals((short) 1, Shorts.constrainToRange((short) 1, (short) 1, (short) 5)); - assertEquals((short) 3, Shorts.constrainToRange((short) 1, (short) 3, (short) 5)); - assertEquals((short) -1, Shorts.constrainToRange((short) 0, (short) -5, (short) -1)); - assertEquals((short) 2, Shorts.constrainToRange((short) 5, (short) 2, (short) 2)); + assertThat(Shorts.constrainToRange((short) 1, (short) 0, (short) 5)).isEqualTo((short) 1); + assertThat(Shorts.constrainToRange((short) 1, (short) 1, (short) 5)).isEqualTo((short) 1); + assertThat(Shorts.constrainToRange((short) 1, (short) 3, (short) 5)).isEqualTo((short) 3); + assertThat(Shorts.constrainToRange((short) 0, (short) -5, (short) -1)).isEqualTo((short) -1); + assertThat(Shorts.constrainToRange((short) 5, (short) 2, (short) 2)).isEqualTo((short) 2); + assertThrows( + IllegalArgumentException.class, + () -> Shorts.constrainToRange((short) 1, (short) 3, (short) 2)); + } + + public void testConcat() { + assertThat(Shorts.concat()).isEqualTo(EMPTY); + assertThat(Shorts.concat(EMPTY)).isEqualTo(EMPTY); + assertThat(Shorts.concat(EMPTY, EMPTY, EMPTY)).isEqualTo(EMPTY); + assertThat(Shorts.concat(ARRAY1)).isEqualTo(ARRAY1); + assertThat(Shorts.concat(ARRAY1)).isNotSameInstanceAs(ARRAY1); + assertThat(Shorts.concat(EMPTY, ARRAY1, EMPTY)).isEqualTo(ARRAY1); + assertThat(Shorts.concat(ARRAY1, ARRAY1, ARRAY1)) + .isEqualTo(new short[] {(short) 1, (short) 1, (short) 1}); + assertThat(Shorts.concat(ARRAY1, ARRAY234)) + .isEqualTo(new short[] {(short) 1, (short) 2, (short) 3, (short) 4}); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_negative() { + int dim1 = 1 << 16; + int dim2 = 1 << 15; + assertThat(dim1 * dim2).isLessThan(0); + testConcatOverflow(dim1, dim2); + } + + @GwtIncompatible // different overflow behavior; could probably be made to work by using ~~ + public void testConcat_overflow_nonNegative() { + int dim1 = 1 << 16; + int dim2 = 1 << 16; + assertThat(dim1 * dim2).isAtLeast(0); + testConcatOverflow(dim1, dim2); + } + + private static void testConcatOverflow(int arraysDim1, int arraysDim2) { + assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2)); + + short[][] arrays = new short[arraysDim1][]; + // it's shared to avoid using too much memory in tests + short[] sharedArray = new short[arraysDim2]; + Arrays.fill(arrays, sharedArray); + try { - Shorts.constrainToRange((short) 1, (short) 3, (short) 2); + Shorts.concat(arrays); fail(); } catch (IllegalArgumentException expected) { } } - public void testConcat() { - assertTrue(Arrays.equals(EMPTY, Shorts.concat())); - assertTrue(Arrays.equals(EMPTY, Shorts.concat(EMPTY))); - assertTrue(Arrays.equals(EMPTY, Shorts.concat(EMPTY, EMPTY, EMPTY))); - assertTrue(Arrays.equals(ARRAY1, Shorts.concat(ARRAY1))); - assertNotSame(ARRAY1, Shorts.concat(ARRAY1)); - assertTrue(Arrays.equals(ARRAY1, Shorts.concat(EMPTY, ARRAY1, EMPTY))); - assertTrue( - Arrays.equals( - new short[] {(short) 1, (short) 1, (short) 1}, Shorts.concat(ARRAY1, ARRAY1, ARRAY1))); - assertTrue( - Arrays.equals( - new short[] {(short) 1, (short) 2, (short) 3, (short) 4}, - Shorts.concat(ARRAY1, ARRAY234))); - } - @GwtIncompatible // Shorts.toByteArray public void testToByteArray() { - assertTrue(Arrays.equals(new byte[] {0x23, 0x45}, Shorts.toByteArray((short) 0x2345))); - assertTrue( - Arrays.equals(new byte[] {(byte) 0xFE, (byte) 0xDC}, Shorts.toByteArray((short) 0xFEDC))); + assertThat(Shorts.toByteArray((short) 0x2345)).isEqualTo(new byte[] {0x23, 0x45}); + assertThat(Shorts.toByteArray((short) 0xFEDC)).isEqualTo(new byte[] {(byte) 0xFE, (byte) 0xDC}); } @GwtIncompatible // Shorts.fromByteArray public void testFromByteArray() { - assertEquals((short) 0x2345, Shorts.fromByteArray(new byte[] {0x23, 0x45})); - assertEquals((short) 0xFEDC, Shorts.fromByteArray(new byte[] {(byte) 0xFE, (byte) 0xDC})); + assertThat(Shorts.fromByteArray(new byte[] {0x23, 0x45})).isEqualTo((short) 0x2345); + assertThat(Shorts.fromByteArray(new byte[] {(byte) 0xFE, (byte) 0xDC})) + .isEqualTo((short) 0xFEDC); } @GwtIncompatible // Shorts.fromByteArray public void testFromByteArrayFails() { - try { - Shorts.fromByteArray(new byte[] {0x01}); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Shorts.fromByteArray(new byte[] {0x01})); } @GwtIncompatible // Shorts.fromBytes public void testFromBytes() { - assertEquals((short) 0x2345, Shorts.fromBytes((byte) 0x23, (byte) 0x45)); - assertEquals((short) 0xFEDC, Shorts.fromBytes((byte) 0xFE, (byte) 0xDC)); + assertThat(Shorts.fromBytes((byte) 0x23, (byte) 0x45)).isEqualTo((short) 0x2345); + assertThat(Shorts.fromBytes((byte) 0xFE, (byte) 0xDC)).isEqualTo((short) 0xFEDC); } @GwtIncompatible // Shorts.fromByteArray, Shorts.toByteArray @@ -274,41 +300,31 @@ public void testByteArrayRoundTrips() { // total overkill, but, it takes 0.1 sec so why not... for (int i = 0; i < 10000; i++) { short num = (short) r.nextInt(); - assertEquals(num, Shorts.fromByteArray(Shorts.toByteArray(num))); + assertThat(Shorts.fromByteArray(Shorts.toByteArray(num))).isEqualTo(num); r.nextBytes(b); - assertTrue(Arrays.equals(b, Shorts.toByteArray(Shorts.fromByteArray(b)))); + assertThat(Shorts.toByteArray(Shorts.fromByteArray(b))).isEqualTo(b); } } public void testEnsureCapacity() { - assertSame(EMPTY, Shorts.ensureCapacity(EMPTY, 0, 1)); - assertSame(ARRAY1, Shorts.ensureCapacity(ARRAY1, 0, 1)); - assertSame(ARRAY1, Shorts.ensureCapacity(ARRAY1, 1, 1)); - assertTrue( - Arrays.equals( - new short[] {(short) 1, (short) 0, (short) 0}, Shorts.ensureCapacity(ARRAY1, 2, 1))); + assertThat(Shorts.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY); + assertThat(Shorts.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1); + assertThat(Shorts.ensureCapacity(ARRAY1, 1, 1)).isSameInstanceAs(ARRAY1); + assertThat(Shorts.ensureCapacity(ARRAY1, 2, 1)) + .isEqualTo(new short[] {(short) 1, (short) 0, (short) 0}); } public void testEnsureCapacity_fail() { - try { - Shorts.ensureCapacity(ARRAY1, -1, 1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - // notice that this should even fail when no growth was needed - Shorts.ensureCapacity(ARRAY1, 1, -1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Shorts.ensureCapacity(ARRAY1, -1, 1)); + assertThrows(IllegalArgumentException.class, () -> Shorts.ensureCapacity(ARRAY1, 1, -1)); } public void testJoin() { - assertEquals("", Shorts.join(",", EMPTY)); - assertEquals("1", Shorts.join(",", ARRAY1)); - assertEquals("1,2", Shorts.join(",", (short) 1, (short) 2)); - assertEquals("123", Shorts.join("", (short) 1, (short) 2, (short) 3)); + assertThat(Shorts.join(",", EMPTY)).isEmpty(); + assertThat(Shorts.join(",", ARRAY1)).isEqualTo("1"); + assertThat(Shorts.join(",", (short) 1, (short) 2)).isEqualTo("1,2"); + assertThat(Shorts.join("", (short) 1, (short) 2, (short) 3)).isEqualTo("123"); } public void testLexicographicalComparator() { @@ -328,10 +344,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = Shorts.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testReverse() { @@ -345,14 +362,14 @@ public void testReverse() { private static void testReverse(short[] input, short[] expectedOutput) { input = Arrays.copyOf(input, input.length); Shorts.reverse(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testReverse( short[] input, int fromIndex, int toIndex, short[] expectedOutput) { input = Arrays.copyOf(input, input.length); Shorts.reverse(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testReverseIndexed() { @@ -364,6 +381,103 @@ public void testReverseIndexed() { testReverse(new short[] {-1, 1, -2, 2}, 1, 3, new short[] {-1, -2, 1, 2}); } + private static void testRotate(short[] input, int distance, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance); + assertThat(input).isEqualTo(expectedOutput); + } + + private static void testRotate( + short[] input, int distance, int fromIndex, int toIndex, short[] expectedOutput) { + input = Arrays.copyOf(input, input.length); + Shorts.rotate(input, distance, fromIndex, toIndex); + assertThat(input).isEqualTo(expectedOutput); + } + + public void testRotate() { + testRotate(new short[] {}, -1, new short[] {}); + testRotate(new short[] {}, 0, new short[] {}); + testRotate(new short[] {}, 1, new short[] {}); + + testRotate(new short[] {1}, -2, new short[] {1}); + testRotate(new short[] {1}, -1, new short[] {1}); + testRotate(new short[] {1}, 0, new short[] {1}); + testRotate(new short[] {1}, 1, new short[] {1}); + testRotate(new short[] {1}, 2, new short[] {1}); + + testRotate(new short[] {1, 2}, -3, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, -2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 0, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 1, new short[] {2, 1}); + testRotate(new short[] {1, 2}, 2, new short[] {1, 2}); + testRotate(new short[] {1, 2}, 3, new short[] {2, 1}); + + testRotate(new short[] {1, 2, 3}, -5, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -4, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, -3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, -2, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, -1, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 0, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 1, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 2, new short[] {2, 3, 1}); + testRotate(new short[] {1, 2, 3}, 3, new short[] {1, 2, 3}); + testRotate(new short[] {1, 2, 3}, 4, new short[] {3, 1, 2}); + testRotate(new short[] {1, 2, 3}, 5, new short[] {2, 3, 1}); + + testRotate(new short[] {1, 2, 3, 4}, -9, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -5, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, -1, new short[] {2, 3, 4, 1}); + testRotate(new short[] {1, 2, 3, 4}, 0, new short[] {1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4}, 1, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 5, new short[] {4, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4}, 9, new short[] {4, 1, 2, 3}); + + testRotate(new short[] {1, 2, 3, 4, 5}, -6, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, -4, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, -3, new short[] {4, 5, 1, 2, 3}); + testRotate(new short[] {1, 2, 3, 4, 5}, -1, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 0, new short[] {1, 2, 3, 4, 5}); + testRotate(new short[] {1, 2, 3, 4, 5}, 1, new short[] {5, 1, 2, 3, 4}); + testRotate(new short[] {1, 2, 3, 4, 5}, 3, new short[] {3, 4, 5, 1, 2}); + testRotate(new short[] {1, 2, 3, 4, 5}, 4, new short[] {2, 3, 4, 5, 1}); + testRotate(new short[] {1, 2, 3, 4, 5}, 6, new short[] {5, 1, 2, 3, 4}); + } + + public void testRotateIndexed() { + testRotate(new short[] {}, 0, 0, 0, new short[] {}); + + testRotate(new short[] {1}, 0, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 0, 1, new short[] {1}); + testRotate(new short[] {1}, 1, 1, 1, new short[] {1}); + + // Rotate the central 5 elements, leaving the ends as-is + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 5, 1, 6, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 14, 1, 6, new short[] {0, 2, 3, 4, 5, 1, 6}); + + // Rotate the first three elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 0, 3, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 0, 3, new short[] {2, 0, 1, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 0, 3, new short[] {1, 2, 0, 3, 4, 5, 6}); + + // Rotate the last four elements + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -6, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -5, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -4, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -3, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, -1, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 0, 3, 7, new short[] {0, 1, 2, 3, 4, 5, 6}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 1, 3, 7, new short[] {0, 1, 2, 6, 3, 4, 5}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 2, 3, 7, new short[] {0, 1, 2, 5, 6, 3, 4}); + testRotate(new short[] {0, 1, 2, 3, 4, 5, 6}, 3, 3, 7, new short[] {0, 1, 2, 4, 5, 6, 3}); + } + public void testSortDescending() { testSortDescending(new short[] {}, new short[] {}); testSortDescending(new short[] {1}, new short[] {1}); @@ -375,14 +489,14 @@ public void testSortDescending() { private static void testSortDescending(short[] input, short[] expectedOutput) { input = Arrays.copyOf(input, input.length); Shorts.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( short[] input, int fromIndex, int toIndex, short[] expectedOutput) { input = Arrays.copyOf(input, input.length); Shorts.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -394,6 +508,7 @@ public void testSortDescendingIndexed() { testSortDescending(new short[] {-1, -2, 1, 2}, 1, 3, new short[] {-1, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testStringConverterSerialization() { SerializableTester.reserializeAndAssert(Shorts.stringConverter()); @@ -402,17 +517,17 @@ public void testStringConverterSerialization() { public void testToArray() { // need explicit type parameter to avoid javac warning!? List none = Arrays.asList(); - assertTrue(Arrays.equals(EMPTY, Shorts.toArray(none))); + assertThat(Shorts.toArray(none)).isEqualTo(EMPTY); List one = Arrays.asList((short) 1); - assertTrue(Arrays.equals(ARRAY1, Shorts.toArray(one))); + assertThat(Shorts.toArray(one)).isEqualTo(ARRAY1); short[] array = {(short) 0, (short) 1, (short) 3}; List three = Arrays.asList((short) 0, (short) 1, (short) 3); - assertTrue(Arrays.equals(array, Shorts.toArray(three))); + assertThat(Shorts.toArray(three)).isEqualTo(array); - assertTrue(Arrays.equals(array, Shorts.toArray(Shorts.asList(array)))); + assertThat(Shorts.toArray(Shorts.asList(array))).isEqualTo(array); } public void testToArray_threadSafe() { @@ -422,21 +537,17 @@ public void testToArray_threadSafe() { Collection misleadingSize = Helpers.misleadingSizeCollection(delta); misleadingSize.addAll(list); short[] arr = Shorts.toArray(misleadingSize); - assertEquals(i, arr.length); + assertThat(arr).hasLength(i); for (int j = 0; j < i; j++) { - assertEquals(VALUES[j], arr[j]); + assertThat(arr[j]).isEqualTo(VALUES[j]); } } } } public void testToArray_withNull() { - List list = Arrays.asList((short) 0, (short) 1, null); - try { - Shorts.toArray(list); - fail(); - } catch (NullPointerException expected) { - } + List<@Nullable Short> list = Arrays.asList((short) 0, (short) 1, null); + assertThrows(NullPointerException.class, () -> Shorts.toArray(list)); } public void testToArray_withConversion() { @@ -449,21 +560,22 @@ public void testToArray_withConversion() { List longs = Arrays.asList((long) 0, (long) 1, (long) 2); List doubles = Arrays.asList((double) 0, (double) 1, (double) 2); - assertTrue(Arrays.equals(array, Shorts.toArray(bytes))); - assertTrue(Arrays.equals(array, Shorts.toArray(shorts))); - assertTrue(Arrays.equals(array, Shorts.toArray(ints))); - assertTrue(Arrays.equals(array, Shorts.toArray(floats))); - assertTrue(Arrays.equals(array, Shorts.toArray(longs))); - assertTrue(Arrays.equals(array, Shorts.toArray(doubles))); + assertThat(Shorts.toArray(bytes)).isEqualTo(array); + assertThat(Shorts.toArray(shorts)).isEqualTo(array); + assertThat(Shorts.toArray(ints)).isEqualTo(array); + assertThat(Shorts.toArray(floats)).isEqualTo(array); + assertThat(Shorts.toArray(longs)).isEqualTo(array); + assertThat(Shorts.toArray(doubles)).isEqualTo(array); } + @J2ktIncompatible // b/239034072: Kotlin varargs copy parameter arrays. public void testAsList_isAView() { short[] array = {(short) 0, (short) 1}; List list = Shorts.asList(array); list.set(0, (short) 2); - assertTrue(Arrays.equals(new short[] {(short) 2, (short) 1}, array)); + assertThat(array).isEqualTo(new short[] {(short) 2, (short) 1}); array[1] = (short) 3; - assertEquals(Arrays.asList((short) 2, (short) 3), list); + assertThat(list).containsExactly((short) 2, (short) 3).inOrder(); } public void testAsList_toArray_roundTrip() { @@ -473,24 +585,24 @@ public void testAsList_toArray_roundTrip() { // Make sure it returned a copy list.set(0, (short) 4); - assertTrue(Arrays.equals(new short[] {(short) 0, (short) 1, (short) 2}, newArray)); + assertThat(newArray).isEqualTo(new short[] {(short) 0, (short) 1, (short) 2}); newArray[1] = (short) 5; - assertEquals((short) 1, (short) list.get(1)); + assertThat((short) list.get(1)).isEqualTo((short) 1); } // This test stems from a real bug found by andrewk public void testAsList_subList_toArray_roundTrip() { short[] array = {(short) 0, (short) 1, (short) 2, (short) 3}; List list = Shorts.asList(array); - assertTrue( - Arrays.equals(new short[] {(short) 1, (short) 2}, Shorts.toArray(list.subList(1, 3)))); - assertTrue(Arrays.equals(new short[] {}, Shorts.toArray(list.subList(2, 2)))); + assertThat(Shorts.toArray(list.subList(1, 3))).isEqualTo(new short[] {(short) 1, (short) 2}); + assertThat(Shorts.toArray(list.subList(2, 2))).isEqualTo(new short[] {}); } public void testAsListEmpty() { - assertSame(Collections.emptyList(), Shorts.asList(EMPTY)); + assertThat(Shorts.asList(EMPTY)).isSameInstanceAs(Collections.emptyList()); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(Shorts.class); @@ -498,40 +610,37 @@ public void testNulls() { public void testStringConverter_convert() { Converter converter = Shorts.stringConverter(); - assertEquals((Short) (short) 1, converter.convert("1")); - assertEquals((Short) (short) 0, converter.convert("0")); - assertEquals((Short) (short) (-1), converter.convert("-1")); - assertEquals((Short) (short) 255, converter.convert("0xff")); - assertEquals((Short) (short) 255, converter.convert("0xFF")); - assertEquals((Short) (short) (-255), converter.convert("-0xFF")); - assertEquals((Short) (short) 255, converter.convert("#0000FF")); - assertEquals((Short) (short) 438, converter.convert("0666")); + assertThat(converter.convert("1")).isEqualTo((Short) (short) 1); + assertThat(converter.convert("0")).isEqualTo((Short) (short) 0); + assertThat(converter.convert("-1")).isEqualTo((Short) (short) (-1)); + assertThat(converter.convert("0xff")).isEqualTo((Short) (short) 255); + assertThat(converter.convert("0xFF")).isEqualTo((Short) (short) 255); + assertThat(converter.convert("-0xFF")).isEqualTo((Short) (short) (-255)); + assertThat(converter.convert("#0000FF")).isEqualTo((Short) (short) 255); + assertThat(converter.convert("0666")).isEqualTo((Short) (short) 438); } public void testStringConverter_convertError() { - try { - Shorts.stringConverter().convert("notanumber"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> Shorts.stringConverter().convert("notanumber")); } public void testStringConverter_nullConversions() { - assertNull(Shorts.stringConverter().convert(null)); - assertNull(Shorts.stringConverter().reverse().convert(null)); + assertThat(Shorts.stringConverter().convert(null)).isNull(); + assertThat(Shorts.stringConverter().reverse().convert(null)).isNull(); } public void testStringConverter_reverse() { Converter converter = Shorts.stringConverter(); - assertEquals("1", converter.reverse().convert((short) 1)); - assertEquals("0", converter.reverse().convert((short) 0)); - assertEquals("-1", converter.reverse().convert((short) -1)); - assertEquals("255", converter.reverse().convert((short) 0xff)); - assertEquals("255", converter.reverse().convert((short) 0xFF)); - assertEquals("-255", converter.reverse().convert((short) -0xFF)); - assertEquals("438", converter.reverse().convert((short) 0666)); + assertThat(converter.reverse().convert((short) 1)).isEqualTo("1"); + assertThat(converter.reverse().convert((short) 0)).isEqualTo("0"); + assertThat(converter.reverse().convert((short) -1)).isEqualTo("-1"); + assertThat(converter.reverse().convert((short) 0xff)).isEqualTo("255"); + assertThat(converter.reverse().convert((short) 0xFF)).isEqualTo("255"); + assertThat(converter.reverse().convert((short) -0xFF)).isEqualTo("-255"); + assertThat(converter.reverse().convert((short) 0666)).isEqualTo("438"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testStringConverter_nullPointerTester() throws Exception { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/primitives/SignedBytesTest.java b/android/guava-tests/test/com/google/common/primitives/SignedBytesTest.java index 86c2ce612bba..e186066e4b63 100644 --- a/android/guava-tests/test/com/google/common/primitives/SignedBytesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/SignedBytesTest.java @@ -16,8 +16,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.primitives.SignedBytes.max; +import static com.google.common.primitives.SignedBytes.min; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; @@ -25,14 +32,15 @@ import java.util.Comparator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Unit test for {@link SignedBytes}. * * @author Kevin Bourrillion */ +@NullMarked @GwtCompatible(emulated = true) -@SuppressWarnings("cast") // redundant casts are intentional and harmless public class SignedBytesTest extends TestCase { private static final byte[] EMPTY = {}; private static final byte[] ARRAY1 = {(byte) 1}; @@ -44,7 +52,7 @@ public class SignedBytesTest extends TestCase { public void testCheckedCast() { for (byte value : VALUES) { - assertEquals(value, SignedBytes.checkedCast((long) value)); + assertThat(SignedBytes.checkedCast((long) value)).isEqualTo(value); } assertCastFails(GREATEST + 1L); assertCastFails(LEAST - 1L); @@ -54,12 +62,12 @@ public void testCheckedCast() { public void testSaturatedCast() { for (byte value : VALUES) { - assertEquals(value, SignedBytes.saturatedCast((long) value)); + assertThat(SignedBytes.saturatedCast((long) value)).isEqualTo(value); } - assertEquals(GREATEST, SignedBytes.saturatedCast(GREATEST + 1L)); - assertEquals(LEAST, SignedBytes.saturatedCast(LEAST - 1L)); - assertEquals(GREATEST, SignedBytes.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, SignedBytes.saturatedCast(Long.MIN_VALUE)); + assertThat(SignedBytes.saturatedCast(GREATEST + 1L)).isEqualTo(GREATEST); + assertThat(SignedBytes.saturatedCast(LEAST - 1L)).isEqualTo(LEAST); + assertThat(SignedBytes.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(SignedBytes.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } private static void assertCastFails(long value) { @@ -67,9 +75,9 @@ private static void assertCastFails(long value) { SignedBytes.checkedCast(value); fail("Cast to byte should have failed: " + value); } catch (IllegalArgumentException ex) { - assertTrue( - value + " not found in exception text: " + ex.getMessage(), - ex.getMessage().contains(String.valueOf(value))); + assertWithMessage(value + " not found in exception text: " + ex.getMessage()) + .that(ex.getMessage().contains(String.valueOf(value))) + .isTrue(); } } @@ -80,56 +88,49 @@ public void testCompare() { int expected = Byte.valueOf(x).compareTo(y); int actual = SignedBytes.compare(x, y); if (expected == 0) { - assertEquals(x + ", " + y, expected, actual); + assertWithMessage(x + ", " + y).that(actual).isEqualTo(expected); } else if (expected < 0) { - assertTrue( - x + ", " + y + " (expected: " + expected + ", actual" + actual + ")", actual < 0); + assertWithMessage(x + ", " + y + " (expected: " + expected + ", actual" + actual + ")") + .that(actual < 0) + .isTrue(); } else { - assertTrue( - x + ", " + y + " (expected: " + expected + ", actual" + actual + ")", actual > 0); + assertWithMessage(x + ", " + y + " (expected: " + expected + ", actual" + actual + ")") + .that(actual > 0) + .isTrue(); } } } } public void testMax_noArgs() { - try { - SignedBytes.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, SignedBytes.max(LEAST)); - assertEquals(GREATEST, SignedBytes.max(GREATEST)); - assertEquals( - (byte) 127, SignedBytes.max((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)).isEqualTo((byte) 127); } public void testMin_noArgs() { - try { - SignedBytes.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, SignedBytes.min(LEAST)); - assertEquals(GREATEST, SignedBytes.min(GREATEST)); - assertEquals( - (byte) -128, SignedBytes.min((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)).isEqualTo((byte) -128); } public void testJoin() { - assertEquals("", SignedBytes.join(",", EMPTY)); - assertEquals("1", SignedBytes.join(",", ARRAY1)); - assertEquals("1,2", SignedBytes.join(",", (byte) 1, (byte) 2)); - assertEquals("123", SignedBytes.join("", (byte) 1, (byte) 2, (byte) 3)); - assertEquals("-128,-1", SignedBytes.join(",", (byte) -128, (byte) -1)); + assertThat(SignedBytes.join(",", EMPTY)).isEmpty(); + assertThat(SignedBytes.join(",", ARRAY1)).isEqualTo("1"); + assertThat(SignedBytes.join(",", (byte) 1, (byte) 2)).isEqualTo("1,2"); + assertThat(SignedBytes.join("", (byte) 1, (byte) 2, (byte) 3)).isEqualTo("123"); + assertThat(SignedBytes.join(",", (byte) -128, (byte) -1)).isEqualTo("-128,-1"); } + @J2ktIncompatible // b/285319375 public void testLexicographicalComparator() { List ordered = Arrays.asList( @@ -147,10 +148,11 @@ public void testLexicographicalComparator() { Helpers.testComparator(comparator, ordered); } + @J2ktIncompatible @GwtIncompatible // SerializableTester public void testLexicographicalComparatorSerializable() { Comparator comparator = SignedBytes.lexicographicalComparator(); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); } public void testSortDescending() { @@ -164,14 +166,14 @@ public void testSortDescending() { private static void testSortDescending(byte[] input, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); SignedBytes.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( byte[] input, int fromIndex, int toIndex, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); SignedBytes.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -183,6 +185,7 @@ public void testSortDescendingIndexed() { testSortDescending(new byte[] {-1, -2, 1, 2}, 1, 3, new byte[] {-1, 1, -2, 2}); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(SignedBytes.class); diff --git a/android/guava-tests/test/com/google/common/primitives/TestPlatform.java b/android/guava-tests/test/com/google/common/primitives/TestPlatform.java index 094c9d7fc09d..944b1f3dc39c 100644 --- a/android/guava-tests/test/com/google/common/primitives/TestPlatform.java +++ b/android/guava-tests/test/com/google/common/primitives/TestPlatform.java @@ -17,8 +17,10 @@ package com.google.common.primitives; import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullUnmarked; @GwtCompatible(emulated = true) +@NullUnmarked class TestPlatform { static int reduceIterationsIfGwt(int iterations) { return iterations; diff --git a/android/guava-tests/test/com/google/common/primitives/UnsignedBytesTest.java b/android/guava-tests/test/com/google/common/primitives/UnsignedBytesTest.java index 352c8f439e37..c65b4019a021 100644 --- a/android/guava-tests/test/com/google/common/primitives/UnsignedBytesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/UnsignedBytesTest.java @@ -16,6 +16,13 @@ package com.google.common.primitives; +import static com.google.common.primitives.UnsignedBytes.max; +import static com.google.common.primitives.UnsignedBytes.min; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static java.lang.Math.signum; +import static org.junit.Assert.assertThrows; + import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; @@ -24,6 +31,7 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link UnsignedBytes}. @@ -31,6 +39,7 @@ * @author Kevin Bourrillion * @author Louis Wasserman */ +@NullUnmarked public class UnsignedBytesTest extends TestCase { private static final byte LEAST = 0; private static final byte GREATEST = (byte) 255; @@ -39,17 +48,17 @@ public class UnsignedBytesTest extends TestCase { private static final byte[] VALUES = {LEAST, 127, (byte) 128, (byte) 129, GREATEST}; public void testToInt() { - assertEquals(0, UnsignedBytes.toInt((byte) 0)); - assertEquals(1, UnsignedBytes.toInt((byte) 1)); - assertEquals(127, UnsignedBytes.toInt((byte) 127)); - assertEquals(128, UnsignedBytes.toInt((byte) -128)); - assertEquals(129, UnsignedBytes.toInt((byte) -127)); - assertEquals(255, UnsignedBytes.toInt((byte) -1)); + assertThat(UnsignedBytes.toInt((byte) 0)).isEqualTo(0); + assertThat(UnsignedBytes.toInt((byte) 1)).isEqualTo(1); + assertThat(UnsignedBytes.toInt((byte) 127)).isEqualTo(127); + assertThat(UnsignedBytes.toInt((byte) -128)).isEqualTo(128); + assertThat(UnsignedBytes.toInt((byte) -127)).isEqualTo(129); + assertThat(UnsignedBytes.toInt((byte) -1)).isEqualTo(255); } public void testCheckedCast() { for (byte value : VALUES) { - assertEquals(value, UnsignedBytes.checkedCast(UnsignedBytes.toInt(value))); + assertThat(UnsignedBytes.checkedCast(UnsignedBytes.toInt(value))).isEqualTo(value); } assertCastFails(256L); assertCastFails(-1L); @@ -59,12 +68,12 @@ public void testCheckedCast() { public void testSaturatedCast() { for (byte value : VALUES) { - assertEquals(value, UnsignedBytes.saturatedCast(UnsignedBytes.toInt(value))); + assertThat(UnsignedBytes.saturatedCast(UnsignedBytes.toInt(value))).isEqualTo(value); } - assertEquals(GREATEST, UnsignedBytes.saturatedCast(256L)); - assertEquals(LEAST, UnsignedBytes.saturatedCast(-1L)); - assertEquals(GREATEST, UnsignedBytes.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, UnsignedBytes.saturatedCast(Long.MIN_VALUE)); + assertThat(UnsignedBytes.saturatedCast(256L)).isEqualTo(GREATEST); + assertThat(UnsignedBytes.saturatedCast(-1L)).isEqualTo(LEAST); + assertThat(UnsignedBytes.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(UnsignedBytes.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } private static void assertCastFails(long value) { @@ -72,9 +81,9 @@ private static void assertCastFails(long value) { UnsignedBytes.checkedCast(value); fail("Cast to byte should have failed: " + value); } catch (IllegalArgumentException ex) { - assertTrue( - value + " not found in exception text: " + ex.getMessage(), - ex.getMessage().contains(String.valueOf(value))); + assertWithMessage(value + " not found in exception text: " + ex.getMessage()) + .that(ex.getMessage().contains(String.valueOf(value))) + .isTrue(); } } @@ -86,44 +95,32 @@ public void testCompare() { byte x = VALUES[i]; byte y = VALUES[j]; // note: spec requires only that the sign is the same - assertEquals( - x + ", " + y, - Math.signum(UnsignedBytes.compare(x, y)), - Math.signum(Ints.compare(i, j))); + assertWithMessage(x + ", " + y) + .that(signum(UnsignedBytes.compare(x, y))) + .isEqualTo(signum(Integer.compare(i, j))); } } } public void testMax_noArgs() { - try { - UnsignedBytes.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, UnsignedBytes.max(LEAST)); - assertEquals(GREATEST, UnsignedBytes.max(GREATEST)); - assertEquals( - (byte) 255, UnsignedBytes.max((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)).isEqualTo((byte) 255); } public void testMin_noArgs() { - try { - UnsignedBytes.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, UnsignedBytes.min(LEAST)); - assertEquals(GREATEST, UnsignedBytes.min(GREATEST)); - assertEquals( - (byte) 0, UnsignedBytes.min((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)); - assertEquals( - (byte) 0, UnsignedBytes.min((byte) -1, (byte) 127, (byte) 1, (byte) -128, (byte) 0)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min((byte) 0, (byte) -128, (byte) -1, (byte) 127, (byte) 1)).isEqualTo((byte) 0); + assertThat(min((byte) -1, (byte) 127, (byte) 1, (byte) -128, (byte) 0)).isEqualTo((byte) 0); } private static void assertParseFails(String value) { @@ -145,7 +142,7 @@ private static void assertParseFails(String value, int radix) { public void testParseUnsignedByte() { // We can easily afford to test this exhaustively. for (int i = 0; i <= 0xff; i++) { - assertEquals((byte) i, UnsignedBytes.parseUnsignedByte(Integer.toString(i))); + assertThat(UnsignedBytes.parseUnsignedByte(Integer.toString(i))).isEqualTo((byte) i); } assertParseFails("1000"); assertParseFails("-1"); @@ -154,15 +151,16 @@ public void testParseUnsignedByte() { } public void testMaxValue() { - assertTrue( - UnsignedBytes.compare(UnsignedBytes.MAX_VALUE, (byte) (UnsignedBytes.MAX_VALUE + 1)) > 0); + assertThat(UnsignedBytes.compare(UnsignedBytes.MAX_VALUE, (byte) (UnsignedBytes.MAX_VALUE + 1))) + .isGreaterThan(0); } public void testParseUnsignedByteWithRadix() throws NumberFormatException { // We can easily afford to test this exhaustively. for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { for (int i = 0; i <= 0xff; i++) { - assertEquals((byte) i, UnsignedBytes.parseUnsignedByte(Integer.toString(i, radix), radix)); + assertThat(UnsignedBytes.parseUnsignedByte(Integer.toString(i, radix), radix)) + .isEqualTo((byte) i); } assertParseFails(Integer.toString(1000, radix), radix); assertParseFails(Integer.toString(-1, radix), radix); @@ -174,30 +172,22 @@ public void testParseUnsignedByteWithRadix() throws NumberFormatException { public void testParseUnsignedByteThrowsExceptionForInvalidRadix() { // Valid radix values are Character.MIN_RADIX to Character.MAX_RADIX, // inclusive. - try { - UnsignedBytes.parseUnsignedByte("0", Character.MIN_RADIX - 1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, + () -> UnsignedBytes.parseUnsignedByte("0", Character.MIN_RADIX - 1)); - try { - UnsignedBytes.parseUnsignedByte("0", Character.MAX_RADIX + 1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, + () -> UnsignedBytes.parseUnsignedByte("0", Character.MAX_RADIX + 1)); // The radix is used as an array index, so try a negative value. - try { - UnsignedBytes.parseUnsignedByte("0", -1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedBytes.parseUnsignedByte("0", -1)); } public void testToString() { // We can easily afford to test this exhaustively. for (int i = 0; i <= 0xff; i++) { - assertEquals(Integer.toString(i), UnsignedBytes.toString((byte) i)); + assertThat(UnsignedBytes.toString((byte) i)).isEqualTo(Integer.toString(i)); } } @@ -205,17 +195,17 @@ public void testToStringWithRadix() { // We can easily afford to test this exhaustively. for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { for (int i = 0; i <= 0xff; i++) { - assertEquals(Integer.toString(i, radix), UnsignedBytes.toString((byte) i, radix)); + assertThat(UnsignedBytes.toString((byte) i, radix)).isEqualTo(Integer.toString(i, radix)); } } } public void testJoin() { - assertEquals("", UnsignedBytes.join(",", new byte[] {})); - assertEquals("1", UnsignedBytes.join(",", new byte[] {(byte) 1})); - assertEquals("1,2", UnsignedBytes.join(",", (byte) 1, (byte) 2)); - assertEquals("123", UnsignedBytes.join("", (byte) 1, (byte) 2, (byte) 3)); - assertEquals("128,255", UnsignedBytes.join(",", (byte) 128, (byte) -1)); + assertThat(UnsignedBytes.join(",", new byte[] {})).isEmpty(); + assertThat(UnsignedBytes.join(",", new byte[] {(byte) 1})).isEqualTo("1"); + assertThat(UnsignedBytes.join(",", (byte) 1, (byte) 2)).isEqualTo("1,2"); + assertThat(UnsignedBytes.join("", (byte) 1, (byte) 2, (byte) 3)).isEqualTo("123"); + assertThat(UnsignedBytes.join(",", (byte) 128, (byte) -1)).isEqualTo("128,255"); } private static String unsafeComparatorClassName() { @@ -251,12 +241,14 @@ private static boolean unsafeComparatorAvailable() { public void testLexicographicalComparatorChoice() throws Exception { Comparator defaultComparator = UnsignedBytes.lexicographicalComparator(); - assertNotNull(defaultComparator); - assertSame(defaultComparator, UnsignedBytes.lexicographicalComparator()); + assertThat(defaultComparator).isNotNull(); + assertThat(UnsignedBytes.lexicographicalComparator()).isSameInstanceAs(defaultComparator); if (unsafeComparatorAvailable()) { - assertSame(defaultComparator.getClass(), Class.forName(unsafeComparatorClassName())); + assertThat(Class.forName(unsafeComparatorClassName())) + .isSameInstanceAs(defaultComparator.getClass()); } else { - assertSame(defaultComparator, UnsignedBytes.lexicographicalComparatorJavaImpl()); + assertThat(UnsignedBytes.lexicographicalComparatorJavaImpl()) + .isSameInstanceAs(defaultComparator); } } @@ -273,36 +265,51 @@ public void testLexicographicalComparator() { new byte[] {GREATEST, GREATEST}, new byte[] {GREATEST, GREATEST, GREATEST}); - // The Unsafe implementation if it's available. Otherwise, the Java implementation. + // The VarHandle, Unsafe, or Java implementation. Comparator comparator = UnsignedBytes.lexicographicalComparator(); Helpers.testComparator(comparator, ordered); - assertSame(comparator, SerializableTester.reserialize(comparator)); + assertThat(SerializableTester.reserialize(comparator)).isSameInstanceAs(comparator); // The Java implementation. Comparator javaImpl = UnsignedBytes.lexicographicalComparatorJavaImpl(); Helpers.testComparator(javaImpl, ordered); - assertSame(javaImpl, SerializableTester.reserialize(javaImpl)); + assertThat(SerializableTester.reserialize(javaImpl)).isSameInstanceAs(javaImpl); + } + + public void testLexicographicalComparatorLongPseudorandomInputs() { + Comparator comparator1 = UnsignedBytes.lexicographicalComparator(); + Comparator comparator2 = UnsignedBytes.lexicographicalComparatorJavaImpl(); + Random rnd = new Random(714958103); + for (int trial = 0; trial < 100; trial++) { + byte[] left = new byte[1 + rnd.nextInt(32)]; + rnd.nextBytes(left); + byte[] right = left.clone(); + assertThat(comparator1.compare(left, right)).isEqualTo(0); + assertThat(comparator2.compare(left, right)).isEqualTo(0); + int i = rnd.nextInt(left.length); + left[i] ^= (byte) (1 + rnd.nextInt(255)); + assertThat(signum(comparator1.compare(left, right))) + .isEqualTo(signum(UnsignedBytes.compare(left[i], right[i]))); + assertThat(signum(comparator2.compare(left, right))) + .isEqualTo(signum(UnsignedBytes.compare(left[i], right[i]))); + } } - @SuppressWarnings("unchecked") - public void testLexicographicalComparatorLongInputs() { - Random rnd = new Random(); - for (Comparator comparator : - Arrays.asList( - UnsignedBytes.lexicographicalComparator(), - UnsignedBytes.lexicographicalComparatorJavaImpl())) { - for (int trials = 10; trials-- > 0; ) { - byte[] left = new byte[1 + rnd.nextInt(32)]; - rnd.nextBytes(left); - byte[] right = left.clone(); - assertTrue(comparator.compare(left, right) == 0); - int i = rnd.nextInt(left.length); - left[i] ^= (byte) (1 + rnd.nextInt(255)); - assertTrue(comparator.compare(left, right) != 0); - assertEquals( - comparator.compare(left, right) > 0, UnsignedBytes.compare(left[i], right[i]) > 0); - } - } + public void testLexicographicalComparatorLongHandwrittenInputs() { + Comparator comparator1 = UnsignedBytes.lexicographicalComparator(); + Comparator comparator2 = UnsignedBytes.lexicographicalComparatorJavaImpl(); + + /* + * These arrays are set up to test that the comparator compares bytes within a word in the + * correct order—in order words, that it doesn't mix up big-endian and little-endian. The first + * array has a smaller element at one index, and then the second array has a smaller element at + * the next. + */ + byte[] a0 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 99, 15, 16, 17}; + byte[] b0 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99, 14, 15, 16, 17}; + + assertThat(comparator1.compare(a0, b0)).isLessThan(0); + assertThat(comparator2.compare(a0, b0)).isLessThan(0); } public void testSort() { @@ -315,13 +322,13 @@ public void testSort() { static void testSort(byte[] input, byte[] expected) { input = Arrays.copyOf(input, input.length); UnsignedBytes.sort(input); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } static void testSort(byte[] input, int from, int to, byte[] expected) { input = Arrays.copyOf(input, input.length); UnsignedBytes.sort(input, from, to); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } public void testSortIndexed() { @@ -344,14 +351,14 @@ public void testSortDescending() { private static void testSortDescending(byte[] input, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedBytes.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( byte[] input, int fromIndex, int toIndex, byte[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedBytes.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { diff --git a/android/guava-tests/test/com/google/common/primitives/UnsignedIntegerTest.java b/android/guava-tests/test/com/google/common/primitives/UnsignedIntegerTest.java index 04bf27d05581..4116438a58ca 100644 --- a/android/guava-tests/test/com/google/common/primitives/UnsignedIntegerTest.java +++ b/android/guava-tests/test/com/google/common/primitives/UnsignedIntegerTest.java @@ -14,14 +14,20 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSet; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import java.math.BigInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code UnsignedInteger}. @@ -29,6 +35,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class UnsignedIntegerTest extends TestCase { private static final ImmutableSet TEST_INTS; private static final ImmutableSet TEST_LONGS; @@ -58,16 +65,18 @@ private static int force32(int value) { public void testFromIntBitsAndIntValueAreInverses() { for (int value : TEST_INTS) { - assertEquals( - UnsignedInts.toString(value), value, UnsignedInteger.fromIntBits(value).intValue()); + assertWithMessage(UnsignedInts.toString(value)) + .that(UnsignedInteger.fromIntBits(value).intValue()) + .isEqualTo(value); } } public void testFromIntBitsLongValue() { for (int value : TEST_INTS) { long expected = value & 0xffffffffL; - assertEquals( - UnsignedInts.toString(value), expected, UnsignedInteger.fromIntBits(value).longValue()); + assertWithMessage(UnsignedInts.toString(value)) + .that(UnsignedInteger.fromIntBits(value).longValue()) + .isEqualTo(expected); } } @@ -77,10 +86,10 @@ public void testValueOfLong() { for (long value : TEST_LONGS) { boolean expectSuccess = value >= min && value <= max; try { - assertEquals(value, UnsignedInteger.valueOf(value).longValue()); - assertTrue(expectSuccess); + assertThat(UnsignedInteger.valueOf(value).longValue()).isEqualTo(value); + assertThat(expectSuccess).isTrue(); } catch (IllegalArgumentException e) { - assertFalse(expectSuccess); + assertThat(expectSuccess).isFalse(); } } } @@ -91,10 +100,10 @@ public void testValueOfBigInteger() { for (long value : TEST_LONGS) { boolean expectSuccess = value >= min && value <= max; try { - assertEquals(value, UnsignedInteger.valueOf(BigInteger.valueOf(value)).longValue()); - assertTrue(expectSuccess); + assertThat(UnsignedInteger.valueOf(BigInteger.valueOf(value)).longValue()).isEqualTo(value); + assertThat(expectSuccess).isTrue(); } catch (IllegalArgumentException e) { - assertFalse(expectSuccess); + assertThat(expectSuccess).isFalse(); } } } @@ -102,16 +111,17 @@ public void testValueOfBigInteger() { public void testToString() { for (int value : TEST_INTS) { UnsignedInteger unsignedValue = UnsignedInteger.fromIntBits(value); - assertEquals(unsignedValue.bigIntegerValue().toString(), unsignedValue.toString()); + assertThat(unsignedValue.toString()).isEqualTo(unsignedValue.bigIntegerValue().toString()); } } + @J2ktIncompatible @GwtIncompatible // too slow public void testToStringRadix() { for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { for (int l : TEST_INTS) { UnsignedInteger value = UnsignedInteger.fromIntBits(l); - assertEquals(value.bigIntegerValue().toString(radix), value.toString(radix)); + assertThat(value.toString(radix)).isEqualTo(value.bigIntegerValue().toString(radix)); } } } @@ -121,7 +131,7 @@ public void testToStringRadixQuick() { for (int radix : radices) { for (int l : TEST_INTS) { UnsignedInteger value = UnsignedInteger.fromIntBits(l); - assertEquals(value.bigIntegerValue().toString(radix), value.toString(radix)); + assertThat(value.toString(radix)).isEqualTo(value.bigIntegerValue().toString(radix)); } } } @@ -129,14 +139,16 @@ public void testToStringRadixQuick() { public void testFloatValue() { for (int value : TEST_INTS) { UnsignedInteger unsignedValue = UnsignedInteger.fromIntBits(value); - assertEquals(unsignedValue.bigIntegerValue().floatValue(), unsignedValue.floatValue()); + assertThat(unsignedValue.floatValue()) + .isEqualTo(unsignedValue.bigIntegerValue().floatValue()); } } public void testDoubleValue() { for (int value : TEST_INTS) { UnsignedInteger unsignedValue = UnsignedInteger.fromIntBits(value); - assertEquals(unsignedValue.bigIntegerValue().doubleValue(), unsignedValue.doubleValue()); + assertThat(unsignedValue.doubleValue()) + .isEqualTo(unsignedValue.bigIntegerValue().doubleValue()); } } @@ -147,7 +159,7 @@ public void testPlus() { UnsignedInteger bUnsigned = UnsignedInteger.fromIntBits(b); int expected = aUnsigned.bigIntegerValue().add(bUnsigned.bigIntegerValue()).intValue(); UnsignedInteger unsignedSum = aUnsigned.plus(bUnsigned); - assertEquals(expected, unsignedSum.intValue()); + assertThat(unsignedSum.intValue()).isEqualTo(expected); } } } @@ -160,11 +172,12 @@ public void testMinus() { int expected = force32(aUnsigned.bigIntegerValue().subtract(bUnsigned.bigIntegerValue()).intValue()); UnsignedInteger unsignedSub = aUnsigned.minus(bUnsigned); - assertEquals(expected, unsignedSub.intValue()); + assertThat(unsignedSub.intValue()).isEqualTo(expected); } } } + @J2ktIncompatible @GwtIncompatible // multiply public void testTimes() { for (int a : TEST_INTS) { @@ -174,7 +187,9 @@ public void testTimes() { int expected = force32(aUnsigned.bigIntegerValue().multiply(bUnsigned.bigIntegerValue()).intValue()); UnsignedInteger unsignedMul = aUnsigned.times(bUnsigned); - assertEquals(aUnsigned + " * " + bUnsigned, expected, unsignedMul.intValue()); + assertWithMessage(aUnsigned + " * " + bUnsigned) + .that(unsignedMul.intValue()) + .isEqualTo(expected); } } } @@ -187,7 +202,7 @@ public void testDividedBy() { UnsignedInteger bUnsigned = UnsignedInteger.fromIntBits(b); int expected = aUnsigned.bigIntegerValue().divide(bUnsigned.bigIntegerValue()).intValue(); UnsignedInteger unsignedDiv = aUnsigned.dividedBy(bUnsigned); - assertEquals(expected, unsignedDiv.intValue()); + assertThat(unsignedDiv.intValue()).isEqualTo(expected); } } } @@ -195,11 +210,11 @@ public void testDividedBy() { public void testDivideByZeroThrows() { for (int a : TEST_INTS) { - try { - UnsignedInteger unused = UnsignedInteger.fromIntBits(a).dividedBy(UnsignedInteger.ZERO); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, + () -> { + UnsignedInteger unused = UnsignedInteger.fromIntBits(a).dividedBy(UnsignedInteger.ZERO); + }); } } @@ -211,7 +226,7 @@ public void testMod() { UnsignedInteger bUnsigned = UnsignedInteger.fromIntBits(b); int expected = aUnsigned.bigIntegerValue().mod(bUnsigned.bigIntegerValue()).intValue(); UnsignedInteger unsignedRem = aUnsigned.mod(bUnsigned); - assertEquals(expected, unsignedRem.intValue()); + assertThat(unsignedRem.intValue()).isEqualTo(expected); } } } @@ -219,11 +234,9 @@ public void testMod() { public void testModByZero() { for (int a : TEST_INTS) { - try { - UnsignedInteger.fromIntBits(a).mod(UnsignedInteger.ZERO); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, + () -> UnsignedInteger.fromIntBits(a).mod(UnsignedInteger.ZERO)); } } @@ -232,13 +245,13 @@ public void testCompare() { for (int b : TEST_INTS) { UnsignedInteger aUnsigned = UnsignedInteger.fromIntBits(a); UnsignedInteger bUnsigned = UnsignedInteger.fromIntBits(b); - assertEquals( - aUnsigned.bigIntegerValue().compareTo(bUnsigned.bigIntegerValue()), - aUnsigned.compareTo(bUnsigned)); + assertThat(aUnsigned.compareTo(bUnsigned)) + .isEqualTo(aUnsigned.bigIntegerValue().compareTo(bUnsigned.bigIntegerValue())); } } } + @J2ktIncompatible @GwtIncompatible // too slow public void testEquals() { EqualsTester equalsTester = new EqualsTester(); @@ -257,10 +270,11 @@ public void testIntValue() { for (int a : TEST_INTS) { UnsignedInteger aUnsigned = UnsignedInteger.fromIntBits(a); int intValue = aUnsigned.bigIntegerValue().intValue(); - assertEquals(intValue, aUnsigned.intValue()); + assertThat(aUnsigned.intValue()).isEqualTo(intValue); } } + @J2ktIncompatible @GwtIncompatible // serialization public void testSerialization() { for (int a : TEST_INTS) { @@ -268,6 +282,7 @@ public void testSerialization() { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(UnsignedInteger.class); diff --git a/android/guava-tests/test/com/google/common/primitives/UnsignedIntsTest.java b/android/guava-tests/test/com/google/common/primitives/UnsignedIntsTest.java index 0d0b3800879e..b2bfd3bfbf81 100644 --- a/android/guava-tests/test/com/google/common/primitives/UnsignedIntsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/UnsignedIntsTest.java @@ -14,10 +14,14 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.primitives.UnsignedInts.max; +import static com.google.common.primitives.UnsignedInts.min; import static com.google.common.truth.Truth.assertThat; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import java.util.Arrays; @@ -25,6 +29,7 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for UnsignedInts @@ -32,6 +37,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class UnsignedIntsTest extends TestCase { private static final long[] UNSIGNED_INTS = { 0L, @@ -52,7 +58,7 @@ public class UnsignedIntsTest extends TestCase { public void testCheckedCast() { for (long value : UNSIGNED_INTS) { - assertEquals(value, UnsignedInts.toLong(UnsignedInts.checkedCast(value))); + assertThat(UnsignedInts.toLong(UnsignedInts.checkedCast(value))).isEqualTo(value); } assertCastFails(1L << 32); assertCastFails(-1L); @@ -65,80 +71,72 @@ private static void assertCastFails(long value) { UnsignedInts.checkedCast(value); fail("Cast to int should have failed: " + value); } catch (IllegalArgumentException ex) { - assertThat(ex.getMessage()).contains(String.valueOf(value)); + assertThat(ex).hasMessageThat().contains(String.valueOf(value)); } } public void testSaturatedCast() { for (long value : UNSIGNED_INTS) { - assertEquals(value, UnsignedInts.toLong(UnsignedInts.saturatedCast(value))); + assertThat(UnsignedInts.toLong(UnsignedInts.saturatedCast(value))).isEqualTo(value); } - assertEquals(GREATEST, UnsignedInts.saturatedCast(1L << 32)); - assertEquals(LEAST, UnsignedInts.saturatedCast(-1L)); - assertEquals(GREATEST, UnsignedInts.saturatedCast(Long.MAX_VALUE)); - assertEquals(LEAST, UnsignedInts.saturatedCast(Long.MIN_VALUE)); + assertThat(UnsignedInts.saturatedCast(1L << 32)).isEqualTo(GREATEST); + assertThat(UnsignedInts.saturatedCast(-1L)).isEqualTo(LEAST); + assertThat(UnsignedInts.saturatedCast(Long.MAX_VALUE)).isEqualTo(GREATEST); + assertThat(UnsignedInts.saturatedCast(Long.MIN_VALUE)).isEqualTo(LEAST); } public void testToLong() { for (long a : UNSIGNED_INTS) { - assertEquals(a, UnsignedInts.toLong((int) a)); + assertThat(UnsignedInts.toLong((int) a)).isEqualTo(a); } } public void testCompare() { for (long a : UNSIGNED_INTS) { for (long b : UNSIGNED_INTS) { - int cmpAsLongs = Longs.compare(a, b); + int cmpAsLongs = Long.compare(a, b); int cmpAsUInt = UnsignedInts.compare((int) a, (int) b); - assertEquals(Integer.signum(cmpAsLongs), Integer.signum(cmpAsUInt)); + assertThat(Integer.signum(cmpAsUInt)).isEqualTo(Integer.signum(cmpAsLongs)); } } } public void testMax_noArgs() { - try { - UnsignedInts.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, UnsignedInts.max(LEAST)); - assertEquals(GREATEST, UnsignedInts.max(GREATEST)); - assertEquals( - (int) 0xff1a618bL, - UnsignedInts.max( - (int) 8L, - (int) 6L, - (int) 7L, - (int) 0x12345678L, - (int) 0x5a4316b8L, - (int) 0xff1a618bL, - (int) 0L)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat( + max( + (int) 8L, + (int) 6L, + (int) 7L, + (int) 0x12345678L, + (int) 0x5a4316b8L, + (int) 0xff1a618bL, + (int) 0L)) + .isEqualTo((int) 0xff1a618bL); } public void testMin_noArgs() { - try { - UnsignedInts.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, UnsignedInts.min(LEAST)); - assertEquals(GREATEST, UnsignedInts.min(GREATEST)); - assertEquals( - (int) 0L, - UnsignedInts.min( - (int) 8L, - (int) 6L, - (int) 7L, - (int) 0x12345678L, - (int) 0x5a4316b8L, - (int) 0xff1a618bL, - (int) 0L)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat( + min( + (int) 8L, + (int) 6L, + (int) 7L, + (int) 0x12345678L, + (int) 0x5a4316b8L, + (int) 0xff1a618bL, + (int) 0L)) + .isEqualTo((int) 0L); } public void testLexicographicalComparator() { @@ -168,13 +166,13 @@ public void testSort() { static void testSort(int[] input, int[] expected) { input = Arrays.copyOf(input, input.length); UnsignedInts.sort(input); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } static void testSort(int[] input, int from, int to, int[] expected) { input = Arrays.copyOf(input, input.length); UnsignedInts.sort(input, from, to); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } public void testSortIndexed() { @@ -196,14 +194,14 @@ public void testSortDescending() { private static void testSortDescending(int[] input, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedInts.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( int[] input, int fromIndex, int toIndex, int[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedInts.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -223,10 +221,10 @@ public void testDivide() { for (long a : UNSIGNED_INTS) { for (long b : UNSIGNED_INTS) { try { - assertEquals((int) (a / b), UnsignedInts.divide((int) a, (int) b)); - assertFalse(b == 0); + assertThat(UnsignedInts.divide((int) a, (int) b)).isEqualTo((int) (a / b)); + assertThat(b).isNotEqualTo(0); } catch (ArithmeticException e) { - assertEquals(0, b); + assertThat(b).isEqualTo(0); } } } @@ -236,10 +234,10 @@ public void testRemainder() { for (long a : UNSIGNED_INTS) { for (long b : UNSIGNED_INTS) { try { - assertEquals((int) (a % b), UnsignedInts.remainder((int) a, (int) b)); - assertFalse(b == 0); + assertThat(UnsignedInts.remainder((int) a, (int) b)).isEqualTo((int) (a % b)); + assertThat(b).isNotEqualTo(0); } catch (ArithmeticException e) { - assertEquals(0, b); + assertThat(b).isEqualTo(0); } } } @@ -253,67 +251,74 @@ public void testDivideRemainderEuclideanProperty() { int dividend = r.nextInt(); int divisor = r.nextInt(); // Test that the Euclidean property is preserved: - assertTrue( - dividend + assertThat( + dividend - (divisor * UnsignedInts.divide(dividend, divisor) - + UnsignedInts.remainder(dividend, divisor)) - == 0); + + UnsignedInts.remainder(dividend, divisor))) + .isEqualTo(0); } } public void testParseInt() { for (long a : UNSIGNED_INTS) { - assertEquals((int) a, UnsignedInts.parseUnsignedInt(Long.toString(a))); + assertThat(UnsignedInts.parseUnsignedInt(Long.toString(a))).isEqualTo((int) a); } } public void testParseIntFail() { - try { - UnsignedInts.parseUnsignedInt(Long.toString(1L << 32)); - fail("Expected NumberFormatException"); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, () -> UnsignedInts.parseUnsignedInt(Long.toString(1L << 32))); } public void testParseIntWithRadix() { for (long a : UNSIGNED_INTS) { for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { - assertEquals((int) a, UnsignedInts.parseUnsignedInt(Long.toString(a, radix), radix)); + assertThat(UnsignedInts.parseUnsignedInt(Long.toString(a, radix), radix)) + .isEqualTo((int) a); } } } public void testParseIntWithRadixLimits() { // loops through all legal radix values. - for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { + for (int r = Character.MIN_RADIX; r <= Character.MAX_RADIX; r++) { + final int radix = r; // tests can successfully parse a number string with this radix. String maxAsString = Long.toString((1L << 32) - 1, radix); - assertEquals(-1, UnsignedInts.parseUnsignedInt(maxAsString, radix)); - - try { - // tests that we get exception whre an overflow would occur. - long overflow = 1L << 32; - String overflowAsString = Long.toString(overflow, radix); - UnsignedInts.parseUnsignedInt(overflowAsString, radix); - fail(); - } catch (NumberFormatException expected) { - } + assertThat(UnsignedInts.parseUnsignedInt(maxAsString, radix)).isEqualTo(-1); + + assertThrows( + NumberFormatException.class, + () -> { + long overflow = 1L << 32; + String overflowAsString = Long.toString(overflow, radix); + UnsignedInts.parseUnsignedInt(overflowAsString, radix); + }); } } public void testParseIntThrowsExceptionForInvalidRadix() { // Valid radix values are Character.MIN_RADIX to Character.MAX_RADIX, // inclusive. + // + // Note: According to the spec, a NumberFormatException is thrown for a number that is not + // parseable, but the spec doesn't seem to say which exception is thrown for an invalid radix. + // In contrast to the JVM, Kotlin native throws an Illegal argument exception in this case + // (which seems to make more sense). try { UnsignedInts.parseUnsignedInt("0", Character.MIN_RADIX - 1); fail(); } catch (NumberFormatException expected) { + } catch (IllegalArgumentException expected) { + // Kotlin native, see above } try { UnsignedInts.parseUnsignedInt("0", Character.MAX_RADIX + 1); fail(); } catch (NumberFormatException expected) { + } catch (IllegalArgumentException expected) { + // Kotlin native, see above } // The radix is used as an array index, so try a negative value. @@ -321,68 +326,54 @@ public void testParseIntThrowsExceptionForInvalidRadix() { UnsignedInts.parseUnsignedInt("0", -1); fail(); } catch (NumberFormatException expected) { + } catch (IllegalArgumentException expected) { + // Kotlin native, see above } } public void testDecodeInt() { - assertEquals(0xffffffff, UnsignedInts.decode("0xffffffff")); - assertEquals(01234567, UnsignedInts.decode("01234567")); // octal - assertEquals(0x12345678, UnsignedInts.decode("#12345678")); - assertEquals(76543210, UnsignedInts.decode("76543210")); - assertEquals(0x13579135, UnsignedInts.decode("0x13579135")); - assertEquals(0x13579135, UnsignedInts.decode("0X13579135")); - assertEquals(0, UnsignedInts.decode("0")); + assertThat(UnsignedInts.decode("0xffffffff")).isEqualTo(0xffffffff); + assertThat(UnsignedInts.decode("01234567")).isEqualTo(01234567); // octal + assertThat(UnsignedInts.decode("#12345678")).isEqualTo(0x12345678); + assertThat(UnsignedInts.decode("76543210")).isEqualTo(76543210); + assertThat(UnsignedInts.decode("0x13579135")).isEqualTo(0x13579135); + assertThat(UnsignedInts.decode("0X13579135")).isEqualTo(0x13579135); + assertThat(UnsignedInts.decode("0")).isEqualTo(0); } public void testDecodeIntFails() { - try { - // One more than maximum value - UnsignedInts.decode("0xfffffffff"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedInts.decode("0xfffffffff")); - try { - UnsignedInts.decode("-5"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedInts.decode("-5")); - try { - UnsignedInts.decode("-0x5"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedInts.decode("-0x5")); - try { - UnsignedInts.decode("-05"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedInts.decode("-05")); } public void testToString() { int[] bases = {2, 5, 7, 8, 10, 16}; for (long a : UNSIGNED_INTS) { for (int base : bases) { - assertEquals(UnsignedInts.toString((int) a, base), Long.toString(a, base)); + assertThat(Long.toString(a, base)).isEqualTo(UnsignedInts.toString((int) a, base)); } } } public void testJoin() { - assertEquals("", join()); - assertEquals("1", join(1)); - assertEquals("1,2", join(1, 2)); - assertEquals("4294967295,2147483648", join(-1, Integer.MIN_VALUE)); + assertThat(join()).isEmpty(); + assertThat(join(1)).isEqualTo("1"); + assertThat(join(1, 2)).isEqualTo("1,2"); + assertThat(join(-1, Integer.MIN_VALUE)).isEqualTo("4294967295,2147483648"); - assertEquals("123", UnsignedInts.join("", 1, 2, 3)); + assertThat(UnsignedInts.join("", 1, 2, 3)).isEqualTo("123"); } private static String join(int... values) { return UnsignedInts.join(",", values); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(UnsignedInts.class); diff --git a/android/guava-tests/test/com/google/common/primitives/UnsignedLongTest.java b/android/guava-tests/test/com/google/common/primitives/UnsignedLongTest.java index cfa2862573aa..87d6bf9e571d 100644 --- a/android/guava-tests/test/com/google/common/primitives/UnsignedLongTest.java +++ b/android/guava-tests/test/com/google/common/primitives/UnsignedLongTest.java @@ -14,14 +14,20 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableSet; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.SerializableTester; import java.math.BigInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@code UnsignedLong}. @@ -29,6 +35,7 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class UnsignedLongTest extends TestCase { private static final ImmutableSet TEST_LONGS; private static final ImmutableSet TEST_BIG_INTEGERS; @@ -69,8 +76,9 @@ public class UnsignedLongTest extends TestCase { public void testAsUnsignedAndLongValueAreInverses() { for (long value : TEST_LONGS) { - assertEquals( - UnsignedLongs.toString(value), value, UnsignedLong.fromLongBits(value).longValue()); + assertWithMessage(UnsignedLongs.toString(value)) + .that(UnsignedLong.fromLongBits(value).longValue()) + .isEqualTo(value); } } @@ -80,10 +88,9 @@ public void testAsUnsignedBigIntegerValue() { (value >= 0) ? BigInteger.valueOf(value) : BigInteger.valueOf(value).add(BigInteger.ZERO.setBit(64)); - assertEquals( - UnsignedLongs.toString(value), - expected, - UnsignedLong.fromLongBits(value).bigIntegerValue()); + assertWithMessage(UnsignedLongs.toString(value)) + .that(UnsignedLong.fromLongBits(value).bigIntegerValue()) + .isEqualTo(expected); } } @@ -91,10 +98,10 @@ public void testValueOfLong() { for (long value : TEST_LONGS) { boolean expectSuccess = value >= 0; try { - assertEquals(value, UnsignedLong.valueOf(value).longValue()); - assertTrue(expectSuccess); + assertThat(UnsignedLong.valueOf(value).longValue()).isEqualTo(value); + assertThat(expectSuccess).isTrue(); } catch (IllegalArgumentException e) { - assertFalse(expectSuccess); + assertThat(expectSuccess).isFalse(); } } } @@ -105,10 +112,10 @@ public void testValueOfBigInteger() { for (BigInteger big : TEST_BIG_INTEGERS) { boolean expectSuccess = big.compareTo(min) >= 0 && big.compareTo(max) <= 0; try { - assertEquals(big, UnsignedLong.valueOf(big).bigIntegerValue()); - assertTrue(expectSuccess); + assertThat(UnsignedLong.valueOf(big).bigIntegerValue()).isEqualTo(big); + assertThat(expectSuccess).isTrue(); } catch (IllegalArgumentException e) { - assertFalse(expectSuccess); + assertThat(expectSuccess).isFalse(); } } } @@ -116,7 +123,7 @@ public void testValueOfBigInteger() { public void testToString() { for (long value : TEST_LONGS) { UnsignedLong unsignedValue = UnsignedLong.fromLongBits(value); - assertEquals(unsignedValue.bigIntegerValue().toString(), unsignedValue.toString()); + assertThat(unsignedValue.toString()).isEqualTo(unsignedValue.bigIntegerValue().toString()); } } @@ -125,7 +132,7 @@ public void testToStringRadix() { for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { for (long l : TEST_LONGS) { UnsignedLong value = UnsignedLong.fromLongBits(l); - assertEquals(value.bigIntegerValue().toString(radix), value.toString(radix)); + assertThat(value.toString(radix)).isEqualTo(value.bigIntegerValue().toString(radix)); } } } @@ -135,7 +142,7 @@ public void testToStringRadixQuick() { for (int radix : radices) { for (long l : TEST_LONGS) { UnsignedLong value = UnsignedLong.fromLongBits(l); - assertEquals(value.bigIntegerValue().toString(radix), value.toString(radix)); + assertThat(value.toString(radix)).isEqualTo(value.bigIntegerValue().toString(radix)); } } } @@ -144,22 +151,18 @@ public void testToStringRadixQuick() { public void testFloatValue() { for (long value : TEST_LONGS) { UnsignedLong unsignedValue = UnsignedLong.fromLongBits(value); - assertEquals( - "Float value of " + unsignedValue, - unsignedValue.bigIntegerValue().floatValue(), - unsignedValue.floatValue(), - 0.0f); + assertWithMessage("Float value of " + unsignedValue) + .that(unsignedValue.floatValue()) + .isEqualTo(unsignedValue.bigIntegerValue().floatValue()); } } public void testDoubleValue() { for (long value : TEST_LONGS) { UnsignedLong unsignedValue = UnsignedLong.fromLongBits(value); - assertEquals( - "Double value of " + unsignedValue, - unsignedValue.bigIntegerValue().doubleValue(), - unsignedValue.doubleValue(), - 0.0); + assertWithMessage("Double value of " + unsignedValue) + .that(unsignedValue.doubleValue()) + .isEqualTo(unsignedValue.bigIntegerValue().doubleValue()); } } @@ -170,7 +173,7 @@ public void testPlus() { UnsignedLong bUnsigned = UnsignedLong.fromLongBits(b); long expected = aUnsigned.bigIntegerValue().add(bUnsigned.bigIntegerValue()).longValue(); UnsignedLong unsignedSum = aUnsigned.plus(bUnsigned); - assertEquals(expected, unsignedSum.longValue()); + assertThat(unsignedSum.longValue()).isEqualTo(expected); } } } @@ -183,7 +186,7 @@ public void testMinus() { long expected = aUnsigned.bigIntegerValue().subtract(bUnsigned.bigIntegerValue()).longValue(); UnsignedLong unsignedSub = aUnsigned.minus(bUnsigned); - assertEquals(expected, unsignedSub.longValue()); + assertThat(unsignedSub.longValue()).isEqualTo(expected); } } } @@ -196,7 +199,7 @@ public void testTimes() { long expected = aUnsigned.bigIntegerValue().multiply(bUnsigned.bigIntegerValue()).longValue(); UnsignedLong unsignedMul = aUnsigned.times(bUnsigned); - assertEquals(expected, unsignedMul.longValue()); + assertThat(unsignedMul.longValue()).isEqualTo(expected); } } } @@ -210,7 +213,7 @@ public void testDividedBy() { long expected = aUnsigned.bigIntegerValue().divide(bUnsigned.bigIntegerValue()).longValue(); UnsignedLong unsignedDiv = aUnsigned.dividedBy(bUnsigned); - assertEquals(expected, unsignedDiv.longValue()); + assertThat(unsignedDiv.longValue()).isEqualTo(expected); } } } @@ -218,11 +221,9 @@ public void testDividedBy() { public void testDivideByZeroThrows() { for (long a : TEST_LONGS) { - try { - UnsignedLong.fromLongBits(a).dividedBy(UnsignedLong.ZERO); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, + () -> UnsignedLong.fromLongBits(a).dividedBy(UnsignedLong.ZERO)); } } @@ -235,7 +236,7 @@ public void testMod() { long expected = aUnsigned.bigIntegerValue().remainder(bUnsigned.bigIntegerValue()).longValue(); UnsignedLong unsignedRem = aUnsigned.mod(bUnsigned); - assertEquals(expected, unsignedRem.longValue()); + assertThat(unsignedRem.longValue()).isEqualTo(expected); } } } @@ -243,11 +244,8 @@ public void testMod() { public void testModByZero() { for (long a : TEST_LONGS) { - try { - UnsignedLong.fromLongBits(a).mod(UnsignedLong.ZERO); - fail("Expected ArithmeticException"); - } catch (ArithmeticException expected) { - } + assertThrows( + ArithmeticException.class, () -> UnsignedLong.fromLongBits(a).mod(UnsignedLong.ZERO)); } } @@ -256,9 +254,8 @@ public void testCompare() { for (long b : TEST_LONGS) { UnsignedLong aUnsigned = UnsignedLong.fromLongBits(a); UnsignedLong bUnsigned = UnsignedLong.fromLongBits(b); - assertEquals( - aUnsigned.bigIntegerValue().compareTo(bUnsigned.bigIntegerValue()), - aUnsigned.compareTo(bUnsigned)); + assertThat(aUnsigned.compareTo(bUnsigned)) + .isEqualTo(aUnsigned.bigIntegerValue().compareTo(bUnsigned.bigIntegerValue())); } } } @@ -282,10 +279,11 @@ public void testIntValue() { for (long a : TEST_LONGS) { UnsignedLong aUnsigned = UnsignedLong.fromLongBits(a); int intValue = aUnsigned.bigIntegerValue().intValue(); - assertEquals(intValue, aUnsigned.intValue()); + assertThat(aUnsigned.intValue()).isEqualTo(intValue); } } + @J2ktIncompatible @GwtIncompatible // serialization public void testSerialization() { for (long a : TEST_LONGS) { @@ -293,6 +291,7 @@ public void testSerialization() { } } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(UnsignedLong.class); diff --git a/android/guava-tests/test/com/google/common/primitives/UnsignedLongsTest.java b/android/guava-tests/test/com/google/common/primitives/UnsignedLongsTest.java index a8b799577ce7..3993a416a57f 100644 --- a/android/guava-tests/test/com/google/common/primitives/UnsignedLongsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/UnsignedLongsTest.java @@ -14,10 +14,15 @@ package com.google.common.primitives; +import static com.google.common.primitives.ReflectionFreeAssertThrows.assertThrows; +import static com.google.common.primitives.UnsignedLongs.max; +import static com.google.common.primitives.UnsignedLongs.min; +import static com.google.common.truth.Truth.assertThat; import static java.math.BigInteger.ONE; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.testing.Helpers; import com.google.common.testing.NullPointerTester; import java.math.BigInteger; @@ -26,6 +31,7 @@ import java.util.List; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for UnsignedLongs @@ -34,63 +40,52 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) +@NullUnmarked public class UnsignedLongsTest extends TestCase { private static final long LEAST = 0L; private static final long GREATEST = 0xffffffffffffffffL; public void testCompare() { // max value - assertTrue(UnsignedLongs.compare(0, 0xffffffffffffffffL) < 0); - assertTrue(UnsignedLongs.compare(0xffffffffffffffffL, 0) > 0); + assertThat(UnsignedLongs.compare(0, 0xffffffffffffffffL)).isLessThan(0); + assertThat(UnsignedLongs.compare(0xffffffffffffffffL, 0)).isGreaterThan(0); // both with high bit set - assertTrue(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0xffffffffffffffffL) < 0); - assertTrue(UnsignedLongs.compare(0xffffffffffffffffL, 0xff1a618b7f65ea12L) > 0); + assertThat(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0xffffffffffffffffL)).isLessThan(0); + assertThat(UnsignedLongs.compare(0xffffffffffffffffL, 0xff1a618b7f65ea12L)).isGreaterThan(0); // one with high bit set - assertTrue(UnsignedLongs.compare(0x5a4316b8c153ac4dL, 0xff1a618b7f65ea12L) < 0); - assertTrue(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0x5a4316b8c153ac4dL) > 0); + assertThat(UnsignedLongs.compare(0x5a4316b8c153ac4dL, 0xff1a618b7f65ea12L)).isLessThan(0); + assertThat(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0x5a4316b8c153ac4dL)).isGreaterThan(0); // neither with high bit set - assertTrue(UnsignedLongs.compare(0x5a4316b8c153ac4dL, 0x6cf78a4b139a4e2aL) < 0); - assertTrue(UnsignedLongs.compare(0x6cf78a4b139a4e2aL, 0x5a4316b8c153ac4dL) > 0); + assertThat(UnsignedLongs.compare(0x5a4316b8c153ac4dL, 0x6cf78a4b139a4e2aL)).isLessThan(0); + assertThat(UnsignedLongs.compare(0x6cf78a4b139a4e2aL, 0x5a4316b8c153ac4dL)).isGreaterThan(0); // same value - assertTrue(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0xff1a618b7f65ea12L) == 0); + assertThat(UnsignedLongs.compare(0xff1a618b7f65ea12L, 0xff1a618b7f65ea12L)).isEqualTo(0); } public void testMax_noArgs() { - try { - UnsignedLongs.max(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> max()); } public void testMax() { - assertEquals(LEAST, UnsignedLongs.max(LEAST)); - assertEquals(GREATEST, UnsignedLongs.max(GREATEST)); - assertEquals( - 0xff1a618b7f65ea12L, - UnsignedLongs.max( - 0x5a4316b8c153ac4dL, 8L, 100L, 0L, 0x6cf78a4b139a4e2aL, 0xff1a618b7f65ea12L)); + assertThat(max(LEAST)).isEqualTo(LEAST); + assertThat(max(GREATEST)).isEqualTo(GREATEST); + assertThat(max(0x5a4316b8c153ac4dL, 8L, 100L, 0L, 0x6cf78a4b139a4e2aL, 0xff1a618b7f65ea12L)) + .isEqualTo(0xff1a618b7f65ea12L); } public void testMin_noArgs() { - try { - UnsignedLongs.min(); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> min()); } public void testMin() { - assertEquals(LEAST, UnsignedLongs.min(LEAST)); - assertEquals(GREATEST, UnsignedLongs.min(GREATEST)); - assertEquals( - 0L, - UnsignedLongs.min( - 0x5a4316b8c153ac4dL, 8L, 100L, 0L, 0x6cf78a4b139a4e2aL, 0xff1a618b7f65ea12L)); + assertThat(min(LEAST)).isEqualTo(LEAST); + assertThat(min(GREATEST)).isEqualTo(GREATEST); + assertThat(min(0x5a4316b8c153ac4dL, 8L, 100L, 0L, 0x6cf78a4b139a4e2aL, 0xff1a618b7f65ea12L)) + .isEqualTo(0L); } public void testLexicographicalComparator() { @@ -120,13 +115,13 @@ public void testSort() { static void testSort(long[] input, long[] expected) { input = Arrays.copyOf(input, input.length); UnsignedLongs.sort(input); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } static void testSort(long[] input, int from, int to, long[] expected) { input = Arrays.copyOf(input, input.length); UnsignedLongs.sort(input, from, to); - assertTrue(Arrays.equals(expected, input)); + assertThat(input).isEqualTo(expected); } public void testSortIndexed() { @@ -149,14 +144,14 @@ public void testSortDescending() { private static void testSortDescending(long[] input, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedLongs.sortDescending(input); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } private static void testSortDescending( long[] input, int fromIndex, int toIndex, long[] expectedOutput) { input = Arrays.copyOf(input, input.length); UnsignedLongs.sortDescending(input, fromIndex, toIndex); - assertTrue(Arrays.equals(expectedOutput, input)); + assertThat(input).isEqualTo(expectedOutput); } public void testSortDescendingIndexed() { @@ -173,24 +168,24 @@ public void testSortDescendingIndexed() { } public void testDivide() { - assertEquals(2, UnsignedLongs.divide(14, 5)); - assertEquals(0, UnsignedLongs.divide(0, 50)); - assertEquals(1, UnsignedLongs.divide(0xfffffffffffffffeL, 0xfffffffffffffffdL)); - assertEquals(0, UnsignedLongs.divide(0xfffffffffffffffdL, 0xfffffffffffffffeL)); - assertEquals(281479271743488L, UnsignedLongs.divide(0xfffffffffffffffeL, 65535)); - assertEquals(0x7fffffffffffffffL, UnsignedLongs.divide(0xfffffffffffffffeL, 2)); - assertEquals(3689348814741910322L, UnsignedLongs.divide(0xfffffffffffffffeL, 5)); + assertThat(UnsignedLongs.divide(14, 5)).isEqualTo(2); + assertThat(UnsignedLongs.divide(0, 50)).isEqualTo(0); + assertThat(UnsignedLongs.divide(0xfffffffffffffffeL, 0xfffffffffffffffdL)).isEqualTo(1); + assertThat(UnsignedLongs.divide(0xfffffffffffffffdL, 0xfffffffffffffffeL)).isEqualTo(0); + assertThat(UnsignedLongs.divide(0xfffffffffffffffeL, 65535)).isEqualTo(281479271743488L); + assertThat(UnsignedLongs.divide(0xfffffffffffffffeL, 2)).isEqualTo(0x7fffffffffffffffL); + assertThat(UnsignedLongs.divide(0xfffffffffffffffeL, 5)).isEqualTo(3689348814741910322L); } public void testRemainder() { - assertEquals(4, UnsignedLongs.remainder(14, 5)); - assertEquals(0, UnsignedLongs.remainder(0, 50)); - assertEquals(1, UnsignedLongs.remainder(0xfffffffffffffffeL, 0xfffffffffffffffdL)); - assertEquals( - 0xfffffffffffffffdL, UnsignedLongs.remainder(0xfffffffffffffffdL, 0xfffffffffffffffeL)); - assertEquals(65534L, UnsignedLongs.remainder(0xfffffffffffffffeL, 65535)); - assertEquals(0, UnsignedLongs.remainder(0xfffffffffffffffeL, 2)); - assertEquals(4, UnsignedLongs.remainder(0xfffffffffffffffeL, 5)); + assertThat(UnsignedLongs.remainder(14, 5)).isEqualTo(4); + assertThat(UnsignedLongs.remainder(0, 50)).isEqualTo(0); + assertThat(UnsignedLongs.remainder(0xfffffffffffffffeL, 0xfffffffffffffffdL)).isEqualTo(1); + assertThat(UnsignedLongs.remainder(0xfffffffffffffffdL, 0xfffffffffffffffeL)) + .isEqualTo(0xfffffffffffffffdL); + assertThat(UnsignedLongs.remainder(0xfffffffffffffffeL, 65535)).isEqualTo(65534L); + assertThat(UnsignedLongs.remainder(0xfffffffffffffffeL, 2)).isEqualTo(0); + assertThat(UnsignedLongs.remainder(0xfffffffffffffffeL, 5)).isEqualTo(4); } @GwtIncompatible // Too slow in GWT (~3min fully optimized) @@ -201,126 +196,98 @@ public void testDivideRemainderEuclideanProperty() { long dividend = r.nextLong(); long divisor = r.nextLong(); // Test that the Euclidean property is preserved: - assertEquals( - 0, - dividend - - (divisor * UnsignedLongs.divide(dividend, divisor) - + UnsignedLongs.remainder(dividend, divisor))); + assertThat( + dividend + - (divisor * UnsignedLongs.divide(dividend, divisor) + + UnsignedLongs.remainder(dividend, divisor))) + .isEqualTo(0); } } public void testParseLong() { - assertEquals(0xffffffffffffffffL, UnsignedLongs.parseUnsignedLong("18446744073709551615")); - assertEquals(0x7fffffffffffffffL, UnsignedLongs.parseUnsignedLong("9223372036854775807")); - assertEquals(0xff1a618b7f65ea12L, UnsignedLongs.parseUnsignedLong("18382112080831834642")); - assertEquals(0x5a4316b8c153ac4dL, UnsignedLongs.parseUnsignedLong("6504067269626408013")); - assertEquals(0x6cf78a4b139a4e2aL, UnsignedLongs.parseUnsignedLong("7851896530399809066")); + assertThat(UnsignedLongs.parseUnsignedLong("18446744073709551615")) + .isEqualTo(0xffffffffffffffffL); + assertThat(UnsignedLongs.parseUnsignedLong("9223372036854775807")) + .isEqualTo(0x7fffffffffffffffL); + assertThat(UnsignedLongs.parseUnsignedLong("18382112080831834642")) + .isEqualTo(0xff1a618b7f65ea12L); + assertThat(UnsignedLongs.parseUnsignedLong("6504067269626408013")) + .isEqualTo(0x5a4316b8c153ac4dL); + assertThat(UnsignedLongs.parseUnsignedLong("7851896530399809066")) + .isEqualTo(0x6cf78a4b139a4e2aL); } public void testParseLongEmptyString() { - try { - UnsignedLongs.parseUnsignedLong(""); - fail("NumberFormatException should have been raised."); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.parseUnsignedLong("")); } public void testParseLongFails() { - try { - // One more than maximum value - UnsignedLongs.parseUnsignedLong("18446744073709551616"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, () -> UnsignedLongs.parseUnsignedLong("18446744073709551616")); } public void testDecodeLong() { - assertEquals(0xffffffffffffffffL, UnsignedLongs.decode("0xffffffffffffffff")); - assertEquals(01234567, UnsignedLongs.decode("01234567")); // octal - assertEquals(0x1234567890abcdefL, UnsignedLongs.decode("#1234567890abcdef")); - assertEquals(987654321012345678L, UnsignedLongs.decode("987654321012345678")); - assertEquals(0x135791357913579L, UnsignedLongs.decode("0x135791357913579")); - assertEquals(0x135791357913579L, UnsignedLongs.decode("0X135791357913579")); - assertEquals(0L, UnsignedLongs.decode("0")); + assertThat(UnsignedLongs.decode("0xffffffffffffffff")).isEqualTo(0xffffffffffffffffL); + assertThat(UnsignedLongs.decode("01234567")).isEqualTo(01234567); // octal + assertThat(UnsignedLongs.decode("#1234567890abcdef")).isEqualTo(0x1234567890abcdefL); + assertThat(UnsignedLongs.decode("987654321012345678")).isEqualTo(987654321012345678L); + assertThat(UnsignedLongs.decode("0x135791357913579")).isEqualTo(0x135791357913579L); + assertThat(UnsignedLongs.decode("0X135791357913579")).isEqualTo(0x135791357913579L); + assertThat(UnsignedLongs.decode("0")).isEqualTo(0L); } public void testDecodeLongFails() { - try { - // One more than maximum value - UnsignedLongs.decode("0xfffffffffffffffff"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.decode("0xfffffffffffffffff")); - try { - UnsignedLongs.decode("-5"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.decode("-5")); - try { - UnsignedLongs.decode("-0x5"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.decode("-0x5")); - try { - UnsignedLongs.decode("-05"); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.decode("-05")); } public void testParseLongWithRadix() { - assertEquals(0xffffffffffffffffL, UnsignedLongs.parseUnsignedLong("ffffffffffffffff", 16)); - assertEquals(0x1234567890abcdefL, UnsignedLongs.parseUnsignedLong("1234567890abcdef", 16)); + assertThat(UnsignedLongs.parseUnsignedLong("ffffffffffffffff", 16)) + .isEqualTo(0xffffffffffffffffL); + assertThat(UnsignedLongs.parseUnsignedLong("1234567890abcdef", 16)) + .isEqualTo(0x1234567890abcdefL); } public void testParseLongWithRadixLimits() { BigInteger max = BigInteger.ZERO.setBit(64).subtract(ONE); // loops through all legal radix values. - for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) { + for (int r = Character.MIN_RADIX; r <= Character.MAX_RADIX; r++) { + final int radix = r; // tests can successfully parse a number string with this radix. String maxAsString = max.toString(radix); - assertEquals(max.longValue(), UnsignedLongs.parseUnsignedLong(maxAsString, radix)); - - try { - // tests that we get exception whre an overflow would occur. - BigInteger overflow = max.add(ONE); - String overflowAsString = overflow.toString(radix); - UnsignedLongs.parseUnsignedLong(overflowAsString, radix); - fail(); - } catch (NumberFormatException expected) { - } + assertThat(UnsignedLongs.parseUnsignedLong(maxAsString, radix)).isEqualTo(max.longValue()); + + assertThrows( + NumberFormatException.class, + () -> { + BigInteger overflow = max.add(ONE); + String overflowAsString = overflow.toString(radix); + UnsignedLongs.parseUnsignedLong(overflowAsString, radix); + }); } - try { - UnsignedLongs.parseUnsignedLong("1234567890abcdef1", 16); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, + () -> UnsignedLongs.parseUnsignedLong("1234567890abcdef1", 16)); } public void testParseLongThrowsExceptionForInvalidRadix() { // Valid radix values are Character.MIN_RADIX to Character.MAX_RADIX, inclusive. - try { - UnsignedLongs.parseUnsignedLong("0", Character.MIN_RADIX - 1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, + () -> UnsignedLongs.parseUnsignedLong("0", Character.MIN_RADIX - 1)); - try { - UnsignedLongs.parseUnsignedLong("0", Character.MAX_RADIX + 1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows( + NumberFormatException.class, + () -> UnsignedLongs.parseUnsignedLong("0", Character.MAX_RADIX + 1)); // The radix is used as an array index, so try a negative value. - try { - UnsignedLongs.parseUnsignedLong("0", -1); - fail(); - } catch (NumberFormatException expected) { - } + assertThrows(NumberFormatException.class, () -> UnsignedLongs.parseUnsignedLong("0", -1)); } public void testToString() { @@ -337,22 +304,23 @@ public void testToString() { for (String x : tests) { BigInteger xValue = new BigInteger(x, 16); long xLong = xValue.longValue(); // signed - assertEquals(xValue.toString(base), UnsignedLongs.toString(xLong, base)); + assertThat(UnsignedLongs.toString(xLong, base)).isEqualTo(xValue.toString(base)); } } } public void testJoin() { - assertEquals("", UnsignedLongs.join(",")); - assertEquals("1", UnsignedLongs.join(",", 1)); - assertEquals("1,2", UnsignedLongs.join(",", 1, 2)); - assertEquals( - "18446744073709551615,9223372036854775808", UnsignedLongs.join(",", -1, Long.MIN_VALUE)); - assertEquals("123", UnsignedLongs.join("", 1, 2, 3)); - assertEquals( - "184467440737095516159223372036854775808", UnsignedLongs.join("", -1, Long.MIN_VALUE)); + assertThat(UnsignedLongs.join(",")).isEmpty(); + assertThat(UnsignedLongs.join(",", 1)).isEqualTo("1"); + assertThat(UnsignedLongs.join(",", 1, 2)).isEqualTo("1,2"); + assertThat(UnsignedLongs.join(",", -1, Long.MIN_VALUE)) + .isEqualTo("18446744073709551615,9223372036854775808"); + assertThat(UnsignedLongs.join("", 1, 2, 3)).isEqualTo("123"); + assertThat(UnsignedLongs.join("", -1, Long.MIN_VALUE)) + .isEqualTo("184467440737095516159223372036854775808"); } + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { new NullPointerTester().testAllPublicStaticMethods(UnsignedLongs.class); diff --git a/android/guava-tests/test/com/google/common/reflect/AbstractInvocationHandlerTest.java b/android/guava-tests/test/com/google/common/reflect/AbstractInvocationHandlerTest.java index 56b20bdfb22a..5684dda5c6e8 100644 --- a/android/guava-tests/test/com/google/common/reflect/AbstractInvocationHandlerTest.java +++ b/android/guava-tests/test/com/google/common/reflect/AbstractInvocationHandlerTest.java @@ -26,12 +26,15 @@ import java.lang.reflect.Proxy; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link AbstractInvocationHandler}. * * @author Ben Yu */ +@NullUnmarked public class AbstractInvocationHandlerTest extends TestCase { private static final ImmutableList LIST1 = ImmutableList.of("one", "two"); @@ -52,10 +55,6 @@ interface A {} interface B {} public void testEquals() { - class AB implements A, B {} - class BA implements B, A {} - AB ab = new AB(); - BA ba = new BA(); new EqualsTester() .addEqualityGroup(newDelegatingList(LIST1)) // Actually, this violates List#equals contract. @@ -136,7 +135,7 @@ private static class DelegatingInvocationHandlerWithEquals extends DelegatingInv } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof DelegatingInvocationHandlerWithEquals) { DelegatingInvocationHandlerWithEquals that = (DelegatingInvocationHandlerWithEquals) obj; return delegate.equals(that.delegate); diff --git a/android/guava-tests/test/com/google/common/reflect/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/reflect/AndroidIncompatible.java index 7dae25d1c8cd..010e1a178416 100644 --- a/android/guava-tests/test/com/google/common/reflect/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/reflect/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java b/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java index d199d1f90cb0..7f3ca3631eec 100644 --- a/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java +++ b/android/guava-tests/test/com/google/common/reflect/ClassPathTest.java @@ -15,10 +15,11 @@ */ package com.google.common.reflect; -import static com.google.common.base.Charsets.US_ASCII; import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; +import static com.google.common.base.StandardSystemProperty.OS_NAME; import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.US_ASCII; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; @@ -33,24 +34,23 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; -import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; -import java.security.Permission; -import java.security.PermissionCollection; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.logging.Logger; import java.util.zip.ZipEntry; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; import org.junit.Test; /** Functional tests of {@link ClassPath}. */ +@NullUnmarked public class ClassPathTest extends TestCase { private static final Logger log = Logger.getLogger(ClassPathTest.class.getName()); private static final File FILE = new File("."); @@ -73,7 +73,7 @@ public void testClassPathEntries_emptyURLClassLoader_noParent() { } @AndroidIncompatible // Android forbids null parent ClassLoader - public void testClassPathEntries_URLClassLoader_noParent() throws Exception { + public void testClassPathEntries_urlClassLoader_noParent() throws Exception { URL url1 = new URL("file:/a"); URL url2 = new URL("file:/b"); URLClassLoader classloader = new URLClassLoader(new URL[] {url1, url2}, null); @@ -82,7 +82,7 @@ public void testClassPathEntries_URLClassLoader_noParent() throws Exception { } @AndroidIncompatible // Android forbids null parent ClassLoader - public void testClassPathEntries_URLClassLoader_withParent() throws Exception { + public void testClassPathEntries_urlClassLoader_withParent() throws Exception { URL url1 = new URL("file:/a"); URL url2 = new URL("file:/b"); URLClassLoader parent = new URLClassLoader(new URL[] {url1}, null); @@ -134,7 +134,7 @@ public void testClassPathEntries_notURLClassLoader_withGrandParent() throws Exce @AndroidIncompatible // Android forbids null parent ClassLoader // https://github.com/google/guava/issues/2152 - public void testClassPathEntries_URLClassLoader_pathWithSpace() throws Exception { + public void testClassPathEntries_urlClassLoader_pathWithSpace() throws Exception { URL url = new URL("file:///c:/Documents and Settings/"); URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null); assertThat(ClassPath.getClassPathEntries(classloader)) @@ -143,7 +143,7 @@ public void testClassPathEntries_URLClassLoader_pathWithSpace() throws Exception @AndroidIncompatible // Android forbids null parent ClassLoader // https://github.com/google/guava/issues/2152 - public void testClassPathEntries_URLClassLoader_pathWithEscapedSpace() throws Exception { + public void testClassPathEntries_urlClassLoader_pathWithEscapedSpace() throws Exception { URL url = new URL("file:///c:/Documents%20and%20Settings/"); URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null); assertThat(ClassPath.getClassPathEntries(classloader)) @@ -160,7 +160,7 @@ public void testToFile() throws Exception { // https://github.com/google/guava/issues/2152 @AndroidIncompatible // works in newer Android versions but fails at the version we test with - public void testToFile_AndroidIncompatible() throws Exception { + public void testToFile_androidIncompatible() throws Exception { assertThat(ClassPath.toFile(new URL("file:///c:\\Documents ~ Settings, or not\\11-12 12:05"))) .isEqualTo(new File("/c:\\Documents ~ Settings, or not\\11-12 12:05")); assertThat(ClassPath.toFile(new URL("file:///C:\\Program Files\\Apache Software Foundation"))) @@ -178,6 +178,7 @@ public void testJarFileWithSpaces() throws Exception { assertThat(ClassPath.from(classloader).getTopLevelClasses()).isNotEmpty(); } + @AndroidIncompatible // ClassPath is documented as not supporting Android public void testScan_classPathCycle() throws IOException { File jarFile = File.createTempFile("with_circular_class_path", ".jar"); @@ -201,6 +202,7 @@ public void testScanFromFile_fileNotExists() throws IOException { .isEmpty(); } + @AndroidIncompatible // ClassPath is documented as not supporting Android public void testScanFromFile_notJarFile() throws IOException { ClassLoader classLoader = ClassPathTest.class.getClassLoader(); @@ -213,6 +215,9 @@ public void testScanFromFile_notJarFile() throws IOException { } public void testGetClassPathEntry() throws MalformedURLException, URISyntaxException { + if (isWindows()) { + return; // TODO: b/136041958 - We need to account for drive letters in the path. + } assertEquals( new File("/usr/test/dep.jar").toURI(), ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "file:/usr/test/dep.jar") @@ -283,6 +288,9 @@ public void testGetClassPathFromManifest_jarInCurrentDirectory() throws IOExcept } public void testGetClassPathFromManifest_absoluteDirectory() throws IOException { + if (isWindows()) { + return; // TODO: b/136041958 - We need to account for drive letters in the path. + } File jarFile = new File("base/some.jar"); Manifest manifest = manifestClasspath("file:/with/absolute/dir"); assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest)) @@ -290,6 +298,9 @@ public void testGetClassPathFromManifest_absoluteDirectory() throws IOException } public void testGetClassPathFromManifest_absoluteJar() throws IOException { + if (isWindows()) { + return; // TODO: b/136041958 - We need to account for drive letters in the path. + } File jarFile = new File("base/some.jar"); Manifest manifest = manifestClasspath("file:/with/absolute.jar"); assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest)) @@ -297,6 +308,9 @@ public void testGetClassPathFromManifest_absoluteJar() throws IOException { } public void testGetClassPathFromManifest_multiplePaths() throws IOException { + if (isWindows()) { + return; // TODO: b/136041958 - We need to account for drive letters in the path. + } File jarFile = new File("base/some.jar"); Manifest manifest = manifestClasspath("file:/with/absolute.jar relative.jar relative/dir"); assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest)) @@ -352,7 +366,11 @@ public void testGetPackageName() { // Test that ResourceInfo.urls() returns identical content to ClassLoader.getResources() + @AndroidIncompatible public void testGetClassPathUrls() throws Exception { + if (isWindows()) { + return; // TODO: b/136041958 - We need to account for drive letters in the path. + } String oldPathSeparator = PATH_SEPARATOR.value(); String oldClassPath = JAVA_CLASS_PATH.value(); System.setProperty(PATH_SEPARATOR.key(), ":"); @@ -399,6 +417,7 @@ public void testNulls() throws IOException { .testAllPublicInstanceMethods(ClassPath.from(getClass().getClassLoader())); } + @AndroidIncompatible // ClassPath is documented as not supporting Android public void testLocationsFrom_idempotentScan() throws IOException { ImmutableSet locations = @@ -429,56 +448,13 @@ public void testLocationEquals() { .testEquals(); } + @AndroidIncompatible // ClassPath is documented as not supporting Android public void testScanAllResources() throws IOException { assertThat(scanResourceNames(ClassLoader.getSystemClassLoader())) .contains("com/google/common/reflect/ClassPathTest.class"); } - - public void testExistsThrowsSecurityException() throws IOException, URISyntaxException { - SecurityManager oldSecurityManager = System.getSecurityManager(); - try { - doTestExistsThrowsSecurityException(); - } finally { - System.setSecurityManager(oldSecurityManager); - } - } - - private void doTestExistsThrowsSecurityException() throws IOException, URISyntaxException { - File file = null; - // In Java 9, Logger may read the TZ database. Only disallow reading the class path URLs. - final PermissionCollection readClassPathFiles = - new FilePermission("", "read").newPermissionCollection(); - for (URL url : ClassPath.parseJavaClassPath()) { - if (url.getProtocol().equalsIgnoreCase("file")) { - file = new File(url.toURI()); - readClassPathFiles.add(new FilePermission(file.getAbsolutePath(), "read")); - } - } - assertThat(file).isNotNull(); - SecurityManager disallowFilesSecurityManager = - new SecurityManager() { - @Override - public void checkPermission(Permission p) { - if (readClassPathFiles.implies(p)) { - throw new SecurityException("Disallowed: " + p); - } - } - }; - System.setSecurityManager(disallowFilesSecurityManager); - try { - file.exists(); - fail("Did not get expected SecurityException"); - } catch (SecurityException expected) { - } - ClassPath classPath = ClassPath.from(getClass().getClassLoader()); - // ClassPath may contain resources from the boot class loader; just not from the class path. - for (ResourceInfo resource : classPath.getResources()) { - assertThat(resource.getResourceName()).doesNotContain("com/google/common/reflect/"); - } - } - private static ClassPath.ClassInfo findClass( Iterable classes, Class cls) { for (ClassPath.ClassInfo classInfo : classes) { @@ -518,7 +494,7 @@ private static void writeSelfReferencingJarFile(File jarFile, String... entries) Closer closer = Closer.create(); try { FileOutputStream fileOut = closer.register(new FileOutputStream(jarFile)); - JarOutputStream jarOut = closer.register(new JarOutputStream(fileOut)); + JarOutputStream jarOut = closer.register(new JarOutputStream(fileOut, manifest)); for (String entry : entries) { jarOut.putNextEntry(new ZipEntry(entry)); Resources.copy(ClassPathTest.class.getResource(entry), jarOut); @@ -543,6 +519,10 @@ private static File fullpath(String path) { } private static URL makeJarUrlWithName(String name) throws IOException { + /* + * TODO: cpovirk - Use java.nio.file.Files.createTempDirectory instead of + * c.g.c.io.Files.createTempDir? + */ File fullPath = new File(Files.createTempDir(), name); File jarFile = pickAnyJarFile(); Files.copy(jarFile, fullPath); @@ -568,4 +548,8 @@ private static ImmutableSet scanResourceNames(ClassLoader loader) throws } return builder.build(); } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } } diff --git a/android/guava-tests/test/com/google/common/reflect/ImmutableTypeToInstanceMapTest.java b/android/guava-tests/test/com/google/common/reflect/ImmutableTypeToInstanceMapTest.java index 064c4b5c1533..56493b56e483 100644 --- a/android/guava-tests/test/com/google/common/reflect/ImmutableTypeToInstanceMapTest.java +++ b/android/guava-tests/test/com/google/common/reflect/ImmutableTypeToInstanceMapTest.java @@ -18,6 +18,7 @@ import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.testing.MapTestSuiteBuilder; @@ -32,12 +33,14 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link ImmutableTypeToInstanceMap}. * * @author Ben Yu */ +@NullUnmarked public class ImmutableTypeToInstanceMapTest extends TestCase { @AndroidIncompatible // problem with suite builders on Android @@ -51,13 +54,13 @@ public static Test suite() { // Other tests will verify what real, warning-free usage looks like // but here we have to do some serious fudging @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public Map create(Object... elements) { ImmutableTypeToInstanceMap.Builder builder = ImmutableTypeToInstanceMap.builder(); for (Object object : elements) { - Entry entry = (Entry) object; - builder.put(entry.getKey(), entry.getValue()); + Entry entry = (Entry) object; + builder.put((TypeToken) entry.getKey(), entry.getValue()); } return (Map) builder.build(); } @@ -102,7 +105,8 @@ public void testParameterizedType() { public void testGenericArrayType() { @SuppressWarnings("unchecked") // Trying to test generic array - ImmutableList[] array = new ImmutableList[] {ImmutableList.of(1)}; + ImmutableList[] array = + (ImmutableList[]) new ImmutableList[] {ImmutableList.of(1)}; TypeToken[]> type = new TypeToken[]>() {}; ImmutableTypeToInstanceMap[]> map = ImmutableTypeToInstanceMap.[]>builder().put(type, array).build(); @@ -121,33 +125,29 @@ public void testWildcardType() { public void testGetInstance_containsTypeVariable() { ImmutableTypeToInstanceMap> map = ImmutableTypeToInstanceMap.of(); - try { - map.getInstance(this.anyIterableType()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> map.getInstance(this.anyIterableType())); } public void testPut_containsTypeVariable() { ImmutableTypeToInstanceMap.Builder> builder = ImmutableTypeToInstanceMap.builder(); - try { - builder.put(this.anyIterableType(), ImmutableList.of(1)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> builder.put(this.anyIterableType(), ImmutableList.of(1))); } private TypeToken> anyIterableType() { return new TypeToken>() {}; } + @SuppressWarnings("rawtypes") // TODO(cpovirk): Can we at least use Class in some places? abstract static class TestTypeToInstanceMapGenerator implements TestMapGenerator { @Override - public TypeToken[] createKeyArray(int length) { - return new TypeToken[length]; + public TypeToken[] createKeyArray(int length) { + return new TypeToken[length]; } @Override @@ -172,7 +172,7 @@ private static Entry entry(TypeToken k, Object v) { @Override @SuppressWarnings("unchecked") public Entry[] createArray(int length) { - return new Entry[length]; + return (Entry[]) new Entry[length]; } @Override diff --git a/android/guava-tests/test/com/google/common/reflect/InvokableTest.java b/android/guava-tests/test/com/google/common/reflect/InvokableTest.java index b7ed42aa40ba..fcfcc7e92a28 100644 --- a/android/guava-tests/test/com/google/common/reflect/InvokableTest.java +++ b/android/guava-tests/test/com/google/common/reflect/InvokableTest.java @@ -17,6 +17,7 @@ package com.google.common.reflect; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -27,13 +28,15 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.TypeVariable; import java.util.Collections; -import javax.annotation.CheckForNull; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@link Invokable}. @@ -41,6 +44,7 @@ * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class InvokableTest extends TestCase { // Historically Invokable inherited from java.lang.reflect.AccessibleObject. That's no longer the // case, but we do check that its API still has the same public methods. We exclude some methods @@ -49,18 +53,12 @@ public class InvokableTest extends TestCase { public void testApiCompatibleWithAccessibleObject() { ImmutableSet invokableMethods = publicMethodSignatures(Invokable.class, ImmutableSet.of()); - ImmutableSet accesibleObjectMethods = + ImmutableSet accessibleObjectMethods = publicMethodSignatures(AccessibleObject.class, ImmutableSet.of("canAccess")); - assertThat(invokableMethods).containsAtLeastElementsIn(accesibleObjectMethods); - Class genericDeclaration; - try { - genericDeclaration = Class.forName("java.lang.reflect.GenericDeclaration"); - ImmutableSet genericDeclarationMethods = - publicMethodSignatures(genericDeclaration, ImmutableSet.of()); - assertThat(invokableMethods).containsAtLeastElementsIn(genericDeclarationMethods); - } catch (ClassNotFoundException e) { - // OK: we're on Java 7, which doesn't have this class - } + assertThat(invokableMethods).containsAtLeastElementsIn(accessibleObjectMethods); + ImmutableSet genericDeclarationMethods = + publicMethodSignatures(GenericDeclaration.class, ImmutableSet.of()); + assertThat(invokableMethods).containsAtLeastElementsIn(genericDeclarationMethods); } private static ImmutableSet publicMethodSignatures( @@ -227,7 +225,6 @@ WithConstructorAndTypeParameter() {} } public void testConstructor_returnType_hasTypeParameter() throws Exception { - @SuppressWarnings("rawtypes") // Foo.class for Foo is always raw type Class type = WithConstructorAndTypeParameter.class; @SuppressWarnings("rawtypes") // Foo.class Constructor constructor = type.getDeclaredConstructor(); @@ -285,11 +282,7 @@ public void testConstructor_returning() throws Exception { public void testConstructor_invalidReturning() throws Exception { Invokable delegate = Prepender.constructor(String.class, int.class); - try { - delegate.returning(SubPrepender.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> delegate.returning(SubPrepender.class)); } public void testStaticMethod_returnType() throws Exception { @@ -352,11 +345,9 @@ public void testStaticMethod_returningRawType() throws Exception { public void testStaticMethod_invalidReturning() throws Exception { Invokable delegate = Prepender.method("prepend", String.class, Iterable.class); - try { - delegate.returning(new TypeToken>() {}); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> delegate.returning(new TypeToken>() {})); } public void testInstanceMethod_returnType() throws Exception { @@ -414,11 +405,9 @@ public void testInstanceMethod_returningRawType() throws Exception { public void testInstanceMethod_invalidReturning() throws Exception { Invokable delegate = Prepender.method("prepend", Iterable.class); - try { - delegate.returning(new TypeToken>() {}); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> delegate.returning(new TypeToken>() {})); } public void testPrivateInstanceMethod_isOverridable() throws Exception { @@ -453,7 +442,7 @@ public void testStaticFinalMethod_isFinal() throws Exception { static class Foo {} - public void testConstructor_isOverridablel() throws Exception { + public void testConstructor_isOverridable() throws Exception { Invokable delegate = Invokable.from(Foo.class.getDeclaredConstructor()); assertFalse(delegate.isOverridable()); assertFalse(delegate.isVarArgs()); @@ -520,7 +509,7 @@ public void testInnerClassWithOneParameterConstructor() { private class InnerWithAnnotatedConstructorParameter { @SuppressWarnings("unused") // called by reflection - InnerWithAnnotatedConstructorParameter(@CheckForNull String s) {} + InnerWithAnnotatedConstructorParameter(@Nullable String s) {} } public void testInnerClassWithAnnotatedConstructorParameter() { @@ -601,7 +590,7 @@ public void run() { } public void testAnonymousClassInConstructor() { - new AnonymousClassInConstructor(); + AnonymousClassInConstructor unused = new AnonymousClassInConstructor(); } private static class AnonymousClassInConstructor { @@ -621,7 +610,7 @@ public void run() { } public void testLocalClassInInstanceInitializer() { - new LocalClassInInstanceInitializer(); + LocalClassInInstanceInitializer unused = new LocalClassInInstanceInitializer(); } private static class LocalClassInInstanceInitializer { @@ -633,7 +622,7 @@ class Local {} } public void testLocalClassInStaticInitializer() { - new LocalClassInStaticInitializer(); + LocalClassInStaticInitializer unused = new LocalClassInStaticInitializer(); } private static class LocalClassInStaticInitializer { @@ -644,8 +633,9 @@ class Local {} } } - public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_BUG() { - new LocalClassWithSeeminglyHiddenThisInStaticInitializer(); + public void testLocalClassWithSeeminglyHiddenThisInStaticInitializer_bug() { + LocalClassWithSeeminglyHiddenThisInStaticInitializer unused = + new LocalClassWithSeeminglyHiddenThisInStaticInitializer(); } /** @@ -683,7 +673,7 @@ public LocalWithOneParameterConstructor(String x) { public void testLocalClassWithAnnotatedConstructorParameter() throws Exception { class LocalWithAnnotatedConstructorParameter { @SuppressWarnings("unused") // called by reflection - LocalWithAnnotatedConstructorParameter(@CheckForNull String s) {} + LocalWithAnnotatedConstructorParameter(@Nullable String s) {} } Constructor constructor = LocalWithAnnotatedConstructorParameter.class.getDeclaredConstructors()[0]; @@ -732,7 +722,7 @@ private static class Prepender { private final String prefix; private final int times; - Prepender(@NotBlank String prefix, int times) throws NullPointerException { + Prepender(@NotBlank @Nullable String prefix, int times) throws NullPointerException { this.prefix = prefix; this.times = times; } diff --git a/android/guava-tests/test/com/google/common/reflect/MutableTypeToInstanceMapTest.java b/android/guava-tests/test/com/google/common/reflect/MutableTypeToInstanceMapTest.java index cea81e3a2832..42dc61a9f2db 100644 --- a/android/guava-tests/test/com/google/common/reflect/MutableTypeToInstanceMapTest.java +++ b/android/guava-tests/test/com/google/common/reflect/MutableTypeToInstanceMapTest.java @@ -18,6 +18,7 @@ import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -31,12 +32,14 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Unit test of {@link MutableTypeToInstanceMap}. * * @author Ben Yu */ +@NullUnmarked public class MutableTypeToInstanceMapTest extends TestCase { @AndroidIncompatible // problem with suite builders on Android @@ -50,7 +53,7 @@ public static Test suite() { // Other tests will verify what real, warning-free usage looks like // but here we have to do some serious fudging @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public Map create(Object... elements) { MutableTypeToInstanceMap map = new MutableTypeToInstanceMap<>(); for (Object object : elements) { @@ -81,62 +84,47 @@ protected void setUp() throws Exception { } public void testPutThrows() { - try { - map.put(TypeToken.of(Integer.class), new Integer(5)); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> map.put(TypeToken.of(Integer.class), Integer.valueOf(5))); } public void testPutAllThrows() { - try { - map.putAll(ImmutableMap.of(TypeToken.of(Integer.class), new Integer(5))); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, + () -> map.putAll(ImmutableMap.of(TypeToken.of(Integer.class), Integer.valueOf(5)))); } public void testEntrySetMutationThrows() { map.putInstance(String.class, "test"); assertEquals(TypeToken.of(String.class), map.entrySet().iterator().next().getKey()); assertEquals("test", map.entrySet().iterator().next().getValue()); - try { - map.entrySet().iterator().next().setValue(1); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows( + UnsupportedOperationException.class, () -> map.entrySet().iterator().next().setValue(1)); } public void testEntrySetToArrayMutationThrows() { map.putInstance(String.class, "test"); @SuppressWarnings("unchecked") // Should get a CCE later if cast is wrong - Entry entry = (Entry) map.entrySet().toArray()[0]; + Entry entry = (Entry) map.entrySet().toArray()[0]; assertEquals(TypeToken.of(String.class), entry.getKey()); assertEquals("test", entry.getValue()); - try { - entry.setValue(1); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entry.setValue(1)); } public void testEntrySetToTypedArrayMutationThrows() { map.putInstance(String.class, "test"); @SuppressWarnings("unchecked") // Should get a CCE later if cast is wrong - Entry entry = map.entrySet().toArray(new Entry[0])[0]; + Entry entry = (Entry) map.entrySet().toArray(new Entry[0])[0]; assertEquals(TypeToken.of(String.class), entry.getKey()); assertEquals("test", entry.getValue()); - try { - entry.setValue(1); - fail(); - } catch (UnsupportedOperationException expected) { - } + assertThrows(UnsupportedOperationException.class, () -> entry.setValue(1)); } public void testPutAndGetInstance() { - assertNull(map.putInstance(Integer.class, new Integer(5))); + assertNull(map.putInstance(Integer.class, Integer.valueOf(5))); - Integer oldValue = map.putInstance(Integer.class, new Integer(7)); + Integer oldValue = map.putInstance(Integer.class, Integer.valueOf(7)); assertEquals(5, (int) oldValue); Integer newValue = map.getInstance(Integer.class); @@ -147,11 +135,9 @@ public void testPutAndGetInstance() { } public void testNull() { - try { - map.putInstance((TypeToken) null, new Integer(1)); - fail(); - } catch (NullPointerException expected) { - } + assertThrows( + NullPointerException.class, + () -> map.putInstance((TypeToken) null, Integer.valueOf(1))); map.putInstance(Integer.class, null); assertTrue(map.containsKey(TypeToken.of(Integer.class))); assertTrue(map.entrySet().contains(immutableEntry(TypeToken.of(Integer.class), null))); @@ -193,7 +179,8 @@ public void testParameterizedType() { public void testGenericArrayType() { @SuppressWarnings("unchecked") // Trying to test generic array - ImmutableList[] array = new ImmutableList[] {ImmutableList.of(1)}; + ImmutableList[] array = + (ImmutableList[]) new ImmutableList[] {ImmutableList.of(1)}; TypeToken[]> type = new TypeToken[]>() {}; map.putInstance(type, array); assertEquals(1, map.size()); @@ -208,19 +195,14 @@ public void testWildcardType() { } public void testGetInstance_withTypeVariable() { - try { - map.getInstance(this.anyIterableType()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> map.getInstance(this.anyIterableType())); } public void testPutInstance_withTypeVariable() { - try { - map.putInstance(this.anyIterableType(), ImmutableList.of(1)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> map.putInstance(this.anyIterableType(), ImmutableList.of(1))); } private TypeToken> anyIterableType() { diff --git a/android/guava-tests/test/com/google/common/reflect/PackageSanityTests.java b/android/guava-tests/test/com/google/common/reflect/PackageSanityTests.java index ba11fe80c778..9d3a4a2cf3b6 100644 --- a/android/guava-tests/test/com/google/common/reflect/PackageSanityTests.java +++ b/android/guava-tests/test/com/google/common/reflect/PackageSanityTests.java @@ -17,7 +17,9 @@ package com.google.common.reflect; import com.google.common.testing.AbstractPackageSanityTests; +import org.jspecify.annotations.NullUnmarked; /** Tests nulls for the entire package. */ +@NullUnmarked public class PackageSanityTests extends AbstractPackageSanityTests {} diff --git a/android/guava-tests/test/com/google/common/reflect/ParameterTest.java b/android/guava-tests/test/com/google/common/reflect/ParameterTest.java index 6e0500a9ce08..890ae32a0313 100644 --- a/android/guava-tests/test/com/google/common/reflect/ParameterTest.java +++ b/android/guava-tests/test/com/google/common/reflect/ParameterTest.java @@ -20,15 +20,27 @@ import com.google.common.testing.NullPointerTester; import java.lang.reflect.Method; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Parameter}. * * @author Ben Yu */ +@NullUnmarked public class ParameterTest extends TestCase { public void testNulls() { + try { + Class.forName("java.lang.reflect.AnnotatedType"); + } catch (ClassNotFoundException runningInAndroidVm) { + /* + * Parameter declares a method that returns AnnotatedType, which isn't available on Android. + * This would cause NullPointerTester, which calls Class.getDeclaredMethods, to throw + * NoClassDefFoundError. + */ + return; + } for (Method method : ParameterTest.class.getDeclaredMethods()) { for (Parameter param : Invokable.from(method).getParameters()) { new NullPointerTester().testAllPublicInstanceMethods(param); diff --git a/android/guava-tests/test/com/google/common/reflect/ReflectionTest.java b/android/guava-tests/test/com/google/common/reflect/ReflectionTest.java index 2885f895661f..6a0a7ce34b18 100644 --- a/android/guava-tests/test/com/google/common/reflect/ReflectionTest.java +++ b/android/guava-tests/test/com/google/common/reflect/ReflectionTest.java @@ -16,13 +16,17 @@ package com.google.common.reflect; +import static org.junit.Assert.assertThrows; + import com.google.common.testing.NullPointerTester; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for {@link Reflection} */ +@NullUnmarked public class ReflectionTest extends TestCase { public void testGetPackageName() throws Exception { @@ -39,11 +43,8 @@ public void testNewProxy() throws Exception { } public void testNewProxyCantWorkOnAClass() throws Exception { - try { - Reflection.newProxy(Object.class, X_RETURNER); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> Reflection.newProxy(Object.class, X_RETURNER)); } private static final InvocationHandler X_RETURNER = diff --git a/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java b/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java index 3eec668433fb..eda28b5ec790 100644 --- a/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java +++ b/android/guava-tests/test/com/google/common/reflect/SubtypeTester.java @@ -30,6 +30,8 @@ import java.util.Arrays; import java.util.Comparator; import javax.lang.model.element.Modifier; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tester of subtyping relationships between two types. @@ -64,6 +66,7 @@ *

    The declaration methods must be public. */ @AndroidIncompatible // only used by android incompatible tests. +@NullUnmarked abstract class SubtypeTester implements Cloneable { /** Annotates a public method that declares subtype assertion. */ @@ -78,7 +81,7 @@ abstract class SubtypeTester implements Cloneable { boolean suppressGetSupertype() default false; } - private Method method = null; + private @Nullable Method method = null; /** Call this in a {@link TestSubtype} public method asserting subtype relationship. */ final T isSubtype(T sub) { @@ -105,7 +108,7 @@ final T isSubtype(T sub) { * Call this in a {@link TestSubtype} public method asserting that subtype relationship does not * hold. */ - final X notSubtype(@SuppressWarnings("unused") Object sub) { + final @Nullable X notSubtype(@SuppressWarnings("unused") Object sub) { Type returnType = method.getGenericReturnType(); Type paramType = getOnlyParameterType(); TestSubtype spec = method.getAnnotation(TestSubtype.class); diff --git a/android/guava-tests/test/com/google/common/reflect/TypeParameterTest.java b/android/guava-tests/test/com/google/common/reflect/TypeParameterTest.java index b83e48596c0a..e1d2cb021c50 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeParameterTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeParameterTest.java @@ -16,17 +16,21 @@ package com.google.common.reflect; +import static org.junit.Assert.assertThrows; + import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link TypeParameter}. * * @author Ben Yu */ +@NullUnmarked public class TypeParameterTest extends TestCase { public void testCaptureTypeParameter() throws Exception { @@ -38,11 +42,7 @@ public void testCaptureTypeParameter() throws Exception { } public void testConcreteTypeRejected() { - try { - new TypeParameter() {}; - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> new TypeParameter() {}); } public void testEquals() throws Exception { diff --git a/android/guava-tests/test/com/google/common/reflect/TypeResolverTest.java b/android/guava-tests/test/com/google/common/reflect/TypeResolverTest.java index e970a8d99a36..464eebdd466c 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeResolverTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeResolverTest.java @@ -16,11 +16,14 @@ package com.google.common.reflect; +import static org.junit.Assert.assertThrows; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests of {@link TypeResolver}. @@ -28,6 +31,7 @@ * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class TypeResolverTest extends TestCase { public void testWhere_noMapping() { @@ -80,11 +84,7 @@ public void testWhere_wildcardSelfMapping() { public void testWhere_duplicateMapping() { Type t = aTypeVariable(); TypeResolver resolver = new TypeResolver().where(t, String.class); - try { - resolver.where(t, String.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> resolver.where(t, String.class)); } public > void testWhere_recursiveMapping() { @@ -153,87 +153,77 @@ public void testWhere_wildcardTypeMapping() { } public void testWhere_incompatibleGenericArrayMapping() { - try { - new TypeResolver().where(new TypeCapture() {}.capture(), String.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> new TypeResolver().where(new TypeCapture() {}.capture(), String.class)); } public void testWhere_incompatibleParameterizedTypeMapping() { - try { - new TypeResolver().where(new TypeCapture>() {}.capture(), List.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> new TypeResolver().where(new TypeCapture>() {}.capture(), List.class)); } public void testWhere_impossibleParameterizedTypeMapping() { - try { - new TypeResolver() - .where( - new TypeCapture>() {}.capture(), - new TypeCapture>() {}.capture()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where( + new TypeCapture>() {}.capture(), + new TypeCapture>() {}.capture())); } public void testWhere_incompatibleWildcardUpperBound() { - try { - new TypeResolver() - .where( - new TypeCapture>() {}.capture(), - new TypeCapture>() {}.capture()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where( + new TypeCapture>() {}.capture(), + new TypeCapture>() {}.capture())); } public void testWhere_incompatibleWildcardLowerBound() { - try { - new TypeResolver() - .where( - new TypeCapture>() {}.capture(), - new TypeCapture>() {}.capture()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where( + new TypeCapture>() {}.capture(), + new TypeCapture>() {}.capture())); } public void testWhere_incompatibleWildcardBounds() { - try { - new TypeResolver() - .where( - new TypeCapture>() {}.capture(), - new TypeCapture>() {}.capture()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where( + new TypeCapture>() {}.capture(), + new TypeCapture>() {}.capture())); } public void testWhere_wrongOrder() { - try { - new TypeResolver().where(String.class, aTypeVariable()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> new TypeResolver().where(String.class, aTypeVariable())); } public void testWhere_mapFromConcreteParameterizedType() { - try { - new TypeResolver().where(new TypeCapture>() {}.capture(), aTypeVariable()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where(new TypeCapture>() {}.capture(), aTypeVariable())); } public void testWhere_mapFromConcreteGenericArrayType() { - try { - new TypeResolver().where(new TypeCapture>() {}.capture(), aTypeVariable()); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + new TypeResolver() + .where(new TypeCapture>() {}.capture(), aTypeVariable())); } public void testWhere_actualArgHasWildcard() { diff --git a/android/guava-tests/test/com/google/common/reflect/TypeTokenResolutionTest.java b/android/guava-tests/test/com/google/common/reflect/TypeTokenResolutionTest.java index 69e59ff18895..72e452beee43 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeTokenResolutionTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeTokenResolutionTest.java @@ -17,6 +17,7 @@ package com.google.common.reflect; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.base.Predicate; import com.google.common.base.Supplier; @@ -29,6 +30,7 @@ import java.util.List; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link TypeToken} and {@link TypeResolver}. @@ -36,6 +38,7 @@ * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class TypeTokenResolutionTest extends TestCase { private static class Foo { @@ -178,10 +181,12 @@ public void testResolveNestedClass() { assertEquals(String.class, new Owner.Nested() {}.getTypeArgument()); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testResolveInnerClass() { assertEquals(String.class, new Owner().new Inner() {}.getTypeArgument()); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testResolveOwnerClass() { assertEquals(Integer.class, new Owner().new Inner() {}.getOwnerType()); } @@ -246,14 +251,10 @@ public void testResolveType() { TypeToken.of(StringIterable.class) .resolveType(Iterable.class.getTypeParameters()[0]) .getType()); - try { - TypeToken.of(this.getClass()).resolveType(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> TypeToken.of(this.getClass()).resolveType(null)); } - public void testConextIsParameterizedType() throws Exception { + public void testContextIsParameterizedType() throws Exception { class Context { @SuppressWarnings("unused") // used by reflection Map returningMap() { diff --git a/android/guava-tests/test/com/google/common/reflect/TypeTokenSubtypeTest.java b/android/guava-tests/test/com/google/common/reflect/TypeTokenSubtypeTest.java index 0dd2318e9776..542d77f61df5 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeTokenSubtypeTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeTokenSubtypeTest.java @@ -17,13 +17,16 @@ package com.google.common.reflect; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import java.io.Serializable; import java.util.Comparator; import java.util.List; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class TypeTokenSubtypeTest extends TestCase { public void testOwnerTypeSubtypes() throws Exception { @@ -39,38 +42,41 @@ public void testWildcardSubtypes() throws Exception { * recursively bounded. */ public void testRecursiveWildcardSubtypeBug() throws Exception { - try { - new RecursiveTypeBoundBugExample<>().testAllDeclarations(); - fail(); - } catch (Exception e) { - assertThat(e).hasCauseThat().isInstanceOf(AssertionError.class); - } + Exception e = + assertThrows( + Exception.class, () -> new RecursiveTypeBoundBugExample<>().testAllDeclarations()); + assertThat(e).hasCauseThat().isInstanceOf(AssertionError.class); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testSubtypeOfInnerClass_nonStaticAnonymousClass() { TypeToken supertype = new TypeToken.Shop>() {}; Class subclass = new Mall().new Shop() {}.getClass(); assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype)); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfOwnerTypeNotMatch() { TypeToken supertype = new TypeToken.Shop>() {}; Class subclass = new Mall().new Shop() {}.getClass(); assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype)); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfInnerTypeNotMatch() { TypeToken supertype = new TypeToken.Shop>() {}; Class subclass = new Mall().new Shop() {}.getClass(); assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype)); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public static void testSubtypeOfInnerClass_staticAnonymousClass() { TypeToken supertype = new TypeToken.Shop>() {}; Class subclass = new Mall().new Shop() {}.getClass(); assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype)); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public static void testSubtypeOfStaticAnonymousClass() { Class superclass = new Mall().new Shop() {}.getClass(); assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass)); @@ -79,6 +85,7 @@ public static void testSubtypeOfStaticAnonymousClass() { .isSubtypeOf(superclass)); } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testSubtypeOfNonStaticAnonymousClass() { Class superclass = new Mall().new Shop() {}.getClass(); assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass)); @@ -90,11 +97,7 @@ public void testSubtypeOfNonStaticAnonymousClass() { public void testGetSubtypeOf_impossibleWildcard() { TypeToken> numberList = new TypeToken>() {}; abstract class StringList implements List {} - try { - numberList.getSubtype(StringList.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> numberList.getSubtype(StringList.class)); } private static class OwnerTypeSubtypingTests extends SubtypeTester { @@ -231,7 +234,7 @@ private static class RecursiveTypeBoundBugExample> ifYouUseTheTypeVariableOnTheClassAndItIsRecursive( List>> arg) { - return notSubtype(arg); // isSubtype() currently incorectly considers it a subtype. + return notSubtype(arg); // isSubtype() currently incorrectly considers it a subtype. } } diff --git a/android/guava-tests/test/com/google/common/reflect/TypeTokenTest.java b/android/guava-tests/test/com/google/common/reflect/TypeTokenTest.java index 171ef49fdb80..cfaf9f6ffdd1 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeTokenTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeTokenTest.java @@ -17,6 +17,7 @@ package com.google.common.reflect; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; @@ -44,6 +45,7 @@ import java.util.List; import java.util.Map; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test cases for {@link TypeToken}. @@ -52,6 +54,7 @@ * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class TypeTokenTest extends TestCase { private abstract static class StringList implements List {} @@ -64,11 +67,18 @@ public void testValueEqualityNotInstanceEquality() { assertEquals(a, b); } + @SuppressWarnings("TestExceptionChecker") // see comment below public void testVariableTypeTokenNotAllowed() { + /* + * We'd use assertThrows here, but that causes no exception to be thrown under Java 8, + * presumably because the ThrowingRunnable lambda triggers some kind of bug in Java 8's + * reflection implementation. + */ try { new TypeToken() {}; fail(); } catch (IllegalStateException expected) { + // Type variables aren't allowed. } } @@ -402,7 +412,7 @@ public void testGetGenericSuperclass_typeVariable_unbounded() { assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); } - public & CharSequence> + public & Serializable> void testGetGenericSuperclass_typeVariable_boundIsClass() { assertEquals( new TypeToken>() {}, @@ -410,7 +420,7 @@ void testGetGenericSuperclass_typeVariable_boundIsClass() { assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); } - public & CharSequence> + public & Serializable> void testGetGenericSuperclass_typeVariable_boundIsFBoundedClass() { assertEquals( new TypeToken>() {}, @@ -418,13 +428,13 @@ void testGetGenericSuperclass_typeVariable_boundIsFBoundedClass() { assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); } - public & CharSequence> + public & Serializable> void testGetGenericSuperclass_typeVariable_boundIsInterface() { assertNull(TypeToken.of(new TypeCapture() {}.capture()).getGenericSuperclass()); assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); } - public & CharSequence, T1 extends T> + public & Serializable, T1 extends T> void testGetGenericSuperclass_typeVariable_boundIsTypeVariableAndClass() { assertEquals( TypeToken.of(new TypeCapture() {}.capture()), @@ -432,7 +442,7 @@ void testGetGenericSuperclass_typeVariable_boundIsTypeVariableAndClass() { assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); } - public & CharSequence, T1 extends T> + public & Serializable, T1 extends T> void testGetGenericSuperclass_typeVariable_boundIsTypeVariableAndInterface() { assertNull(TypeToken.of(new TypeCapture() {}.capture()).getGenericSuperclass()); assertEquals(TypeToken.of(Object.class), new TypeToken() {}.getGenericSuperclass()); @@ -574,9 +584,9 @@ private abstract static class Third extends Second {} private abstract static class Fourth extends Third {} - private static class ConcreteIS extends Fourth {} + private static class ConcreteIntegerString extends Fourth {} - private static class ConcreteSI extends Fourth {} + private static class ConcreteStringInteger extends Fourth {} public void testAssignableClassToClass() { @SuppressWarnings("rawtypes") // To test TypeToken @@ -749,8 +759,8 @@ public void testAssignableClassToType() { assertFalse(tokenL.isSupertypeOf(List.class)); TypeToken> tokenF = new TypeToken>() {}; - assertTrue(tokenF.isSupertypeOf(ConcreteIS.class)); - assertFalse(tokenF.isSupertypeOf(ConcreteSI.class)); + assertTrue(tokenF.isSupertypeOf(ConcreteIntegerString.class)); + assertFalse(tokenF.isSupertypeOf(ConcreteStringInteger.class)); } public void testAssignableClassToArrayType() { @@ -765,8 +775,8 @@ public void testAssignableParameterizedTypeToType() { assertFalse(tokenL.isSupertypeOf(IntegerList.class.getGenericInterfaces()[0])); TypeToken> tokenF = new TypeToken>() {}; - assertTrue(tokenF.isSupertypeOf(ConcreteIS.class.getGenericSuperclass())); - assertFalse(tokenF.isSupertypeOf(ConcreteSI.class.getGenericSuperclass())); + assertTrue(tokenF.isSupertypeOf(ConcreteIntegerString.class.getGenericSuperclass())); + assertFalse(tokenF.isSupertypeOf(ConcreteStringInteger.class.getGenericSuperclass())); } public void testGenericArrayTypeToArrayType() { @@ -788,8 +798,8 @@ public void testAssignableTokenToType() { assertFalse(tokenF.isSupertypeOf(new TypeToken>() {})); assertTrue(tokenF.isSupertypeOf(new TypeToken>() {})); assertFalse(tokenF.isSupertypeOf(new TypeToken>() {})); - assertTrue(tokenF.isSupertypeOf(new TypeToken() {})); - assertFalse(tokenF.isSupertypeOf(new TypeToken() {})); + assertTrue(tokenF.isSupertypeOf(new TypeToken() {})); + assertFalse(tokenF.isSupertypeOf(new TypeToken() {})); } public void testAssignableWithWildcards() { @@ -1101,7 +1111,7 @@ public void testGetSupertype_withoutTypeVariable() { } public void testGetSupertype_chained() { - @SuppressWarnings("unchecked") // StringListIterable extensd ListIterable + @SuppressWarnings("unchecked") // StringListIterable extends ListIterable TypeToken> listIterableType = (TypeToken>) TypeToken.of(StringListIterable.class).getSupertype(ListIterable.class); @@ -1147,11 +1157,9 @@ public void testGetSupertype_fromRawClass() { @SuppressWarnings({"rawtypes", "unchecked"}) // purpose is to test raw type public void testGetSupertype_notSupertype() { - try { - new TypeToken>() {}.getSupertype((Class) String.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> new TypeToken>() {}.getSupertype((Class) String.class)); } public void testGetSupertype_fromArray() { @@ -1235,11 +1243,7 @@ public void testGetSubtype_fromWildcard_lowerBoundNotSupertype() { TypeToken> type = (TypeToken>) TypeToken.of(Types.supertypeOf(new TypeToken>() {}.getType())); - try { - type.getSubtype(List.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> type.getSubtype(List.class)); } public void testGetSubtype_fromWildcard_upperBounded() { @@ -1247,18 +1251,21 @@ public void testGetSubtype_fromWildcard_upperBounded() { TypeToken> type = (TypeToken>) TypeToken.of(Types.subtypeOf(new TypeToken>() {}.getType())); - try { - type.getSubtype(Iterable.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> type.getSubtype(Iterable.class)); } + @SuppressWarnings("TestExceptionChecker") // see comment below public > void testGetSubtype_fromTypeVariable() { + /* + * We'd use assertThrows here, but that causes capture() to return null under Java 8, presumably + * because the ThrowingRunnable lambda triggers some kind of bug in Java 8's reflection + * implementation. + */ try { TypeToken.of(new TypeCapture() {}.capture()).getSubtype(List.class); fail(); } catch (IllegalArgumentException expected) { + // Type variables aren't allowed. } } @@ -1377,7 +1384,9 @@ public void testGetSubtype_genericSubtypeOfGenericTypeWithFewerParameters() { } public void testGetSubtype_genericSubtypeOfRawTypeWithFewerTypeParameters() { + @SuppressWarnings("rawtypes") // test of raw types TypeToken supertype = new TypeToken() {}; + @SuppressWarnings("rawtypes") // test of raw types TypeToken subtype = new TypeToken() {}; assertTrue(subtype.isSubtypeOf(supertype)); Class actualSubtype = (Class) supertype.getSubtype(subtype.getRawType()).getType(); @@ -1450,17 +1459,19 @@ TypeToken> fieldTypeAsSubBar() { @SuppressWarnings("unchecked") // To construct TypeToken with TypeToken.of() public void testWhere_circleRejected() { TypeToken> type = new TypeToken>() {}; - try { - type.where( - new TypeParameter() {}, - (TypeToken) TypeToken.of(new TypeCapture() {}.capture())); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + type.where( + new TypeParameter() {}, + (TypeToken) TypeToken.of(new TypeCapture() {}.capture()))); } + @SuppressWarnings("JUnitIncompatibleType") public void testWhere() { assertEquals(new TypeToken>() {}, mapOf(String.class, Integer.class)); + // Type inference is doomed here: int.class is the same as Integer.class, so this is comparing + // TypeToken and TypeToken. assertEquals(new TypeToken() {}, arrayOf(int.class)); assertEquals(int[].class, arrayOf(int.class).getRawType()); } @@ -1587,11 +1598,7 @@ public void testMethod_getOwnerType() throws NoSuchMethodException { public void testMethod_notDeclaredByType() throws NoSuchMethodException { Method sizeMethod = Map.class.getMethod("size"); - try { - TypeToken.of(List.class).method(sizeMethod); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> TypeToken.of(List.class).method(sizeMethod)); } public void testMethod_declaredBySuperclass() throws Exception { @@ -1654,20 +1661,14 @@ public void testConstructor_getOwnerType() throws NoSuchMethodException { public void testConstructor_notDeclaredByType() throws NoSuchMethodException { Constructor constructor = String.class.getConstructor(); - try { - TypeToken.of(Object.class).constructor(constructor); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> TypeToken.of(Object.class).constructor(constructor)); } public void testConstructor_declaredBySuperclass() throws NoSuchMethodException { Constructor constructor = Object.class.getConstructor(); - try { - TypeToken.of(String.class).constructor(constructor); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> TypeToken.of(String.class).constructor(constructor)); } public void testConstructor_equals() throws NoSuchMethodException { @@ -1747,6 +1748,7 @@ Type type() { } } + @SuppressWarnings("RestrictedApiChecker") // crashes under JDK8, which EP no longer supports public void testRejectTypeVariable_withOwnerType() { // Neither has subclass assertHasTypeVariable(new From().new To().type()); @@ -1793,7 +1795,7 @@ private abstract static class RawTypeConsistencyTester & CharS abstract > void acceptT2(T2 t2); - static void verifyConsitentRawType() { + static void verifyConsistentRawType() { for (Method method : RawTypeConsistencyTester.class.getDeclaredMethods()) { assertEquals( method.getReturnType(), TypeToken.of(method.getGenericReturnType()).getRawType()); @@ -1807,7 +1809,7 @@ static void verifyConsitentRawType() { } public void testRawTypes() { - RawTypeConsistencyTester.verifyConsitentRawType(); + RawTypeConsistencyTester.verifyConsistentRawType(); assertEquals(Object.class, TypeToken.of(Types.subtypeOf(Object.class)).getRawType()); assertEquals( CharSequence.class, TypeToken.of(Types.subtypeOf(CharSequence.class)).getRawType()); @@ -1839,19 +1841,13 @@ public , B extends A> void testSerializable reserialize(new TypeToken>() {}); reserialize(new IKnowMyType>() {}.type()); reserialize(TypeToken.of(new TypeCapture() {}.capture()).getTypes().rawTypes()); - try { - SerializableTester.reserialize(TypeToken.of(new TypeCapture() {}.capture())); - fail(); - } catch (RuntimeException expected) { - } + assertThrows( + RuntimeException.class, + () -> SerializableTester.reserialize(TypeToken.of(new TypeCapture() {}.capture()))); } public void testSerializable_typeVariableNotSupported() { - try { - new ITryToSerializeMyTypeVariable().go(); - fail(); - } catch (RuntimeException expected) { - } + assertThrows(RuntimeException.class, () -> new ITryToSerializeMyTypeVariable().go()); } private static class ITryToSerializeMyTypeVariable { @@ -1889,7 +1885,7 @@ private abstract class SubInner extends BaseInner {} } } - // For Guava bug http://code.google.com/p/guava-libraries/issues/detail?id=1025 + // For Guava bug https://github.com/google/guava/issues/1025 public void testDespiteGenericSignatureFormatError() { ImmutableSet unused = ImmutableSet.copyOf( diff --git a/android/guava-tests/test/com/google/common/reflect/TypeVisitorTest.java b/android/guava-tests/test/com/google/common/reflect/TypeVisitorTest.java index 4cb53cfee481..d466067c8970 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypeVisitorTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypeVisitorTest.java @@ -24,12 +24,14 @@ import java.util.ArrayList; import java.util.EnumSet; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests of {@link TypeVisitor}. * * @author Ben Yu */ +@NullUnmarked public class TypeVisitorTest extends TestCase { public void testVisitNull() { diff --git a/android/guava-tests/test/com/google/common/reflect/TypesTest.java b/android/guava-tests/test/com/google/common/reflect/TypesTest.java index 436b2bbffe65..70f0e0c1876c 100644 --- a/android/guava-tests/test/com/google/common/reflect/TypesTest.java +++ b/android/guava-tests/test/com/google/common/reflect/TypesTest.java @@ -18,12 +18,14 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; +import static org.junit.Assert.assertThrows; import com.google.common.collect.Lists; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.common.testing.NullPointerTester.Visibility; import com.google.common.testing.SerializableTester; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.ParameterizedType; @@ -36,6 +38,7 @@ import java.util.Map; import java.util.Map.Entry; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Types}. @@ -43,6 +46,7 @@ * @author Ben Yu */ @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? +@NullUnmarked public class TypesTest extends TestCase { public void testNewParameterizedType_ownerTypeImplied() throws Exception { ParameterizedType jvmType = @@ -81,10 +85,10 @@ class LocalClass {} } public void testNewParameterizedType_staticLocalClass() { - doTestNewParameterizedType_staticLocalClass(); + doTestNewParameterizedTypeStaticLocalClass(); } - private static void doTestNewParameterizedType_staticLocalClass() { + private static void doTestNewParameterizedTypeStaticLocalClass() { class LocalClass {} Type jvmType = new LocalClass() {}.getClass().getGenericSuperclass(); Type ourType = Types.newParameterizedType(LocalClass.class, String.class); @@ -117,11 +121,9 @@ public void testNewParameterizedType_serializable() { } public void testNewParameterizedType_ownerMismatch() { - try { - Types.newParameterizedTypeWithOwner(Number.class, List.class, String.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Types.newParameterizedTypeWithOwner(Number.class, List.class, String.class)); } public void testNewParameterizedType_ownerMissing() { @@ -131,25 +133,22 @@ public void testNewParameterizedType_ownerMissing() { } public void testNewParameterizedType_invalidTypeParameters() { - try { - Types.newParameterizedTypeWithOwner(Map.class, Entry.class, String.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Types.newParameterizedTypeWithOwner(Map.class, Entry.class, String.class)); } public void testNewParameterizedType_primitiveTypeParameters() { - try { - Types.newParameterizedTypeWithOwner(Map.class, Entry.class, int.class, int.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Types.newParameterizedTypeWithOwner(Map.class, Entry.class, int.class, int.class)); } public void testNewArrayType() { Type jvmType1 = new TypeCapture[]>() {}.capture(); GenericArrayType ourType1 = (GenericArrayType) Types.newArrayType(Types.newParameterizedType(List.class, String.class)); + @SuppressWarnings("rawtypes") // test of raw types Type jvmType2 = new TypeCapture() {}.capture(); Type ourType2 = Types.newArrayType(List.class); new EqualsTester() @@ -234,11 +233,7 @@ public void testNewWildcardType() throws Exception { } public void testNewWildcardType_primitiveTypeBound() { - try { - Types.subtypeOf(int.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> Types.subtypeOf(int.class)); } public void testNewWildcardType_serializable() { @@ -265,7 +260,16 @@ private static class WithTypeVariable { @SuppressWarnings("unused") void withoutBound(List list) {} - @SuppressWarnings("unused") + @SuppressWarnings({ + "unused", + /* + * Since reflection can't tell the difference between and , it doesn't + * make a ton of sense to have a separate tests for each. But having tests for each doesn't + * really hurt anything, and maybe it will serve a purpose in a future in which Java has a + * built-in nullness feature? + */ + "ExtendsObject", + }) void withObjectBound(List list) {} @SuppressWarnings("unused") @@ -301,19 +305,15 @@ public void testNewTypeVariable() throws Exception { } public void testNewTypeVariable_primitiveTypeBound() { - try { - Types.newArtificialTypeVariable(List.class, "E", int.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Types.newArtificialTypeVariable(List.class, "E", int.class)); } public void testNewTypeVariable_serializable() throws Exception { - try { - SerializableTester.reserialize(Types.newArtificialTypeVariable(List.class, "E")); - fail(); - } catch (RuntimeException expected) { - } + assertThrows( + RuntimeException.class, + () -> SerializableTester.reserialize(Types.newArtificialTypeVariable(List.class, "E"))); } private static TypeVariable withBounds( @@ -325,6 +325,7 @@ private static TypeVariable withBounds( private static class TypeVariableEqualsTester { private final EqualsTester tester = new EqualsTester(); + @CanIgnoreReturnValue TypeVariableEqualsTester addEqualityGroup(Type jvmType, Type... types) { if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { tester.addEqualityGroup(jvmType); @@ -372,11 +373,9 @@ public void testNewParameterizedTypeImmutability() { } public void testNewParameterizedTypeWithWrongNumberOfTypeArguments() { - try { - Types.newParameterizedType(Map.class, String.class, Integer.class, Long.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> Types.newParameterizedType(Map.class, String.class, Integer.class, Long.class)); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java index f49b96245db4..22aa4ee10e9d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractAbstractFutureTest.java @@ -20,6 +20,7 @@ import static com.google.common.util.concurrent.Futures.getDone; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.util.concurrent.Runnables.doNothing; import static com.google.common.util.concurrent.TestPlatform.getDoneFromTimeoutOverload; import static com.google.common.util.concurrent.TestPlatform.verifyGetOnPendingFuture; @@ -29,17 +30,21 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.util.concurrent.AbstractFutureTest.TimedWaiterThread; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Base class for tests for emulated {@link AbstractFuture} that allow subclasses to swap in a * different "source Future" for {@link AbstractFuture#setFuture} calls. */ @GwtCompatible(emulated = true) +@NullUnmarked abstract class AbstractAbstractFutureTest extends TestCase { private TestedFuture future; private AbstractFuture delegate; @@ -109,38 +114,26 @@ public void testSetFutureDelegateLaterSuccessful() throws Exception { } public void testSetFutureDelegateAlreadyCancelled() throws Exception { - delegate.cancel( - false - /** mayInterruptIfRunning */ - ); + delegate.cancel(/* mayInterruptIfRunning= */ false); assertThat(future.setFuture(delegate)).isTrue(); assertCancelled(future, false); } public void testSetFutureDelegateLaterCancelled() throws Exception { assertThat(future.setFuture(delegate)).isTrue(); - delegate.cancel( - false - /** mayInterruptIfRunning */ - ); + delegate.cancel(/* mayInterruptIfRunning= */ false); assertCancelled(future, false); } public void testSetFutureDelegateAlreadyInterrupted() throws Exception { - delegate.cancel( - true - /** mayInterruptIfRunning */ - ); + delegate.cancel(/* mayInterruptIfRunning= */ true); assertThat(future.setFuture(delegate)).isTrue(); assertCancelled(future, /* expectWasInterrupted= */ false); } public void testSetFutureDelegateLaterInterrupted() throws Exception { assertThat(future.setFuture(delegate)).isTrue(); - delegate.cancel( - true - /** mayInterruptIfRunning */ - ); + delegate.cancel(/* mayInterruptIfRunning= */ true); assertCancelled(future, /* expectWasInterrupted= */ false); } @@ -329,28 +322,16 @@ public void run() { } public void testNullListener() { - try { - future.addListener(null, directExecutor()); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.addListener(null, directExecutor())); } public void testNullExecutor() { - try { - future.addListener(doNothing(), null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.addListener(doNothing(), null)); } public void testNullTimeUnit() throws Exception { future.set(1); - try { - future.get(0, null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.get(0, null)); } public void testNegativeTimeout() throws Exception { @@ -358,8 +339,8 @@ public void testNegativeTimeout() throws Exception { assertEquals(1, future.get(-1, SECONDS).intValue()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testOverflowTimeout() throws Exception { // First, sanity check that naive multiplication would really overflow to a negative number: long nanosPerSecond = NANOSECONDS.convert(1, SECONDS); @@ -374,17 +355,14 @@ public void testOverflowTimeout() throws Exception { waiter.join(); } + @J2ktIncompatible // TODO(b/324550390): Enable public void testSetNull() throws Exception { future.set(null); assertSuccessful(future, null); } public void testSetExceptionNull() throws Exception { - try { - future.setException(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.setException(null)); assertThat(future.isDone()).isFalse(); assertThat(future.set(1)).isTrue(); @@ -392,11 +370,7 @@ public void testSetExceptionNull() throws Exception { } public void testSetFutureNull() throws Exception { - try { - future.setFuture(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.setFuture(null)); assertThat(future.isDone()).isFalse(); assertThat(future.set(1)).isTrue(); @@ -444,7 +418,8 @@ private static void assertPending(AbstractFuture future) { verifyTimedGetOnPendingFuture(future); } - private static void assertSuccessful(AbstractFuture future, Integer expectedResult) + private static void assertSuccessful( + AbstractFuture future, @Nullable Integer expectedResult) throws InterruptedException, TimeoutException, ExecutionException { assertDone(future); assertThat(future.isCancelled()).isFalse(); @@ -462,7 +437,7 @@ private static void assertFailed(AbstractFuture future, Throwable expec getDone(future); fail(); } catch (ExecutionException e) { - assertThat(e.getCause()).isSameInstanceAs(expectedException); + assertThat(e).hasCauseThat().isSameInstanceAs(expectedException); } try { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractChainedListenableFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractChainedListenableFutureTest.java index 85c4e14a5dce..261c14637992 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractChainedListenableFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractChainedListenableFutureTest.java @@ -17,11 +17,13 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.util.concurrent.testing.MockFutureListener; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for any listenable future that chains other listenable futures. Unit tests need only @@ -29,6 +31,7 @@ * * @author Nishant Thakkar */ +@NullUnmarked public abstract class AbstractChainedListenableFutureTest extends TestCase { protected static final int EXCEPTION_DATA = -1; protected static final int VALID_INPUT_DATA = 1; @@ -49,11 +52,7 @@ protected void setUp() throws Exception { public void testFutureGetBeforeCallback() throws Exception { // Verify that get throws a timeout exception before the callback is called. - try { - resultFuture.get(1L, TimeUnit.MILLISECONDS); - fail("The data is not yet ready, so a TimeoutException is expected"); - } catch (TimeoutException expected) { - } + assertThrows(TimeoutException.class, () -> resultFuture.get(1L, MILLISECONDS)); } public void testFutureGetThrowsWrappedException() throws Exception { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractClosingFutureTest.java similarity index 90% rename from android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureTest.java rename to android/guava-tests/test/com/google/common/util/concurrent/AbstractClosingFutureTest.java index f5f12db824ca..10e6942ab7de 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractClosingFutureTest.java @@ -20,6 +20,7 @@ import static com.google.common.collect.Lists.asList; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.common.util.concurrent.ClosingFuture.withoutCloser; import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static com.google.common.util.concurrent.Futures.immediateFuture; @@ -30,6 +31,7 @@ import static java.util.Arrays.asList; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -73,6 +75,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; import org.mockito.Mockito; /** @@ -81,7 +84,8 @@ * ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor)} paths to complete a * {@link ClosingFuture} pipeline. */ -public abstract class ClosingFutureTest extends TestCase { +@NullUnmarked +public abstract class AbstractClosingFutureTest extends TestCase { // TODO(dpb): Use Expect once that supports JUnit 3, or we can use JUnit 4. final List failures = new ArrayList<>(); final StandardSubjectBuilder expect = @@ -338,6 +342,23 @@ public ClosingFuture call(DeferredCloser closer) throws Exception { assertClosed(closeable1, closeable2); } + public void testAutoCloseable() throws Exception { + AutoCloseable autoCloseable = closeable1::close; + ClosingFuture closingFuture = + ClosingFuture.submit( + new ClosingCallable() { + @Override + public String call(DeferredCloser closer) throws Exception { + closer.eventuallyClose(autoCloseable, closingExecutor); + return "foo"; + } + }, + executor); + assertThat(getFinalValue(closingFuture)).isEqualTo("foo"); + waitUntilClosed(closingFuture); + assertClosed(closeable1); + } + public void testStatusFuture() throws Exception { ClosingFuture closingFuture = ClosingFuture.submit( @@ -675,7 +696,7 @@ public TestCloseable call(DeferredCloser closer) throws Exception { }, executor) .transformAsync( - ClosingFuture.withoutCloser( + withoutCloser( new AsyncFunction() { @Override public ListenableFuture apply(TestCloseable v) throws Exception { @@ -722,11 +743,7 @@ public TestCloseable call(DeferredCloser closer, Peeker peeker) throws Exception waitUntilClosed(closingFuture); assertStillOpen(closeable2); assertClosed(closeable1); - try { - capturedPeeker.get().getDone(input1); - fail("Peeker should not be able to peek except during call."); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> capturedPeeker.get().getDone(input1)); } public void testWhenAllComplete_call_cancelledPipeline() throws Exception { @@ -808,11 +825,7 @@ public ClosingFuture call(DeferredCloser closer, Peeker peeker) assertThat(getFinalValue(closingFuture)).isSameInstanceAs(closeable2); waitUntilClosed(closingFuture); assertClosed(closeable1, closeable2); - try { - capturedPeeker.get().getDone(input1); - fail("Peeker should not be able to peek except during call."); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> capturedPeeker.get().getDone(input1)); } public void testWhenAllComplete_callAsync_cancelledPipeline() throws Exception { @@ -1480,7 +1493,7 @@ public void testCatchingAsync_preventsFurtherOperations() { ClosingFuture unused = closingFuture.catchingAsync( Exception.class, - ClosingFuture.withoutCloser( + withoutCloser( new AsyncFunction() { @Override public ListenableFuture apply(Exception x) throws Exception { @@ -1616,7 +1629,7 @@ public Closeable call(DeferredCloser closer) throws Exception { /** * Marks the given step final and waits for it to fail. Expects the failure exception to match - * {@link ClosingFutureTest#exception}. + * {@link AbstractClosingFutureTest#exception}. */ abstract void assertFinallyFailsWithException(ClosingFuture closingFuture); @@ -1628,191 +1641,6 @@ void waitUntilClosed(ClosingFuture closingFuture) { assertTrue(awaitUninterruptibly(closingFuture.whenClosedCountDown(), 1, SECONDS)); } - /** Tests for {@link ClosingFuture} that exercise {@link ClosingFuture#finishToFuture()}. */ - - public static class FinishToFutureTest extends ClosingFutureTest { - - public void testFinishToFuture_throwsIfCalledTwice() throws Exception { - ClosingFuture closingFuture = - ClosingFuture.submit( - new ClosingCallable() { - @Override - public Closeable call(DeferredCloser closer) throws Exception { - return closer.eventuallyClose(mockCloseable, executor); - } - }, - executor); - FluentFuture unused = closingFuture.finishToFuture(); - try { - FluentFuture unused2 = closingFuture.finishToFuture(); - fail("should have thrown"); - } catch (IllegalStateException expected) { - } - } - - public void testFinishToFuture_throwsAfterCallingFinishToValueAndCloser() throws Exception { - ClosingFuture closingFuture = - ClosingFuture.submit( - new ClosingCallable() { - @Override - public Closeable call(DeferredCloser closer) throws Exception { - return closer.eventuallyClose(mockCloseable, executor); - } - }, - executor); - closingFuture.finishToValueAndCloser(new NoOpValueAndCloserConsumer<>(), directExecutor()); - try { - FluentFuture unused = closingFuture.finishToFuture(); - fail("should have thrown"); - } catch (IllegalStateException expected) { - } - } - - public void testFinishToFuture_preventsFurtherDerivation() { - ClosingFuture closingFuture = ClosingFuture.from(immediateFuture("value1")); - FluentFuture unused = closingFuture.finishToFuture(); - assertDerivingThrowsIllegalStateException(closingFuture); - } - - @Override - T getFinalValue(ClosingFuture closingFuture) throws ExecutionException { - return getUninterruptibly(closingFuture.finishToFuture()); - } - - @Override - void assertFinallyFailsWithException(ClosingFuture closingFuture) { - assertThatFutureFailsWithException(closingFuture.finishToFuture()); - } - - @Override - void assertBecomesCanceled(ClosingFuture closingFuture) throws ExecutionException { - assertThatFutureBecomesCancelled(closingFuture.finishToFuture()); - } - - @Override - void cancelFinalStepAndWait(ClosingFuture closingFuture) { - assertThat(closingFuture.finishToFuture().cancel(false)).isTrue(); - waitUntilClosed(closingFuture); - futureCancelled.countDown(); - } - } - - /** - * Tests for {@link ClosingFuture} that exercise {@link - * ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor)}. - */ - - public static class FinishToValueAndCloserTest extends ClosingFutureTest { - - private final ExecutorService finishToValueAndCloserExecutor = newSingleThreadExecutor(); - private volatile ValueAndCloser valueAndCloser; - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - assertWithMessage("finishToValueAndCloserExecutor was shut down") - .that(shutdownAndAwaitTermination(finishToValueAndCloserExecutor, 10, SECONDS)) - .isTrue(); - } - - public void testFinishToValueAndCloser_throwsIfCalledTwice() throws Exception { - ClosingFuture closingFuture = - ClosingFuture.submit( - new ClosingCallable() { - @Override - public Closeable call(DeferredCloser closer) throws Exception { - return closer.eventuallyClose(mockCloseable, executor); - } - }, - executor); - closingFuture.finishToValueAndCloser( - new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor); - try { - closingFuture.finishToValueAndCloser( - new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor); - fail("should have thrown"); - } catch (IllegalStateException expected) { - } - } - - public void testFinishToValueAndCloser_throwsAfterCallingFinishToFuture() throws Exception { - ClosingFuture closingFuture = - ClosingFuture.submit( - new ClosingCallable() { - @Override - public Closeable call(DeferredCloser closer) throws Exception { - return closer.eventuallyClose(mockCloseable, executor); - } - }, - executor); - FluentFuture unused = closingFuture.finishToFuture(); - try { - closingFuture.finishToValueAndCloser( - new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor); - fail("should have thrown"); - } catch (IllegalStateException expected) { - } - } - - @Override - T getFinalValue(ClosingFuture closingFuture) throws ExecutionException { - return finishToValueAndCloser(closingFuture).get(); - } - - @Override - void assertFinallyFailsWithException(ClosingFuture closingFuture) { - assertThatFutureFailsWithException(closingFuture.statusFuture()); - ValueAndCloser valueAndCloser = finishToValueAndCloser(closingFuture); - try { - valueAndCloser.get(); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(exception); - } - valueAndCloser.closeAsync(); - } - - @Override - void assertBecomesCanceled(ClosingFuture closingFuture) throws ExecutionException { - assertThatFutureBecomesCancelled(closingFuture.statusFuture()); - } - - @Override - void waitUntilClosed(ClosingFuture closingFuture) { - if (valueAndCloser != null) { - valueAndCloser.closeAsync(); - } - super.waitUntilClosed(closingFuture); - } - - @Override - void cancelFinalStepAndWait(ClosingFuture closingFuture) { - assertThat(closingFuture.cancel(false)).isTrue(); - ValueAndCloser unused = finishToValueAndCloser(closingFuture); - waitUntilClosed(closingFuture); - futureCancelled.countDown(); - } - - private ValueAndCloser finishToValueAndCloser(ClosingFuture closingFuture) { - final CountDownLatch valueAndCloserSet = new CountDownLatch(1); - closingFuture.finishToValueAndCloser( - new ValueAndCloserConsumer() { - @Override - public void accept(ValueAndCloser valueAndCloser) { - FinishToValueAndCloserTest.this.valueAndCloser = valueAndCloser; - valueAndCloserSet.countDown(); - } - }, - finishToValueAndCloserExecutor); - assertWithMessage("valueAndCloser was set") - .that(awaitUninterruptibly(valueAndCloserSet, 10, SECONDS)) - .isTrue(); - @SuppressWarnings("unchecked") - ValueAndCloser valueAndCloserWithType = (ValueAndCloser) valueAndCloser; - return valueAndCloserWithType; - } - } - void assertThatFutureFailsWithException(Future future) { try { getUninterruptibly(future); @@ -1822,7 +1650,7 @@ void assertThatFutureFailsWithException(Future future) { } } - private static void assertThatFutureBecomesCancelled(Future future) throws ExecutionException { + static void assertThatFutureBecomesCancelled(Future future) throws ExecutionException { try { getUninterruptibly(future); fail("Expected future to be canceled: " + future); @@ -1900,53 +1728,65 @@ static final class Waiter { private final CountDownLatch returned = new CountDownLatch(1); private Object proxy; + @SuppressWarnings("unchecked") // proxy for a generic class Callable waitFor(Callable callable) { return waitFor(callable, Callable.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingCallable waitFor(ClosingCallable closingCallable) { return waitFor(closingCallable, ClosingCallable.class); } + @SuppressWarnings("unchecked") // proxy for a generic class AsyncClosingCallable waitFor(AsyncClosingCallable asyncClosingCallable) { return waitFor(asyncClosingCallable, AsyncClosingCallable.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingFunction waitFor(ClosingFunction closingFunction) { return waitFor(closingFunction, ClosingFunction.class); } + @SuppressWarnings("unchecked") // proxy for a generic class AsyncClosingFunction waitFor(AsyncClosingFunction asyncClosingFunction) { return waitFor(asyncClosingFunction, AsyncClosingFunction.class); } + @SuppressWarnings("unchecked") // proxy for a generic class CombiningCallable waitFor(CombiningCallable combiningCallable) { return waitFor(combiningCallable, CombiningCallable.class); } + @SuppressWarnings("unchecked") // proxy for a generic class AsyncCombiningCallable waitFor(AsyncCombiningCallable asyncCombiningCallable) { return waitFor(asyncCombiningCallable, AsyncCombiningCallable.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingFunction2 waitFor(ClosingFunction2 closingFunction2) { return waitFor(closingFunction2, ClosingFunction2.class); } + @SuppressWarnings("unchecked") // proxy for a generic class AsyncClosingFunction2 waitFor( AsyncClosingFunction2 asyncClosingFunction2) { return waitFor(asyncClosingFunction2, AsyncClosingFunction2.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingFunction3 waitFor( ClosingFunction3 closingFunction3) { return waitFor(closingFunction3, ClosingFunction3.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingFunction4 waitFor( ClosingFunction4 closingFunction4) { return waitFor(closingFunction4, ClosingFunction4.class); } + @SuppressWarnings("unchecked") // proxy for a generic class ClosingFunction5 waitFor( ClosingFunction5 closingFunction5) { return waitFor(closingFunction5, ClosingFunction5.class); @@ -1989,7 +1829,7 @@ void awaitReturned() { } } - private static final class NoOpValueAndCloserConsumer implements ValueAndCloserConsumer { + static final class NoOpValueAndCloserConsumer implements ValueAndCloserConsumer { @Override public void accept(ValueAndCloser valueAndCloser) {} } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractExecutionThreadServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractExecutionThreadServiceTest.java index 557fc0b5f2fe..82126ef425b0 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractExecutionThreadServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractExecutionThreadServiceTest.java @@ -17,6 +17,8 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.testing.TearDown; import com.google.common.testing.TearDownStack; @@ -27,15 +29,16 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link AbstractExecutionThreadService}. * * @author Jesse Wilson */ +@NullUnmarked public class AbstractExecutionThreadServiceTest extends TestCase { private final TearDownStack tearDownStack = new TearDownStack(true); @@ -68,7 +71,6 @@ protected final void tearDown() { thrownByExecutionThread); } - public void testServiceStartStop() throws Exception { WaitOnRunService service = new WaitOnRunService(); assertFalse(service.startUpCalled); @@ -85,7 +87,6 @@ public void testServiceStartStop() throws Exception { executionThread.join(); } - public void testServiceStopIdempotence() throws Exception { WaitOnRunService service = new WaitOnRunService(); @@ -102,7 +103,6 @@ public void testServiceStopIdempotence() throws Exception { executionThread.join(); } - public void testServiceExitingOnItsOwn() throws Exception { WaitOnRunService service = new WaitOnRunService(); service.expectedShutdownState = Service.State.RUNNING; @@ -173,18 +173,14 @@ protected Executor executor() { } } - public void testServiceThrowOnStartUp() throws Exception { ThrowOnStartUpService service = new ThrowOnStartUpService(); assertFalse(service.startUpCalled); service.startAsync(); - try { - service.awaitRunning(); - fail(); - } catch (IllegalStateException expected) { - assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> service.awaitRunning()); + assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); executionThread.join(); assertTrue(service.startUpCalled); @@ -212,40 +208,32 @@ protected Executor executor() { } } - public void testServiceThrowOnRun() throws Exception { ThrowOnRunService service = new ThrowOnRunService(); service.startAsync(); - try { - service.awaitTerminated(); - fail(); - } catch (IllegalStateException expected) { - executionThread.join(); - assertThat(expected).hasCauseThat().isEqualTo(service.failureCause()); - assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); - } + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> service.awaitTerminated()); + executionThread.join(); + assertThat(expected).hasCauseThat().isEqualTo(service.failureCause()); + assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); assertTrue(service.shutDownCalled); assertEquals(Service.State.FAILED, service.state()); } - public void testServiceThrowOnRunAndThenAgainOnShutDown() throws Exception { ThrowOnRunService service = new ThrowOnRunService(); service.throwOnShutDown = true; service.startAsync(); - try { - service.awaitTerminated(); - fail(); - } catch (IllegalStateException expected) { - executionThread.join(); - assertThat(expected).hasCauseThat().isEqualTo(service.failureCause()); - assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); - } - + IllegalStateException expected = + assertThrows(IllegalStateException.class, () -> service.awaitTerminated()); + executionThread.join(); + assertThat(expected).hasCauseThat().isEqualTo(service.failureCause()); + assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("kaboom!"); assertTrue(service.shutDownCalled); assertEquals(Service.State.FAILED, service.state()); + assertThat(expected.getCause().getSuppressed()[0]).hasMessageThat().isEqualTo("double kaboom!"); } private class ThrowOnRunService extends AbstractExecutionThreadService { @@ -271,7 +259,6 @@ protected Executor executor() { } } - public void testServiceThrowOnShutDown() throws Exception { ThrowOnShutDown service = new ThrowOnShutDown(); @@ -310,12 +297,10 @@ protected Executor executor() { public void testServiceTimeoutOnStartUp() throws Exception { TimeoutOnStartUp service = new TimeoutOnStartUp(); - try { - service.startAsync().awaitRunning(1, TimeUnit.MILLISECONDS); - fail(); - } catch (TimeoutException e) { - assertThat(e.getMessage()).contains(Service.State.STARTING.toString()); - } + TimeoutException e = + assertThrows( + TimeoutException.class, () -> service.startAsync().awaitRunning(1, MILLISECONDS)); + assertThat(e).hasMessageThat().contains(Service.State.STARTING.toString()); } private class TimeoutOnStartUp extends AbstractExecutionThreadService { @@ -331,7 +316,6 @@ public void execute(Runnable command) {} protected void run() throws Exception {} } - public void testStopWhileStarting_runNotCalled() throws Exception { final CountDownLatch started = new CountDownLatch(1); FakeService service = @@ -361,7 +345,6 @@ public void testStop_noStart() { assertEquals(0, service.shutdownCalled); } - public void testDefaultService() throws InterruptedException { WaitOnRunService service = new WaitOnRunService(); service.startAsync().awaitRunning(); @@ -386,14 +369,12 @@ protected String serviceName() { return "Foo"; } }; - try { - service.startAsync().awaitRunning(1, TimeUnit.MILLISECONDS); - fail("Expected timeout"); - } catch (TimeoutException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); - } + TimeoutException e = + assertThrows( + TimeoutException.class, () -> service.startAsync().awaitRunning(1, MILLISECONDS)); + assertThat(e) + .hasMessageThat() + .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); } private class FakeService extends AbstractExecutionThreadService implements TearDown { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureBenchmarks.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureBenchmarks.java index ed5e6a96db0f..1dd4986b6a3c 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureBenchmarks.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureBenchmarks.java @@ -25,9 +25,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.AbstractQueuedSynchronizer; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** Utilities for the AbstractFutureBenchmarks */ +@NullUnmarked final class AbstractFutureBenchmarks { private AbstractFutureBenchmarks() {} @@ -85,6 +87,7 @@ Facade newFacade() { abstract Facade newFacade(); } + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. static void awaitWaiting(Thread t) { while (true) { Thread.State state = t.getState(); @@ -218,7 +221,7 @@ public void addListener(Runnable listener, Executor exec) { * @return true if the state was successfully changed. */ @CanIgnoreReturnValue - protected boolean set(@CheckForNull V value) { + protected boolean set(@Nullable V value) { boolean result = sync.set(value); if (result) { executionList.execute(); @@ -360,7 +363,7 @@ boolean wasInterrupted() { } /** Transition to the COMPLETED state and set the value. */ - boolean set(@CheckForNull V v) { + boolean set(@Nullable V v) { return complete(v, null, COMPLETED); } @@ -384,7 +387,7 @@ boolean cancel(boolean interrupt) { * @param t the exception to set as the result of the computation. * @param finalState the state to transition to. */ - private boolean complete(@CheckForNull V v, @CheckForNull Throwable t, int finalState) { + private boolean complete(@Nullable V v, @Nullable Throwable t, int finalState) { boolean doCompletion = compareAndSetState(RUNNING, COMPLETING); if (doCompletion) { // If this thread successfully transitioned to COMPLETING, set the value @@ -405,8 +408,8 @@ private boolean complete(@CheckForNull V v, @CheckForNull Throwable t, int final } } - static final CancellationException cancellationExceptionWithCause( - @CheckForNull String message, @CheckForNull Throwable cause) { + static CancellationException cancellationExceptionWithCause( + @Nullable String message, @Nullable Throwable cause) { CancellationException exception = new CancellationException(message); exception.initCause(cause); return exception; diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureCancellationCauseTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureCancellationCauseTest.java index 6c921b5b67d5..48c4d41ac0de 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureCancellationCauseTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureCancellationCauseTest.java @@ -17,7 +17,9 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; +import com.google.errorprone.annotations.concurrent.GuardedBy; import java.lang.reflect.Method; import java.net.URLClassLoader; import java.util.HashMap; @@ -26,11 +28,13 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import javax.annotation.concurrent.GuardedBy; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for {@link AbstractFuture} with the cancellation cause system property set */ +@AndroidIncompatible // custom classloading +@NullUnmarked public class AbstractFutureCancellationCauseTest extends TestCase { private ClassLoader oldClassLoader; @@ -88,12 +92,8 @@ public void testCancel_notDoneNoInterrupt() throws Exception { assertTrue(future.isCancelled()); assertTrue(future.isDone()); assertNull(tryInternalFastPathGetFailure(future)); - try { - future.get(); - fail("Expected CancellationException"); - } catch (CancellationException e) { - assertNotNull(e.getCause()); - } + CancellationException e = assertThrows(CancellationException.class, () -> future.get()); + assertNotNull(e.getCause()); } public void testCancel_notDoneInterrupt() throws Exception { @@ -102,12 +102,8 @@ public void testCancel_notDoneInterrupt() throws Exception { assertTrue(future.isCancelled()); assertTrue(future.isDone()); assertNull(tryInternalFastPathGetFailure(future)); - try { - future.get(); - fail("Expected CancellationException"); - } catch (CancellationException e) { - assertNotNull(e.getCause()); - } + CancellationException e = assertThrows(CancellationException.class, () -> future.get()); + assertNotNull(e.getCause()); } public void testSetFuture_misbehavingFutureDoesNotThrow() throws Exception { @@ -150,13 +146,9 @@ public void addListener(Runnable runnable, Executor executor) { "setFuture", future.getClass().getClassLoader().loadClass(ListenableFuture.class.getName())) .invoke(future, badFuture); - try { - future.get(); - fail(); - } catch (CancellationException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - assertThat(expected).hasCauseThat().hasMessageThat().contains(badFuture.toString()); - } + CancellationException expected = assertThrows(CancellationException.class, () -> future.get()); + assertThat(expected).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(expected).hasCauseThat().hasMessageThat().contains(badFuture.toString()); } private Future newFutureInstance() throws Exception { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java index 208cf24831e1..b9c2ea34ca42 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureFallbackAtomicHelperTest.java @@ -14,6 +14,8 @@ package com.google.common.util.concurrent; +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; + import com.google.common.collect.ImmutableSet; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -23,6 +25,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests our AtomicHelper fallback strategies in AbstractFuture. @@ -30,20 +33,15 @@ *

    On different platforms AbstractFuture uses different strategies for its core synchronization * primitives. The strategies are all implemented as subtypes of AtomicHelper and the strategy is * selected in the static initializer of AbstractFuture. This is convenient and performant but - * introduces some testing difficulties. This test exercises the two fallback strategies in abstract - * future. - * - *

      - *
    • SafeAtomicHelper: uses AtomicReferenceFieldsUpdaters to implement synchronization - *
    • SynchronizedHelper: uses {@code synchronized} blocks for synchronization - *
    + * introduces some testing difficulties. This test exercises the fallback strategies. * - * To force selection of our fallback strategies we load {@link AbstractFuture} (and all of {@code - * com.google.common.util.concurrent}) in degenerate class loaders which make certain platform - * classes unavailable. Then we construct a test suite so we can run the normal AbstractFutureTest - * test methods in these degenerate classloaders. + *

    To force selection of our fallback strategies, we load {@link AbstractFuture} (and all of + * {@code com.google.common.util.concurrent}) in degenerate class loaders which make certain + * platform classes unavailable. Then we construct a test suite so we can run the normal + * AbstractFutureTest test methods in these degenerate classloaders. */ +@NullUnmarked public class AbstractFutureFallbackAtomicHelperTest extends TestCase { // stash these in static fields to avoid loading them over and over again (speeds up test @@ -53,17 +51,16 @@ public class AbstractFutureFallbackAtomicHelperTest extends TestCase { * This classloader disallows {@link sun.misc.Unsafe}, which will prevent us from selecting our * preferred strategy {@code UnsafeAtomicHelper}. */ - private static final ClassLoader NO_UNSAFE = - getClassLoader(ImmutableSet.of(sun.misc.Unsafe.class.getName())); + private static final ClassLoader NO_UNSAFE = getClassLoader(ImmutableSet.of("sun.misc.Unsafe")); /** * This classloader disallows {@link sun.misc.Unsafe} and {@link AtomicReferenceFieldUpdater}, - * which will prevent us from selecting our {@code SafeAtomicHelper} strategy. + * which will prevent us from selecting our {@code AtomicReferenceFieldUpdaterAtomicHelper} + * strategy. */ private static final ClassLoader NO_ATOMIC_REFERENCE_FIELD_UPDATER = getClassLoader( - ImmutableSet.of( - sun.misc.Unsafe.class.getName(), AtomicReferenceFieldUpdater.class.getName())); + ImmutableSet.of("sun.misc.Unsafe", AtomicReferenceFieldUpdater.class.getName())); public static TestSuite suite() { // we create a test suite containing a test for every AbstractFutureTest test method and we @@ -83,7 +80,7 @@ public static TestSuite suite() { public void runTest() throws Exception { // First ensure that our classloaders are initializing the correct helper versions checkHelperVersion(getClass().getClassLoader(), "UnsafeAtomicHelper"); - checkHelperVersion(NO_UNSAFE, "SafeAtomicHelper"); + checkHelperVersion(NO_UNSAFE, "AtomicReferenceFieldUpdaterAtomicHelper"); checkHelperVersion(NO_ATOMIC_REFERENCE_FIELD_UPDATER, "SynchronizedHelper"); // Run the corresponding AbstractFutureTest test method in a new classloader that disallows @@ -113,8 +110,8 @@ private void runTestMethod(ClassLoader classLoader) throws Exception { private void checkHelperVersion(ClassLoader classLoader, String expectedHelperClassName) throws Exception { // Make sure we are actually running with the expected helper implementation - Class abstractFutureClass = classLoader.loadClass(AbstractFuture.class.getName()); - Field helperField = abstractFutureClass.getDeclaredField("ATOMIC_HELPER"); + Class abstractFutureStateClass = classLoader.loadClass(AbstractFutureState.class.getName()); + Field helperField = abstractFutureStateClass.getDeclaredField("ATOMIC_HELPER"); helperField.setAccessible(true); assertEquals(expectedHelperClassName, helperField.get(null).getClass().getSimpleName()); } @@ -140,4 +137,8 @@ public Class loadClass(String name) throws ClassNotFoundException { } }; } + + private static boolean isJava8() { + return JAVA_SPECIFICATION_VERSION.value().equals("1.8"); + } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java index e55a31e725de..dfc4e8464e81 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java @@ -16,13 +16,26 @@ package com.google.common.util.concurrent; +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.base.StandardSystemProperty.OS_NAME; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; +import static com.google.common.util.concurrent.Futures.immediateFailedFuture; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.SneakyThrows.sneakyThrow; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Iterables; import com.google.common.collect.Range; import com.google.common.collect.Sets; +import com.google.common.primitives.Ints; import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; import java.util.ArrayList; import java.util.Arrays; @@ -46,13 +59,15 @@ import java.util.concurrent.locks.LockSupport; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Tests for {@link AbstractFuture}. * * @author Brian Stoler */ - +@NullUnmarked public class AbstractFutureTest extends TestCase { public void testSuccess() throws ExecutionException, InterruptedException { final Object value = new Object(); @@ -94,13 +109,8 @@ public void testCancel_notDoneNoInterrupt() throws Exception { assertTrue(future.isDone()); assertFalse(future.wasInterrupted()); assertFalse(future.interruptTaskWasCalled); - try { - future.get(); - fail("Expected CancellationException"); - } catch (CancellationException e) { - // See AbstractFutureCancellationCauseTest for how to set causes - assertThat(e).hasCauseThat().isNull(); - } + CancellationException e = assertThrows(CancellationException.class, () -> future.get()); + assertThat(e).hasCauseThat().isNull(); } public void testCancel_notDoneInterrupt() throws Exception { @@ -110,13 +120,8 @@ public void testCancel_notDoneInterrupt() throws Exception { assertTrue(future.isDone()); assertTrue(future.wasInterrupted()); assertTrue(future.interruptTaskWasCalled); - try { - future.get(); - fail("Expected CancellationException"); - } catch (CancellationException e) { - // See AbstractFutureCancellationCauseTest for how to set causes - assertThat(e).hasCauseThat().isNull(); - } + CancellationException e = assertThrows(CancellationException.class, () -> future.get()); + assertThat(e).hasCauseThat().isNull(); } public void testCancel_done() throws Exception { @@ -138,7 +143,7 @@ public void testGetWithTimeoutDoneFuture() throws Exception { set("foo"); } }; - assertEquals("foo", future.get(0, TimeUnit.SECONDS)); + assertEquals("foo", future.get(0, SECONDS)); } public void testEvilFuture_setFuture() throws Exception { @@ -153,12 +158,8 @@ public void addListener(Runnable r, Executor e) { AbstractFuture normalFuture = new AbstractFuture() {}; normalFuture.setFuture(evilFuture); assertTrue(normalFuture.isDone()); - try { - normalFuture.get(); - fail(); - } catch (ExecutionException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(exception); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> normalFuture.get()); + assertThat(e).hasCauseThat().isSameInstanceAs(exception); } public void testRemoveWaiter_interruption() throws Exception { @@ -263,13 +264,9 @@ public String pendingToString() { assertThat(testFuture.toString()) .matches( "[^\\[]+\\[status=PENDING, info=\\[cause=\\[Because this test isn't done\\]\\]\\]"); - try { - testFuture.get(1, TimeUnit.NANOSECONDS); - fail(); - } catch (TimeoutException e) { - assertThat(e.getMessage()).contains("1 nanoseconds"); - assertThat(e.getMessage()).contains("Because this test isn't done"); - } + TimeoutException e = assertThrows(TimeoutException.class, () -> testFuture.get(1, NANOSECONDS)); + assertThat(e).hasMessageThat().contains("1 nanoseconds"); + assertThat(e).hasMessageThat().contains("Because this test isn't done"); } public void testToString_completesDuringToString() throws Exception { @@ -291,30 +288,36 @@ public String pendingToString() { * get() call. As measurements of time are prone to flakiness, it tries to assert based on ranges * derived from observing how much time actually passed for various operations. */ - @SuppressWarnings({"DeprecatedThreadMethods", "ThreadPriorityCheck"}) + @SuppressWarnings("ThreadPriorityCheck") + @AndroidIncompatible // Thread.suspend public void testToString_delayedTimeout() throws Exception { - TimedWaiterThread thread = - new TimedWaiterThread(new AbstractFuture() {}, 2, TimeUnit.SECONDS); + Integer javaVersion = Ints.tryParse(JAVA_SPECIFICATION_VERSION.value()); + // Parsing to an integer might fail because Java 8 returns "1.8" instead of "8." + // We can continue if it's 1.8, and we can continue if it's an integer in [9, 20). + if (javaVersion != null && javaVersion >= 20) { + // TODO(b/261217224, b/361604053): Make this test work under newer JDKs. + return; + } + TimedWaiterThread thread = new TimedWaiterThread(new AbstractFuture() {}, 2, SECONDS); thread.start(); thread.awaitWaiting(); - thread.suspend(); + Thread.class.getMethod("suspend").invoke(thread); // Sleep for enough time to add 1500 milliseconds of overwait to the get() call. - long toWaitMillis = 3500 - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - thread.startTime); + long toWaitMillis = 3500 - NANOSECONDS.toMillis(System.nanoTime() - thread.startTime); Thread.sleep(toWaitMillis); thread.setPriority(Thread.MAX_PRIORITY); - thread.resume(); + Thread.class.getMethod("resume").invoke(thread); thread.join(); // It's possible to race and suspend the thread just before the park call actually takes effect, // causing the thread to be suspended for 3.5 seconds, and then park itself for 2 seconds after // being resumed. To avoid a flake in this scenario, calculate how long that thread actually // waited and assert based on that time. Empirically, the race where the thread ends up waiting // for 5.5 seconds happens about 2% of the time. - boolean longWait = TimeUnit.NANOSECONDS.toSeconds(thread.timeSpentBlocked) >= 5; + boolean longWait = NANOSECONDS.toSeconds(thread.timeSpentBlocked) >= 5; // Count how long it actually took to return; we'll accept any number between the expected delay // and the approximate actual delay, to be robust to variance in thread scheduling. char overWaitNanosFirstDigit = - Long.toString( - thread.timeSpentBlocked - TimeUnit.MILLISECONDS.toNanos(longWait ? 5000 : 3000)) + Long.toString(thread.timeSpentBlocked - MILLISECONDS.toNanos(longWait ? 5000 : 3000)) .charAt(0); if (overWaitNanosFirstDigit < '4') { overWaitNanosFirstDigit = '9'; @@ -352,12 +355,11 @@ public String pendingToString() { } public void testToString_cancelled() throws Exception { - assertThat(Futures.immediateCancelledFuture().toString()) - .matches("[^\\[]+\\[status=CANCELLED\\]"); + assertThat(immediateCancelledFuture().toString()).matches("[^\\[]+\\[status=CANCELLED\\]"); } public void testToString_failed() { - assertThat(Futures.immediateFailedFuture(new RuntimeException("foo")).toString()) + assertThat(immediateFailedFuture(new RuntimeException("foo")).toString()) .matches("[^\\[]+\\[status=FAILURE, cause=\\[java.lang.RuntimeException: foo\\]\\]"); } @@ -426,6 +428,9 @@ public void run() { */ public void testFutureBash() { + if (isWindows()) { + return; // TODO: b/136041958 - Running very slowly on Windows CI. + } final CyclicBarrier barrier = new CyclicBarrier( 6 // for the setter threads @@ -435,10 +440,10 @@ public void testFutureBash() { final ExecutorService executor = Executors.newFixedThreadPool(barrier.getParties()); final AtomicReference> currentFuture = Atomics.newReference(); final AtomicInteger numSuccessfulSetCalls = new AtomicInteger(); - Callable completeSuccessfullyRunnable = - new Callable() { + Callable<@Nullable Void> completeSuccessfullyRunnable = + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().set("set")) { numSuccessfulSetCalls.incrementAndGet(); } @@ -446,12 +451,12 @@ public Void call() { return null; } }; - Callable completeExceptionallyRunnable = - new Callable() { + Callable<@Nullable Void> completeExceptionallyRunnable = + new Callable<@Nullable Void>() { Exception failureCause = new Exception("setException"); @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().setException(failureCause)) { numSuccessfulSetCalls.incrementAndGet(); } @@ -459,10 +464,10 @@ public Void call() { return null; } }; - Callable cancelRunnable = - new Callable() { + Callable<@Nullable Void> cancelRunnable = + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().cancel(true)) { numSuccessfulSetCalls.incrementAndGet(); } @@ -470,12 +475,12 @@ public Void call() { return null; } }; - Callable setFutureCompleteSuccessfullyRunnable = - new Callable() { - ListenableFuture future = Futures.immediateFuture("setFuture"); + Callable<@Nullable Void> setFutureCompleteSuccessfullyRunnable = + new Callable<@Nullable Void>() { + ListenableFuture future = immediateFuture("setFuture"); @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().setFuture(future)) { numSuccessfulSetCalls.incrementAndGet(); } @@ -483,13 +488,12 @@ public Void call() { return null; } }; - Callable setFutureCompleteExceptionallyRunnable = - new Callable() { - ListenableFuture future = - Futures.immediateFailedFuture(new Exception("setFuture")); + Callable<@Nullable Void> setFutureCompleteExceptionallyRunnable = + new Callable<@Nullable Void>() { + ListenableFuture future = immediateFailedFuture(new Exception("setFuture")); @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().setFuture(future)) { numSuccessfulSetCalls.incrementAndGet(); } @@ -497,12 +501,12 @@ public Void call() { return null; } }; - Callable setFutureCancelRunnable = - new Callable() { - ListenableFuture future = Futures.immediateCancelledFuture(); + Callable<@Nullable Void> setFutureCancelRunnable = + new Callable<@Nullable Void>() { + ListenableFuture future = immediateCancelledFuture(); @Override - public Void call() { + public @Nullable Void call() { if (currentFuture.get().setFuture(future)) { numSuccessfulSetCalls.incrementAndGet(); } @@ -534,7 +538,7 @@ public void run() { Future future = currentFuture.get(); while (true) { try { - String result = Uninterruptibles.getUninterruptibly(future, 0, TimeUnit.SECONDS); + String result = Uninterruptibles.getUninterruptibly(future, 0, SECONDS); finalResults.add(result); break; } catch (ExecutionException e) { @@ -559,15 +563,15 @@ public void run() { allTasks.add(setFutureCancelRunnable); for (int k = 0; k < 50; k++) { // For each listener we add a task that submits it to the executor directly for the blocking - // get usecase and another task that adds it as a listener to the future to exercise both + // get use case and another task that adds it as a listener to the future to exercise both // racing addListener calls and addListener calls completing after the future completes. final Runnable listener = k % 2 == 0 ? collectResultsRunnable : collectResultsTimedGetRunnable; allTasks.add(Executors.callable(listener)); allTasks.add( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { currentFuture.get().addListener(listener, executor); return null; } @@ -607,6 +611,9 @@ public Void call() throws Exception { // setFuture and cancel() interact in more complicated ways than the other setters. public void testSetFutureCancelBash() { + if (isWindows()) { + return; // TODO: b/136041958 - Running very slowly on Windows CI. + } final int size = 50; final CyclicBarrier barrier = new CyclicBarrier( @@ -662,7 +669,7 @@ public void run() { Future future = currentFuture.get(); while (true) { try { - String result = Uninterruptibles.getUninterruptibly(future, 0, TimeUnit.SECONDS); + String result = Uninterruptibles.getUninterruptibly(future, 0, SECONDS); finalResults.add(result); break; } catch (ExecutionException e) { @@ -683,7 +690,7 @@ public void run() { allTasks.add(setFutureCompleteSuccessfullyRunnable); for (int k = 0; k < size; k++) { // For each listener we add a task that submits it to the executor directly for the blocking - // get usecase and another task that adds it as a listener to the future to exercise both + // get use case and another task that adds it as a listener to the future to exercise both // racing addListener calls and addListener calls completing after the future completes. final Runnable listener = k % 2 == 0 ? collectResultsRunnable : collectResultsTimedGetRunnable; @@ -751,21 +758,21 @@ public void testSetFutureCancelBash_withDoneFuture() { final AtomicReference> currentFuture = Atomics.newReference(); final AtomicBoolean setFutureSuccess = new AtomicBoolean(); final AtomicBoolean cancellationSuccess = new AtomicBoolean(); - Callable cancelRunnable = - new Callable() { + Callable<@Nullable Void> cancelRunnable = + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { cancellationSuccess.set(currentFuture.get().cancel(true)); awaitUnchecked(barrier); return null; } }; - Callable setFutureCompleteSuccessfullyRunnable = - new Callable() { - final ListenableFuture future = Futures.immediateFuture("hello"); + Callable<@Nullable Void> setFutureCompleteSuccessfullyRunnable = + new Callable<@Nullable Void>() { + final ListenableFuture future = immediateFuture("hello"); @Override - public Void call() { + public @Nullable Void call() { setFutureSuccess.set(currentFuture.get().setFuture(future)); awaitUnchecked(barrier); return null; @@ -839,8 +846,9 @@ public void testSetFuture_stackOverflow() { // Verify that StackOverflowError in a long chain of SetFuture doesn't cause the entire toString // call to fail + @J2ktIncompatible @GwtIncompatible - @AndroidIncompatible + @AndroidIncompatible // b/391667564: crashes from stack overflows public void testSetFutureToString_stackOverflow() { SettableFuture orig = SettableFuture.create(); SettableFuture prev = orig; @@ -981,6 +989,7 @@ public String toString() { + " java.lang.NullPointerException]]"); } + @AndroidIncompatible // b/391667564: crashes from stack overflows public void testSetIndirectSelf_toString() { final SettableFuture orig = SettableFuture.create(); // unlike the above this indirection defeats the trivial cycle detection and causes a SOE @@ -1010,7 +1019,7 @@ public void run() { ranImmediately.set(true); } }, - MoreExecutors.directExecutor()); + directExecutor()); assertThat(ranImmediately.get()).isTrue(); } }; @@ -1026,7 +1035,7 @@ public void testListenersExecuteImmediately_afterWaiterWakesUp() throws Exceptio protected void afterDone() { // this simply delays executing listeners try { - Thread.sleep(TimeUnit.SECONDS.toMillis(10)); + Thread.sleep(SECONDS.toMillis(10)); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); // preserve status } @@ -1049,57 +1058,65 @@ public void run() { ranImmediately.set(true); } }, - MoreExecutors.directExecutor()); + directExecutor()); assertThat(ranImmediately.get()).isTrue(); t.interrupt(); t.join(); } - public void testTrustedGetFailure_Completed() { + public void testCatchesUndeclaredThrowableFromListener() { + AbstractFuture f = new AbstractFuture() {}; + f.set("foo"); + f.addListener(() -> sneakyThrow(new SomeCheckedException()), directExecutor()); + } + + private static final class SomeCheckedException extends Exception {} + + public void testTrustedGetFailure_completed() { SettableFuture future = SettableFuture.create(); future.set("261"); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testTrustedGetFailure_Failed() { + public void testTrustedGetFailure_failed() { SettableFuture future = SettableFuture.create(); Throwable failure = new Throwable(); future.setException(failure); assertThat(future.tryInternalFastPathGetFailure()).isEqualTo(failure); } - public void testTrustedGetFailure_NotCompleted() { + public void testTrustedGetFailure_notCompleted() { SettableFuture future = SettableFuture.create(); assertThat(future.isDone()).isFalse(); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testTrustedGetFailure_CanceledNoCause() { + public void testTrustedGetFailure_canceledNoCause() { SettableFuture future = SettableFuture.create(); future.cancel(false); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testGetFailure_Completed() { + public void testGetFailure_completed() { AbstractFuture future = new AbstractFuture() {}; future.set("261"); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testGetFailure_Failed() { + public void testGetFailure_failed() { AbstractFuture future = new AbstractFuture() {}; final Throwable failure = new Throwable(); future.setException(failure); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testGetFailure_NotCompleted() { + public void testGetFailure_notCompleted() { AbstractFuture future = new AbstractFuture() {}; assertThat(future.isDone()).isFalse(); assertThat(future.tryInternalFastPathGetFailure()).isNull(); } - public void testGetFailure_CanceledNoCause() { + public void testGetFailure_canceledNoCause() { AbstractFuture future = new AbstractFuture() {}; future.cancel(false); assertThat(future.tryInternalFastPathGetFailure()).isNull(); @@ -1154,12 +1171,8 @@ public void addListener(Runnable listener, Executor executor) { SettableFuture normalFuture = SettableFuture.create(); normalFuture.setFuture(new FailFuture(exception)); assertTrue(normalFuture.isDone()); - try { - normalFuture.get(); - fail(); - } catch (ExecutionException e) { - assertSame(exception, e.getCause()); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> normalFuture.get()); + assertSame(exception, e.getCause()); } private static void awaitUnchecked(final CyclicBarrier barrier) { @@ -1189,24 +1202,18 @@ private static int findStackFrame(ExecutionException e, String clazz, String met return i; } } - AssertionFailedError failure = - new AssertionFailedError( - "Expected element " + clazz + "." + method + " not found in stack trace"); - failure.initCause(e); - throw failure; + throw new AssertionError( + "Expected element " + clazz + "." + method + " not found in stack trace", e); } private ExecutionException getExpectingExecutionException(AbstractFuture future) throws InterruptedException { try { String got = future.get(); - fail("Expected exception but got " + got); + throw new AssertionError("Expected exception but got " + got); } catch (ExecutionException e) { return e; } - - // unreachable, but compiler doesn't know that fail() always throws - return null; } private static final class WaiterThread extends Thread { @@ -1225,6 +1232,7 @@ public void run() { } } + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. void awaitWaiting() { while (!isBlocked()) { if (getState() == State.TERMINATED) { @@ -1266,6 +1274,7 @@ public void run() { } } + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. void awaitWaiting() { while (!isBlocked()) { if (getState() == State.TERMINATED) { @@ -1292,7 +1301,7 @@ private PollingThread(AbstractFuture future) { public void run() { while (true) { try { - future.get(0, TimeUnit.SECONDS); + future.get(0, SECONDS); return; } catch (InterruptedException | ExecutionException e) { return; @@ -1318,4 +1327,8 @@ protected void interruptTask() { interruptTaskWasCalled = true; } } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java index 7cad8b0fa712..f46960f55a2a 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractIdleServiceTest.java @@ -18,13 +18,15 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.Lists; import java.util.List; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link AbstractIdleService}. @@ -32,67 +34,8 @@ * @author Chris Nokleberg * @author Ben Yu */ +@NullUnmarked public class AbstractIdleServiceTest extends TestCase { - - // Functional tests using real thread. We only verify publicly visible state. - // Interaction assertions are done by the single-threaded unit tests. - - public static class FunctionalTest extends TestCase { - - private static class DefaultService extends AbstractIdleService { - @Override - protected void startUp() throws Exception {} - - @Override - protected void shutDown() throws Exception {} - } - - public void testServiceStartStop() throws Exception { - AbstractIdleService service = new DefaultService(); - service.startAsync().awaitRunning(); - assertEquals(Service.State.RUNNING, service.state()); - service.stopAsync().awaitTerminated(); - assertEquals(Service.State.TERMINATED, service.state()); - } - - public void testStart_failed() throws Exception { - final Exception exception = new Exception("deliberate"); - AbstractIdleService service = - new DefaultService() { - @Override - protected void startUp() throws Exception { - throw exception; - } - }; - try { - service.startAsync().awaitRunning(); - fail(); - } catch (RuntimeException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(exception); - } - assertEquals(Service.State.FAILED, service.state()); - } - - public void testStop_failed() throws Exception { - final Exception exception = new Exception("deliberate"); - AbstractIdleService service = - new DefaultService() { - @Override - protected void shutDown() throws Exception { - throw exception; - } - }; - service.startAsync().awaitRunning(); - try { - service.stopAsync().awaitTerminated(); - fail(); - } catch (RuntimeException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(exception); - } - assertEquals(Service.State.FAILED, service.state()); - } - } - public void testStart() { TestService service = new TestService(); assertEquals(0, service.startUpCalled); @@ -113,12 +56,9 @@ protected void startUp() throws Exception { } }; assertEquals(0, service.startUpCalled); - try { - service.startAsync().awaitRunning(); - fail(); - } catch (RuntimeException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(exception); - } + RuntimeException e = + assertThrows(RuntimeException.class, () -> service.startAsync().awaitRunning()); + assertThat(e).hasCauseThat().isSameInstanceAs(exception); assertEquals(1, service.startUpCalled); assertEquals(Service.State.FAILED, service.state()); assertThat(service.transitionStates).containsExactly(Service.State.STARTING); @@ -160,12 +100,9 @@ protected void shutDown() throws Exception { service.startAsync().awaitRunning(); assertEquals(1, service.startUpCalled); assertEquals(0, service.shutDownCalled); - try { - service.stopAsync().awaitTerminated(); - fail(); - } catch (RuntimeException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(exception); - } + RuntimeException e = + assertThrows(RuntimeException.class, () -> service.stopAsync().awaitTerminated()); + assertThat(e).hasCauseThat().isSameInstanceAs(exception); assertEquals(1, service.startUpCalled); assertEquals(1, service.shutDownCalled); assertEquals(Service.State.FAILED, service.state()); @@ -200,14 +137,12 @@ protected String serviceName() { return "Foo"; } }; - try { - service.startAsync().awaitRunning(1, TimeUnit.MILLISECONDS); - fail("Expected timeout"); - } catch (TimeoutException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); - } + TimeoutException e = + assertThrows( + TimeoutException.class, () -> service.startAsync().awaitRunning(1, MILLISECONDS)); + assertThat(e) + .hasMessageThat() + .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); } private static class TestService extends AbstractIdleService { @@ -237,4 +172,54 @@ protected Executor executor() { return directExecutor(); } } + + // Functional tests using real thread. We only verify publicly visible state. + // Interaction assertions are done by the single-threaded unit tests. + + private static class DefaultService extends AbstractIdleService { + @Override + protected void startUp() throws Exception {} + + @Override + protected void shutDown() throws Exception {} + } + + public void testFunctionalServiceStartStop() { + AbstractIdleService service = new DefaultService(); + service.startAsync().awaitRunning(); + assertEquals(Service.State.RUNNING, service.state()); + service.stopAsync().awaitTerminated(); + assertEquals(Service.State.TERMINATED, service.state()); + } + + public void testFunctionalStart_failed() { + final Exception exception = new Exception("deliberate"); + AbstractIdleService service = + new DefaultService() { + @Override + protected void startUp() throws Exception { + throw exception; + } + }; + RuntimeException e = + assertThrows(RuntimeException.class, () -> service.startAsync().awaitRunning()); + assertThat(e).hasCauseThat().isSameInstanceAs(exception); + assertEquals(Service.State.FAILED, service.state()); + } + + public void testFunctionalStop_failed() { + final Exception exception = new Exception("deliberate"); + AbstractIdleService service = + new DefaultService() { + @Override + protected void shutDown() throws Exception { + throw exception; + } + }; + service.startAsync().awaitRunning(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> service.stopAsync().awaitTerminated()); + assertThat(e).hasCauseThat().isSameInstanceAs(exception); + assertEquals(Service.State.FAILED, service.state()); + } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractListeningExecutorServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractListeningExecutorServiceTest.java index 139581f7c1dd..bd2c95b0e520 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractListeningExecutorServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractListeningExecutorServiceTest.java @@ -23,12 +23,14 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link AbstractListeningExecutorService}. * * @author Colin Decker */ +@NullUnmarked public class AbstractListeningExecutorServiceTest extends TestCase { public void testSubmit() throws Exception { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java index a2411ad50e03..24f7c9b2e21c 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractScheduledServiceTest.java @@ -19,7 +19,10 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.AbstractScheduledService.Scheduler.newFixedDelaySchedule; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.util.concurrent.AbstractScheduledService.Cancellable; import com.google.common.util.concurrent.AbstractScheduledService.Scheduler; @@ -41,17 +44,19 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit test for {@link AbstractScheduledService}. * * @author Luke Sandberg */ - +@NullUnmarked public class AbstractScheduledServiceTest extends TestCase { - volatile Scheduler configuration = newFixedDelaySchedule(0, 10, TimeUnit.MILLISECONDS); - volatile ScheduledFuture future = null; + volatile Scheduler configuration = newFixedDelaySchedule(0, 10, MILLISECONDS); + volatile @Nullable ScheduledFuture future = null; volatile boolean atFixedRateCalled = false; volatile boolean withFixedDelayCalled = false; @@ -95,11 +100,7 @@ public void testFailOnExceptionFromRun() throws Exception { service.startAsync().awaitRunning(); service.runFirstBarrier.await(); service.runSecondBarrier.await(); - try { - future.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> future.get()); // An execution exception holds a runtime exception (from throwables.propagate) that holds our // original exception. assertEquals(service.runException, service.failureCause()); @@ -109,12 +110,9 @@ public void testFailOnExceptionFromRun() throws Exception { public void testFailOnExceptionFromStartUp() { TestService service = new TestService(); service.startUpException = new Exception(); - try { - service.startAsync().awaitRunning(); - fail(); - } catch (IllegalStateException e) { - assertEquals(service.startUpException, e.getCause()); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.startAsync().awaitRunning()); + assertThat(e).hasCauseThat().isEqualTo(service.startUpException); assertEquals(0, service.numberOfTimesRunCalled.get()); assertEquals(Service.State.FAILED, service.state()); } @@ -152,12 +150,9 @@ public void testFailOnExceptionFromShutDown() throws Exception { service.runFirstBarrier.await(); service.stopAsync(); service.runSecondBarrier.await(); - try { - service.awaitTerminated(); - fail(); - } catch (IllegalStateException e) { - assertEquals(service.shutDownException, e.getCause()); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.awaitTerminated()); + assertThat(e).hasCauseThat().isEqualTo(service.shutDownException); assertEquals(Service.State.FAILED, service.state()); } @@ -208,7 +203,7 @@ protected ScheduledExecutorService executor() { @Override protected Scheduler scheduler() { - return newFixedDelaySchedule(0, 1, TimeUnit.MILLISECONDS); + return newFixedDelaySchedule(0, 1, MILLISECONDS); } }; @@ -217,7 +212,7 @@ protected Scheduler scheduler() { service.awaitRunning(); service.stopAsync(); service.awaitTerminated(); - assertTrue(executor.get().awaitTermination(100, TimeUnit.MILLISECONDS)); + assertTrue(executor.get().awaitTermination(100, MILLISECONDS)); } public void testDefaultExecutorIsShutdownWhenServiceFails() throws Exception { @@ -240,17 +235,13 @@ protected ScheduledExecutorService executor() { @Override protected Scheduler scheduler() { - return newFixedDelaySchedule(0, 1, TimeUnit.MILLISECONDS); + return newFixedDelaySchedule(0, 1, MILLISECONDS); } }; - try { - service.startAsync().awaitRunning(); - fail("Expected service to fail during startup"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.startAsync().awaitRunning()); - assertTrue(executor.get().awaitTermination(100, TimeUnit.MILLISECONDS)); + assertTrue(executor.get().awaitTermination(100, MILLISECONDS)); } public void testSchedulerOnlyCalledOnce() throws Exception { @@ -277,7 +268,7 @@ public void testTimeout() { new AbstractScheduledService() { @Override protected Scheduler scheduler() { - return Scheduler.newFixedDelaySchedule(0, 1, TimeUnit.NANOSECONDS); + return Scheduler.newFixedDelaySchedule(0, 1, NANOSECONDS); } @Override @@ -293,14 +284,12 @@ protected String serviceName() { return "Foo"; } }; - try { - service.startAsync().awaitRunning(1, TimeUnit.MILLISECONDS); - fail("Expected timeout"); - } catch (TimeoutException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); - } + TimeoutException e = + assertThrows( + TimeoutException.class, () -> service.startAsync().awaitRunning(1, MILLISECONDS)); + assertThat(e) + .hasMessageThat() + .isEqualTo("Timed out waiting for Foo [STARTING] to reach the RUNNING state."); } private class TestService extends AbstractScheduledService { @@ -312,9 +301,9 @@ private class TestService extends AbstractScheduledService { AtomicInteger numberOfTimesRunCalled = new AtomicInteger(0); AtomicInteger numberOfTimesExecutorCalled = new AtomicInteger(0); AtomicInteger numberOfTimesSchedulerCalled = new AtomicInteger(0); - volatile Exception runException = null; - volatile Exception startUpException = null; - volatile Exception shutDownException = null; + volatile @Nullable Exception runException = null; + volatile @Nullable Exception startUpException = null; + volatile @Nullable Exception shutDownException = null; @Override protected void runOneIteration() throws Exception { @@ -363,324 +352,308 @@ protected Scheduler scheduler() { } } - public static class SchedulerTest extends TestCase { - // These constants are arbitrary and just used to make sure that the correct method is called - // with the correct parameters. - private static final int initialDelay = 10; - private static final int delay = 20; - private static final TimeUnit unit = TimeUnit.MILLISECONDS; - - // Unique runnable object used for comparison. - final Runnable testRunnable = - new Runnable() { - @Override - public void run() {} - }; - boolean called = false; - - private void assertSingleCallWithCorrectParameters( - Runnable command, long initialDelay, long delay, TimeUnit unit) { - assertFalse(called); // only called once. - called = true; - assertEquals(SchedulerTest.initialDelay, initialDelay); - assertEquals(SchedulerTest.delay, delay); - assertEquals(SchedulerTest.unit, unit); - assertEquals(testRunnable, command); - } - - public void testFixedRateSchedule() { - Scheduler schedule = Scheduler.newFixedRateSchedule(initialDelay, delay, unit); - Cancellable unused = - schedule.schedule( - null, - new ScheduledThreadPoolExecutor(1) { - @Override - public ScheduledFuture scheduleAtFixedRate( - Runnable command, long initialDelay, long period, TimeUnit unit) { - assertSingleCallWithCorrectParameters(command, initialDelay, delay, unit); - return new ThrowingScheduledFuture<>(); - } - }, - testRunnable); - assertTrue(called); - } + // Tests for Scheduler: - public void testFixedDelaySchedule() { - Scheduler schedule = newFixedDelaySchedule(initialDelay, delay, unit); - Cancellable unused = - schedule.schedule( - null, - new ScheduledThreadPoolExecutor(10) { - @Override - public ScheduledFuture scheduleWithFixedDelay( - Runnable command, long initialDelay, long delay, TimeUnit unit) { - assertSingleCallWithCorrectParameters(command, initialDelay, delay, unit); - return new ThrowingScheduledFuture<>(); - } - }, - testRunnable); - assertTrue(called); - } - - private static final class ThrowingScheduledFuture extends ForwardingFuture - implements ScheduledFuture { - @Override - protected Future delegate() { - throw new UnsupportedOperationException("test should not care about this"); - } + // These constants are arbitrary and just used to make sure that the correct method is called + // with the correct parameters. + private static final int INITIAL_DELAY = 10; + private static final int DELAY = 20; + private static final TimeUnit UNIT = MILLISECONDS; - @Override - public long getDelay(TimeUnit unit) { - throw new UnsupportedOperationException("test should not care about this"); - } + // Unique runnable object used for comparison. + final Runnable testRunnable = + new Runnable() { + @Override + public void run() {} + }; + boolean called = false; + + private void assertSingleCallWithCorrectParameters( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + assertFalse(called); // only called once. + called = true; + assertEquals(INITIAL_DELAY, initialDelay); + assertEquals(DELAY, delay); + assertEquals(UNIT, unit); + assertEquals(testRunnable, command); + } - @Override - public int compareTo(Delayed other) { - throw new UnsupportedOperationException("test should not care about this"); - } - } + public void testFixedRateSchedule() { + Scheduler schedule = Scheduler.newFixedRateSchedule(INITIAL_DELAY, DELAY, UNIT); + Cancellable unused = + schedule.schedule( + null, + new ScheduledThreadPoolExecutor(1) { + @Override + public ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + assertSingleCallWithCorrectParameters(command, initialDelay, period, unit); + return new ThrowingScheduledFuture<>(); + } + }, + testRunnable); + assertTrue(called); + } + public void testFixedDelaySchedule() { + Scheduler schedule = newFixedDelaySchedule(INITIAL_DELAY, DELAY, UNIT); + Cancellable unused = + schedule.schedule( + null, + new ScheduledThreadPoolExecutor(10) { + @Override + public ScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + assertSingleCallWithCorrectParameters(command, initialDelay, delay, unit); + return new ThrowingScheduledFuture<>(); + } + }, + testRunnable); + assertTrue(called); + } - public void testFixedDelayScheduleFarFuturePotentiallyOverflowingScheduleIsNeverReached() - throws Exception { - TestAbstractScheduledCustomService service = - new TestAbstractScheduledCustomService() { - @Override - protected Scheduler scheduler() { - return newFixedDelaySchedule(Long.MAX_VALUE, Long.MAX_VALUE, SECONDS); - } - }; - service.startAsync().awaitRunning(); - try { - service.firstBarrier.await(5, SECONDS); - fail(); - } catch (TimeoutException expected) { - } - assertEquals(0, service.numIterations.get()); - service.stopAsync(); - service.awaitTerminated(); + private static final class ThrowingScheduledFuture extends ForwardingFuture + implements ScheduledFuture { + @Override + protected Future delegate() { + throw new UnsupportedOperationException("test should not care about this"); } - - public void testCustomSchedulerFarFuturePotentiallyOverflowingScheduleIsNeverReached() - throws Exception { - TestAbstractScheduledCustomService service = - new TestAbstractScheduledCustomService() { - @Override - protected Scheduler scheduler() { - return new AbstractScheduledService.CustomScheduler() { - @Override - protected Schedule getNextSchedule() throws Exception { - return new Schedule(Long.MAX_VALUE, SECONDS); - } - }; - } - }; - service.startAsync().awaitRunning(); - try { - service.firstBarrier.await(5, SECONDS); - fail(); - } catch (TimeoutException expected) { - } - assertEquals(0, service.numIterations.get()); - service.stopAsync(); - service.awaitTerminated(); + @Override + public long getDelay(TimeUnit unit) { + throw new UnsupportedOperationException("test should not care about this"); } - private class TestCustomScheduler extends AbstractScheduledService.CustomScheduler { - public AtomicInteger scheduleCounter = new AtomicInteger(0); - - @Override - protected Schedule getNextSchedule() throws Exception { - scheduleCounter.incrementAndGet(); - return new Schedule(0, TimeUnit.SECONDS); - } + @Override + public int compareTo(Delayed other) { + throw new UnsupportedOperationException("test should not care about this"); } + } + public void testFixedDelayScheduleFarFuturePotentiallyOverflowingScheduleIsNeverReached() + throws Exception { + TestAbstractScheduledCustomService service = + new TestAbstractScheduledCustomService() { + @Override + protected Scheduler scheduler() { + return newFixedDelaySchedule(Long.MAX_VALUE, Long.MAX_VALUE, SECONDS); + } + }; + service.startAsync().awaitRunning(); + assertThrows(TimeoutException.class, () -> service.firstBarrier.await(5, SECONDS)); + assertEquals(0, service.numIterations.get()); + service.stopAsync(); + service.awaitTerminated(); + } - public void testCustomSchedule_startStop() throws Exception { - final CyclicBarrier firstBarrier = new CyclicBarrier(2); - final CyclicBarrier secondBarrier = new CyclicBarrier(2); - final AtomicBoolean shouldWait = new AtomicBoolean(true); - Runnable task = - new Runnable() { - @Override - public void run() { - try { - if (shouldWait.get()) { - firstBarrier.await(); - secondBarrier.await(); - } - } catch (Exception e) { - throw new RuntimeException(e); + public void testCustomSchedulerFarFuturePotentiallyOverflowingScheduleIsNeverReached() + throws Exception { + TestAbstractScheduledCustomService service = + new TestAbstractScheduledCustomService() { + @Override + protected Scheduler scheduler() { + return new AbstractScheduledService.CustomScheduler() { + @Override + protected Schedule getNextSchedule() throws Exception { + return new Schedule(Long.MAX_VALUE, SECONDS); } - } - }; - TestCustomScheduler scheduler = new TestCustomScheduler(); - Cancellable future = scheduler.schedule(null, Executors.newScheduledThreadPool(10), task); - firstBarrier.await(); - assertEquals(1, scheduler.scheduleCounter.get()); - secondBarrier.await(); - firstBarrier.await(); - assertEquals(2, scheduler.scheduleCounter.get()); - shouldWait.set(false); - secondBarrier.await(); - future.cancel(false); - } + }; + } + }; + service.startAsync().awaitRunning(); + assertThrows(TimeoutException.class, () -> service.firstBarrier.await(5, SECONDS)); + assertEquals(0, service.numIterations.get()); + service.stopAsync(); + service.awaitTerminated(); + } + private static class TestCustomScheduler extends AbstractScheduledService.CustomScheduler { + public AtomicInteger scheduleCounter = new AtomicInteger(0); - public void testCustomSchedulerServiceStop() throws Exception { - TestAbstractScheduledCustomService service = new TestAbstractScheduledCustomService(); - service.startAsync().awaitRunning(); - service.firstBarrier.await(); - assertEquals(1, service.numIterations.get()); - service.stopAsync(); - service.secondBarrier.await(); - service.awaitTerminated(); - // Sleep for a while just to ensure that our task wasn't called again. - Thread.sleep(unit.toMillis(3 * delay)); - assertEquals(1, service.numIterations.get()); + @Override + protected Schedule getNextSchedule() throws Exception { + scheduleCounter.incrementAndGet(); + return new Schedule(0, SECONDS); } + } - - public void testCustomScheduler_deadlock() throws InterruptedException, BrokenBarrierException { - final CyclicBarrier inGetNextSchedule = new CyclicBarrier(2); - // This will flakily deadlock, so run it multiple times to increase the flake likelihood - for (int i = 0; i < 1000; i++) { - Service service = - new AbstractScheduledService() { - @Override - protected void runOneIteration() {} - - @Override - protected Scheduler scheduler() { - return new CustomScheduler() { - @Override - protected Schedule getNextSchedule() throws Exception { - if (state() != State.STARTING) { - inGetNextSchedule.await(); - Thread.yield(); - throw new RuntimeException("boom"); - } - return new Schedule(0, TimeUnit.NANOSECONDS); - } - }; + public void testCustomSchedule_startStop() throws Exception { + final CyclicBarrier firstBarrier = new CyclicBarrier(2); + final CyclicBarrier secondBarrier = new CyclicBarrier(2); + final AtomicBoolean shouldWait = new AtomicBoolean(true); + Runnable task = + new Runnable() { + @Override + public void run() { + try { + if (shouldWait.get()) { + firstBarrier.await(); + secondBarrier.await(); } - }; - service.startAsync().awaitRunning(); - inGetNextSchedule.await(); - service.stopAsync(); - } - } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + TestCustomScheduler scheduler = new TestCustomScheduler(); + Cancellable future = scheduler.schedule(null, Executors.newScheduledThreadPool(10), task); + firstBarrier.await(); + assertEquals(1, scheduler.scheduleCounter.get()); + secondBarrier.await(); + firstBarrier.await(); + assertEquals(2, scheduler.scheduleCounter.get()); + shouldWait.set(false); + secondBarrier.await(); + future.cancel(false); + } + + public void testCustomSchedulerServiceStop() throws Exception { + TestAbstractScheduledCustomService service = new TestAbstractScheduledCustomService(); + service.startAsync().awaitRunning(); + service.firstBarrier.await(); + assertEquals(1, service.numIterations.get()); + service.stopAsync(); + service.secondBarrier.await(); + service.awaitTerminated(); + // Sleep for a while just to ensure that our task wasn't called again. + Thread.sleep(UNIT.toMillis(3 * DELAY)); + assertEquals(1, service.numIterations.get()); + } + public void testCustomScheduler_deadlock() throws InterruptedException, BrokenBarrierException { + final CyclicBarrier inGetNextSchedule = new CyclicBarrier(2); + // This will flakily deadlock, so run it multiple times to increase the flake likelihood + for (int i = 0; i < 1000; i++) { + Service service = + new AbstractScheduledService() { + @Override + protected void runOneIteration() {} - public void testBig() throws Exception { - TestAbstractScheduledCustomService service = - new TestAbstractScheduledCustomService() { @Override protected Scheduler scheduler() { - return new AbstractScheduledService.CustomScheduler() { + return new CustomScheduler() { @Override + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races protected Schedule getNextSchedule() throws Exception { - // Explicitly yield to increase the probability of a pathological scheduling. - Thread.yield(); - return new Schedule(0, TimeUnit.SECONDS); + if (state() != State.STARTING) { + inGetNextSchedule.await(); + Thread.yield(); + throw new RuntimeException("boom"); + } + return new Schedule(0, NANOSECONDS); } }; } }; - service.useBarriers = false; service.startAsync().awaitRunning(); - Thread.sleep(50); - service.useBarriers = true; - service.firstBarrier.await(); - int numIterations = service.numIterations.get(); + inGetNextSchedule.await(); service.stopAsync(); - service.secondBarrier.await(); - service.awaitTerminated(); - assertEquals(numIterations, service.numIterations.get()); } + } - private static class TestAbstractScheduledCustomService extends AbstractScheduledService { - final AtomicInteger numIterations = new AtomicInteger(0); - volatile boolean useBarriers = true; - final CyclicBarrier firstBarrier = new CyclicBarrier(2); - final CyclicBarrier secondBarrier = new CyclicBarrier(2); - - @Override - protected void runOneIteration() throws Exception { - numIterations.incrementAndGet(); - if (useBarriers) { - firstBarrier.await(); - secondBarrier.await(); - } - } - - @Override - protected ScheduledExecutorService executor() { - // use a bunch of threads so that weird overlapping schedules are more likely to happen. - return Executors.newScheduledThreadPool(10); - } - - @Override - protected Scheduler scheduler() { - return new CustomScheduler() { + public void testBig() throws Exception { + TestAbstractScheduledCustomService service = + new TestAbstractScheduledCustomService() { @Override - protected Schedule getNextSchedule() throws Exception { - return new Schedule(delay, unit); + protected Scheduler scheduler() { + return new AbstractScheduledService.CustomScheduler() { + @Override + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races + protected Schedule getNextSchedule() throws Exception { + // Explicitly yield to increase the probability of a pathological scheduling. + Thread.yield(); + return new Schedule(0, SECONDS); + } + }; } }; + service.useBarriers = false; + service.startAsync().awaitRunning(); + Thread.sleep(50); + service.useBarriers = true; + service.firstBarrier.await(); + int numIterations = service.numIterations.get(); + service.stopAsync(); + service.secondBarrier.await(); + service.awaitTerminated(); + assertEquals(numIterations, service.numIterations.get()); + } + + private static class TestAbstractScheduledCustomService extends AbstractScheduledService { + final AtomicInteger numIterations = new AtomicInteger(0); + volatile boolean useBarriers = true; + final CyclicBarrier firstBarrier = new CyclicBarrier(2); + final CyclicBarrier secondBarrier = new CyclicBarrier(2); + + @Override + protected void runOneIteration() throws Exception { + numIterations.incrementAndGet(); + if (useBarriers) { + firstBarrier.await(); + secondBarrier.await(); } } + @Override + protected ScheduledExecutorService executor() { + // use a bunch of threads so that weird overlapping schedules are more likely to happen. + return Executors.newScheduledThreadPool(10); + } - public void testCustomSchedulerFailure() throws Exception { - TestFailingCustomScheduledService service = new TestFailingCustomScheduledService(); - service.startAsync().awaitRunning(); - for (int i = 1; i < 4; i++) { - service.firstBarrier.await(); - assertEquals(i, service.numIterations.get()); - service.secondBarrier.await(); - } - Thread.sleep(1000); - try { - service.stopAsync().awaitTerminated(100, TimeUnit.SECONDS); - fail(); - } catch (IllegalStateException e) { - assertEquals(State.FAILED, service.state()); - } + @Override + protected Scheduler scheduler() { + return new CustomScheduler() { + @Override + protected Schedule getNextSchedule() throws Exception { + return new Schedule(DELAY, UNIT); + } + }; } + } - private static class TestFailingCustomScheduledService extends AbstractScheduledService { - final AtomicInteger numIterations = new AtomicInteger(0); - final CyclicBarrier firstBarrier = new CyclicBarrier(2); - final CyclicBarrier secondBarrier = new CyclicBarrier(2); + public void testCustomSchedulerFailure() throws Exception { + TestFailingCustomScheduledService service = new TestFailingCustomScheduledService(); + service.startAsync().awaitRunning(); + for (int i = 1; i < 4; i++) { + service.firstBarrier.await(); + assertEquals(i, service.numIterations.get()); + service.secondBarrier.await(); + } + Thread.sleep(1000); + assertThrows( + IllegalStateException.class, () -> service.stopAsync().awaitTerminated(100, SECONDS)); + assertEquals(State.FAILED, service.state()); + } - @Override - protected void runOneIteration() throws Exception { - numIterations.incrementAndGet(); - firstBarrier.await(); - secondBarrier.await(); - } + private static class TestFailingCustomScheduledService extends AbstractScheduledService { + final AtomicInteger numIterations = new AtomicInteger(0); + final CyclicBarrier firstBarrier = new CyclicBarrier(2); + final CyclicBarrier secondBarrier = new CyclicBarrier(2); - @Override - protected ScheduledExecutorService executor() { - // use a bunch of threads so that weird overlapping schedules are more likely to happen. - return Executors.newScheduledThreadPool(10); - } + @Override + protected void runOneIteration() throws Exception { + numIterations.incrementAndGet(); + firstBarrier.await(); + secondBarrier.await(); + } - @Override - protected Scheduler scheduler() { - return new CustomScheduler() { - @Override - protected Schedule getNextSchedule() throws Exception { - if (numIterations.get() > 2) { - throw new IllegalStateException("Failed"); - } - return new Schedule(delay, unit); + @Override + protected ScheduledExecutorService executor() { + // use a bunch of threads so that weird overlapping schedules are more likely to happen. + return Executors.newScheduledThreadPool(10); + } + + @Override + protected Scheduler scheduler() { + return new CustomScheduler() { + @Override + protected Schedule getNextSchedule() throws Exception { + if (numIterations.get() > 2) { + throw new IllegalStateException("Failed"); } - }; - } + return new Schedule(DELAY, UNIT); + } + }; } } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java index e3c22a8673df..f3828d7dacaa 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractServiceTest.java @@ -19,7 +19,9 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.lang.Thread.currentThread; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -30,16 +32,17 @@ import java.lang.Thread.UncaughtExceptionHandler; import java.util.List; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link AbstractService}. * * @author Jesse Wilson */ +@NullUnmarked public class AbstractServiceTest extends TestCase { private static final long LONG_TIMEOUT_MILLIS = 10000; @@ -330,7 +333,6 @@ protected void doStop() { } } - public void testAwaitTerminated() throws Exception { final NoOpService service = new NoOpService(); Thread waiter = @@ -348,8 +350,7 @@ public void run() { assertFalse(waiter.isAlive()); } - - public void testAwaitTerminated_FailedService() throws Exception { + public void testAwaitTerminated_failedService() throws Exception { final ManualSwitchedService service = new ManualSwitchedService(); final AtomicReference exception = Atomics.newReference(); Thread waiter = @@ -376,7 +377,6 @@ public void run() { assertThat(exception.get()).hasCauseThat().isEqualTo(EXCEPTION); } - public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable { ThreadedService service = new ThreadedService(); RecordingListener listener = RecordingListener.record(service); @@ -394,7 +394,6 @@ public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable { listener.getStateHistory()); } - public void testThreadedServiceStopIdempotence() throws Throwable { ThreadedService service = new ThreadedService(); @@ -410,7 +409,6 @@ public void testThreadedServiceStopIdempotence() throws Throwable { throwIfSet(thrownByExecutionThread); } - public void testThreadedServiceStopIdempotenceAfterWait() throws Throwable { ThreadedService service = new ThreadedService(); @@ -428,7 +426,6 @@ public void testThreadedServiceStopIdempotenceAfterWait() throws Throwable { throwIfSet(thrownByExecutionThread); } - public void testThreadedServiceStopIdempotenceDoubleWait() throws Throwable { ThreadedService service = new ThreadedService(); @@ -455,12 +452,9 @@ public void testManualServiceFailureIdempotence() { service.notifyFailed(new Exception("1")); service.notifyFailed(new Exception("2")); assertThat(service.failureCause()).hasMessageThat().isEqualTo("1"); - try { - service.awaitRunning(); - fail(); - } catch (IllegalStateException e) { - assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("1"); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.awaitRunning()); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("1"); } private class ThreadedService extends AbstractService { @@ -537,11 +531,7 @@ public void testStopUnstartedService() throws Exception { service.stopAsync(); assertEquals(State.TERMINATED, service.state()); - try { - service.startAsync(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.startAsync()); assertEquals(State.TERMINATED, Iterables.getOnlyElement(listener.getStateHistory())); } @@ -549,13 +539,10 @@ public void testFailingServiceStartAndWait() throws Exception { StartFailingService service = new StartFailingService(); RecordingListener listener = RecordingListener.record(service); - try { - service.startAsync().awaitRunning(); - fail(); - } catch (IllegalStateException e) { - assertEquals(EXCEPTION, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.startAsync().awaitRunning()); + assertEquals(EXCEPTION, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); assertEquals(ImmutableList.of(State.STARTING, State.FAILED), listener.getStateHistory()); } @@ -564,13 +551,10 @@ public void testFailingServiceStopAndWait_stopFailing() throws Exception { RecordingListener listener = RecordingListener.record(service); service.startAsync().awaitRunning(); - try { - service.stopAsync().awaitTerminated(); - fail(); - } catch (IllegalStateException e) { - assertEquals(EXCEPTION, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.stopAsync().awaitTerminated()); + assertEquals(EXCEPTION, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); assertEquals( ImmutableList.of(State.STARTING, State.RUNNING, State.STOPPING, State.FAILED), listener.getStateHistory()); @@ -581,13 +565,10 @@ public void testFailingServiceStopAndWait_runFailing() throws Exception { RecordingListener listener = RecordingListener.record(service); service.startAsync(); - try { - service.awaitRunning(); - fail(); - } catch (IllegalStateException e) { - assertEquals(EXCEPTION, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.awaitRunning()); + assertEquals(EXCEPTION, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); assertEquals( ImmutableList.of(State.STARTING, State.RUNNING, State.FAILED), listener.getStateHistory()); } @@ -596,13 +577,10 @@ public void testThrowingServiceStartAndWait() throws Exception { StartThrowingService service = new StartThrowingService(); RecordingListener listener = RecordingListener.record(service); - try { - service.startAsync().awaitRunning(); - fail(); - } catch (IllegalStateException e) { - assertEquals(service.exception, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(service.exception); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.startAsync().awaitRunning()); + assertEquals(service.exception, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(service.exception); assertEquals(ImmutableList.of(State.STARTING, State.FAILED), listener.getStateHistory()); } @@ -611,13 +589,10 @@ public void testThrowingServiceStopAndWait_stopThrowing() throws Exception { RecordingListener listener = RecordingListener.record(service); service.startAsync().awaitRunning(); - try { - service.stopAsync().awaitTerminated(); - fail(); - } catch (IllegalStateException e) { - assertEquals(service.exception, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(service.exception); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.stopAsync().awaitTerminated()); + assertEquals(service.exception, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(service.exception); assertEquals( ImmutableList.of(State.STARTING, State.RUNNING, State.STOPPING, State.FAILED), listener.getStateHistory()); @@ -628,40 +603,25 @@ public void testThrowingServiceStopAndWait_runThrowing() throws Exception { RecordingListener listener = RecordingListener.record(service); service.startAsync(); - try { - service.awaitTerminated(); - fail(); - } catch (IllegalStateException e) { - assertEquals(service.exception, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(service.exception); - } + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.awaitTerminated()); + assertEquals(service.exception, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(service.exception); assertEquals( ImmutableList.of(State.STARTING, State.RUNNING, State.FAILED), listener.getStateHistory()); } public void testFailureCause_throwsIfNotFailed() { StopFailingService service = new StopFailingService(); - try { - service.failureCause(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.failureCause()); service.startAsync().awaitRunning(); - try { - service.failureCause(); - fail(); - } catch (IllegalStateException expected) { - } - try { - service.stopAsync().awaitTerminated(); - fail(); - } catch (IllegalStateException e) { - assertEquals(EXCEPTION, service.failureCause()); - assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); - } + assertThrows(IllegalStateException.class, () -> service.failureCause()); + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> service.stopAsync().awaitTerminated()); + assertEquals(EXCEPTION, service.failureCause()); + assertThat(e).hasCauseThat().isEqualTo(EXCEPTION); } - public void testAddListenerAfterFailureDoesntCauseDeadlock() throws InterruptedException { final StartFailingService service = new StartFailingService(); service.startAsync(); @@ -681,7 +641,6 @@ public void run() { assertFalse(thread + " is deadlocked", thread.isAlive()); } - public void testListenerDoesntDeadlockOnStartAndWaitFromRunning() throws Exception { final NoOpThreadedService service = new NoOpThreadedService(); service.addListener( @@ -692,11 +651,10 @@ public void running() { } }, directExecutor()); - service.startAsync().awaitRunning(LONG_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + service.startAsync().awaitRunning(LONG_TIMEOUT_MILLIS, MILLISECONDS); service.stopAsync(); } - public void testListenerDoesntDeadlockOnStopAndWaitFromTerminated() throws Exception { final NoOpThreadedService service = new NoOpThreadedService(); service.addListener( @@ -921,40 +879,24 @@ public synchronized void failed(State from, Throwable failure) { public void testNotifyStartedWhenNotStarting() { AbstractService service = new DefaultService(); - try { - service.notifyStarted(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.notifyStarted()); } public void testNotifyStoppedWhenNotRunning() { AbstractService service = new DefaultService(); - try { - service.notifyStopped(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.notifyStopped()); } public void testNotifyFailedWhenNotStarted() { AbstractService service = new DefaultService(); - try { - service.notifyFailed(new Exception()); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.notifyFailed(new Exception())); } public void testNotifyFailedWhenTerminated() { NoOpService service = new NoOpService(); service.startAsync().awaitRunning(); service.stopAsync().awaitTerminated(); - try { - service.notifyFailed(new Exception()); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> service.notifyFailed(new Exception())); } private static class DefaultService extends AbstractService { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java index fec93946f794..727db2f2f20a 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AggregateFutureStateFallbackAtomicHelperTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; /** * Tests our AtomicHelper fallback strategy in AggregateFutureState. @@ -40,11 +41,12 @@ * * * To force selection of our fallback strategies we load {@link AggregateFutureState} (and all of - * {@code com.google.common.util.concurrent} in degenerate class loaders which make certain platform - * classes unavailable. Then we construct a test suite so we can run the normal FuturesTest test - * methods in these degenerate classloaders. + * {@code com.google.common.util.concurrent}) in degenerate class loaders which make certain + * platform classes unavailable. Then we construct a test suite so we can run the normal FuturesTest + * test methods in these degenerate classloaders. */ +@NullUnmarked public class AggregateFutureStateFallbackAtomicHelperTest extends TestCase { /** @@ -66,7 +68,13 @@ public static TestSuite suite() { // corresponding method on FuturesTest in the correct classloader. TestSuite suite = new TestSuite(AggregateFutureStateFallbackAtomicHelperTest.class.getName()); for (Method method : FuturesTest.class.getDeclaredMethods()) { - if (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("test")) { + if (Modifier.isPublic(method.getModifiers()) + && method.getName().startsWith("test") + /* + * When we block access to AtomicReferenceFieldUpdater, we can't even reflect on + * AbstractFuture, since it declares methods that use that type in their signatures. + */ + && !method.getName().equals("testFutures_nullChecks")) { suite.addTest( TestSuite.createTest( AggregateFutureStateFallbackAtomicHelperTest.class, method.getName())); @@ -104,8 +112,9 @@ private void runTestMethod(ClassLoader classLoader) throws Exception { private void checkHelperVersion(ClassLoader classLoader, String expectedHelperClassName) throws Exception { // Make sure we are actually running with the expected helper implementation - Class abstractFutureClass = classLoader.loadClass(AggregateFutureState.class.getName()); - Field helperField = abstractFutureClass.getDeclaredField("ATOMIC_HELPER"); + Class aggregateFutureStateClass = + classLoader.loadClass(AggregateFutureState.class.getName()); + Field helperField = aggregateFutureStateClass.getDeclaredField("ATOMIC_HELPER"); helperField.setAccessible(true); assertEquals(expectedHelperClassName, helperField.get(null).getClass().getSimpleName()); } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AndroidIncompatible.java b/android/guava-tests/test/com/google/common/util/concurrent/AndroidIncompatible.java index b18a1535e84c..6f6dcd239665 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AndroidIncompatible.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AndroidIncompatible.java @@ -30,7 +30,7 @@ /** * Signifies that a test should not be run under Android. This annotation is respected only by our * Google-internal Android suite generators. Note that those generators also suppress any test - * annotated with MediumTest or LargeTest. + * annotated with LargeTest. * *

    For more discussion, see {@linkplain com.google.common.base.AndroidIncompatible the * documentation on another copy of this annotation}. diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleArrayTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleArrayTest.java index 70c186d345da..7e3d63320af5 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleArrayTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleArrayTest.java @@ -13,9 +13,16 @@ package com.google.common.util.concurrent; +import static org.junit.Assert.assertThrows; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.testing.NullPointerTester; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; /** Unit test for {@link AtomicDoubleArray}. */ +@NullUnmarked public class AtomicDoubleArrayTest extends JSR166TestCase { private static final double[] VALUES = { @@ -48,6 +55,14 @@ static void assertBitEquals(double x, double y) { assertEquals(Double.doubleToRawLongBits(x), Double.doubleToRawLongBits(y)); } + @J2ktIncompatible + @GwtIncompatible // NullPointerTester + public void testNulls() { + new NullPointerTester().testAllPublicStaticMethods(AtomicDoubleArray.class); + new NullPointerTester().testAllPublicConstructors(AtomicDoubleArray.class); + new NullPointerTester().testAllPublicInstanceMethods(new AtomicDoubleArray(1)); + } + /** constructor creates array of given size with all elements zero */ public void testConstructor() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); @@ -59,11 +74,7 @@ public void testConstructor() { /** constructor with null array throws NPE */ public void testConstructor2NPE() { double[] a = null; - try { - new AtomicDoubleArray(a); - fail(); - } catch (NullPointerException success) { - } + assertThrows(NullPointerException.class, () -> new AtomicDoubleArray(a)); } /** constructor with array is of same size and has all elements */ @@ -79,63 +90,27 @@ public void testConstructor2() { public void testConstructorEmptyArray() { AtomicDoubleArray aa = new AtomicDoubleArray(new double[0]); assertEquals(0, aa.length()); - try { - aa.get(0); - fail(); - } catch (IndexOutOfBoundsException success) { - } + assertThrows(IndexOutOfBoundsException.class, () -> aa.get(0)); } /** constructor with length zero has size 0 and contains no elements */ public void testConstructorZeroLength() { AtomicDoubleArray aa = new AtomicDoubleArray(0); assertEquals(0, aa.length()); - try { - aa.get(0); - fail(); - } catch (IndexOutOfBoundsException success) { - } + assertThrows(IndexOutOfBoundsException.class, () -> aa.get(0)); } /** get and set for out of bound indices throw IndexOutOfBoundsException */ public void testIndexing() { AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int index : new int[] {-1, SIZE}) { - try { - aa.get(index); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.set(index, 1.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.lazySet(index, 1.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.compareAndSet(index, 1.0, 2.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.weakCompareAndSet(index, 1.0, 2.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.getAndAdd(index, 1.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } - try { - aa.addAndGet(index, 1.0); - fail(); - } catch (IndexOutOfBoundsException success) { - } + assertThrows(IndexOutOfBoundsException.class, () -> aa.get(index)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.set(index, 1.0)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.lazySet(index, 1.0)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.compareAndSet(index, 1.0, 2.0)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.weakCompareAndSet(index, 1.0, 2.0)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.getAndAdd(index, 1.0)); + assertThrows(IndexOutOfBoundsException.class, () -> aa.addAndGet(index, 1.0)); } } @@ -181,13 +156,14 @@ public void testCompareAndSet() { } /** compareAndSet in one thread enables another waiting for value to succeed */ - public void testCompareAndSetInMultipleThreads() throws InterruptedException { final AtomicDoubleArray a = new AtomicDoubleArray(1); a.set(0, 1.0); Thread t = newStartedThread( new CheckedRunnable() { + @Override + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public void realRun() { while (!a.compareAndSet(0, 2.0, 3.0)) { Thread.yield(); @@ -210,7 +186,8 @@ public void testWeakCompareAndSet() { assertBitEquals(prev, aa.get(i)); assertFalse(aa.weakCompareAndSet(i, unused, x)); assertBitEquals(prev, aa.get(i)); - while (!aa.weakCompareAndSet(i, prev, x)) {; + while (!aa.weakCompareAndSet(i, prev, x)) { + ; } assertBitEquals(x, aa.get(i)); prev = x; @@ -270,6 +247,7 @@ class Counter extends CheckedRunnable { aa = a; } + @Override public void realRun() { for (; ; ) { boolean done = true; @@ -294,7 +272,6 @@ public void realRun() { * Multiple threads using same array of counters successfully update a number of times equal to * total count */ - public void testCountingInMultipleThreads() throws InterruptedException { final AtomicDoubleArray aa = new AtomicDoubleArray(SIZE); for (int i = 0; i < SIZE; i++) { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleTest.java index fe68e009952f..73d6025a26e6 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicDoubleTest.java @@ -13,8 +13,12 @@ package com.google.common.util.concurrent; +import static com.google.common.truth.Truth.assertThat; + +import org.jspecify.annotations.NullUnmarked; /** Unit test for {@link AtomicDouble}. */ +@NullUnmarked public class AtomicDoubleTest extends JSR166TestCase { private static final double[] VALUES = { @@ -97,12 +101,13 @@ public void testCompareAndSet() { } /** compareAndSet in one thread enables another waiting for value to succeed */ - public void testCompareAndSetInMultipleThreads() throws Exception { final AtomicDouble at = new AtomicDouble(1.0); Thread t = newStartedThread( new CheckedRunnable() { + @Override + @SuppressWarnings("ThreadPriorityCheck") // doing our best to test for races public void realRun() { while (!at.compareAndSet(2.0, 3.0)) { Thread.yield(); @@ -124,7 +129,8 @@ public void testWeakCompareAndSet() { assertBitEquals(prev, at.get()); assertFalse(at.weakCompareAndSet(unused, x)); assertBitEquals(prev, at.get()); - while (!at.weakCompareAndSet(prev, x)) {; + while (!at.weakCompareAndSet(prev, x)) { + ; } assertBitEquals(x, at.get()); prev = x; @@ -225,7 +231,7 @@ public void testFloatValue() { /** doubleValue returns current value. */ public void testDoubleValue() { AtomicDouble at = new AtomicDouble(); - assertEquals(0.0d, at.doubleValue()); + assertThat(at.doubleValue()).isEqualTo(0.0d); for (double x : VALUES) { at.set(x); assertBitEquals(x, at.doubleValue()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java index 4765825855ba..994ddaca9906 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapBasherTest.java @@ -16,44 +16,48 @@ package com.google.common.util.concurrent; +import static java.util.concurrent.TimeUnit.SECONDS; + import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.util.ArrayList; import java.util.Random; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Basher test for {@link AtomicLongMap}. * * @author mike nonemacher */ +@J2ktIncompatible // threads @GwtIncompatible // threads - +@NullUnmarked public class AtomicLongMapBasherTest extends TestCase { private final Random random = new Random(301); - public void testModify_basher() throws InterruptedException { + public void testModify_basher() throws Exception { int nTasks = 3000; int nThreads = 100; final int getsPerTask = 1000; final int deltaRange = 10000; final String key = "key"; - final AtomicLong sum = new AtomicLong(); final AtomicLongMap map = AtomicLongMap.create(); ExecutorService threadPool = Executors.newFixedThreadPool(nThreads); + ArrayList> futures = new ArrayList<>(); for (int i = 0; i < nTasks; i++) { - @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored - Future possiblyIgnoredError = + futures.add( threadPool.submit( - new Runnable() { + new Callable() { @Override - public void run() { - int threadSum = 0; + public Long call() { + long threadSum = 0; for (int j = 0; j < getsPerTask; j++) { long delta = random.nextInt(deltaRange); int behavior = random.nextInt(10); @@ -106,14 +110,16 @@ public void run() { throw new AssertionError(); } } - sum.addAndGet(threadSum); + return threadSum; } - }); + })); } - threadPool.shutdown(); - assertTrue(threadPool.awaitTermination(300, TimeUnit.SECONDS)); - - assertEquals(sum.get(), map.get(key)); + assertTrue(threadPool.awaitTermination(300, SECONDS)); + long sum = 0; + for (Future f : futures) { + sum += f.get(); + } + assertEquals(sum, map.get(key)); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapTest.java index 878ea5ec87ac..0eccd0c43be0 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicLongMapTest.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; @@ -26,6 +27,7 @@ import java.util.Random; import java.util.Set; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link AtomicLongMap}. @@ -33,12 +35,14 @@ * @author mike nonemacher */ @GwtCompatible(emulated = true) +@NullUnmarked public class AtomicLongMapTest extends TestCase { private static final int ITERATIONS = 100; private static final int MAX_ADDEND = 100; private final Random random = new Random(301); + @J2ktIncompatible @GwtIncompatible // NullPointerTester public void testNulls() { NullPointerTester tester = new NullPointerTester(); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AtomicsTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AtomicsTest.java index b903e6ce8623..1cb9fd8c4c00 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AtomicsTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AtomicsTest.java @@ -16,15 +16,19 @@ package com.google.common.util.concurrent; +import static org.junit.Assert.assertThrows; + import com.google.common.testing.NullPointerTester; import java.util.concurrent.atomic.AtomicReferenceArray; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link Atomics}. * * @author Kurt Alfred Kluever */ +@NullUnmarked public class AtomicsTest extends TestCase { private static final Object OBJECT = new Object(); @@ -44,19 +48,11 @@ public void testNewReferenceArray_withLength() throws Exception { for (int i = 0; i < length; ++i) { assertEquals(null, refArray.get(i)); } - try { - refArray.get(length); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> refArray.get(length)); } public void testNewReferenceArray_withNegativeLength() throws Exception { - try { - Atomics.newReferenceArray(-1); - fail(); - } catch (NegativeArraySizeException expected) { - } + assertThrows(NegativeArraySizeException.class, () -> Atomics.newReferenceArray(-1)); } public void testNewReferenceArray_withStringArray() throws Exception { @@ -65,19 +61,11 @@ public void testNewReferenceArray_withStringArray() throws Exception { for (int i = 0; i < array.length; ++i) { assertEquals(array[i], refArray.get(i)); } - try { - refArray.get(array.length); - fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows(IndexOutOfBoundsException.class, () -> refArray.get(array.length)); } public void testNewReferenceArray_withNullArray() throws Exception { - try { - Atomics.newReferenceArray(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> Atomics.newReferenceArray(null)); } public void testNullPointers() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/CallablesTest.java b/android/guava-tests/test/com/google/common/util/concurrent/CallablesTest.java index 5bc92dc20633..75de1d6df65f 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/CallablesTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/CallablesTest.java @@ -17,15 +17,20 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import java.security.Permission; +import com.google.common.util.concurrent.TestExceptions.SomeCheckedException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@link Callables}. @@ -33,8 +38,10 @@ * @author Isaac Shum */ @GwtCompatible(emulated = true) +@NullUnmarked public class CallablesTest extends TestCase { + @J2ktIncompatible // TODO(b/324550390): Enable public void testReturning() throws Exception { assertNull(Callables.returning(null).call()); @@ -45,6 +52,7 @@ public void testReturning() throws Exception { assertSame(value, callable.call()); } + @J2ktIncompatible @GwtIncompatible public void testAsAsyncCallable() throws Exception { final String expected = "MyCallableString"; @@ -57,12 +65,13 @@ public String call() throws Exception { }; AsyncCallable asyncCallable = - Callables.asAsyncCallable(callable, MoreExecutors.newDirectExecutorService()); + Callables.asAsyncCallable(callable, newDirectExecutorService()); ListenableFuture future = asyncCallable.call(); assertSame(expected, future.get()); } + @J2ktIncompatible @GwtIncompatible public void testAsAsyncCallable_exception() throws Exception { final Exception expected = new IllegalArgumentException(); @@ -75,25 +84,22 @@ public String call() throws Exception { }; AsyncCallable asyncCallable = - Callables.asAsyncCallable(callable, MoreExecutors.newDirectExecutorService()); + Callables.asAsyncCallable(callable, newDirectExecutorService()); ListenableFuture future = asyncCallable.call(); - try { - future.get(); - fail("Expected exception to be thrown"); - } catch (ExecutionException e) { - assertThat(e).hasCauseThat().isSameInstanceAs(expected); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> future.get()); + assertThat(e).hasCauseThat().isSameInstanceAs(expected); } + @J2ktIncompatible @GwtIncompatible // threads public void testRenaming() throws Exception { String oldName = Thread.currentThread().getName(); final Supplier newName = Suppliers.ofInstance("MyCrazyThreadName"); - Callable callable = - new Callable() { + Callable<@Nullable Void> callable = + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { assertEquals(Thread.currentThread().getName(), newName.get()); return null; } @@ -102,57 +108,21 @@ public Void call() throws Exception { assertEquals(oldName, Thread.currentThread().getName()); } + @J2ktIncompatible @GwtIncompatible // threads public void testRenaming_exceptionalReturn() throws Exception { String oldName = Thread.currentThread().getName(); final Supplier newName = Suppliers.ofInstance("MyCrazyThreadName"); - class MyException extends Exception {} - Callable callable = - new Callable() { + Callable<@Nullable Void> callable = + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { assertEquals(Thread.currentThread().getName(), newName.get()); - throw new MyException(); + throw new SomeCheckedException(); } }; - try { - Callables.threadRenaming(callable, newName).call(); - fail(); - } catch (MyException expected) { - } + assertThrows( + SomeCheckedException.class, () -> Callables.threadRenaming(callable, newName).call()); assertEquals(oldName, Thread.currentThread().getName()); } - - @GwtIncompatible // threads - - public void testRenaming_noPermissions() throws Exception { - System.setSecurityManager( - new SecurityManager() { - @Override - public void checkAccess(Thread t) { - throw new SecurityException(); - } - - @Override - public void checkPermission(Permission perm) { - // Do nothing so we can clear the security manager at the end - } - }); - try { - final String oldName = Thread.currentThread().getName(); - Supplier newName = Suppliers.ofInstance("MyCrazyThreadName"); - Callable callable = - new Callable() { - @Override - public Void call() throws Exception { - assertEquals(Thread.currentThread().getName(), oldName); - return null; - } - }; - Callables.threadRenaming(callable, newName).call(); - assertEquals(oldName, Thread.currentThread().getName()); - } finally { - System.setSecurityManager(null); - } - } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ClassPathUtil.java b/android/guava-tests/test/com/google/common/util/concurrent/ClassPathUtil.java index a36aa34e4e56..16e88a5df94c 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ClassPathUtil.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ClassPathUtil.java @@ -23,9 +23,11 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import org.jspecify.annotations.NullUnmarked; // TODO(b/65488446): Make this a public API. /** Utility method to parse the system class path. */ +@NullUnmarked final class ClassPathUtil { private ClassPathUtil() {} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToFutureTest.java new file mode 100644 index 000000000000..c42357cac0b2 --- /dev/null +++ b/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToFutureTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; +import static org.junit.Assert.assertThrows; + +import com.google.common.util.concurrent.ClosingFuture.ClosingCallable; +import com.google.common.util.concurrent.ClosingFuture.DeferredCloser; +import java.io.Closeable; +import java.util.concurrent.ExecutionException; +import org.jspecify.annotations.NullUnmarked; + +/** Tests for {@link ClosingFuture} that exercise {@link ClosingFuture#finishToFuture()}. */ +@NullUnmarked +public class ClosingFutureFinishToFutureTest extends AbstractClosingFutureTest { + public void testFinishToFuture_throwsIfCalledTwice() throws Exception { + ClosingFuture closingFuture = + ClosingFuture.submit( + new ClosingCallable() { + @Override + public Closeable call(DeferredCloser closer) throws Exception { + return closer.eventuallyClose(mockCloseable, executor); + } + }, + executor); + FluentFuture unused = closingFuture.finishToFuture(); + assertThrows( + IllegalStateException.class, + () -> { + FluentFuture unused2 = closingFuture.finishToFuture(); + }); + } + + public void testFinishToFuture_throwsAfterCallingFinishToValueAndCloser() throws Exception { + ClosingFuture closingFuture = + ClosingFuture.submit( + new ClosingCallable() { + @Override + public Closeable call(DeferredCloser closer) throws Exception { + return closer.eventuallyClose(mockCloseable, executor); + } + }, + executor); + closingFuture.finishToValueAndCloser(new NoOpValueAndCloserConsumer<>(), directExecutor()); + assertThrows( + IllegalStateException.class, + () -> { + FluentFuture unused = closingFuture.finishToFuture(); + }); + } + + public void testFinishToFuture_preventsFurtherDerivation() { + ClosingFuture closingFuture = ClosingFuture.from(immediateFuture("value1")); + FluentFuture unused = closingFuture.finishToFuture(); + assertDerivingThrowsIllegalStateException(closingFuture); + } + + @Override + T getFinalValue(ClosingFuture closingFuture) throws ExecutionException { + return getUninterruptibly(closingFuture.finishToFuture()); + } + + @Override + void assertFinallyFailsWithException(ClosingFuture closingFuture) { + assertThatFutureFailsWithException(closingFuture.finishToFuture()); + } + + @Override + void assertBecomesCanceled(ClosingFuture closingFuture) throws ExecutionException { + assertThatFutureBecomesCancelled(closingFuture.finishToFuture()); + } + + @Override + void cancelFinalStepAndWait(ClosingFuture closingFuture) { + assertThat(closingFuture.finishToFuture().cancel(false)).isTrue(); + waitUntilClosed(closingFuture); + futureCancelled.countDown(); + } +} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToValueAndCloserTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToValueAndCloserTest.java new file mode 100644 index 000000000000..c03230315189 --- /dev/null +++ b/android/guava-tests/test/com/google/common/util/concurrent/ClosingFutureFinishToValueAndCloserTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2017 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination; +import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; + +import com.google.common.util.concurrent.ClosingFuture.ClosingCallable; +import com.google.common.util.concurrent.ClosingFuture.DeferredCloser; +import com.google.common.util.concurrent.ClosingFuture.ValueAndCloser; +import com.google.common.util.concurrent.ClosingFuture.ValueAndCloserConsumer; +import java.io.Closeable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import org.jspecify.annotations.NullUnmarked; + +/** + * Tests for {@link ClosingFuture} that exercise {@link + * ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor)}. + */ +@NullUnmarked +public class ClosingFutureFinishToValueAndCloserTest extends AbstractClosingFutureTest { + private final ExecutorService finishToValueAndCloserExecutor = newSingleThreadExecutor(); + private volatile ValueAndCloser valueAndCloser; + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + assertWithMessage("finishToValueAndCloserExecutor was shut down") + .that(shutdownAndAwaitTermination(finishToValueAndCloserExecutor, 10, SECONDS)) + .isTrue(); + } + + public void testFinishToValueAndCloser_throwsIfCalledTwice() throws Exception { + ClosingFuture closingFuture = + ClosingFuture.submit( + new ClosingCallable() { + @Override + public Closeable call(DeferredCloser closer) throws Exception { + return closer.eventuallyClose(mockCloseable, executor); + } + }, + executor); + closingFuture.finishToValueAndCloser( + new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor); + assertThrows( + IllegalStateException.class, + () -> + closingFuture.finishToValueAndCloser( + new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor)); + } + + public void testFinishToValueAndCloser_throwsAfterCallingFinishToFuture() throws Exception { + ClosingFuture closingFuture = + ClosingFuture.submit( + new ClosingCallable() { + @Override + public Closeable call(DeferredCloser closer) throws Exception { + return closer.eventuallyClose(mockCloseable, executor); + } + }, + executor); + FluentFuture unused = closingFuture.finishToFuture(); + assertThrows( + IllegalStateException.class, + () -> + closingFuture.finishToValueAndCloser( + new NoOpValueAndCloserConsumer<>(), finishToValueAndCloserExecutor)); + } + + @Override + T getFinalValue(ClosingFuture closingFuture) throws ExecutionException { + return finishToValueAndCloser(closingFuture).get(); + } + + @Override + void assertFinallyFailsWithException(ClosingFuture closingFuture) { + assertThatFutureFailsWithException(closingFuture.statusFuture()); + ValueAndCloser valueAndCloser = finishToValueAndCloser(closingFuture); + try { + valueAndCloser.get(); + fail(); + } catch (ExecutionException expected) { + assertThat(expected).hasCauseThat().isSameInstanceAs(exception); + } + valueAndCloser.closeAsync(); + } + + @Override + void assertBecomesCanceled(ClosingFuture closingFuture) throws ExecutionException { + assertThatFutureBecomesCancelled(closingFuture.statusFuture()); + } + + @Override + void waitUntilClosed(ClosingFuture closingFuture) { + if (valueAndCloser != null) { + valueAndCloser.closeAsync(); + } + super.waitUntilClosed(closingFuture); + } + + @Override + void cancelFinalStepAndWait(ClosingFuture closingFuture) { + assertThat(closingFuture.cancel(false)).isTrue(); + ValueAndCloser unused = finishToValueAndCloser(closingFuture); + waitUntilClosed(closingFuture); + futureCancelled.countDown(); + } + + private ValueAndCloser finishToValueAndCloser(ClosingFuture closingFuture) { + final CountDownLatch valueAndCloserSet = new CountDownLatch(1); + closingFuture.finishToValueAndCloser( + new ValueAndCloserConsumer() { + @Override + public void accept(ValueAndCloser valueAndCloser) { + ClosingFutureFinishToValueAndCloserTest.this.valueAndCloser = valueAndCloser; + valueAndCloserSet.countDown(); + } + }, + finishToValueAndCloserExecutor); + assertWithMessage("valueAndCloser was set") + .that(awaitUninterruptibly(valueAndCloserSet, 10, SECONDS)) + .isTrue(); + @SuppressWarnings("unchecked") + ValueAndCloser valueAndCloserWithType = (ValueAndCloser) valueAndCloser; + return valueAndCloserWithType; + } +} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java b/android/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java index 18e69b10ee39..ba7d11ef8f67 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/CycleDetectingLockFactoryTest.java @@ -16,25 +16,27 @@ package com.google.common.util.concurrent; +import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.junit.Assert.assertThrows; import com.google.common.base.Joiner; import com.google.common.util.concurrent.CycleDetectingLockFactory.Policies; import com.google.common.util.concurrent.CycleDetectingLockFactory.Policy; import com.google.common.util.concurrent.CycleDetectingLockFactory.PotentialDeadlockException; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unittests for {@link CycleDetectingLockFactory}. * * @author Darick Tong */ +@NullUnmarked public class CycleDetectingLockFactoryTest extends TestCase { private ReentrantLock lockA; @@ -103,24 +105,15 @@ public void testDeadlock_twoLocks() { // The opposite order should fail (Policies.THROW). PotentialDeadlockException firstException = null; lockB.lock(); - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); - firstException = expected; - } - + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); + checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); + firstException = expected; // Second time should also fail, with a cached causal chain. - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); - // The causal chain should be cached. - assertSame(firstException.getCause(), expected.getCause()); - } - + expected = assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); + checkMessage(expected, "LockB -> LockA", "LockA -> LockB"); + // The causal chain should be cached. + assertSame(firstException.getCause(), expected.getCause()); // lockA should work after lockB is released. lockB.unlock(); lockA.lock(); @@ -140,12 +133,9 @@ public void testDeadlock_threeLocks() { lockB.unlock(); // lockC -> lockA should fail. - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); + checkMessage(expected, "LockC -> LockA", "LockB -> LockC", "LockA -> LockB"); } public void testReentrancy_noDeadlock() { @@ -164,29 +154,18 @@ public void testExplicitOrdering_noViolations() { public void testExplicitOrdering_violations() { lock3.lock(); - try { - lock2.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lock2.lock()); + checkMessage(expected, "MyOrder.THIRD -> MyOrder.SECOND"); - try { - lock1.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST"); - } + expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock()); + checkMessage(expected, "MyOrder.THIRD -> MyOrder.FIRST"); lock3.unlock(); lock2.lock(); - try { - lock1.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST"); - } + expected = assertThrows(PotentialDeadlockException.class, () -> lock1.lock()); + checkMessage(expected, "MyOrder.SECOND -> MyOrder.FIRST"); } public void testDifferentOrderings_noViolations() { @@ -199,26 +178,18 @@ public void testExplicitOrderings_generalCycleDetection() { lock01.lock(); // OtherOrder, ordinal() == 1 lock3.unlock(); - try { - lock3.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "OtherOrder.FIRST -> MyOrder.THIRD", "MyOrder.THIRD -> OtherOrder.FIRST"); - } - + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lock3.lock()); + checkMessage( + expected, "OtherOrder.FIRST -> MyOrder.THIRD", "MyOrder.THIRD -> OtherOrder.FIRST"); lockA.lock(); lock01.unlock(); lockB.lock(); lockA.unlock(); - try { - lock01.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "LockB -> OtherOrder.FIRST", "LockA -> LockB", "OtherOrder.FIRST -> LockA"); - } + expected = assertThrows(PotentialDeadlockException.class, () -> lock01.lock()); + checkMessage( + expected, "LockB -> OtherOrder.FIRST", "LockA -> LockB", "OtherOrder.FIRST -> LockA"); } public void testExplicitOrdering_cycleWithUnorderedLock() { @@ -227,16 +198,13 @@ public void testExplicitOrdering_cycleWithUnorderedLock() { myLock.lock(); lock03.unlock(); - try { - lock01.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "MyLock -> OtherOrder.FIRST", - "OtherOrder.THIRD -> MyLock", - "OtherOrder.FIRST -> OtherOrder.THIRD"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lock01.lock()); + checkMessage( + expected, + "MyLock -> OtherOrder.FIRST", + "OtherOrder.THIRD -> MyLock", + "OtherOrder.FIRST -> OtherOrder.THIRD"); } public void testExplicitOrdering_reentrantAcquisition() { @@ -262,11 +230,7 @@ public void testExplicitOrdering_acquiringMultipleLocksWithSameRank() { Lock lockB = factory.newReentrantReadWriteLock(OtherOrder.FIRST).readLock(); lockA.lock(); - try { - lockB.lock(); - fail("Expected IllegalStateException"); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> lockB.lock()); lockA.unlock(); lockB.lock(); @@ -279,12 +243,9 @@ public void testReadLock_deadlock() { readLockA.unlock(); lockB.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); + checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); } public void testReadLock_transitive() { @@ -301,13 +262,10 @@ public void testReadLock_transitive() { // readLockC -> readLockA readLockC.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, "ReadWriteC -> ReadWriteA", "LockB -> ReadWriteC", "ReadWriteA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); + checkMessage( + expected, "ReadWriteC -> ReadWriteA", "LockB -> ReadWriteC", "ReadWriteA -> LockB"); } public void testWriteLock_threeLockDeadLock() { @@ -323,16 +281,13 @@ public void testWriteLock_threeLockDeadLock() { writeLockB.unlock(); // writeLockC -> writeLockA should fail. - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage( - expected, - "ReadWriteC -> ReadWriteA", - "ReadWriteB -> ReadWriteC", - "ReadWriteA -> ReadWriteB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); + checkMessage( + expected, + "ReadWriteC -> ReadWriteA", + "ReadWriteB -> ReadWriteC", + "ReadWriteA -> ReadWriteB"); } public void testWriteToReadLockDowngrading() { @@ -344,12 +299,9 @@ public void testWriteToReadLockDowngrading() { readLockA.unlock(); // lockB -> writeLockA should fail - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); + checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); } public void testReadWriteLockDeadlock() { @@ -360,12 +312,9 @@ public void testReadWriteLockDeadlock() { // lockB -> readLockA should fail. lockB.lock(); - try { - readLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> readLockA.lock()); + checkMessage(expected, "LockB -> ReadWriteA", "ReadWriteA -> LockB"); } public void testReadWriteLockDeadlock_transitive() { @@ -382,12 +331,9 @@ public void testReadWriteLockDeadlock_transitive() { // lockC -> writeLockA should fail. lockC.lock(); - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockC -> ReadWriteA", "LockB -> LockC", "ReadWriteA -> LockB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); + checkMessage(expected, "LockC -> ReadWriteA", "LockB -> LockC", "ReadWriteA -> LockB"); } public void testReadWriteLockDeadlock_treatedEquivalently() { @@ -398,12 +344,9 @@ public void testReadWriteLockDeadlock_treatedEquivalently() { // readLockB -> writeLockA should fail. readLockB.lock(); - try { - writeLockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> writeLockA.lock()); + checkMessage(expected, "ReadWriteB -> ReadWriteA", "ReadWriteA -> ReadWriteB"); } public void testDifferentLockFactories() { @@ -418,12 +361,9 @@ public void testDifferentLockFactories() { // lockD -> lockA should fail even though lockD is from a different factory. lockD.lock(); - try { - lockA.lock(); - fail("Expected PotentialDeadlockException"); - } catch (PotentialDeadlockException expected) { - checkMessage(expected, "LockD -> LockA", "LockA -> LockD"); - } + PotentialDeadlockException expected = + assertThrows(PotentialDeadlockException.class, () -> lockA.lock()); + checkMessage(expected, "LockD -> LockA", "LockA -> LockD"); } public void testDifferentLockFactories_policyExecution() { @@ -442,7 +382,6 @@ public void testDifferentLockFactories_policyExecution() { lockD.lock(); } - public void testReentrantLock_tryLock() throws Exception { LockingThread thread = new LockingThread(lockA); thread.start(); @@ -454,7 +393,6 @@ public void testReentrantLock_tryLock() throws Exception { assertTrue(lockA.tryLock()); } - public void testReentrantWriteLock_tryLock() throws Exception { LockingThread thread = new LockingThread(writeLockA); thread.start(); @@ -468,7 +406,6 @@ public void testReentrantWriteLock_tryLock() throws Exception { assertTrue(readLockA.tryLock()); } - public void testReentrantReadLock_tryLock() throws Exception { LockingThread thread = new LockingThread(readLockA); thread.start(); @@ -497,7 +434,7 @@ public void run() { lock.lock(); try { locked.countDown(); - finishLatch.await(1, TimeUnit.MINUTES); + finishLatch.await(1, MINUTES); } catch (InterruptedException e) { fail(e.toString()); } finally { @@ -506,7 +443,7 @@ public void run() { } void waitUntilHoldingLock() throws InterruptedException { - locked.await(1, TimeUnit.MINUTES); + locked.await(1, MINUTES); } void releaseLockAndFinish() throws InterruptedException { @@ -546,16 +483,6 @@ private enum OtherOrder { // "LockA -> LockB \b.*\b LockB -> LockC \b.*\b LockC -> LockA" private void checkMessage(IllegalStateException exception, String... expectedLockCycle) { String regex = Joiner.on("\\b.*\\b").join(expectedLockCycle); - assertContainsRegex(regex, exception.getMessage()); - } - - // TODO(cpovirk): consider adding support for regex to Truth - private static void assertContainsRegex(String expectedRegex, String actual) { - Pattern pattern = Pattern.compile(expectedRegex); - Matcher matcher = pattern.matcher(actual); - if (!matcher.find()) { - String actualDesc = (actual == null) ? "null" : ('<' + actual + '>'); - fail("expected to contain regex:<" + expectedRegex + "> but was:" + actualDesc); - } + assertThat(exception).hasMessageThat().containsMatch(regex); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ExecutionListTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ExecutionListTest.java index 5bd3cf7f44da..da262806a3ec 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ExecutionListTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ExecutionListTest.java @@ -17,14 +17,15 @@ package com.google.common.util.concurrent; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.testing.NullPointerTester; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link ExecutionList}. @@ -32,11 +33,11 @@ * @author Nishant Thakkar * @author Sven Mawson */ +@NullUnmarked public class ExecutionListTest extends TestCase { private final ExecutionList list = new ExecutionList(); - public void testRunOnPopulatedList() throws Exception { Executor exec = Executors.newCachedThreadPool(); CountDownLatch countDownLatch = new CountDownLatch(3); @@ -48,7 +49,7 @@ public void testRunOnPopulatedList() throws Exception { list.execute(); // Verify that all of the runnables execute in a reasonable amount of time. - assertTrue(countDownLatch.await(1L, TimeUnit.SECONDS)); + assertTrue(countDownLatch.await(1L, SECONDS)); } public void testExecute_idempotent() { @@ -67,7 +68,6 @@ public void run() { assertEquals(1, runCalled.get()); } - public void testExecute_idempotentConcurrently() throws InterruptedException { final CountDownLatch okayToRun = new CountDownLatch(1); final AtomicInteger runCalled = new AtomicInteger(); @@ -103,7 +103,6 @@ public void run() { assertEquals(1, runCalled.get()); } - public void testAddAfterRun() throws Exception { // Run the previous test testRunOnPopulatedList(); @@ -111,7 +110,7 @@ public void testAddAfterRun() throws Exception { // If it passed, then verify an Add will be executed without calling run CountDownLatch countDownLatch = new CountDownLatch(1); list.add(new MockRunnable(countDownLatch), Executors.newCachedThreadPool()); - assertTrue(countDownLatch.await(1L, TimeUnit.SECONDS)); + assertTrue(countDownLatch.await(1L, SECONDS)); } public void testOrdering() throws Exception { @@ -125,7 +124,7 @@ public void run() { integer.compareAndSet(expectedCount, expectedCount + 1); } }, - MoreExecutors.directExecutor()); + directExecutor()); } list.execute(); assertEquals(10, integer.get()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ExecutionSequencerTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ExecutionSequencerTest.java index 1c03f5ac4e57..ee51de22fded 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ExecutionSequencerTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ExecutionSequencerTest.java @@ -21,6 +21,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.testing.GcFinalization; import com.google.common.testing.TestLogHandler; @@ -34,17 +35,19 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** Tests for {@link ExecutionSequencer} */ +@NullUnmarked public class ExecutionSequencerTest extends TestCase { ExecutorService executor; private ExecutionSequencer serializer; - private SettableFuture firstFuture; + private SettableFuture<@Nullable Void> firstFuture; private TestCallable firstCallable; @Override @@ -76,7 +79,8 @@ public void testCancellationDoesNotViolateSerialization() { @SuppressWarnings({"unused", "nullness"}) Future possiblyIgnoredError = serializer.submitAsync(firstCallable, directExecutor()); TestCallable secondCallable = new TestCallable(Futures.immediateFuture(null)); - ListenableFuture secondFuture = serializer.submitAsync(secondCallable, directExecutor()); + ListenableFuture<@Nullable Void> secondFuture = + serializer.submitAsync(secondCallable, directExecutor()); TestCallable thirdCallable = new TestCallable(Futures.immediateFuture(null)); @SuppressWarnings({"unused", "nullness"}) Future possiblyIgnoredError1 = serializer.submitAsync(thirdCallable, directExecutor()); @@ -88,10 +92,9 @@ public void testCancellationDoesNotViolateSerialization() { assertThat(thirdCallable.called).isTrue(); } - public void testCancellationMultipleThreads() throws Exception { final BlockingCallable blockingCallable = new BlockingCallable(); - ListenableFuture unused = serializer.submit(blockingCallable, executor); + ListenableFuture<@Nullable Void> unused = serializer.submit(blockingCallable, executor); ListenableFuture future2 = serializer.submit( new Callable() { @@ -112,14 +115,13 @@ public Boolean call() { // Stop the first task. The second task should then run. blockingCallable.stop(); executor.shutdown(); - assertThat(executor.awaitTermination(10, TimeUnit.SECONDS)).isTrue(); + assertThat(executor.awaitTermination(10, SECONDS)).isTrue(); assertThat(getDone(future2)).isFalse(); } - public void testSecondTaskWaitsForFirstEvenIfCancelled() throws Exception { final BlockingCallable blockingCallable = new BlockingCallable(); - ListenableFuture future1 = serializer.submit(blockingCallable, executor); + ListenableFuture<@Nullable Void> future1 = serializer.submit(blockingCallable, executor); ListenableFuture future2 = serializer.submit( new Callable() { @@ -145,22 +147,23 @@ public Boolean call() { // Stop the first task. The second task should then run. blockingCallable.stop(); executor.shutdown(); - assertThat(executor.awaitTermination(10, TimeUnit.SECONDS)).isTrue(); + assertThat(executor.awaitTermination(10, SECONDS)).isTrue(); assertThat(getDone(future2)).isFalse(); } + @J2ktIncompatible @GwtIncompatible @J2ObjCIncompatible // gc @AndroidIncompatible public void testCancellationWithReferencedObject() throws Exception { Object toBeGCed = new Object(); WeakReference ref = new WeakReference<>(toBeGCed); - final SettableFuture settableFuture = SettableFuture.create(); + final SettableFuture<@Nullable Void> settableFuture = SettableFuture.create(); ListenableFuture ignored = serializer.submitAsync( - new AsyncCallable() { + new AsyncCallable<@Nullable Void>() { @Override - public ListenableFuture call() { + public ListenableFuture<@Nullable Void> call() { return settableFuture; } }, @@ -197,9 +200,9 @@ public void execute(Runnable task) { final Future[] thingToCancel = new Future[1]; results.add( serializer.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { thingToCancel[0].cancel(false); return null; } @@ -225,13 +228,13 @@ public Void call() { } public void testAvoidsStackOverflow_manySubmitted() throws Exception { - final SettableFuture settableFuture = SettableFuture.create(); - ArrayList> results = new ArrayList<>(50_001); + final SettableFuture<@Nullable Void> settableFuture = SettableFuture.create(); + ArrayList> results = new ArrayList<>(50_001); results.add( serializer.submitAsync( - new AsyncCallable() { + new AsyncCallable<@Nullable Void>() { @Override - public ListenableFuture call() { + public ListenableFuture<@Nullable Void> call() { return settableFuture; } }, @@ -244,12 +247,12 @@ public ListenableFuture call() { } public void testAvoidsStackOverflow_manyCancelled() throws Exception { - final SettableFuture settableFuture = SettableFuture.create(); - ListenableFuture unused = + final SettableFuture<@Nullable Void> settableFuture = SettableFuture.create(); + ListenableFuture<@Nullable Void> unused = serializer.submitAsync( - new AsyncCallable() { + new AsyncCallable<@Nullable Void>() { @Override - public ListenableFuture call() { + public ListenableFuture<@Nullable Void> call() { return settableFuture; } }, @@ -272,12 +275,12 @@ public Integer call() { } public void testAvoidsStackOverflow_alternatingCancelledAndSubmitted() throws Exception { - final SettableFuture settableFuture = SettableFuture.create(); - ListenableFuture unused = + final SettableFuture<@Nullable Void> settableFuture = SettableFuture.create(); + ListenableFuture<@Nullable Void> unused = serializer.submitAsync( - new AsyncCallable() { + new AsyncCallable<@Nullable Void>() { @Override - public ListenableFuture call() { + public ListenableFuture<@Nullable Void> call() { return settableFuture; } }, @@ -326,8 +329,8 @@ private static final class LongHolder { private static final int ITERATION_COUNT = 50_000; private static final int DIRECT_EXECUTIONS_PER_THREAD = 100; + @J2ktIncompatible @GwtIncompatible // threads - public void testAvoidsStackOverflow_multipleThreads() throws Exception { final LongHolder holder = new LongHolder(); final ArrayList> lengthChecks = new ArrayList<>(); @@ -347,12 +350,12 @@ public Integer call() { }, service) .get(); - final SettableFuture settableFuture = SettableFuture.create(); + final SettableFuture<@Nullable Void> settableFuture = SettableFuture.create(); ListenableFuture unused = serializer.submitAsync( - new AsyncCallable() { + new AsyncCallable<@Nullable Void>() { @Override - public ListenableFuture call() { + public ListenableFuture<@Nullable Void> call() { return settableFuture; } }, @@ -362,9 +365,9 @@ public ListenableFuture call() { // after some number of iterations, switch threads unused = serializer.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { holder.count++; return null; } @@ -386,9 +389,9 @@ public Integer call() { // Otherwise, schedule a task on directExecutor unused = serializer.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() { + public @Nullable Void call() { holder.count++; return null; } @@ -419,14 +422,14 @@ public void testToString() { assertThat(second.toString()).contains(secondCallable.future.toString()); } - private static class BlockingCallable implements Callable { + private static class BlockingCallable implements Callable<@Nullable Void> { private final CountDownLatch startLatch = new CountDownLatch(1); private final CountDownLatch stopLatch = new CountDownLatch(1); private volatile boolean running = false; @Override - public Void call() throws InterruptedException { + public @Nullable Void call() throws InterruptedException { running = true; startLatch.countDown(); stopLatch.await(); @@ -447,17 +450,17 @@ public boolean isRunning() { } } - private static final class TestCallable implements AsyncCallable { + private static final class TestCallable implements AsyncCallable<@Nullable Void> { - private final ListenableFuture future; + private final ListenableFuture<@Nullable Void> future; private boolean called = false; - private TestCallable(ListenableFuture future) { + private TestCallable(ListenableFuture<@Nullable Void> future) { this.future = future; } @Override - public ListenableFuture call() throws Exception { + public ListenableFuture<@Nullable Void> call() throws Exception { called = true; return future; } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FakeTimeLimiterTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FakeTimeLimiterTest.java index a0e0634695ed..922e02747e4f 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FakeTimeLimiterTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FakeTimeLimiterTest.java @@ -17,17 +17,20 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link FakeTimeLimiter}. * * @author Jens Nyman */ +@NullUnmarked public class FakeTimeLimiterTest extends TestCase { private static final int DELAY_MS = 50; @@ -43,63 +46,59 @@ protected void setUp() throws Exception { public void testCallWithTimeout_propagatesReturnValue() throws Exception { String result = - timeLimiter.callWithTimeout( - Callables.returning(RETURN_VALUE), DELAY_MS, TimeUnit.MILLISECONDS); + timeLimiter.callWithTimeout(Callables.returning(RETURN_VALUE), DELAY_MS, MILLISECONDS); assertThat(result).isEqualTo(RETURN_VALUE); } public void testCallWithTimeout_wrapsCheckedException() throws Exception { Exception exception = new SampleCheckedException(); - try { - timeLimiter.callWithTimeout(callableThrowing(exception), DELAY_MS, TimeUnit.MILLISECONDS); - fail("Expected ExecutionException"); - } catch (ExecutionException e) { - assertThat(e.getCause()).isEqualTo(exception); - } + ExecutionException e = + assertThrows( + ExecutionException.class, + () -> timeLimiter.callWithTimeout(callableThrowing(exception), DELAY_MS, MILLISECONDS)); + assertThat(e).hasCauseThat().isEqualTo(exception); } public void testCallWithTimeout_wrapsUncheckedException() throws Exception { Exception exception = new RuntimeException("test"); - try { - timeLimiter.callWithTimeout(callableThrowing(exception), DELAY_MS, TimeUnit.MILLISECONDS); - fail("Expected UncheckedExecutionException"); - } catch (UncheckedExecutionException e) { - assertThat(e.getCause()).isEqualTo(exception); - } + UncheckedExecutionException e = + assertThrows( + UncheckedExecutionException.class, + () -> timeLimiter.callWithTimeout(callableThrowing(exception), DELAY_MS, MILLISECONDS)); + assertThat(e).hasCauseThat().isEqualTo(exception); } public void testCallUninterruptiblyWithTimeout_propagatesReturnValue() throws Exception { String result = timeLimiter.callUninterruptiblyWithTimeout( - Callables.returning(RETURN_VALUE), DELAY_MS, TimeUnit.MILLISECONDS); + Callables.returning(RETURN_VALUE), DELAY_MS, MILLISECONDS); assertThat(result).isEqualTo(RETURN_VALUE); } public void testRunWithTimeout_returnsWithoutException() throws Exception { - timeLimiter.runWithTimeout(Runnables.doNothing(), DELAY_MS, TimeUnit.MILLISECONDS); + timeLimiter.runWithTimeout(Runnables.doNothing(), DELAY_MS, MILLISECONDS); } public void testRunWithTimeout_wrapsUncheckedException() throws Exception { RuntimeException exception = new RuntimeException("test"); - try { - timeLimiter.runWithTimeout(runnableThrowing(exception), DELAY_MS, TimeUnit.MILLISECONDS); - fail("Expected UncheckedExecutionException"); - } catch (UncheckedExecutionException e) { - assertThat(e.getCause()).isEqualTo(exception); - } + UncheckedExecutionException e = + assertThrows( + UncheckedExecutionException.class, + () -> timeLimiter.runWithTimeout(runnableThrowing(exception), DELAY_MS, MILLISECONDS)); + assertThat(e).hasCauseThat().isEqualTo(exception); } public void testRunUninterruptiblyWithTimeout_wrapsUncheckedException() throws Exception { RuntimeException exception = new RuntimeException("test"); - try { - timeLimiter.runUninterruptiblyWithTimeout( - runnableThrowing(exception), DELAY_MS, TimeUnit.MILLISECONDS); - fail("Expected UncheckedExecutionException"); - } catch (UncheckedExecutionException e) { - assertThat(e.getCause()).isEqualTo(exception); - } + UncheckedExecutionException e = + assertThrows( + UncheckedExecutionException.class, + () -> + timeLimiter.runUninterruptiblyWithTimeout( + runnableThrowing(exception), DELAY_MS, MILLISECONDS)); + assertThat(e).hasCauseThat().isEqualTo(exception); } public static Callable callableThrowing(final Exception exception) { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FluentFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FluentFutureTest.java index cc4751d0f217..4379f7b170d4 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FluentFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FluentFutureTest.java @@ -23,22 +23,27 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.concurrent.Executors.newScheduledThreadPool; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; /** * Tests for {@link FluentFuture}. The tests cover only the basics for the API. The actual logic is * tested in {@link FuturesTest}. */ +@NullMarked @GwtCompatible(emulated = true) public class FluentFutureTest extends TestCase { + @SuppressWarnings({"deprecation", "InlineMeInliner"}) // test of a deprecated method public void testFromFluentFuture() { FluentFuture f = FluentFuture.from(SettableFuture.create()); assertThat(FluentFuture.from(f)).isSameInstanceAs(f); @@ -74,9 +79,12 @@ public void onFailure(Throwable t) {} assertThat(called[0]).isTrue(); } + // Avoid trouble with automatic mapping between JRE and Kotlin runtime classes. + static class CustomRuntimeException extends RuntimeException {} + public void testCatching() throws Exception { FluentFuture f = - FluentFuture.from(immediateFailedFuture(new RuntimeException())) + FluentFuture.from(immediateFailedFuture(new CustomRuntimeException())) .catching( Throwable.class, new Function>() { @@ -86,12 +94,12 @@ public Class apply(Throwable input) { } }, directExecutor()); - assertThat(f.get()).isEqualTo(RuntimeException.class); + assertThat(f.get()).isEqualTo(CustomRuntimeException.class); } public void testCatchingAsync() throws Exception { FluentFuture f = - FluentFuture.from(immediateFailedFuture(new RuntimeException())) + FluentFuture.from(immediateFailedFuture(new CustomRuntimeException())) .catchingAsync( Throwable.class, new AsyncFunction>() { @@ -101,7 +109,7 @@ public ListenableFuture> apply(Throwable input) { } }, directExecutor()); - assertThat(f.get()).isEqualTo(RuntimeException.class); + assertThat(f.get()).isEqualTo(CustomRuntimeException.class); } public void testTransform() throws Exception { @@ -132,19 +140,15 @@ public ListenableFuture apply(Integer input) { assertThat(f.get()).isEqualTo(2); } - + @J2ktIncompatible @GwtIncompatible // withTimeout public void testWithTimeout() throws Exception { ScheduledExecutorService executor = newScheduledThreadPool(1); try { FluentFuture f = FluentFuture.from(SettableFuture.create()).withTimeout(0, SECONDS, executor); - try { - f.get(); - fail(); - } catch (ExecutionException e) { - assertThat(e).hasCauseThat().isInstanceOf(TimeoutException.class); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> f.get()); + assertThat(e).hasCauseThat().isInstanceOf(TimeoutException.class); } finally { executor.shutdown(); } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingDequeTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingDequeTest.java index 35cecee71e2a..002ad8581176 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingDequeTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingDequeTest.java @@ -17,12 +17,14 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test for {@link ForwardingBlockingDeque} * * @author Emily Soldal */ +@NullUnmarked public class ForwardingBlockingDequeTest extends TestCase { public void testForwarding() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingQueueTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingQueueTest.java index eda852f6cedc..0c7c1b4c45b1 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingQueueTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingBlockingQueueTest.java @@ -17,8 +17,10 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link ForwardingBlockingQueue} */ +@NullUnmarked public class ForwardingBlockingQueueTest extends TestCase { public void testForwarding() { ForwardingObjectTester.testForwardingObject(ForwardingBlockingQueue.class); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingExecutorServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingExecutorServiceTest.java index ccd3eb8000e9..f2474e7f6ff3 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingExecutorServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingExecutorServiceTest.java @@ -17,8 +17,10 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link ForwardingExecutorService} */ +@NullUnmarked public class ForwardingExecutorServiceTest extends TestCase { public void testForwarding() { ForwardingObjectTester.testForwardingObject(ForwardingExecutorService.class); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingFutureTest.java index f5d282a141f9..eaf8e964ff70 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingFutureTest.java @@ -17,8 +17,10 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link ForwardingFuture} */ +@NullUnmarked public class ForwardingFutureTest extends TestCase { public void testForwarding() { ForwardingObjectTester.testForwardingObject(ForwardingFuture.class); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListenableFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListenableFutureTest.java index 827f50ea55d1..435d17d49d1d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListenableFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListenableFutureTest.java @@ -17,12 +17,14 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ForwardingListenableFuture}. * * @author Ben Yu */ +@NullUnmarked public class ForwardingListenableFutureTest extends TestCase { public void testForwarding() { ForwardingObjectTester.testForwardingObject(ForwardingListenableFuture.class); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListeningExecutorServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListeningExecutorServiceTest.java index ecfa7ddb32bb..1eabe021238d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListeningExecutorServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingListeningExecutorServiceTest.java @@ -17,8 +17,10 @@ package com.google.common.util.concurrent; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link ForwardingListeningExecutorService} */ +@NullUnmarked public class ForwardingListeningExecutorServiceTest extends TestCase { public void testForwarding() { ForwardingObjectTester.testForwardingObject(ForwardingListeningExecutorService.class); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTester.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTester.java index 84b0426a6830..24eff3ef47b3 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTester.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTester.java @@ -26,12 +26,14 @@ import com.google.common.testing.ForwardingWrapperTester; import java.lang.reflect.Method; import java.util.Arrays; +import org.jspecify.annotations.NullUnmarked; /** - * Tester for typical subclass of {@link ForwardingObject} by using EasyMock partial mocks. + * Tester for typical subclass of {@link ForwardingObject} by using Mockito. * * @author Ben Yu */ +@NullUnmarked final class ForwardingObjectTester { private static final Method DELEGATE_METHOD; diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTesterTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTesterTest.java index 51a0842af36d..d96ef1e2ddbe 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTesterTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ForwardingObjectTesterTest.java @@ -18,12 +18,14 @@ import com.google.common.collect.ForwardingObject; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ForwardingObjectTester}. * * @author Ben Yu */ +@NullUnmarked public class ForwardingObjectTesterTest extends TestCase { public void testFailsToForward() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FutureCallbackTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FutureCallbackTest.java index 5a1464d05ce7..8b4b63614bda 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FutureCallbackTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FutureCallbackTest.java @@ -19,21 +19,23 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.Futures.addCallback; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; +import com.google.common.util.concurrent.TestExceptions.SomeError; import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; -import javax.annotation.CheckForNull; import junit.framework.TestCase; -import org.mockito.Mockito; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Test for {@link FutureCallback}. * * @author Anthony Zana */ -@GwtCompatible(emulated = true) +@GwtCompatible +@NullUnmarked public class FutureCallbackTest extends TestCase { public void testSameThreadSuccess() { SettableFuture f = SettableFuture.create(); @@ -64,6 +66,7 @@ public void testCancel() { SettableFuture f = SettableFuture.create(); FutureCallback callback = new FutureCallback() { + private final Object monitor = new Object(); private boolean called = false; @Override @@ -72,10 +75,12 @@ public void onSuccess(String result) { } @Override - public synchronized void onFailure(Throwable t) { - assertFalse(called); - assertThat(t).isInstanceOf(CancellationException.class); - called = true; + public void onFailure(Throwable t) { + synchronized (monitor) { + assertFalse(called); + assertThat(t).isInstanceOf(CancellationException.class); + called = true; + } } }; addCallback(f, callback, directExecutor()); @@ -89,56 +94,73 @@ public void testThrowErrorFromGet() { addCallback(f, callback, directExecutor()); } - public void testRuntimeExeceptionFromGet() { + public void testRuntimeExceptionFromGet() { RuntimeException e = new IllegalArgumentException("foo not found"); ListenableFuture f = UncheckedThrowingFuture.throwingRuntimeException(e); MockCallback callback = new MockCallback(e); addCallback(f, callback, directExecutor()); } - @GwtIncompatible // Mockito public void testOnSuccessThrowsRuntimeException() throws Exception { RuntimeException exception = new RuntimeException(); String result = "result"; SettableFuture future = SettableFuture.create(); - @SuppressWarnings("unchecked") // Safe for a mock - FutureCallback callback = Mockito.mock(FutureCallback.class); + int[] successCalls = new int[1]; + int[] failureCalls = new int[1]; + FutureCallback callback = + new FutureCallback() { + @Override + public void onSuccess(String result) { + successCalls[0]++; + throw exception; + } + + @Override + public void onFailure(Throwable t) { + failureCalls[0]++; + } + }; addCallback(future, callback, directExecutor()); - Mockito.doThrow(exception).when(callback).onSuccess(result); future.set(result); assertEquals(result, future.get()); - Mockito.verify(callback).onSuccess(result); - Mockito.verifyNoMoreInteractions(callback); + assertThat(successCalls[0]).isEqualTo(1); + assertThat(failureCalls[0]).isEqualTo(0); } - @GwtIncompatible // Mockito public void testOnSuccessThrowsError() throws Exception { - class TestError extends Error {} - TestError error = new TestError(); + SomeError error = new SomeError(); String result = "result"; SettableFuture future = SettableFuture.create(); - @SuppressWarnings("unchecked") // Safe for a mock - FutureCallback callback = Mockito.mock(FutureCallback.class); + int[] successCalls = new int[1]; + int[] failureCalls = new int[1]; + FutureCallback callback = + new FutureCallback() { + @Override + public void onSuccess(String result) { + successCalls[0]++; + throw error; + } + + @Override + public void onFailure(Throwable t) { + failureCalls[0]++; + } + }; addCallback(future, callback, directExecutor()); - Mockito.doThrow(error).when(callback).onSuccess(result); - try { - future.set(result); - fail("Should have thrown"); - } catch (TestError e) { - assertSame(error, e); - } + SomeError e = assertThrows(SomeError.class, () -> future.set(result)); + assertSame(error, e); assertEquals(result, future.get()); - Mockito.verify(callback).onSuccess(result); - Mockito.verifyNoMoreInteractions(callback); + assertThat(successCalls[0]).isEqualTo(1); + assertThat(failureCalls[0]).isEqualTo(0); } public void testWildcardFuture() { SettableFuture settable = SettableFuture.create(); ListenableFuture f = settable; - FutureCallback callback = - new FutureCallback() { + FutureCallback<@Nullable Object> callback = + new FutureCallback<@Nullable Object>() { @Override - public void onSuccess(Object result) {} + public void onSuccess(@Nullable Object result) {} @Override public void onFailure(Throwable t) {} @@ -157,9 +179,10 @@ public void execute(Runnable command) { } private final class MockCallback implements FutureCallback { - @CheckForNull private String value = null; - @CheckForNull private Throwable failure = null; + @Nullable private String value = null; + @Nullable private Throwable failure = null; private boolean wasCalled = false; + private final Object monitor = new Object(); MockCallback(String expectedValue) { this.value = expectedValue; @@ -170,17 +193,21 @@ public MockCallback(Throwable expectedFailure) { } @Override - public synchronized void onSuccess(String result) { - assertFalse(wasCalled); - wasCalled = true; - assertEquals(value, result); + public void onSuccess(String result) { + synchronized (monitor) { + assertFalse(wasCalled); + wasCalled = true; + assertEquals(value, result); + } } @Override public synchronized void onFailure(Throwable t) { - assertFalse(wasCalled); - wasCalled = true; - assertEquals(failure, t); + synchronized (monitor) { + assertFalse(wasCalled); + wasCalled = true; + assertEquals(failure, t); + } } } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedInputs.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedInputs.java index 27916d8a1005..71708beca24c 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedInputs.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedInputs.java @@ -18,11 +18,14 @@ import com.google.common.annotations.GwtCompatible; import java.util.concurrent.Future; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Classes and futures used in {@link FuturesGetCheckedTest} and {@link FuturesGetUncheckedTest}. */ @GwtCompatible +@NullUnmarked final class FuturesGetCheckedInputs { static final Exception CHECKED_EXCEPTION = new Exception("mymessage"); static final Future FAILED_FUTURE_CHECKED_EXCEPTION = @@ -58,6 +61,47 @@ private ExceptionWithPrivateConstructor(String message, Throwable cause) { } } + public static final class ExceptionWithManyConstructorsButOnlyOneThrowable extends Exception { + private @Nullable Throwable antecedent; + + public ExceptionWithManyConstructorsButOnlyOneThrowable(String message, String a1) { + super(message); + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable(String message, String a1, String a2) { + super(message); + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable( + String message, String a1, String a2, String a3) { + super(message); + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable(String message, Throwable antecedent) { + super(message); + this.antecedent = antecedent; + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable( + String message, String a1, String a2, String a3, String a4) { + super(message); + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable( + String message, String a1, String a2, String a3, String a4, String a5) { + super(message); + } + + public ExceptionWithManyConstructorsButOnlyOneThrowable( + String message, String a1, String a2, String a3, String a4, String a5, String a6) { + super(message); + } + + public Throwable getAntecedent() { + return antecedent; + } + } + @SuppressWarnings("unused") // we're testing that they're not used public static final class ExceptionWithSomePrivateConstructors extends Exception { private ExceptionWithSomePrivateConstructors(String a) {} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedTest.java index 3bc69bd328ff..7a9e818df755 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetCheckedTest.java @@ -30,11 +30,13 @@ import static com.google.common.util.concurrent.FuturesGetCheckedInputs.RUNTIME_EXCEPTION_FUTURE; import static com.google.common.util.concurrent.FuturesGetCheckedInputs.UNCHECKED_EXCEPTION; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.testing.GcFinalization; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithBadConstructor; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithGoodAndBadConstructor; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithManyConstructors; +import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithManyConstructorsButOnlyOneThrowable; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithPrivateConstructor; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithSomePrivateConstructors; import com.google.common.util.concurrent.FuturesGetCheckedInputs.ExceptionWithWrongTypesConstructor; @@ -45,11 +47,12 @@ import java.net.URLClassLoader; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link Futures#getChecked(Future, Class)}. */ +@NullUnmarked public class FuturesGetCheckedTest extends TestCase { // Boring untimed-get tests: @@ -74,60 +77,52 @@ public void testGetCheckedUntimed_interrupted() { public void testGetCheckedUntimed_cancelled() throws TwoArgConstructorException { SettableFuture future = SettableFuture.create(); future.cancel(true); - try { - getChecked(future, TwoArgConstructorException.class); - fail(); - } catch (CancellationException expected) { - } + assertThrows( + CancellationException.class, () -> getChecked(future, TwoArgConstructorException.class)); } - public void testGetCheckedUntimed_ExecutionExceptionChecked() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class); - fail(); - } catch (TwoArgConstructorException expected) { - assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); - } + public void testGetCheckedUntimed_executionExceptionChecked() { + TwoArgConstructorException expected = + assertThrows( + TwoArgConstructorException.class, + () -> getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class)); + assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); } - public void testGetCheckedUntimed_ExecutionExceptionUnchecked() + public void testGetCheckedUntimed_executionExceptionUnchecked() throws TwoArgConstructorException { - try { - getChecked(FAILED_FUTURE_UNCHECKED_EXCEPTION, TwoArgConstructorException.class); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION); - } + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, + () -> getChecked(FAILED_FUTURE_UNCHECKED_EXCEPTION, TwoArgConstructorException.class)); + assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION); } - public void testGetCheckedUntimed_ExecutionExceptionError() throws TwoArgConstructorException { - try { - getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isEqualTo(ERROR); - } + public void testGetCheckedUntimed_executionExceptionError() throws TwoArgConstructorException { + ExecutionError expected = + assertThrows( + ExecutionError.class, + () -> getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class)); + assertThat(expected).hasCauseThat().isEqualTo(ERROR); } - public void testGetCheckedUntimed_ExecutionExceptionOtherThrowable() { - try { - getChecked(FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class); - fail(); - } catch (TwoArgConstructorException expected) { - assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE); - } + public void testGetCheckedUntimed_executionExceptionOtherThrowable() { + TwoArgConstructorException expected = + assertThrows( + TwoArgConstructorException.class, + () -> getChecked(FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class)); + assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE); } - public void testGetCheckedUntimed_RuntimeException() throws TwoArgConstructorException { - try { - getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class); - fail(); - } catch (RuntimeException expected) { - assertEquals(RUNTIME_EXCEPTION, expected); - } + public void testGetCheckedUntimed_runtimeException() throws TwoArgConstructorException { + RuntimeException expected = + assertThrows( + RuntimeException.class, + () -> getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class)); + assertEquals(RUNTIME_EXCEPTION, expected); } - public void testGetCheckedUntimed_Error() throws TwoArgConstructorException { + public void testGetCheckedUntimed_error() throws TwoArgConstructorException { try { getChecked(ERROR_FUTURE, TwoArgConstructorException.class); } catch (Error expected) { @@ -139,29 +134,26 @@ public void testGetCheckedUntimed_Error() throws TwoArgConstructorException { public void testGetCheckedUntimed_badExceptionConstructor_failsEvenForSuccessfulInput() throws Exception { - try { - getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class)); } public void testGetCheckedUntimed_badExceptionConstructor_wrapsOriginalChecked() throws Exception { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class)); } public void testGetCheckedUntimed_withGoodAndBadExceptionConstructor() throws Exception { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithGoodAndBadConstructor.class); - fail(); - } catch (ExceptionWithGoodAndBadConstructor expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION); - } + ExceptionWithGoodAndBadConstructor expected = + assertThrows( + ExceptionWithGoodAndBadConstructor.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithGoodAndBadConstructor.class)); + assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION); } // Boring timed-get tests: @@ -188,59 +180,62 @@ public void testGetCheckedTimed_interrupted() { public void testGetCheckedTimed_cancelled() throws TwoArgConstructorException { SettableFuture future = SettableFuture.create(); future.cancel(true); - try { - getChecked(future, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (CancellationException expected) { - } - } - - public void testGetCheckedTimed_ExecutionExceptionChecked() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (TwoArgConstructorException expected) { - assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); - } - } - - public void testGetCheckedTimed_ExecutionExceptionUnchecked() throws TwoArgConstructorException { - try { - getChecked(FAILED_FUTURE_UNCHECKED_EXCEPTION, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (UncheckedExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION); - } - } - - public void testGetCheckedTimed_ExecutionExceptionError() throws TwoArgConstructorException { - try { - getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (ExecutionError expected) { - assertThat(expected).hasCauseThat().isEqualTo(ERROR); - } - } - - public void testGetCheckedTimed_ExecutionExceptionOtherThrowable() { - try { - getChecked(FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (TwoArgConstructorException expected) { - assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE); - } - } - - public void testGetCheckedTimed_RuntimeException() throws TwoArgConstructorException { - try { - getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (RuntimeException expected) { - assertEquals(RUNTIME_EXCEPTION, expected); - } - } - - public void testGetCheckedTimed_Error() throws TwoArgConstructorException { + assertThrows( + CancellationException.class, + () -> getChecked(future, TwoArgConstructorException.class, 0, SECONDS)); + } + + public void testGetCheckedTimed_executionExceptionChecked() { + TwoArgConstructorException expected = + assertThrows( + TwoArgConstructorException.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorException.class, 0, SECONDS)); + assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); + } + + public void testGetCheckedTimed_executionExceptionUnchecked() throws TwoArgConstructorException { + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, + () -> + getChecked( + FAILED_FUTURE_UNCHECKED_EXCEPTION, + TwoArgConstructorException.class, + 0, + SECONDS)); + assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION); + } + + public void testGetCheckedTimed_executionExceptionError() throws TwoArgConstructorException { + ExecutionError expected = + assertThrows( + ExecutionError.class, + () -> getChecked(FAILED_FUTURE_ERROR, TwoArgConstructorException.class, 0, SECONDS)); + assertThat(expected).hasCauseThat().isEqualTo(ERROR); + } + + public void testGetCheckedTimed_executionExceptionOtherThrowable() { + TwoArgConstructorException expected = + assertThrows( + TwoArgConstructorException.class, + () -> + getChecked( + FAILED_FUTURE_OTHER_THROWABLE, TwoArgConstructorException.class, 0, SECONDS)); + assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE); + } + + public void testGetCheckedTimed_runtimeException() throws TwoArgConstructorException { + RuntimeException expected = + assertThrows( + RuntimeException.class, + () -> + getChecked(RUNTIME_EXCEPTION_FUTURE, TwoArgConstructorException.class, 0, SECONDS)); + assertEquals(RUNTIME_EXCEPTION, expected); + } + + public void testGetCheckedTimed_error() throws TwoArgConstructorException { try { getChecked(ERROR_FUTURE, TwoArgConstructorException.class, 0, SECONDS); } catch (Error expected) { @@ -250,110 +245,113 @@ public void testGetCheckedTimed_Error() throws TwoArgConstructorException { fail(); } - public void testGetCheckedTimed_TimeoutException() { + public void testGetCheckedTimed_timeoutException() { SettableFuture future = SettableFuture.create(); - try { - getChecked(future, TwoArgConstructorException.class, 0, SECONDS); - fail(); - } catch (TwoArgConstructorException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(TimeoutException.class); - } + TwoArgConstructorException expected = + assertThrows( + TwoArgConstructorException.class, + () -> getChecked(future, TwoArgConstructorException.class, 0, SECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(TimeoutException.class); } public void testGetCheckedTimed_badExceptionConstructor_failsEvenForSuccessfulInput() throws Exception { - try { - getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class, 1, TimeUnit.SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> getChecked(immediateFuture("x"), ExceptionWithBadConstructor.class, 1, SECONDS)); } public void testGetCheckedTimed_badExceptionConstructor_wrapsOriginalChecked() throws Exception { - try { - getChecked( - FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class, 1, TimeUnit.SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithBadConstructor.class, 1, SECONDS)); } public void testGetCheckedTimed_withGoodAndBadExceptionConstructor() { - try { - getChecked( - FAILED_FUTURE_CHECKED_EXCEPTION, - ExceptionWithGoodAndBadConstructor.class, - 1, - TimeUnit.SECONDS); - fail(); - } catch (ExceptionWithGoodAndBadConstructor expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION); - } + ExceptionWithGoodAndBadConstructor expected = + assertThrows( + ExceptionWithGoodAndBadConstructor.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, + ExceptionWithGoodAndBadConstructor.class, + 1, + SECONDS)); + assertThat(expected).hasCauseThat().isSameInstanceAs(CHECKED_EXCEPTION); } // Edge case tests of the exception-construction code through untimed get(): @SuppressWarnings("FuturesGetCheckedIllegalExceptionType") public void testGetCheckedUntimed_exceptionClassIsRuntimeException() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorRuntimeException.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, TwoArgConstructorRuntimeException.class)); } public void testGetCheckedUntimed_exceptionClassSomePrivateConstructors() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithSomePrivateConstructors.class); - fail(); - } catch (ExceptionWithSomePrivateConstructors expected) { - } + assertThrows( + ExceptionWithSomePrivateConstructors.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithSomePrivateConstructors.class)); } @SuppressWarnings("FuturesGetCheckedIllegalExceptionType") public void testGetCheckedUntimed_exceptionClassNoPublicConstructor() throws ExceptionWithPrivateConstructor { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithPrivateConstructor.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithPrivateConstructor.class)); } @SuppressWarnings("FuturesGetCheckedIllegalExceptionType") public void testGetCheckedUntimed_exceptionClassPublicConstructorWrongType() throws ExceptionWithWrongTypesConstructor { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithWrongTypesConstructor.class); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, + () -> + getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithWrongTypesConstructor.class)); } public void testGetCheckedUntimed_exceptionClassPrefersStringConstructor() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithManyConstructors.class); - fail(); - } catch (ExceptionWithManyConstructors expected) { - assertTrue(expected.usedExpectedConstructor); - } + ExceptionWithManyConstructors expected = + assertThrows( + ExceptionWithManyConstructors.class, + () -> getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithManyConstructors.class)); + assertTrue(expected.usedExpectedConstructor); } public void testGetCheckedUntimed_exceptionClassUsedInitCause() { - try { - getChecked(FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithoutThrowableConstructor.class); - fail(); - } catch (ExceptionWithoutThrowableConstructor expected) { - assertThat(expected).hasMessageThat().contains("mymessage"); - assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); - } + ExceptionWithoutThrowableConstructor expected = + assertThrows( + ExceptionWithoutThrowableConstructor.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, ExceptionWithoutThrowableConstructor.class)); + assertThat(expected).hasMessageThat().contains("mymessage"); + assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); + } + + public void testPrefersConstructorWithThrowableParameter() { + ExceptionWithManyConstructorsButOnlyOneThrowable exception = + assertThrows( + ExceptionWithManyConstructorsButOnlyOneThrowable.class, + () -> + getChecked( + FAILED_FUTURE_CHECKED_EXCEPTION, + ExceptionWithManyConstructorsButOnlyOneThrowable.class)); + assertThat(exception).hasMessageThat().contains("mymessage"); + assertThat(exception.getAntecedent()).isEqualTo(CHECKED_EXCEPTION); } // Class unloading test: public static final class WillBeUnloadedException extends Exception {} - + @AndroidIncompatible // "Parent ClassLoader may not be null"; maybe avoidable if we try? public void testGetChecked_classUnloading() throws Exception { WeakReference classUsedByGetChecked = doTestClassUnloading(); GcFinalization.awaitClear(classUsedByGetChecked); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetDoneTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetDoneTest.java index cf9cb9df36b3..7c8cf27d1914 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetDoneTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetDoneTest.java @@ -19,46 +19,39 @@ import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** Unit tests for {@link Futures#getDone}. */ @GwtCompatible +@NullUnmarked public class FuturesGetDoneTest extends TestCase { public void testSuccessful() throws ExecutionException { assertThat(getDone(immediateFuture("a"))).isEqualTo("a"); } public void testSuccessfulNull() throws ExecutionException { - assertThat(getDone(immediateFuture((String) null))).isEqualTo(null); + assertThat(getDone(Futures.<@Nullable String>immediateFuture(null))).isEqualTo(null); } public void testFailed() { Exception failureCause = new Exception(); - try { - getDone(immediateFailedFuture(failureCause)); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isEqualTo(failureCause); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(immediateFailedFuture(failureCause))); + assertThat(expected).hasCauseThat().isEqualTo(failureCause); } public void testCancelled() throws ExecutionException { - try { - getDone(immediateCancelledFuture()); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> getDone(immediateCancelledFuture())); } public void testPending() throws ExecutionException { - try { - getDone(SettableFuture.create()); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> getDone(SettableFuture.create())); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetUncheckedTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetUncheckedTest.java index 93baaf651230..b2cb616bd710 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetUncheckedTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesGetUncheckedTest.java @@ -14,6 +14,7 @@ package com.google.common.util.concurrent; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.Futures.getUnchecked; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.FuturesGetCheckedInputs.CHECKED_EXCEPTION; @@ -27,20 +28,25 @@ import static com.google.common.util.concurrent.FuturesGetCheckedInputs.RUNTIME_EXCEPTION; import static com.google.common.util.concurrent.FuturesGetCheckedInputs.RUNTIME_EXCEPTION_FUTURE; import static com.google.common.util.concurrent.FuturesGetCheckedInputs.UNCHECKED_EXCEPTION; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.CancellationException; import java.util.concurrent.Future; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link Futures#getUnchecked(Future)}. */ @GwtCompatible(emulated = true) +@NullUnmarked public class FuturesGetUncheckedTest extends TestCase { public void testGetUnchecked_success() { assertEquals("foo", getUnchecked(immediateFuture("foo"))); } + @J2ktIncompatible @GwtIncompatible // Thread.interrupt public void testGetUnchecked_interrupted() { Thread.currentThread().interrupt(); @@ -55,59 +61,44 @@ public void testGetUnchecked_interrupted() { public void testGetUnchecked_cancelled() { SettableFuture future = SettableFuture.create(); future.cancel(true); - try { - getUnchecked(future); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> getUnchecked(future)); } - public void testGetUnchecked_ExecutionExceptionChecked() { - try { - getUnchecked(FAILED_FUTURE_CHECKED_EXCEPTION); - fail(); - } catch (UncheckedExecutionException expected) { - assertEquals(CHECKED_EXCEPTION, expected.getCause()); - } + public void testGetUnchecked_executionExceptionChecked() { + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, () -> getUnchecked(FAILED_FUTURE_CHECKED_EXCEPTION)); + assertThat(expected).hasCauseThat().isEqualTo(CHECKED_EXCEPTION); } - public void testGetUnchecked_ExecutionExceptionUnchecked() { - try { - getUnchecked(FAILED_FUTURE_UNCHECKED_EXCEPTION); - fail(); - } catch (UncheckedExecutionException expected) { - assertEquals(UNCHECKED_EXCEPTION, expected.getCause()); - } + public void testGetUnchecked_executionExceptionUnchecked() { + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, + () -> getUnchecked(FAILED_FUTURE_UNCHECKED_EXCEPTION)); + assertThat(expected).hasCauseThat().isEqualTo(UNCHECKED_EXCEPTION); } - public void testGetUnchecked_ExecutionExceptionError() { - try { - getUnchecked(FAILED_FUTURE_ERROR); - fail(); - } catch (ExecutionError expected) { - assertEquals(ERROR, expected.getCause()); - } + public void testGetUnchecked_executionExceptionError() { + ExecutionError expected = + assertThrows(ExecutionError.class, () -> getUnchecked(FAILED_FUTURE_ERROR)); + assertThat(expected).hasCauseThat().isEqualTo(ERROR); } - public void testGetUnchecked_ExecutionExceptionOtherThrowable() { - try { - getUnchecked(FAILED_FUTURE_OTHER_THROWABLE); - fail(); - } catch (UncheckedExecutionException expected) { - assertEquals(OTHER_THROWABLE, expected.getCause()); - } + public void testGetUnchecked_executionExceptionOtherThrowable() { + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, () -> getUnchecked(FAILED_FUTURE_OTHER_THROWABLE)); + assertThat(expected).hasCauseThat().isEqualTo(OTHER_THROWABLE); } - public void testGetUnchecked_RuntimeException() { - try { - getUnchecked(RUNTIME_EXCEPTION_FUTURE); - fail(); - } catch (RuntimeException expected) { - assertEquals(RUNTIME_EXCEPTION, expected); - } + public void testGetUnchecked_runtimeException() { + RuntimeException expected = + assertThrows(RuntimeException.class, () -> getUnchecked(RUNTIME_EXCEPTION_FUTURE)); + assertEquals(RUNTIME_EXCEPTION, expected); } - public void testGetUnchecked_Error() { + public void testGetUnchecked_error() { try { getUnchecked(ERROR_FUTURE); } catch (Error expected) { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTest.java index 8f225975a971..d4c676975c9d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTest.java @@ -44,6 +44,7 @@ import static com.google.common.util.concurrent.Futures.whenAllComplete; import static com.google.common.util.concurrent.Futures.whenAllSucceed; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.util.concurrent.TestPlatform.clearInterrupt; import static com.google.common.util.concurrent.TestPlatform.getDoneFromTimeoutOverload; import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; @@ -58,6 +59,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; @@ -67,6 +69,8 @@ import com.google.common.testing.ClassSanityTester; import com.google.common.testing.GcFinalization; import com.google.common.testing.TestLogHandler; +import com.google.common.util.concurrent.TestExceptions.SomeError; +import com.google.common.util.concurrent.TestExceptions.SomeUncheckedException; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.FileNotFoundException; import java.io.IOException; @@ -88,15 +92,17 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.LogRecord; import java.util.logging.Logger; -import javax.annotation.CheckForNull; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Unit tests for {@link Futures}. * * @author Nishant Thakkar */ +@NullMarked @GwtCompatible(emulated = true) public class FuturesTest extends TestCase { private static final Logger aggregateFutureLogger = @@ -141,7 +147,7 @@ public void testImmediateFuture() throws Exception { } public void testImmediateVoidFuture() throws Exception { - ListenableFuture voidFuture = immediateVoidFuture(); + ListenableFuture<@Nullable Void> voidFuture = immediateVoidFuture(); assertThat(getDone(voidFuture)).isNull(); assertThat(getDoneFromTimeoutOverload(voidFuture)).isNull(); @@ -153,19 +159,11 @@ public void testImmediateFailedFuture() throws Exception { ListenableFuture future = immediateFailedFuture(exception); assertThat(future.toString()).endsWith("[status=FAILURE, cause=[" + exception + "]]"); - try { - getDone(future); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(future)); + assertSame(exception, expected.getCause()); - try { - getDoneFromTimeoutOverload(future); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + expected = assertThrows(ExecutionException.class, () -> getDoneFromTimeoutOverload(future)); + assertSame(exception, expected.getCause()); } public void testImmediateFailedFuture_cancellationException() throws Exception { @@ -174,19 +172,11 @@ public void testImmediateFailedFuture_cancellationException() throws Exception { assertFalse(future.isCancelled()); assertThat(future.toString()).endsWith("[status=FAILURE, cause=[" + exception + "]]"); - try { - getDone(future); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(future)); + assertSame(exception, expected.getCause()); - try { - getDoneFromTimeoutOverload(future); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + expected = assertThrows(ExecutionException.class, () -> getDoneFromTimeoutOverload(future)); + assertSame(exception, expected.getCause()); } public void testImmediateCancelledFutureBasic() throws Exception { @@ -194,27 +184,23 @@ public void testImmediateCancelledFutureBasic() throws Exception { assertTrue(future.isCancelled()); } + @J2ktIncompatible @GwtIncompatible public void testImmediateCancelledFutureStack() throws Exception { ListenableFuture future = CallerClass1.makeImmediateCancelledFuture(); assertTrue(future.isCancelled()); - try { - CallerClass2.get(future); - fail(); - } catch (CancellationException expected) { - // There should be two CancellationException chained together. The outer one should have the - // stack trace of where the get() call was made, and the inner should have the stack trace of - // where the immediateCancelledFuture() call was made. - List stackTrace = ImmutableList.copyOf(expected.getStackTrace()); - assertFalse(Iterables.any(stackTrace, hasClassName(CallerClass1.class))); - assertTrue(Iterables.any(stackTrace, hasClassName(CallerClass2.class))); - - // See AbstractFutureCancellationCauseTest for how to set causes. - assertThat(expected.getCause()).isNull(); - } + CancellationException expected = + assertThrows(CancellationException.class, () -> CallerClass2.get(future)); + List stackTrace = ImmutableList.copyOf(expected.getStackTrace()); + assertFalse(Iterables.any(stackTrace, hasClassName(CallerClass1.class))); + assertTrue(Iterables.any(stackTrace, hasClassName(CallerClass2.class))); + + // See AbstractFutureCancellationCauseTest for how to set causes. + assertThat(expected).hasCauseThat().isNull(); } + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static Predicate hasClassName(final Class clazz) { return new Predicate() { @@ -249,12 +235,14 @@ private static class Bar {} private static class BarChild extends Bar {} + @J2ktIncompatible // TODO(b/324550390): Enable public void testTransform_genericsNull() throws Exception { ListenableFuture nullFuture = immediateFuture(null); ListenableFuture transformedFuture = transform(nullFuture, constant(null), directExecutor()); assertNull(getDone(transformedFuture)); } + @J2ktIncompatible // TODO(b/324550390): Enable public void testTransform_genericsHierarchy() throws Exception { ListenableFuture future = immediateFuture(null); final BarChild barChild = new BarChild(); @@ -269,43 +257,28 @@ public BarChild apply(Foo unused) { assertSame(barChild, bar); } - /* - * Android does not handle this stack overflow gracefully... though somehow some other - * stack-overflow tests work. It must depend on the exact place the error occurs. - */ - @AndroidIncompatible + @J2ktIncompatible @GwtIncompatible // StackOverflowError - public void testTransform_StackOverflow() throws Exception { - { - /* - * Initialize all relevant classes before running the test, which may otherwise poison any - * classes it is trying to load during its stack overflow. - */ - SettableFuture root = SettableFuture.create(); - ListenableFuture unused = transform(root, identity(), directExecutor()); - root.set("foo"); - } - - SettableFuture root = SettableFuture.create(); - ListenableFuture output = root; - for (int i = 0; i < 10000; i++) { - output = transform(output, identity(), directExecutor()); - } - try { - root.set("foo"); - fail(); - } catch (StackOverflowError expected) { - } + public void testTransform_stackOverflow() throws Exception { + SettableFuture input = SettableFuture.create(); + ListenableFuture output = transform(input, identity(), directExecutor()); + output.addListener( + () -> { + throw new StackOverflowError(); + }, + directExecutor()); + assertThrows(StackOverflowError.class, () -> input.set("foo")); } - public void testTransform_ErrorAfterCancellation() throws Exception { + public void testTransform_errorAfterCancellation() throws Exception { class Transformer implements Function { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public Object apply(Object input) { output.cancel(false); - throw new MyError(); + throw new SomeError(); } } Transformer transformer = new Transformer(); @@ -318,14 +291,15 @@ public Object apply(Object input) { assertTrue(output.isCancelled()); } - public void testTransform_ExceptionAfterCancellation() throws Exception { + public void testTransform_exceptionAfterCancellation() throws Exception { class Transformer implements Function { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public Object apply(Object input) { output.cancel(false); - throw new MyRuntimeException(); + throw new SomeUncheckedException(); } } Transformer transformer = new Transformer(); @@ -340,27 +314,19 @@ public Object apply(Object input) { public void testTransform_getThrowsRuntimeException() throws Exception { ListenableFuture input = - UncheckedThrowingFuture.throwingRuntimeException(new MyRuntimeException()); + UncheckedThrowingFuture.throwingRuntimeException(new SomeUncheckedException()); ListenableFuture output = transform(input, identity(), directExecutor()); - try { - getDone(output); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyRuntimeException.class); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(output)); + assertThat(expected).hasCauseThat().isInstanceOf(SomeUncheckedException.class); } public void testTransform_getThrowsError() throws Exception { - ListenableFuture input = UncheckedThrowingFuture.throwingError(new MyError()); + ListenableFuture input = UncheckedThrowingFuture.throwingError(new SomeError()); ListenableFuture output = transform(input, identity(), directExecutor()); - try { - getDone(output); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyError.class); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(output)); + assertThat(expected).hasCauseThat().isInstanceOf(SomeError.class); } public void testTransform_listenerThrowsError() throws Exception { @@ -371,15 +337,11 @@ public void testTransform_listenerThrowsError() throws Exception { new Runnable() { @Override public void run() { - throw new MyError(); + throw new SomeError(); } }, directExecutor()); - try { - input.set("foo"); - fail(); - } catch (MyError expected) { - } + assertThrows(SomeError.class, () -> input.set("foo")); } public void testTransformAsync_cancelPropagatesToInput() throws Exception { @@ -388,7 +350,7 @@ public void testTransformAsync_cancelPropagatesToInput() throws Exception { new AsyncFunction() { @Override public ListenableFuture apply(Foo unused) { - throw new AssertionFailedError("Unexpeted call to apply."); + throw new AssertionFailedError("Unexpected call to apply."); } }; assertTrue(transformAsync(input, function, directExecutor()).cancel(false)); @@ -402,7 +364,7 @@ public void testTransformAsync_interruptPropagatesToInput() throws Exception { new AsyncFunction() { @Override public ListenableFuture apply(Foo unused) { - throw new AssertionFailedError("Unexpeted call to apply."); + throw new AssertionFailedError("Unexpected call to apply."); } }; assertTrue(transformAsync(input, function, directExecutor()).cancel(true)); @@ -410,8 +372,8 @@ public ListenableFuture apply(Foo unused) { assertTrue(input.wasInterrupted()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testTransformAsync_interruptPropagatesToTransformingThread() throws Exception { SettableFuture input = SettableFuture.create(); final CountDownLatch inFunction = new CountDownLatch(1); @@ -432,22 +394,20 @@ public ListenableFuture apply(String s) throws Exception { } }; - ListenableFuture futureResult = - transformAsync(input, function, newSingleThreadExecutor()); + ExecutorService service = newSingleThreadExecutor(); + ListenableFuture futureResult = transformAsync(input, function, service); input.set("value"); inFunction.await(); futureResult.cancel(true); shouldCompleteFunction.countDown(); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); // TODO(cpovirk): implement interruption, updating this test: // https://github.com/google/guava/issues/1989 assertEquals(1, gotException.getCount()); // gotException.await(); + service.shutdown(); + service.awaitTermination(30, SECONDS); } public void testTransformAsync_cancelPropagatesToAsyncOutput() throws Exception { @@ -500,43 +460,28 @@ public ListenableFuture apply(Foo unused) { assertFalse(((AbstractFuture) f2).wasInterrupted()); } - /* - * Android does not handle this stack overflow gracefully... though somehow some other - * stack-overflow tests work. It must depend on the exact place the error occurs. - */ - @AndroidIncompatible + @J2ktIncompatible @GwtIncompatible // StackOverflowError - public void testTransformAsync_StackOverflow() throws Exception { - { - /* - * Initialize all relevant classes before running the test, which may otherwise poison any - * classes it is trying to load during its stack overflow. - */ - SettableFuture root = SettableFuture.create(); - ListenableFuture unused = transformAsync(root, asyncIdentity(), directExecutor()); - root.set("foo"); - } - - SettableFuture root = SettableFuture.create(); - ListenableFuture output = root; - for (int i = 0; i < 10000; i++) { - output = transformAsync(output, asyncIdentity(), directExecutor()); - } - try { - root.set("foo"); - fail(); - } catch (StackOverflowError expected) { - } + public void testTransformAsync_stackOverflow() throws Exception { + SettableFuture input = SettableFuture.create(); + ListenableFuture output = transformAsync(input, asyncIdentity(), directExecutor()); + output.addListener( + () -> { + throw new StackOverflowError(); + }, + directExecutor()); + assertThrows(StackOverflowError.class, () -> input.set("foo")); } - public void testTransformAsync_ErrorAfterCancellation() throws Exception { + public void testTransformAsync_errorAfterCancellation() throws Exception { class Transformer implements AsyncFunction { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public ListenableFuture apply(Object input) { output.cancel(false); - throw new MyError(); + throw new SomeError(); } } Transformer transformer = new Transformer(); @@ -549,14 +494,15 @@ public ListenableFuture apply(Object input) { assertTrue(output.isCancelled()); } - public void testTransformAsync_ExceptionAfterCancellation() throws Exception { + public void testTransformAsync_exceptionAfterCancellation() throws Exception { class Transformer implements AsyncFunction { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public ListenableFuture apply(Object input) { output.cancel(false); - throw new MyRuntimeException(); + throw new SomeUncheckedException(); } } Transformer transformer = new Transformer(); @@ -571,27 +517,19 @@ public ListenableFuture apply(Object input) { public void testTransformAsync_getThrowsRuntimeException() throws Exception { ListenableFuture input = - UncheckedThrowingFuture.throwingRuntimeException(new MyRuntimeException()); + UncheckedThrowingFuture.throwingRuntimeException(new SomeUncheckedException()); ListenableFuture output = transformAsync(input, asyncIdentity(), directExecutor()); - try { - getDone(output); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyRuntimeException.class); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(output)); + assertThat(expected).hasCauseThat().isInstanceOf(SomeUncheckedException.class); } public void testTransformAsync_getThrowsError() throws Exception { - ListenableFuture input = UncheckedThrowingFuture.throwingError(new MyError()); + ListenableFuture input = UncheckedThrowingFuture.throwingError(new SomeError()); ListenableFuture output = transformAsync(input, asyncIdentity(), directExecutor()); - try { - getDone(output); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyError.class); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(output)); + assertThat(expected).hasCauseThat().isInstanceOf(SomeError.class); } public void testTransformAsync_listenerThrowsError() throws Exception { @@ -602,15 +540,11 @@ public void testTransformAsync_listenerThrowsError() throws Exception { new Runnable() { @Override public void run() { - throw new MyError(); + throw new SomeError(); } }, directExecutor()); - try { - input.set("foo"); - fail(); - } catch (MyError expected) { - } + assertThrows(SomeError.class, () -> input.set("foo")); } public void testTransform_rejectionPropagatesToOutput() throws Exception { @@ -618,12 +552,9 @@ public void testTransform_rejectionPropagatesToOutput() throws Exception { Function identity = identity(); ListenableFuture transformed = transform(input, identity, REJECTING_EXECUTOR); input.set(new Foo()); - try { - getDone(transformed); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RejectedExecutionException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(transformed)); + assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); } public void testTransformAsync_rejectionPropagatesToOutput() throws Exception { @@ -631,12 +562,9 @@ public void testTransformAsync_rejectionPropagatesToOutput() throws Exception { AsyncFunction asyncIdentity = asyncIdentity(); ListenableFuture transformed = transformAsync(input, asyncIdentity, REJECTING_EXECUTOR); input.set(new Foo()); - try { - getDone(transformed); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RejectedExecutionException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(transformed)); + assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); } /** Tests that the function is invoked only once, even if it throws an exception. */ @@ -688,14 +616,11 @@ public Integer apply(Integer from) { getDoneFromTimeoutOverload(transform(immediateFuture, adder, directExecutor())).intValue()); } - static class MyError extends Error {} - - static class MyRuntimeException extends RuntimeException {} - /** * Test that the function is invoked only once, even if it throws an exception. Also, test that * that function's result is wrapped in an ExecutionException. */ + @J2ktIncompatible @GwtIncompatible // reflection public void testTransformExceptionRemainsMemoized() throws Throwable { // We need to test with two input futures since ExecutionList.execute @@ -706,14 +631,14 @@ public void testTransformExceptionRemainsMemoized() throws Throwable { ListenableFuture exceptionComposedFuture = transform(exceptionInput, newOneTimeExceptionThrower(), directExecutor()); exceptionInput.set(0); - runGetIdempotencyTest(exceptionComposedFuture, MyRuntimeException.class); + runGetIdempotencyTest(exceptionComposedFuture, SomeUncheckedException.class); SettableFuture errorInput = SettableFuture.create(); ListenableFuture errorComposedFuture = transform(errorInput, newOneTimeErrorThrower(), directExecutor()); errorInput.set(0); - runGetIdempotencyTest(errorComposedFuture, MyError.class); + runGetIdempotencyTest(errorComposedFuture, SomeError.class); /* * Try again when the input's value is already filled in, since the flow is @@ -721,13 +646,14 @@ public void testTransformExceptionRemainsMemoized() throws Throwable { */ exceptionComposedFuture = transform(exceptionInput, newOneTimeExceptionThrower(), directExecutor()); - runGetIdempotencyTest(exceptionComposedFuture, MyRuntimeException.class); + runGetIdempotencyTest(exceptionComposedFuture, SomeUncheckedException.class); runGetIdempotencyTest( - transform(errorInput, newOneTimeErrorThrower(), directExecutor()), MyError.class); - runGetIdempotencyTest(errorComposedFuture, MyError.class); + transform(errorInput, newOneTimeErrorThrower(), directExecutor()), SomeError.class); + runGetIdempotencyTest(errorComposedFuture, SomeError.class); } + @J2ktIncompatible @GwtIncompatible // reflection private static void runGetIdempotencyTest( Future transformedFuture, Class expectedExceptionClass) @@ -744,6 +670,7 @@ private static void runGetIdempotencyTest( } } + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static Function newOneTimeExceptionThrower() { return new Function() { @@ -754,11 +681,12 @@ public Integer apply(Integer from) { if (++calls > 1) { fail(); } - throw new MyRuntimeException(); + throw new SomeUncheckedException(); } }; } + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static Function newOneTimeErrorThrower() { return new Function() { @@ -769,7 +697,7 @@ public Integer apply(Integer from) { if (++calls > 1) { fail(); } - throw new MyError(); + throw new SomeError(); } }; } @@ -791,7 +719,7 @@ public void execute(Runnable command) { } } - public void testTransform_Executor() throws Exception { + public void testTransform_executor() throws Exception { Object value = new Object(); ExecutorSpy spy = new ExecutorSpy(directExecutor()); @@ -803,8 +731,8 @@ public void testTransform_Executor() throws Exception { assertTrue(spy.wasExecuted); } + @J2ktIncompatible @GwtIncompatible // Threads - public void testTransformAsync_functionToString() throws Exception { final CountDownLatch functionCalled = new CountDownLatch(1); final CountDownLatch functionBlocking = new CountDownLatch(1); @@ -832,6 +760,7 @@ public ListenableFuture apply(Object input) throws Exception { } } + @J2ktIncompatible @GwtIncompatible // lazyTransform public void testLazyTransform() throws Exception { FunctionSpy spy = new FunctionSpy<>(constant("bar")); @@ -844,6 +773,7 @@ public void testLazyTransform() throws Exception { spy.verifyCallCount(2); } + @J2ktIncompatible @GwtIncompatible // lazyTransform public void testLazyTransform_exception() throws Exception { final RuntimeException exception = new RuntimeException("deliberate"); @@ -855,18 +785,12 @@ public String apply(Integer input) { } }; Future transformed = lazyTransform(immediateFuture(1), function); - try { - getDone(transformed); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } - try { - getDoneFromTimeoutOverload(transformed); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(transformed)); + assertSame(exception, expected.getCause()); + expected = + assertThrows(ExecutionException.class, () -> getDoneFromTimeoutOverload(transformed)); + assertSame(exception, expected.getCause()); } private static class FunctionSpy implements Function { @@ -892,7 +816,7 @@ private static Function unexpectedFunction() { return new Function() { @Override public V apply(X t) { - throw newAssertionError("Unexpected fallback", t); + throw new AssertionError("Unexpected fallback", t); } }; } @@ -928,18 +852,11 @@ private static AsyncFunction unexpectedAsyncFunct return new AsyncFunction() { @Override public ListenableFuture apply(X t) { - throw newAssertionError("Unexpected fallback", t); + throw new AssertionError("Unexpected fallback", t); } }; } - /** Alternative to AssertionError(String, Throwable), which doesn't exist in GWT 2.6.1. */ - private static AssertionError newAssertionError(String message, Throwable cause) { - AssertionError e = new AssertionError(message); - e.initCause(cause); - return e; - } - // catchingAsync tests cloned from the old withFallback tests: public void testCatchingAsync_inputDoesNotRaiseException() throws Exception { @@ -968,6 +885,7 @@ public ListenableFuture apply(Throwable t) throws Exception { fallback.verifyCallCount(1); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatchingAsync_inputCancelledWithoutFallback() throws Exception { AsyncFunction fallback = unexpectedAsyncFunction(); @@ -997,12 +915,12 @@ public ListenableFuture apply(Throwable t) throws Exception { } }; ListenableFuture failingFuture = immediateFailedFuture(new RuntimeException()); - try { - getDone(catchingAsync(failingFuture, Throwable.class, fallback, directExecutor())); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> + getDone(catchingAsync(failingFuture, Throwable.class, fallback, directExecutor()))); + assertSame(error, expected.getCause()); } public void testCatchingAsync_fallbackReturnsRuntimeException() throws Exception { @@ -1079,9 +997,6 @@ public void testCatchingAsync_resultCancelledBeforeFallback() throws Exception { assertFalse(primary.wasInterrupted()); } - @GwtIncompatible // mocks - // TODO(cpovirk): eliminate use of mocks - @SuppressWarnings("unchecked") public void testCatchingAsync_resultCancelledAfterFallback() throws Exception { final SettableFuture secondary = SettableFuture.create(); final RuntimeException raisedException = new RuntimeException(); @@ -1105,6 +1020,7 @@ public ListenableFuture apply(Throwable t) throws Exception { fallback.verifyCallCount(1); } + @J2ktIncompatible // Nullability public void testCatchingAsync_nullInsteadOfFuture() throws Exception { ListenableFuture inputFuture = immediateFailedFuture(new Exception()); ListenableFuture chainedFuture = @@ -1119,21 +1035,18 @@ public ListenableFuture apply(Throwable t) { } }, directExecutor()); - try { - getDone(chainedFuture); - fail(); - } catch (ExecutionException expected) { - NullPointerException cause = (NullPointerException) expected.getCause(); - assertThat(cause) - .hasMessageThat() - .contains( - "AsyncFunction.apply returned null instead of a Future. " - + "Did you mean to return immediateFuture(null)?"); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(chainedFuture)); + NullPointerException cause = (NullPointerException) expected.getCause(); + assertThat(cause) + .hasMessageThat() + .contains( + "AsyncFunction.apply returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)?"); } + @J2ktIncompatible @GwtIncompatible // threads - public void testCatchingAsync_interruptPropagatesToTransformingThread() throws Exception { SettableFuture input = SettableFuture.create(); final CountDownLatch inFunction = new CountDownLatch(1); @@ -1154,26 +1067,25 @@ public ListenableFuture apply(Throwable t) throws Exception { } }; + ExecutorService executor = newSingleThreadExecutor(); ListenableFuture futureResult = - catchingAsync(input, Exception.class, function, newSingleThreadExecutor()); + catchingAsync(input, Exception.class, function, executor); input.setException(new Exception()); inFunction.await(); futureResult.cancel(true); shouldCompleteFunction.countDown(); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); // TODO(cpovirk): implement interruption, updating this test: // https://github.com/google/guava/issues/1989 assertEquals(1, gotException.getCount()); // gotException.await(); + executor.shutdown(); + executor.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // Threads - public void testCatchingAsync_functionToString() throws Exception { final CountDownLatch functionCalled = new CountDownLatch(1); final CountDownLatch functionBlocking = new CountDownLatch(1); @@ -1251,6 +1163,7 @@ public Integer apply(Throwable t) { fallback.verifyCallCount(1); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatching_inputCancelledWithoutFallback() throws Exception { Function fallback = unexpectedFunction(); @@ -1280,12 +1193,11 @@ public Integer apply(Throwable t) { } }; ListenableFuture failingFuture = immediateFailedFuture(new RuntimeException()); - try { - getDone(catching(failingFuture, Throwable.class, fallback, directExecutor())); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> getDone(catching(failingFuture, Throwable.class, fallback, directExecutor()))); + assertSame(error, expected.getCause()); } /* @@ -1345,7 +1257,7 @@ public void testCatching_resultCancelledBeforeFallback() throws Exception { // Some tests of the exceptionType parameter: - public void testCatching_Throwable() throws Exception { + public void testCatching_throwable() throws Exception { Function fallback = functionReturningOne(); ListenableFuture originalFuture = immediateFailedFuture(new IOException()); ListenableFuture faultTolerantFuture = @@ -1353,6 +1265,7 @@ public void testCatching_Throwable() throws Exception { assertEquals(1, (int) getDone(faultTolerantFuture)); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatching_customTypeMatch() throws Exception { Function fallback = functionReturningOne(); @@ -1362,53 +1275,41 @@ public void testCatching_customTypeMatch() throws Exception { assertEquals(1, (int) getDone(faultTolerantFuture)); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatching_customTypeNoMatch() throws Exception { Function fallback = functionReturningOne(); ListenableFuture originalFuture = immediateFailedFuture(new RuntimeException()); ListenableFuture faultTolerantFuture = catching(originalFuture, IOException.class, fallback, directExecutor()); - try { - getDone(faultTolerantFuture); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RuntimeException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(faultTolerantFuture)); + assertThat(expected).hasCauseThat().isInstanceOf(RuntimeException.class); } + @J2ktIncompatible @GwtIncompatible // StackOverflowError - public void testCatching_StackOverflow() throws Exception { - { - /* - * Initialize all relevant classes before running the test, which may otherwise poison any - * classes it is trying to load during its stack overflow. - */ - SettableFuture root = SettableFuture.create(); - ListenableFuture unused = - catching(root, MyException.class, identity(), directExecutor()); - root.setException(new MyException()); - } - - SettableFuture root = SettableFuture.create(); - ListenableFuture output = root; - for (int i = 0; i < 10000; i++) { - output = catching(output, MyException.class, identity(), directExecutor()); - } - try { - root.setException(new MyException()); - fail(); - } catch (StackOverflowError expected) { - } + public void testCatching_stackOverflow() throws Exception { + SettableFuture input = SettableFuture.create(); + ListenableFuture output = + catching(input, MyException.class, identity(), directExecutor()); + output.addListener( + () -> { + throw new StackOverflowError(); + }, + directExecutor()); + assertThrows(StackOverflowError.class, () -> input.setException(new MyException())); } - public void testCatching_ErrorAfterCancellation() throws Exception { + public void testCatching_errorAfterCancellation() throws Exception { class Fallback implements Function { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public Object apply(Throwable input) { output.cancel(false); - throw new MyError(); + throw new SomeError(); } } Fallback fallback = new Fallback(); @@ -1421,14 +1322,15 @@ public Object apply(Throwable input) { assertTrue(output.isCancelled()); } - public void testCatching_ExceptionAfterCancellation() throws Exception { + public void testCatching_exceptionAfterCancellation() throws Exception { class Fallback implements Function { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public Object apply(Throwable input) { output.cancel(false); - throw new MyRuntimeException(); + throw new SomeUncheckedException(); } } Fallback fallback = new Fallback(); @@ -1443,21 +1345,21 @@ public Object apply(Throwable input) { public void testCatching_getThrowsRuntimeException() throws Exception { ListenableFuture input = - UncheckedThrowingFuture.throwingRuntimeException(new MyRuntimeException()); + UncheckedThrowingFuture.throwingRuntimeException(new SomeUncheckedException()); - // We'd catch only MyRuntimeException.class here, but then the test won't compile under GWT. + // We'd catch only SomeUncheckedException.class here, but then the test won't compile under GWT. ListenableFuture output = catching(input, Throwable.class, identity(), directExecutor()); - assertThat(getDone(output)).isInstanceOf(MyRuntimeException.class); + assertThat(getDone(output)).isInstanceOf(SomeUncheckedException.class); } public void testCatching_getThrowsError() throws Exception { - ListenableFuture input = UncheckedThrowingFuture.throwingError(new MyError()); + ListenableFuture input = UncheckedThrowingFuture.throwingError(new SomeError()); - // We'd catch only MyError.class here, but then the test won't compile under GWT. + // We'd catch only SomeError.class here, but then the test won't compile under GWT. ListenableFuture output = catching(input, Throwable.class, identity(), directExecutor()); - assertThat(getDone(output)).isInstanceOf(MyError.class); + assertThat(getDone(output)).isInstanceOf(SomeError.class); } public void testCatching_listenerThrowsError() throws Exception { @@ -1469,18 +1371,14 @@ public void testCatching_listenerThrowsError() throws Exception { new Runnable() { @Override public void run() { - throw new MyError(); + throw new SomeError(); } }, directExecutor()); - try { - input.setException(new MyException()); - fail(); - } catch (MyError expected) { - } + assertThrows(SomeError.class, () -> input.setException(new MyException())); } - public void testCatchingAsync_Throwable() throws Exception { + public void testCatchingAsync_throwable() throws Exception { AsyncFunction fallback = asyncFunctionReturningOne(); ListenableFuture originalFuture = immediateFailedFuture(new IOException()); ListenableFuture faultTolerantFuture = @@ -1488,6 +1386,7 @@ public void testCatchingAsync_Throwable() throws Exception { assertEquals(1, (int) getDone(faultTolerantFuture)); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatchingAsync_customTypeMatch() throws Exception { AsyncFunction fallback = asyncFunctionReturningOne(); @@ -1497,53 +1396,41 @@ public void testCatchingAsync_customTypeMatch() throws Exception { assertEquals(1, (int) getDone(faultTolerantFuture)); } + @J2ktIncompatible @GwtIncompatible // non-Throwable exceptionType public void testCatchingAsync_customTypeNoMatch() throws Exception { AsyncFunction fallback = asyncFunctionReturningOne(); ListenableFuture originalFuture = immediateFailedFuture(new RuntimeException()); ListenableFuture faultTolerantFuture = catchingAsync(originalFuture, IOException.class, fallback, directExecutor()); - try { - getDone(faultTolerantFuture); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RuntimeException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(faultTolerantFuture)); + assertThat(expected).hasCauseThat().isInstanceOf(RuntimeException.class); } + @J2ktIncompatible @GwtIncompatible // StackOverflowError - public void testCatchingAsync_StackOverflow() throws Exception { - { - /* - * Initialize all relevant classes before running the test, which may otherwise poison any - * classes it is trying to load during its stack overflow. - */ - SettableFuture root = SettableFuture.create(); - ListenableFuture unused = - catchingAsync(root, MyException.class, asyncIdentity(), directExecutor()); - root.setException(new MyException()); - } - - SettableFuture root = SettableFuture.create(); - ListenableFuture output = root; - for (int i = 0; i < 10000; i++) { - output = catchingAsync(output, MyException.class, asyncIdentity(), directExecutor()); - } - try { - root.setException(new MyException()); - fail(); - } catch (StackOverflowError expected) { - } + public void testCatchingAsync_stackOverflow() throws Exception { + SettableFuture input = SettableFuture.create(); + ListenableFuture output = + catchingAsync(input, MyException.class, asyncIdentity(), directExecutor()); + output.addListener( + () -> { + throw new StackOverflowError(); + }, + directExecutor()); + assertThrows(StackOverflowError.class, () -> input.setException(new MyException())); } - public void testCatchingAsync_ErrorAfterCancellation() throws Exception { + public void testCatchingAsync_errorAfterCancellation() throws Exception { class Fallback implements AsyncFunction { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public ListenableFuture apply(Throwable input) { output.cancel(false); - throw new MyError(); + throw new SomeError(); } } Fallback fallback = new Fallback(); @@ -1557,14 +1444,15 @@ public ListenableFuture apply(Throwable input) { assertTrue(output.isCancelled()); } - public void testCatchingAsync_ExceptionAfterCancellation() throws Exception { + public void testCatchingAsync_exceptionAfterCancellation() throws Exception { class Fallback implements AsyncFunction { + @SuppressWarnings("nullness:initialization.field.uninitialized") ListenableFuture output; @Override public ListenableFuture apply(Throwable input) { output.cancel(false); - throw new MyRuntimeException(); + throw new SomeUncheckedException(); } } Fallback fallback = new Fallback(); @@ -1580,21 +1468,21 @@ public ListenableFuture apply(Throwable input) { public void testCatchingAsync_getThrowsRuntimeException() throws Exception { ListenableFuture input = - UncheckedThrowingFuture.throwingRuntimeException(new MyRuntimeException()); + UncheckedThrowingFuture.throwingRuntimeException(new SomeUncheckedException()); - // We'd catch only MyRuntimeException.class here, but then the test won't compile under GWT. + // We'd catch only SomeUncheckedException.class here, but then the test won't compile under GWT. ListenableFuture output = catchingAsync(input, Throwable.class, asyncIdentity(), directExecutor()); - assertThat(getDone(output)).isInstanceOf(MyRuntimeException.class); + assertThat(getDone(output)).isInstanceOf(SomeUncheckedException.class); } public void testCatchingAsync_getThrowsError() throws Exception { - ListenableFuture input = UncheckedThrowingFuture.throwingError(new MyError()); + ListenableFuture input = UncheckedThrowingFuture.throwingError(new SomeError()); - // We'd catch only MyError.class here, but then the test won't compile under GWT. + // We'd catch only SomeError.class here, but then the test won't compile under GWT. ListenableFuture output = catchingAsync(input, Throwable.class, asyncIdentity(), directExecutor()); - assertThat(getDone(output)).isInstanceOf(MyError.class); + assertThat(getDone(output)).isInstanceOf(SomeError.class); } public void testCatchingAsync_listenerThrowsError() throws Exception { @@ -1606,15 +1494,11 @@ public void testCatchingAsync_listenerThrowsError() throws Exception { new Runnable() { @Override public void run() { - throw new MyError(); + throw new SomeError(); } }, directExecutor()); - try { - input.setException(new MyException()); - fail(); - } catch (MyError expected) { - } + assertThrows(SomeError.class, () -> input.setException(new MyException())); } public void testCatching_rejectionPropagatesToOutput() throws Exception { @@ -1622,12 +1506,9 @@ public void testCatching_rejectionPropagatesToOutput() throws Exception { ListenableFuture transformed = catching(input, Throwable.class, constant("foo"), REJECTING_EXECUTOR); input.setException(new Exception()); - try { - getDone(transformed); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RejectedExecutionException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(transformed)); + assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); } public void testCatchingAsync_rejectionPropagatesToOutput() throws Exception { @@ -1639,12 +1520,9 @@ public void testCatchingAsync_rejectionPropagatesToOutput() throws Exception { constantAsyncFunction(immediateFuture("foo")), REJECTING_EXECUTOR); input.setException(new Exception()); - try { - getDone(transformed); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(RejectedExecutionException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(transformed)); + assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); } private Function functionReturningOne() { @@ -1666,7 +1544,7 @@ public ListenableFuture apply(X t) { } private static AsyncFunction constantAsyncFunction( - final ListenableFuture output) { + final @Nullable ListenableFuture output) { return new AsyncFunction() { @Override public ListenableFuture apply(I input) { @@ -1675,14 +1553,16 @@ public ListenableFuture apply(I input) { }; } - public void testTransformAsync_genericsWildcard_AsyncFunction() throws Exception { + @J2ktIncompatible // Wildcard generics + public void testTransformAsync_genericsWildcard_asyncFunction() throws Exception { ListenableFuture nullFuture = immediateFuture(null); ListenableFuture chainedFuture = transformAsync(nullFuture, constantAsyncFunction(nullFuture), directExecutor()); assertNull(getDone(chainedFuture)); } - public void testTransformAsync_genericsHierarchy_AsyncFunction() throws Exception { + @J2ktIncompatible // TODO(b/324550390): Enable + public void testTransformAsync_genericsHierarchy_asyncFunction() throws Exception { ListenableFuture future = immediateFuture(null); final BarChild barChild = new BarChild(); AsyncFunction function = @@ -1698,17 +1578,14 @@ public AbstractFuture apply(Foo unused) { assertSame(barChild, bar); } + @J2ktIncompatible @GwtIncompatible // get() timeout public void testTransformAsync_asyncFunction_timeout() throws InterruptedException, ExecutionException { AsyncFunction function = constantAsyncFunction(immediateFuture(1)); ListenableFuture future = transformAsync(SettableFuture.create(), function, directExecutor()); - try { - future.get(1, MILLISECONDS); - fail(); - } catch (TimeoutException expected) { - } + assertThrows(TimeoutException.class, () -> future.get(1, MILLISECONDS)); } public void testTransformAsync_asyncFunction_error() throws InterruptedException { @@ -1724,33 +1601,28 @@ public ListenableFuture apply(String input) { ListenableFuture outputFuture = transformAsync(inputFuture, function, directExecutor()); inputFuture.set("value"); - try { - getDone(outputFuture); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(outputFuture)); + assertSame(error, expected.getCause()); } + @J2ktIncompatible // Nullability public void testTransformAsync_asyncFunction_nullInsteadOfFuture() throws Exception { ListenableFuture inputFuture = immediateFuture("a"); ListenableFuture chainedFuture = transformAsync(inputFuture, constantAsyncFunction(null), directExecutor()); - try { - getDone(chainedFuture); - fail(); - } catch (ExecutionException expected) { - NullPointerException cause = (NullPointerException) expected.getCause(); - assertThat(cause) - .hasMessageThat() - .contains( - "AsyncFunction.apply returned null instead of a Future. " - + "Did you mean to return immediateFuture(null)?"); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(chainedFuture)); + NullPointerException cause = (NullPointerException) expected.getCause(); + assertThat(cause) + .hasMessageThat() + .contains( + "AsyncFunction.apply returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)?"); } + @J2ktIncompatible @GwtIncompatible // threads - public void testTransformAsync_asyncFunction_cancelledWhileApplyingFunction() throws InterruptedException, ExecutionException { final CountDownLatch inFunction = new CountDownLatch(1); @@ -1766,26 +1638,20 @@ public ListenableFuture apply(String input) throws Exception { } }; SettableFuture inputFuture = SettableFuture.create(); - ListenableFuture future = - transformAsync(inputFuture, function, newSingleThreadExecutor()); + ExecutorService service = newSingleThreadExecutor(); + ListenableFuture future = transformAsync(inputFuture, function, service); inputFuture.set("value"); inFunction.await(); future.cancel(false); functionDone.countDown(); - try { - future.get(); - fail(); - } catch (CancellationException expected) { - } - try { - resultFuture.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> future.get()); + assertThrows(CancellationException.class, () -> resultFuture.get()); + service.shutdown(); + service.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // threads - public void testTransformAsync_asyncFunction_cancelledBeforeApplyingFunction() throws InterruptedException { final AtomicBoolean functionCalled = new AtomicBoolean(); @@ -1835,31 +1701,26 @@ public ListenableFuture call() { SettableFuture inputFuture = SettableFuture.create(); ListenableFuture outputFuture = submitAsync(callable, directExecutor()); inputFuture.set("value"); - try { - getDone(outputFuture); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(outputFuture)); + assertSame(error, expected.getCause()); } + @J2ktIncompatible // TODO(b/324550390): Enable public void testSubmitAsync_asyncCallable_nullInsteadOfFuture() throws Exception { ListenableFuture chainedFuture = submitAsync(constantAsyncCallable(null), directExecutor()); - try { - getDone(chainedFuture); - fail(); - } catch (ExecutionException expected) { - NullPointerException cause = (NullPointerException) expected.getCause(); - assertThat(cause) - .hasMessageThat() - .contains( - "AsyncCallable.call returned null instead of a Future. " - + "Did you mean to return immediateFuture(null)?"); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(chainedFuture)); + NullPointerException cause = (NullPointerException) expected.getCause(); + assertThat(cause) + .hasMessageThat() + .contains( + "AsyncCallable.call returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)?"); } + @J2ktIncompatible @GwtIncompatible // threads - public void testSubmitAsync_asyncCallable_cancelledWhileApplyingFunction() throws InterruptedException, ExecutionException { final CountDownLatch inFunction = new CountDownLatch(1); @@ -1875,25 +1736,20 @@ public ListenableFuture call() throws InterruptedException { } }; SettableFuture inputFuture = SettableFuture.create(); - ListenableFuture future = submitAsync(callable, newSingleThreadExecutor()); + ExecutorService service = newSingleThreadExecutor(); + ListenableFuture future = submitAsync(callable, service); inputFuture.set("value"); inFunction.await(); future.cancel(false); callableDone.countDown(); - try { - future.get(); - fail(); - } catch (CancellationException expected) { - } - try { - resultFuture.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> future.get()); + assertThrows(CancellationException.class, () -> resultFuture.get()); + service.shutdown(); + service.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // threads - public void testSubmitAsync_asyncCallable_cancelledBeforeApplyingFunction() throws InterruptedException { final AtomicBoolean callableCalled = new AtomicBoolean(); @@ -1926,8 +1782,8 @@ public void run() { assertFalse(callableCalled.get()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testSubmitAsync_asyncCallable_returnsInterruptedFuture() throws InterruptedException { assertThat(Thread.interrupted()).isFalse(); SettableFuture cancelledFuture = SettableFuture.create(); @@ -1962,12 +1818,8 @@ public Integer call() throws Exception { } }; ListenableFuture future = submit(callable, directExecutor()); - try { - getDone(future); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(exception); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(future)); + assertThat(expected).hasCauseThat().isSameInstanceAs(exception); } public void testSubmit_runnable_completesAfterRun() throws Exception { @@ -1987,7 +1839,7 @@ public void execute(Runnable runnable) { pendingRunnables.add(runnable); } }; - ListenableFuture future = submit(runnable, executor); + ListenableFuture<@Nullable Void> future = submit(runnable, executor); assertThat(future.isDone()).isFalse(); assertThat(executedRunnables).isEmpty(); assertThat(pendingRunnables).hasSize(1); @@ -2006,17 +1858,13 @@ public void run() { throw exception; } }; - ListenableFuture future = submit(runnable, directExecutor()); - try { - getDone(future); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isSameInstanceAs(exception); - } + ListenableFuture<@Nullable Void> future = submit(runnable, directExecutor()); + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(future)); + assertThat(expected).hasCauseThat().isSameInstanceAs(exception); } + @J2ktIncompatible @GwtIncompatible // threads - public void testScheduleAsync_asyncCallable_error() throws InterruptedException { final Error error = new Error("deliberate"); AsyncCallable callable = @@ -2029,35 +1877,31 @@ public ListenableFuture call() { SettableFuture inputFuture = SettableFuture.create(); ListenableFuture outputFuture = submitAsync(callable, directExecutor()); inputFuture.set("value"); - try { - getDone(outputFuture); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(outputFuture)); + assertSame(error, expected.getCause()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testScheduleAsync_asyncCallable_nullInsteadOfFuture() throws Exception { + ExecutorService service = newSingleThreadScheduledExecutor(); ListenableFuture chainedFuture = scheduleAsync( constantAsyncCallable(null), 1, NANOSECONDS, newSingleThreadScheduledExecutor()); - try { - chainedFuture.get(); - fail(); - } catch (ExecutionException expected) { - NullPointerException cause = (NullPointerException) expected.getCause(); - assertThat(cause) - .hasMessageThat() - .contains( - "AsyncCallable.call returned null instead of a Future. " - + "Did you mean to return immediateFuture(null)?"); - } - } - + ExecutionException expected = assertThrows(ExecutionException.class, () -> chainedFuture.get()); + NullPointerException cause = (NullPointerException) expected.getCause(); + assertThat(cause) + .hasMessageThat() + .contains( + "AsyncCallable.call returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)?"); + service.shutdown(); + service.awaitTermination(30, SECONDS); + } + + @J2ktIncompatible @GwtIncompatible // threads - public void testScheduleAsync_asyncCallable_cancelledWhileApplyingFunction() throws InterruptedException, ExecutionException { final CountDownLatch inFunction = new CountDownLatch(1); @@ -2072,25 +1916,19 @@ public ListenableFuture call() throws InterruptedException { return resultFuture; } }; - ListenableFuture future = - scheduleAsync(callable, 1, NANOSECONDS, newSingleThreadScheduledExecutor()); + ScheduledExecutorService service = newSingleThreadScheduledExecutor(); + ListenableFuture future = scheduleAsync(callable, 1, NANOSECONDS, service); inFunction.await(); future.cancel(false); callableDone.countDown(); - try { - future.get(); - fail(); - } catch (CancellationException expected) { - } - try { - resultFuture.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> future.get()); + assertThrows(CancellationException.class, () -> resultFuture.get()); + service.shutdown(); + service.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // threads - public void testScheduleAsync_asyncCallable_cancelledBeforeCallingFunction() throws InterruptedException { final AtomicBoolean callableCalled = new AtomicBoolean(); @@ -2123,7 +1961,8 @@ public void run() { assertFalse(callableCalled.get()); } - private static AsyncCallable constantAsyncCallable(final ListenableFuture returnValue) { + private static AsyncCallable constantAsyncCallable( + final @Nullable ListenableFuture returnValue) { return new AsyncCallable() { @Override public ListenableFuture call() { @@ -2161,7 +2000,6 @@ public void testAllAsList() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); SettableFuture future3 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2, future3); // Attach a listener @@ -2195,7 +2033,6 @@ public void testAllAsList_emptyList() throws Exception { public void testAllAsList_emptyArray() throws Exception { SingleCallListener listener = new SingleCallListener(); listener.expectCall(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(); compound.addListener(listener, directExecutor()); assertThat(getDone(compound)).isEmpty(); @@ -2206,7 +2043,6 @@ public void testAllAsList_failure() throws Exception { SingleCallListener listener = new SingleCallListener(); SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2); compound.addListener(listener, directExecutor()); @@ -2217,12 +2053,8 @@ public void testAllAsList_failure() throws Exception { assertTrue(listener.wasCalled()); assertFalse(future2.isDone()); - try { - getDone(compound); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(compound)); + assertSame(exception, expected.getCause()); } public void testAllAsList_singleFailure() throws Exception { @@ -2230,12 +2062,8 @@ public void testAllAsList_singleFailure() throws Exception { ListenableFuture future = immediateFailedFuture(exception); ListenableFuture> compound = allAsList(ImmutableList.of(future)); - try { - getDone(compound); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(compound)); + assertSame(exception, expected.getCause()); } public void testAllAsList_immediateFailure() throws Exception { @@ -2244,12 +2072,8 @@ public void testAllAsList_immediateFailure() throws Exception { ListenableFuture future2 = immediateFuture("results"); ListenableFuture> compound = allAsList(ImmutableList.of(future1, future2)); - try { - getDone(compound); - fail(); - } catch (ExecutionException expected) { - assertSame(exception, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(compound)); + assertSame(exception, expected.getCause()); } public void testAllAsList_error() throws Exception { @@ -2259,19 +2083,14 @@ public void testAllAsList_error() throws Exception { ListenableFuture> compound = allAsList(ImmutableList.of(future1, future2)); future1.setException(error); - try { - getDone(compound); - fail(); - } catch (ExecutionException expected) { - assertSame(error, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(compound)); + assertSame(error, expected.getCause()); } public void testAllAsList_cancelled() throws Exception { SingleCallListener listener = new SingleCallListener(); SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2); compound.addListener(listener, directExecutor()); @@ -2281,17 +2100,12 @@ public void testAllAsList_cancelled() throws Exception { assertTrue(listener.wasCalled()); assertFalse(future2.isDone()); - try { - getDone(compound); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> getDone(compound)); } public void testAllAsList_resultCancelled() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2); future2.set(DATA2); @@ -2335,7 +2149,6 @@ public void testAllAsList_resultCancelled_withSecondaryListFuture() throws Excep public void testAllAsList_resultInterrupted() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2); future2.set(DATA2); @@ -2363,7 +2176,6 @@ public void testAllAsList_doneFutures() throws Exception { future2.set(DATA2); future3.set(DATA3); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = allAsList(future1, future2, future3); // Attach a listener @@ -2378,50 +2190,45 @@ public void testAllAsList_doneFutures() throws Exception { } /** A single non-error failure is not logged because it is reported via the output future. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_exception() throws Exception { - try { - getDone(allAsList(immediateFailedFuture(new MyException()))); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyException.class); - assertEquals( - "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> getDone(allAsList(immediateFailedFuture(new MyException())))); + assertThat(expected).hasCauseThat().isInstanceOf(MyException.class); + assertEquals( + "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); } /** Ensure that errors are always logged. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_error() throws Exception { - try { - getDone(allAsList(immediateFailedFuture(new MyError()))); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyError.class); - List logged = aggregateFutureLogHandler.getStoredLogRecords(); - assertThat(logged).hasSize(1); // errors are always logged - assertThat(logged.get(0).getThrown()).isInstanceOf(MyError.class); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> getDone(allAsList(immediateFailedFuture(new SomeError())))); + assertThat(expected).hasCauseThat().isInstanceOf(SomeError.class); + List logged = aggregateFutureLogHandler.getStoredLogRecords(); + assertThat(logged).hasSize(1); // errors are always logged + assertThat(logged.get(0).getThrown()).isInstanceOf(SomeError.class); } /** All as list will log extra exceptions that have already occurred. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_multipleExceptions_alreadyDone() throws Exception { - try { - getDone( - allAsList( - immediateFailedFuture(new MyException()), immediateFailedFuture(new MyException()))); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyException.class); - List logged = aggregateFutureLogHandler.getStoredLogRecords(); - assertThat(logged).hasSize(1); // the second failure is logged - assertThat(logged.get(0).getThrown()).isInstanceOf(MyException.class); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> + getDone( + allAsList( + immediateFailedFuture(new MyException()), + immediateFailedFuture(new MyException())))); + assertThat(expected).hasCauseThat().isInstanceOf(MyException.class); + List logged = aggregateFutureLogHandler.getStoredLogRecords(); + assertThat(logged).hasSize(1); // the second failure is logged + assertThat(logged.get(0).getThrown()).isInstanceOf(MyException.class); } /** All as list will log extra exceptions that occur later. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_multipleExceptions_doneLater() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); @@ -2432,29 +2239,27 @@ public void testAllAsList_logging_multipleExceptions_doneLater() throws Exceptio future2.setException(new MyException()); future3.setException(new MyException()); - try { - getDone(all); - fail(); - } catch (ExecutionException expected) { - List logged = aggregateFutureLogHandler.getStoredLogRecords(); - assertThat(logged).hasSize(2); // failures after the first are logged - assertThat(logged.get(0).getThrown()).isInstanceOf(MyException.class); - assertThat(logged.get(1).getThrown()).isInstanceOf(MyException.class); - } + assertThrows(ExecutionException.class, () -> getDone(all)); + List logged = aggregateFutureLogHandler.getStoredLogRecords(); + assertThat(logged).hasSize(2); // failures after the first are logged + assertThat(logged.get(0).getThrown()).isInstanceOf(MyException.class); + assertThat(logged.get(1).getThrown()).isInstanceOf(MyException.class); } /** The same exception happening on multiple futures should not be logged. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_same_exception() throws Exception { - try { - MyException sameInstance = new MyException(); - getDone(allAsList(immediateFailedFuture(sameInstance), immediateFailedFuture(sameInstance))); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyException.class); - assertEquals( - "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> { + MyException sameInstance = new MyException(); + getDone( + allAsList( + immediateFailedFuture(sameInstance), immediateFailedFuture(sameInstance))); + }); + assertThat(expected).hasCauseThat().isInstanceOf(MyException.class); + assertEquals( + "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); } public void testAllAsList_logging_seenExceptionUpdateRace() throws Exception { @@ -2478,13 +2283,9 @@ public void run() { directExecutor()); firstFuture.setException(sameInstance); - try { - getDone(bulkFuture); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyException.class); - assertThat(aggregateFutureLogHandler.getStoredLogRecords()).isEmpty(); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(bulkFuture)); + assertThat(expected).hasCauseThat().isInstanceOf(MyException.class); + assertThat(aggregateFutureLogHandler.getStoredLogRecords()).isEmpty(); } public void testAllAsList_logging_seenExceptionUpdateCancelRace() throws Exception { @@ -2508,44 +2309,41 @@ public void run() { directExecutor()); firstFuture.cancel(false); - try { - getDone(bulkFuture); - fail(); - } catch (CancellationException expected) { - assertThat(getOnlyElement(aggregateFutureLogHandler.getStoredLogRecords()).getThrown()) - .isSameInstanceAs(subsequentFailure); - } + assertThrows(CancellationException.class, () -> getDone(bulkFuture)); + assertThat(getOnlyElement(aggregateFutureLogHandler.getStoredLogRecords()).getThrown()) + .isSameInstanceAs(subsequentFailure); } /** * Different exceptions happening on multiple futures with the same cause should not be logged. */ - @SuppressWarnings("unchecked") public void testAllAsList_logging_same_cause() throws Exception { - try { - MyException exception1 = new MyException(); - MyException exception2 = new MyException(); - MyException exception3 = new MyException(); - - MyException sameInstance = new MyException(); - exception1.initCause(sameInstance); - exception2.initCause(sameInstance); - exception3.initCause(exception2); - getDone(allAsList(immediateFailedFuture(exception1), immediateFailedFuture(exception3))); - fail(); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(MyException.class); - assertEquals( - "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> { + MyException exception1 = new MyException(); + MyException exception2 = new MyException(); + MyException exception3 = new MyException(); + + MyException sameInstance = new MyException(); + exception1.initCause(sameInstance); + exception2.initCause(sameInstance); + exception3.initCause(exception2); + getDone( + allAsList(immediateFailedFuture(exception1), immediateFailedFuture(exception3))); + }); + assertThat(expected).hasCauseThat().isInstanceOf(MyException.class); + assertEquals( + "Nothing should be logged", 0, aggregateFutureLogHandler.getStoredLogRecords().size()); } private static String createCombinedResult(Integer i, Boolean b) { return "-" + i + "-" + b; } + @J2ktIncompatible @GwtIncompatible // threads - public void testWhenAllComplete_noLeakInterruption() throws Exception { final SettableFuture stringFuture = SettableFuture.create(); AsyncCallable combiner = @@ -2563,6 +2361,7 @@ public ListenableFuture call() throws Exception { assertThat(Thread.interrupted()).isFalse(); } + @J2ktIncompatible // Wildcard generics public void testWhenAllComplete_wildcard() throws Exception { ListenableFuture futureA = immediateFuture("a"); ListenableFuture futureB = immediateFuture("b"); @@ -2588,8 +2387,8 @@ public String call() throws Exception { unused = whenAllComplete(asList(futures)).call(combiner, directExecutor()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testWhenAllComplete_asyncResult() throws Exception { SettableFuture futureInteger = SettableFuture.create(); SettableFuture futureBoolean = SettableFuture.create(); @@ -2672,16 +2471,13 @@ public ListenableFuture call() throws Exception { Boolean booleanPartial = true; futureBoolean.set(booleanPartial); - try { - getDone(futureResult); - fail(); - } catch (ExecutionException expected) { - assertSame(thrown, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(futureResult)); + assertSame(thrown, expected.getCause()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testWhenAllComplete_cancelledNotInterrupted() throws Exception { SettableFuture stringFuture = SettableFuture.create(); SettableFuture booleanFuture = SettableFuture.create(); @@ -2698,29 +2494,24 @@ public ListenableFuture call() throws Exception { } }; + ExecutorService service = newSingleThreadExecutor(); ListenableFuture futureResult = - whenAllComplete(stringFuture, booleanFuture).callAsync(combiner, newSingleThreadExecutor()); + whenAllComplete(stringFuture, booleanFuture).callAsync(combiner, service); stringFuture.set("value"); booleanFuture.set(true); inFunction.await(); futureResult.cancel(false); shouldCompleteFunction.countDown(); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); - try { - resultFuture.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); + service.shutdown(); + service.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // threads - public void testWhenAllComplete_interrupted() throws Exception { SettableFuture stringFuture = SettableFuture.create(); SettableFuture booleanFuture = SettableFuture.create(); @@ -2741,19 +2532,18 @@ public ListenableFuture call() throws Exception { } }; + ExecutorService service = newSingleThreadExecutor(); ListenableFuture futureResult = - whenAllComplete(stringFuture, booleanFuture).callAsync(combiner, newSingleThreadExecutor()); + whenAllComplete(stringFuture, booleanFuture).callAsync(combiner, service); stringFuture.set("value"); booleanFuture.set(true); inFunction.await(); futureResult.cancel(true); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); gotException.await(); + service.shutdown(); + service.awaitTermination(30, SECONDS); } public void testWhenAllComplete_runnableResult() throws Exception { @@ -2804,16 +2594,13 @@ public void run() { Boolean booleanPartial = true; futureBoolean.set(booleanPartial); - try { - getDone(futureResult); - fail(); - } catch (ExecutionException expected) { - assertSame(thrown, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(futureResult)); + assertSame(thrown, expected.getCause()); } + @J2ktIncompatible @GwtIncompatible // threads - public void testWhenAllCompleteRunnable_resultCanceledWithoutInterrupt_doesNotInterruptRunnable() throws Exception { SettableFuture stringFuture = SettableFuture.create(); @@ -2837,25 +2624,24 @@ public void run() { } }; + ExecutorService service = newSingleThreadExecutor(); ListenableFuture futureResult = - whenAllComplete(stringFuture, booleanFuture).run(combiner, newSingleThreadExecutor()); + whenAllComplete(stringFuture, booleanFuture).run(combiner, service); stringFuture.set("value"); booleanFuture.set(true); inFunction.await(); futureResult.cancel(false); shouldCompleteFunction.countDown(); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); combinerCompletedWithoutInterrupt.await(); + service.shutdown(); + service.awaitTermination(30, SECONDS); } + @J2ktIncompatible @GwtIncompatible // threads - - public void testWhenAllCompleteRunnable_resultCanceledWithInterrupt_InterruptsRunnable() + public void testWhenAllCompleteRunnable_resultCanceledWithInterrupt_interruptsRunnable() throws Exception { SettableFuture stringFuture = SettableFuture.create(); SettableFuture booleanFuture = SettableFuture.create(); @@ -2876,19 +2662,18 @@ public void run() { } }; + ExecutorService service = newSingleThreadExecutor(); ListenableFuture futureResult = - whenAllComplete(stringFuture, booleanFuture).run(combiner, newSingleThreadExecutor()); + whenAllComplete(stringFuture, booleanFuture).run(combiner, service); stringFuture.set("value"); booleanFuture.set(true); inFunction.await(); futureResult.cancel(true); - try { - futureResult.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> futureResult.get()); gotException.await(); + service.shutdown(); + service.awaitTermination(30, SECONDS); } public void testWhenAllSucceed() throws Exception { @@ -2910,15 +2695,13 @@ public ListenableFuture call() throws Exception { futureInteger.setException(partialResultException); Boolean booleanPartial = true; futureBoolean.set(booleanPartial); - try { - getDone(futureResult); - fail(); - } catch (ExecutionException expected) { - assertSame(partialResultException, expected.getCause()); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> getDone(futureResult)); + assertSame(partialResultException, expected.getCause()); } @AndroidIncompatible + @J2ktIncompatible @GwtIncompatible public void testWhenAllSucceed_releasesInputFuturesUponSubmission() throws Exception { SettableFuture future1 = SettableFuture.create(); @@ -2954,6 +2737,7 @@ public Long call() { } @AndroidIncompatible + @J2ktIncompatible @GwtIncompatible public void testWhenAllComplete_releasesInputFuturesUponCancellation() throws Exception { SettableFuture future = SettableFuture.create(); @@ -2977,6 +2761,7 @@ public Long call() { } @AndroidIncompatible + @J2ktIncompatible @GwtIncompatible public void testWhenAllSucceed_releasesCallable() throws Exception { AsyncCallable combiner = @@ -3006,6 +2791,7 @@ public ListenableFuture call() { * finisher}, a task that will complete the future in some fashion when it is called, allowing for * testing both before and after the completion of the future. */ + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static final class TestFuture { @@ -3028,6 +2814,7 @@ private static final class TestFuture { *

    Each test requires a new {@link TestFutureBatch} because we need new delayed futures each * time, as the old delayed futures were completed as part of the old test. */ + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static final class TestFutureBatch { @@ -3161,7 +2948,7 @@ String smartToString(ImmutableSet> inputs) { void smartAssertTrue( ImmutableSet> inputs, Exception cause, boolean expression) { if (!expression) { - throw failureWithCause(cause, smartToString(inputs)); + throw new AssertionError(smartToString(inputs), cause); } } @@ -3213,6 +3000,7 @@ void assertHasImmediateCancel( * {@link Futures#allAsList(Iterable)} or {@link Futures#successfulAsList(Iterable)}, hidden * behind a common interface for testing. */ + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private interface Merger { @@ -3244,6 +3032,7 @@ public ListenableFuture> merged( * forever in the case of failure. */ @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // threads static V pseudoTimedGetUninterruptibly(final Future input, long timeout, TimeUnit unit) throws ExecutionException, TimeoutException { @@ -3262,7 +3051,7 @@ public V call() throws Exception { } catch (ExecutionException e) { propagateIfInstanceOf(e.getCause(), ExecutionException.class); propagateIfInstanceOf(e.getCause(), CancellationException.class); - throw failureWithCause(e, "Unexpected exception"); + throw new AssertionError("Unexpected exception", e); } finally { executor.shutdownNow(); // TODO(cpovirk): assertTrue(awaitTerminationUninterruptibly(executor, 10, SECONDS)); @@ -3275,6 +3064,7 @@ public V call() throws Exception { * before future completion, and untimed after future completion) return or throw the proper * values. */ + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static void runExtensiveMergerTest(Merger merger) throws InterruptedException { int inputCount = new TestFutureBatch().allFutures.size(); @@ -3354,6 +3144,7 @@ private static void runExtensiveMergerTest(Merger merger) throws InterruptedExce * that is expected to succeed; the fact that the numbers match is only a coincidence.) See the * comment below for how to restore the fast but hang-y version. */ + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static List conditionalPseudoTimedGetUninterruptibly( TestFutureBatch inputs, @@ -3373,13 +3164,13 @@ private static List conditionalPseudoTimedGetUninterruptibly( : pseudoTimedGetUninterruptibly(future, 2500, MILLISECONDS); } - + @J2ktIncompatible @GwtIncompatible // threads public void testAllAsList_extensive() throws InterruptedException { runExtensiveMergerTest(Merger.allMerger); } - + @J2ktIncompatible @GwtIncompatible // threads public void testSuccessfulAsList_extensive() throws InterruptedException { runExtensiveMergerTest(Merger.successMerger); @@ -3390,7 +3181,6 @@ public void testSuccessfulAsList() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); SettableFuture future3 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2, future3); // Attach a listener @@ -3424,7 +3214,6 @@ public void testSuccessfulAsList_emptyList() throws Exception { public void testSuccessfulAsList_emptyArray() throws Exception { SingleCallListener listener = new SingleCallListener(); listener.expectCall(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(); compound.addListener(listener, directExecutor()); assertThat(getDone(compound)).isEmpty(); @@ -3435,7 +3224,6 @@ public void testSuccessfulAsList_partialFailure() throws Exception { SingleCallListener listener = new SingleCallListener(); SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); compound.addListener(listener, directExecutor()); @@ -3454,7 +3242,6 @@ public void testSuccessfulAsList_totalFailure() throws Exception { SingleCallListener listener = new SingleCallListener(); SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); compound.addListener(listener, directExecutor()); @@ -3473,7 +3260,6 @@ public void testSuccessfulAsList_cancelled() throws Exception { SingleCallListener listener = new SingleCallListener(); SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); compound.addListener(listener, directExecutor()); @@ -3491,7 +3277,6 @@ public void testSuccessfulAsList_cancelled() throws Exception { public void testSuccessfulAsList_resultCancelled() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); future2.set(DATA2); @@ -3507,7 +3292,7 @@ public void testSuccessfulAsList_resultCancelledRacingInputDone() throws Excepti Logger exceptionLogger = Logger.getLogger(AbstractFuture.class.getName()); exceptionLogger.addHandler(listenerLoggerHandler); try { - doTestSuccessfulAsList_resultCancelledRacingInputDone(); + doTestSuccessfulAsListResultCancelledRacingInputDone(); assertWithMessage("Nothing should be logged") .that(listenerLoggerHandler.getStoredLogRecords()) @@ -3517,7 +3302,7 @@ public void testSuccessfulAsList_resultCancelledRacingInputDone() throws Excepti } } - private static void doTestSuccessfulAsList_resultCancelledRacingInputDone() throws Exception { + private static void doTestSuccessfulAsListResultCancelledRacingInputDone() throws Exception { // Simple (combined.cancel -> input.cancel -> setOneValue): successfulAsList(ImmutableList.of(SettableFuture.create())).cancel(true); @@ -3528,7 +3313,6 @@ private static void doTestSuccessfulAsList_resultCancelledRacingInputDone() thro */ final SettableFuture future1 = SettableFuture.create(); final SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); future1.addListener( @@ -3563,7 +3347,6 @@ public void run() { public void testSuccessfulAsList_resultInterrupted() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2); future2.set(DATA2); @@ -3579,7 +3362,6 @@ public void testSuccessfulAsList_mixed() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); SettableFuture future3 = SettableFuture.create(); - @SuppressWarnings("unchecked") // array is never modified ListenableFuture> compound = successfulAsList(future1, future2, future3); compound.addListener(listener, directExecutor()); @@ -3598,7 +3380,7 @@ public void testSuccessfulAsList_mixed() throws Exception { } /** Non-Error exceptions are never logged. */ - @SuppressWarnings("unchecked") + @J2ktIncompatible // TODO(b/324550390): Enable public void testSuccessfulAsList_logging_exception() throws Exception { assertEquals( newArrayList((Object) null), @@ -3621,14 +3403,14 @@ public void testSuccessfulAsList_logging_exception() throws Exception { } /** Ensure that errors are always logged. */ - @SuppressWarnings("unchecked") + @J2ktIncompatible // TODO(b/324550390): Enable public void testSuccessfulAsList_logging_error() throws Exception { assertEquals( newArrayList((Object) null), - getDone(successfulAsList(immediateFailedFuture(new MyError())))); + getDone(successfulAsList(immediateFailedFuture(new SomeError())))); List logged = aggregateFutureLogHandler.getStoredLogRecords(); assertThat(logged).hasSize(1); // errors are always logged - assertThat(logged.get(0).getThrown()).isInstanceOf(MyError.class); + assertThat(logged.get(0).getThrown()).isInstanceOf(SomeError.class); } public void testSuccessfulAsList_failureLoggedEvenAfterOutputCancelled() throws Exception { @@ -3667,12 +3449,8 @@ public void testNonCancellationPropagating_failure() throws Exception { assertFalse(wrapper.isDone()); input.setException(failure); - try { - getDone(wrapper); - fail(); - } catch (ExecutionException expected) { - assertSame(failure, expected.getCause()); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(wrapper)); + assertSame(failure, expected.getCause()); } public void testNonCancellationPropagating_delegateCancelled() throws Exception { @@ -3695,14 +3473,16 @@ public void testNonCancellationPropagating_doesNotPropagate() throws Exception { assertFalse(input.isDone()); } + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private static class TestException extends Exception { - TestException(@CheckForNull Throwable cause) { + TestException(@Nullable Throwable cause) { super(cause); } } + @J2ktIncompatible @GwtIncompatible // used only in GwtIncompatible tests private interface MapperFunction extends Function {} @@ -3750,12 +3530,8 @@ public void testCompletionOrderExceptionThrown() throws Exception { if (expectedResult != 2) { assertEquals((Long) expectedResult, getDone(future)); } else { - try { - getDone(future); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("2L"); - } + ExecutionException expected = assertThrows(ExecutionException.class, () -> getDone(future)); + assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("2L"); } expectedResult++; } @@ -3782,11 +3558,7 @@ public void testCompletionOrderFutureCancelled() throws Exception { if (expectedResult != 4) { assertEquals((Long) expectedResult, getDone(future)); } else { - try { - getDone(future); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> getDone(future)); } expectedResult++; } @@ -3852,6 +3624,7 @@ public void testCancellingAllDelegatesIsNotQuadratic() throws Exception { } @AndroidIncompatible // reference is never cleared under some versions of the emulator + @J2ktIncompatible @GwtIncompatible public void testInputGCedIfUnreferenced() throws Exception { SettableFuture future1 = SettableFuture.create(); @@ -3876,6 +3649,7 @@ public void testInputGCedIfUnreferenced() throws Exception { } // Mostly an example of how it would look like to use a list of mixed types + @J2ktIncompatible // Wildcard generics public void testCompletionOrderMixedBagOTypes() throws Exception { SettableFuture future1 = SettableFuture.create(); SettableFuture future2 = SettableFuture.create(); @@ -3894,6 +3668,7 @@ public void testCompletionOrderMixedBagOTypes() throws Exception { } } + @J2ktIncompatible @GwtIncompatible // ClassSanityTester public void testFutures_nullChecks() throws Exception { new ClassSanityTester() @@ -3902,12 +3677,6 @@ public void testFutures_nullChecks() throws Exception { .testNulls(); } - static AssertionFailedError failureWithCause(Throwable cause, String message) { - AssertionFailedError failure = new AssertionFailedError(message); - failure.initCause(cause); - return failure; - } - // This test covers a bug where an Error thrown from a callback could cause the TimeoutFuture to // never complete when timing out. Notably, nothing would get logged since the Error would get // stuck in the ScheduledFuture inside of TimeoutFuture and nothing ever calls get on it. diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java index d0fcee2384be..9c58e89c1e6d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformAsyncTest.java @@ -17,20 +17,24 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.transformAsync; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; +import static org.junit.Assert.assertThrows; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)}. * * @author Nishant Thakkar */ +@NullUnmarked public class FuturesTransformAsyncTest extends AbstractChainedListenableFutureTest { protected static final int SLOW_OUTPUT_VALID_INPUT_DATA = 2; protected static final int SLOW_FUNC_VALID_INPUT_DATA = 3; @@ -82,23 +86,13 @@ public void testFutureGetThrowsFunctionException() throws Exception { public void testFutureGetThrowsCancellationIfInputCancelled() throws Exception { inputFuture.cancel(true); // argument is ignored - try { - resultFuture.get(); - fail("Result future must throw CancellationException" + " if input future is cancelled."); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); } public void testFutureGetThrowsCancellationIfOutputCancelled() throws Exception { inputFuture.set(SLOW_OUTPUT_VALID_INPUT_DATA); outputFuture.cancel(true); // argument is ignored - try { - resultFuture.get(); - fail( - "Result future must throw CancellationException" - + " if function output future is cancelled."); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); } public void testAsyncToString() throws Exception { @@ -111,11 +105,7 @@ public void testFutureCancelBeforeInputCompletion() throws Exception { assertTrue(resultFuture.isCancelled()); assertTrue(inputFuture.isCancelled()); assertFalse(outputFuture.isCancelled()); - try { - resultFuture.get(); - fail("Result future is cancelled and should have thrown a" + " CancellationException"); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); } public void testFutureCancellableBeforeOutputCompletion() throws Exception { @@ -124,14 +114,9 @@ public void testFutureCancellableBeforeOutputCompletion() throws Exception { assertTrue(resultFuture.isCancelled()); assertFalse(inputFuture.isCancelled()); assertTrue(outputFuture.isCancelled()); - try { - resultFuture.get(); - fail("Result future is cancelled and should have thrown a" + " CancellationException"); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); } - public void testFutureCancellableBeforeFunctionCompletion() throws Exception { // Set the result in a separate thread since this test runs the function // (which will block) in the same thread. @@ -147,20 +132,10 @@ public void run() { assertTrue(resultFuture.isCancelled()); assertFalse(inputFuture.isCancelled()); assertFalse(outputFuture.isCancelled()); - try { - resultFuture.get(); - fail("Result future is cancelled and should have thrown a" + " CancellationException"); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> resultFuture.get()); funcCompletionLatch.countDown(); // allow the function to complete - try { - outputFuture.get(); - fail( - "The function output future is cancelled and should have thrown a" - + " CancellationException"); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> outputFuture.get()); } public void testFutureCancelAfterCompletion() throws Exception { @@ -173,14 +148,10 @@ public void testFutureCancelAfterCompletion() throws Exception { } public void testFutureGetThrowsRuntimeException() throws Exception { - BadFuture badInput = new BadFuture(Futures.immediateFuture(20)); + BadFuture badInput = new BadFuture(immediateFuture(20)); ListenableFuture chain = buildChainingFuture(badInput); - try { - chain.get(); - fail("Future.get must throw an exception when the input future fails."); - } catch (ExecutionException e) { - assertSame(RuntimeException.class, e.getCause().getClass()); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> chain.get()); + assertSame(RuntimeException.class, e.getCause().getClass()); } /** Proxy to throw a {@link RuntimeException} out of the {@link #get()} method. */ diff --git a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java index 9f211dd8b8b2..301bdb6a6fc0 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/FuturesTransformTest.java @@ -21,12 +21,14 @@ import com.google.common.base.Function; import java.lang.reflect.UndeclaredThrowableException; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Futures#transform(ListenableFuture, Function, Executor)}. * * @author Nishant Thakkar */ +@NullUnmarked public class FuturesTransformTest extends AbstractChainedListenableFutureTest { private static final String RESULT_DATA = "SUCCESS"; private static final UndeclaredThrowableException WRAPPED_EXCEPTION = diff --git a/android/guava-tests/test/com/google/common/util/concurrent/GeneratedMonitorTest.java b/android/guava-tests/test/com/google/common/util/concurrent/GeneratedMonitorTest.java index 872197be8b91..aee24a5f60bf 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/GeneratedMonitorTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/GeneratedMonitorTest.java @@ -20,18 +20,22 @@ import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Generated tests for {@link Monitor}. @@ -43,7 +47,7 @@ * * @author Justin T. Sampson */ - +@NullUnmarked public class GeneratedMonitorTest extends TestCase { public static TestSuite suite() { @@ -58,7 +62,7 @@ public static TestSuite suite() { } } - assertEquals(548, suite.testCount()); + assertEquals(980, suite.testCount()); return suite; } @@ -186,14 +190,26 @@ private static boolean isGuarded(Method method) { return parameterTypes.length >= 1 && parameterTypes[0] == Monitor.Guard.class; } - /** Determines whether the given method takes a time and unit as its last two parameters. */ + /** Determines whether the given method is time-based. */ private static boolean isTimed(Method method) { + return isLongTimeUnitBased(method) || isDurationBased(method); + } + + /** Determines whether the given method takes a time and unit as its last two parameters. */ + private static boolean isLongTimeUnitBased(Method method) { Class[] parameterTypes = method.getParameterTypes(); return parameterTypes.length >= 2 && parameterTypes[parameterTypes.length - 2] == long.class && parameterTypes[parameterTypes.length - 1] == TimeUnit.class; } + /** Determines whether the given method takes a Duration as its last parameter. */ + private static boolean isDurationBased(Method method) { + Class[] parameterTypes = method.getParameterTypes(); + return parameterTypes.length >= 1 + && parameterTypes[parameterTypes.length - 1] == Duration.class; + } + /** Determines whether the given method returns a boolean value. */ private static boolean isBoolean(Method method) { return method.getReturnType() == boolean.class; @@ -215,7 +231,7 @@ public int compare(Method m1, Method m2) { if (nameComparison != 0) { return nameComparison; } else { - return Ints.compare(m1.getParameterTypes().length, m2.getParameterTypes().length); + return Integer.compare(m1.getParameterTypes().length, m2.getParameterTypes().length); } } }); @@ -233,11 +249,21 @@ private static void validateMethod(Method method) { assertFalse(desc, isTimed(method)); break; case 1: - assertTrue(desc, isGuarded(method)); - assertFalse(desc, isTimed(method)); + if (isDurationBased(method)) { + assertFalse(desc, isGuarded(method)); + } else { + assertTrue(desc, isGuarded(method)); + } + // we can't make an assumption about isTimed() because now we have single-parameter methods + // that accept a java.time.Duration + assertFalse(desc, isLongTimeUnitBased(method)); break; case 2: - assertFalse(desc, isGuarded(method)); + if (isDurationBased(method)) { + assertTrue(desc, isGuarded(method)); + } else { + assertFalse(desc, isGuarded(method)); + } assertTrue(desc, isTimed(method)); break; case 3: @@ -436,7 +462,11 @@ public void setSatisfied(boolean satisfied) { private final CountDownLatch callCompletedLatch; private GeneratedMonitorTest( - Method method, Scenario scenario, boolean fair, Timeout timeout, Outcome expectedOutcome) { + Method method, + Scenario scenario, + boolean fair, + @Nullable Timeout timeout, + Outcome expectedOutcome) { super(nameFor(method, scenario, fair, timeout, expectedOutcome)); this.method = method; this.scenario = scenario; @@ -470,7 +500,7 @@ public void run() { runChosenTest(); } }; - final FutureTask task = new FutureTask<>(runChosenTest, null); + final FutureTask<@Nullable Void> task = new FutureTask<>(runChosenTest, null); startThread( new Runnable() { @Override @@ -619,21 +649,22 @@ private void doWaitScenarioSetUp() { } private Outcome doCall() { - boolean guarded = isGuarded(method); - boolean timed = isTimed(method); - Object[] arguments = new Object[(guarded ? 1 : 0) + (timed ? 2 : 0)]; - if (guarded) { - arguments[0] = guard; + List arguments = new ArrayList<>(); + if (isGuarded(method)) { + arguments.add(guard); + } + if (isLongTimeUnitBased(method)) { + arguments.add(timeout.millis); + arguments.add(TimeUnit.MILLISECONDS); } - if (timed) { - arguments[arguments.length - 2] = timeout.millis; - arguments[arguments.length - 1] = TimeUnit.MILLISECONDS; + if (isDurationBased(method)) { + arguments.add(Duration.ofMillis(timeout.millis)); } try { Object result; doingCallLatch.countDown(); try { - result = method.invoke(monitor, arguments); + result = method.invoke(monitor, arguments.toArray()); } finally { callCompletedLatch.countDown(); } @@ -649,10 +680,10 @@ private Outcome doCall() { if (actualException instanceof InterruptedException) { return Outcome.INTERRUPT; } else { - throw newAssertionError("unexpected exception", targetException); + throw new AssertionError("unexpected exception", targetException); } } catch (IllegalAccessException e) { - throw newAssertionError("unexpected exception", e); + throw new AssertionError("unexpected exception", e); } } @@ -718,8 +749,15 @@ protected void runTest() throws Throwable { Monitor monitor1 = new Monitor(fair1); Monitor monitor2 = new Monitor(fair2); FlagGuard guard = new FlagGuard(monitor2); - Object[] arguments = - (timed ? new Object[] {guard, 0L, TimeUnit.MILLISECONDS} : new Object[] {guard}); + List arguments = new ArrayList<>(); + arguments.add(guard); + if (isDurationBased(method)) { + arguments.add(Duration.ZERO); + } + if (isLongTimeUnitBased(method)) { + arguments.add(0L); + arguments.add(TimeUnit.MILLISECONDS); + } boolean occupyMonitor = isWaitFor(method); if (occupyMonitor) { // If we don't already occupy the monitor, we'll get an IMSE regardless of the guard (see @@ -727,7 +765,7 @@ protected void runTest() throws Throwable { monitor1.enter(); } try { - method.invoke(monitor1, arguments); + method.invoke(monitor1, arguments.toArray()); fail("expected IllegalMonitorStateException"); } catch (InvocationTargetException e) { assertEquals(IllegalMonitorStateException.class, e.getTargetException().getClass()); @@ -757,10 +795,17 @@ private static TestCase generateWaitForWhenNotOccupyingTestCase( protected void runTest() throws Throwable { Monitor monitor = new Monitor(fair); FlagGuard guard = new FlagGuard(monitor); - Object[] arguments = - (timed ? new Object[] {guard, 0L, TimeUnit.MILLISECONDS} : new Object[] {guard}); + List arguments = new ArrayList<>(); + arguments.add(guard); + if (isDurationBased(method)) { + arguments.add(Duration.ZERO); + } + if (isLongTimeUnitBased(method)) { + arguments.add(0L); + arguments.add(TimeUnit.MILLISECONDS); + } try { - method.invoke(monitor, arguments); + method.invoke(monitor, arguments.toArray()); fail("expected IllegalMonitorStateException"); } catch (InvocationTargetException e) { assertEquals(IllegalMonitorStateException.class, e.getTargetException().getClass()); @@ -768,11 +813,4 @@ protected void runTest() throws Throwable { } }; } - - /** Alternative to AssertionError(String, Throwable), which doesn't exist in Java 1.6 */ - private static AssertionError newAssertionError(String message, Throwable cause) { - AssertionError e = new AssertionError(message); - e.initCause(cause); - return e; - } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleMonitorTest.java b/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleMonitorTest.java index 4d7a45f9c46e..44126f5fed51 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleMonitorTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleMonitorTest.java @@ -16,13 +16,14 @@ package com.google.common.util.concurrent; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Monitor}'s interruptible methods. * * @author Justin T. Sampson */ - +@NullUnmarked public class InterruptibleMonitorTest extends MonitorTestCase { public InterruptibleMonitorTest() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleTaskTest.java b/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleTaskTest.java index afa7eae81ebb..92a7b5accccd 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleTaskTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/InterruptibleTaskTest.java @@ -16,26 +16,28 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; -import java.lang.reflect.Method; +import com.google.common.util.concurrent.InterruptibleTask.Blocker; import java.nio.channels.spi.AbstractInterruptibleChannel; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.AbstractOwnableSynchronizer; import java.util.concurrent.locks.LockSupport; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; - +@NullUnmarked public final class InterruptibleTaskTest extends TestCase { // Regression test for a deadlock where a task could be stuck busy waiting for the task to // transition to DONE public void testInterruptThrows() throws Exception { final CountDownLatch isInterruptibleRegistered = new CountDownLatch(1); - InterruptibleTask task = - new InterruptibleTask() { + InterruptibleTask<@Nullable Void> task = + new InterruptibleTask<@Nullable Void>() { @Override - Void runInterruptibly() throws Exception { + @Nullable Void runInterruptibly() throws Exception { BrokenChannel bc = new BrokenChannel(); bc.doBegin(); isInterruptibleRegistered.countDown(); @@ -54,7 +56,7 @@ String toPendingString() { } @Override - void afterRanInterruptiblySuccess(Void result) {} + void afterRanInterruptiblySuccess(@Nullable Void result) {} @Override void afterRanInterruptiblyFailure(Throwable error) {} @@ -62,17 +64,13 @@ void afterRanInterruptiblyFailure(Throwable error) {} Thread runner = new Thread(task); runner.start(); isInterruptibleRegistered.await(); - try { - task.interruptTask(); - fail(); - } catch (RuntimeException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo("I bet you didn't think Thread.interrupt could throw"); - } + RuntimeException expected = assertThrows(RuntimeException.class, () -> task.interruptTask()); + assertThat(expected) + .hasMessageThat() + .isEqualTo("I bet you didn't think Thread.interrupt could throw"); // We need to wait for the runner to exit. It used to be that the runner would get stuck in the // busy loop when interrupt threw. - runner.join(TimeUnit.SECONDS.toMillis(10)); + runner.join(SECONDS.toMillis(10)); } static final class BrokenChannel extends AbstractInterruptibleChannel { @@ -91,13 +89,21 @@ void doBegin() { * protect ourselves from that we want to make sure that tasks don't spin too much waiting for the * interrupting thread to complete the protocol. */ + /* + * This test hangs (or maybe is just *very* slow) under Android. + * + * TODO(b/218700094): Ideally, get this to pass under Android. Failing that, convince ourselves + * that the test isn't exposing a real problem with InterruptibleTask, one that could matter in + * prod. + */ + @AndroidIncompatible public void testInterruptIsSlow() throws Exception { final CountDownLatch isInterruptibleRegistered = new CountDownLatch(1); final SlowChannel slowChannel = new SlowChannel(); - final InterruptibleTask task = - new InterruptibleTask() { + final InterruptibleTask<@Nullable Void> task = + new InterruptibleTask<@Nullable Void>() { @Override - Void runInterruptibly() throws Exception { + @Nullable Void runInterruptibly() throws Exception { slowChannel.doBegin(); isInterruptibleRegistered.countDown(); try { @@ -120,7 +126,7 @@ String toPendingString() { } @Override - void afterRanInterruptiblySuccess(Void result) {} + void afterRanInterruptiblySuccess(@Nullable Void result) {} @Override void afterRanInterruptiblyFailure(Throwable error) {} @@ -145,19 +151,15 @@ public void run() { // waiting for the slow interrupting thread to complete Thread.interrupt awaitBlockedOnInstanceOf(runner, InterruptibleTask.Blocker.class); - Object blocker = LockSupport.getBlocker(runner); - assertThat(blocker).isInstanceOf(AbstractOwnableSynchronizer.class); - Method getExclusiveOwnerThread = - AbstractOwnableSynchronizer.class.getDeclaredMethod("getExclusiveOwnerThread"); - getExclusiveOwnerThread.setAccessible(true); - Thread owner = (Thread) getExclusiveOwnerThread.invoke(blocker); + Blocker blocker = (Blocker) LockSupport.getBlocker(runner); + Thread owner = blocker.getOwner(); assertThat(owner).isSameInstanceAs(interrupter); slowChannel.exitClose.countDown(); // release the interrupter // We need to wait for the runner to exit. To make sure that the interrupting thread wakes it // back up. - runner.join(TimeUnit.SECONDS.toMillis(10)); + runner.join(SECONDS.toMillis(10)); } // waits for the given thread to be blocked on the given object diff --git a/android/guava-tests/test/com/google/common/util/concurrent/InterruptionUtil.java b/android/guava-tests/test/com/google/common/util/concurrent/InterruptionUtil.java index 919b0c8cec62..7460daad43cf 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/InterruptionUtil.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/InterruptionUtil.java @@ -25,6 +25,7 @@ import com.google.common.testing.TearDownAccepter; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import org.jspecify.annotations.NullUnmarked; /** * Utilities for performing thread interruption in tests @@ -32,6 +33,7 @@ * @author Kevin Bourrillion * @author Chris Povirk */ +@NullUnmarked final class InterruptionUtil { private static final Logger logger = Logger.getLogger(InterruptionUtil.class.getName()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/JSR166TestCase.java b/android/guava-tests/test/com/google/common/util/concurrent/JSR166TestCase.java index 2f8e1b7355ac..341cfa83b099 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/JSR166TestCase.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/JSR166TestCase.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FilePermission; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.CodeSource; @@ -47,6 +48,7 @@ import java.util.concurrent.atomic.AtomicReference; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Base class for JSR166 Junit TCK tests. Defines some constants, utility methods and classes, as @@ -98,9 +100,15 @@ * tests. * */ +@SuppressWarnings({ + // We call threadUnexpectedException, which does the right thing for errors. + "AssertionFailureIgnored", + // We're following the upstream naming to reduce diffs. + "IdentifierName", + "ConstantCaseForConstants", +}) +@NullUnmarked abstract class JSR166TestCase extends TestCase { - private static final boolean useSecurityManager = Boolean.getBoolean("jsr166.useSecurityManager"); - protected static final boolean expensiveTests = Boolean.getBoolean("jsr166.expensiveTests"); /** @@ -115,6 +123,7 @@ abstract class JSR166TestCase extends TestCase { */ private static final long profileThreshold = Long.getLong("jsr166.profileThreshold", 100); + @Override protected void runTest() throws Throwable { if (profileTests) runTestProfiled(); else super.runTest(); @@ -280,6 +289,7 @@ public void threadRecordFailure(Throwable t) { threadFailure.compareAndSet(null, t); } + @Override public void setUp() { setDelays(); } @@ -292,6 +302,7 @@ public void setUp() { * *

    Triggers test case failure if interrupt status is set in the main thread. */ + @Override public void tearDown() throws Exception { Throwable t = threadFailure.getAndSet(null); if (t != null) { @@ -431,6 +442,7 @@ public void threadUnexpectedException(Throwable t) { * Delays, via Thread.sleep, for the given millisecond delay, but if the sleep is shorter than * specified, may re-sleep or yield until time elapses. */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait? static void delay(long millis) throws InterruptedException { long startTime = System.nanoTime(); long ns = millis * 1000 * 1000; @@ -445,7 +457,7 @@ static void delay(long millis) throws InterruptedException { } /** Waits out termination of a thread pool or fails doing so. */ - void joinPool(ExecutorService exec) { + void joinPool(ExecutorService exec) throws InterruptedException { try { exec.shutdown(); assertTrue( @@ -453,8 +465,6 @@ void joinPool(ExecutorService exec) { exec.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)); } catch (SecurityException ok) { // Allowed in case test doesn't have privs - } catch (InterruptedException ie) { - fail("Unexpected InterruptedException"); } } @@ -462,47 +472,41 @@ void joinPool(ExecutorService exec) { * Checks that thread does not terminate within the default millisecond delay of {@code * timeoutMillis()}. */ - void assertThreadStaysAlive(Thread thread) { + void assertThreadStaysAlive(Thread thread) throws InterruptedException { assertThreadStaysAlive(thread, timeoutMillis()); } /** Checks that thread does not terminate within the given millisecond delay. */ - void assertThreadStaysAlive(Thread thread, long millis) { - try { - // No need to optimize the failing case via Thread.join. - delay(millis); - assertTrue(thread.isAlive()); - } catch (InterruptedException ie) { - fail("Unexpected InterruptedException"); - } + void assertThreadStaysAlive(Thread thread, long millis) throws InterruptedException { + // No need to optimize the failing case via Thread.join. + delay(millis); + assertTrue(thread.isAlive()); } /** * Checks that the threads do not terminate within the default millisecond delay of {@code * timeoutMillis()}. */ - void assertThreadsStayAlive(Thread... threads) { + void assertThreadsStayAlive(Thread... threads) throws InterruptedException { assertThreadsStayAlive(timeoutMillis(), threads); } /** Checks that the threads do not terminate within the given millisecond delay. */ - void assertThreadsStayAlive(long millis, Thread... threads) { - try { - // No need to optimize the failing case via Thread.join. - delay(millis); - for (Thread thread : threads) assertTrue(thread.isAlive()); - } catch (InterruptedException ie) { - fail("Unexpected InterruptedException"); + void assertThreadsStayAlive(long millis, Thread... threads) throws InterruptedException { + // No need to optimize the failing case via Thread.join. + delay(millis); + for (Thread thread : threads) { + assertTrue(thread.isAlive()); } } /** Checks that future.get times out, with the default timeout of {@code timeoutMillis()}. */ - void assertFutureTimesOut(Future future) { + void assertFutureTimesOut(Future future) { assertFutureTimesOut(future, timeoutMillis()); } /** Checks that future.get times out, with the given millisecond timeout. */ - void assertFutureTimesOut(Future future, long timeoutMillis) { + void assertFutureTimesOut(Future future, long timeoutMillis) { long startTime = System.nanoTime(); try { future.get(timeoutMillis, MILLISECONDS); @@ -531,23 +535,23 @@ public void shouldThrow(String exceptionName) { // Some convenient Integer constants - public static final Integer zero = new Integer(0); - public static final Integer one = new Integer(1); - public static final Integer two = new Integer(2); - public static final Integer three = new Integer(3); - public static final Integer four = new Integer(4); - public static final Integer five = new Integer(5); - public static final Integer six = new Integer(6); - public static final Integer seven = new Integer(7); - public static final Integer eight = new Integer(8); - public static final Integer nine = new Integer(9); - public static final Integer m1 = new Integer(-1); - public static final Integer m2 = new Integer(-2); - public static final Integer m3 = new Integer(-3); - public static final Integer m4 = new Integer(-4); - public static final Integer m5 = new Integer(-5); - public static final Integer m6 = new Integer(-6); - public static final Integer m10 = new Integer(-10); + public static final Integer zero = 0; + public static final Integer one = 1; + public static final Integer two = 2; + public static final Integer three = 3; + public static final Integer four = 4; + public static final Integer five = 5; + public static final Integer six = 6; + public static final Integer seven = 7; + public static final Integer eight = 8; + public static final Integer nine = 9; + public static final Integer m1 = -1; + public static final Integer m2 = -2; + public static final Integer m3 = -3; + public static final Integer m4 = -4; + public static final Integer m5 = -5; + public static final Integer m6 = -6; + public static final Integer m10 = -10; /** * Runs Runnable r with a security policy that permits precisely the specified permissions. If @@ -587,7 +591,7 @@ public void runWithoutPermissions(Runnable r) { } /** A security policy where new permissions can be dynamically added or all cleared. */ - public static class AdjustablePolicy extends java.security.Policy { + public static class AdjustablePolicy extends Policy { Permissions perms = new Permissions(); AdjustablePolicy(Permission... permissions) { @@ -602,18 +606,22 @@ void clearPermissions() { perms = new Permissions(); } + @Override public PermissionCollection getPermissions(CodeSource cs) { return perms; } + @Override public PermissionCollection getPermissions(ProtectionDomain pd) { return perms; } + @Override public boolean implies(ProtectionDomain pd, Permission p) { return perms.implies(p); } + @Override public void refresh() {} } @@ -632,7 +640,7 @@ public static Policy permissivePolicy() { // Permissions needed by the junit test harness new RuntimePermission("accessDeclaredMembers"), new PropertyPermission("*", "read"), - new java.io.FilePermission("<>", "read")); + new FilePermission("<>", "read")); } /** Sleeps until the given time has elapsed. Throws AssertionFailedError if interrupted. */ @@ -650,6 +658,7 @@ void sleep(long millis) { * Spin-waits up to the specified number of milliseconds for the given thread to enter a wait * state: BLOCKED, WAITING, or TIMED_WAITING. */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { long startTime = System.nanoTime(); for (; ; ) { @@ -721,6 +730,7 @@ void awaitTermination(Thread t) { public abstract class CheckedRunnable implements Runnable { protected abstract void realRun() throws Throwable; + @Override public final void run() { try { realRun(); @@ -739,6 +749,7 @@ RunnableShouldThrow(Class exceptionClass) { this.exceptionClass = exceptionClass; } + @Override public final void run() { try { realRun(); @@ -758,6 +769,7 @@ ThreadShouldThrow(Class exceptionClass) { this.exceptionClass = exceptionClass; } + @Override public final void run() { try { realRun(); @@ -771,6 +783,7 @@ public final void run() { public abstract class CheckedInterruptedRunnable implements Runnable { protected abstract void realRun() throws Throwable; + @Override public final void run() { try { realRun(); @@ -786,6 +799,7 @@ public final void run() { public abstract class CheckedCallable implements Callable { protected abstract T realCall() throws Throwable; + @Override public final T call() { try { return realCall(); @@ -799,6 +813,7 @@ public final T call() { public abstract class CheckedInterruptedCallable implements Callable { protected abstract T realCall() throws Throwable; + @Override public final T call() { try { T result = realCall(); @@ -814,10 +829,12 @@ public final T call() { } public static class NoOpRunnable implements Runnable { + @Override public void run() {} } - public static class NoOpCallable implements Callable { + public static class NoOpCallable implements Callable { + @Override public Object call() { return Boolean.TRUE; } @@ -826,6 +843,7 @@ public Object call() { public static final String TEST_STRING = "a test string"; public static class StringTask implements Callable { + @Override public String call() { return TEST_STRING; } @@ -833,6 +851,7 @@ public String call() { public Callable latchAwaitingStringTask(final CountDownLatch latch) { return new CheckedCallable() { + @Override protected String realCall() { try { latch.await(); @@ -845,6 +864,7 @@ protected String realCall() { public Runnable awaiter(final CountDownLatch latch) { return new CheckedRunnable() { + @Override public void realRun() throws InterruptedException { await(latch); } @@ -887,36 +907,42 @@ public void await(Semaphore semaphore) { // } public static class NPETask implements Callable { + @Override public String call() { throw new NullPointerException(); } } public static class CallableOne implements Callable { + @Override public Integer call() { return one; } } public class ShortRunnable extends CheckedRunnable { + @Override protected void realRun() throws Throwable { delay(SHORT_DELAY_MS); } } public class ShortInterruptedRunnable extends CheckedInterruptedRunnable { + @Override protected void realRun() throws InterruptedException { delay(SHORT_DELAY_MS); } } public class SmallRunnable extends CheckedRunnable { + @Override protected void realRun() throws Throwable { delay(SMALL_DELAY_MS); } } public class SmallPossiblyInterruptedRunnable extends CheckedRunnable { + @Override protected void realRun() { try { delay(SMALL_DELAY_MS); @@ -925,7 +951,8 @@ protected void realRun() { } } - public class SmallCallable extends CheckedCallable { + public class SmallCallable extends CheckedCallable { + @Override protected Object realCall() throws InterruptedException { delay(SMALL_DELAY_MS); return Boolean.TRUE; @@ -933,12 +960,14 @@ protected Object realCall() throws InterruptedException { } public class MediumRunnable extends CheckedRunnable { + @Override protected void realRun() throws Throwable { delay(MEDIUM_DELAY_MS); } } public class MediumInterruptedRunnable extends CheckedInterruptedRunnable { + @Override protected void realRun() throws InterruptedException { delay(MEDIUM_DELAY_MS); } @@ -946,6 +975,7 @@ protected void realRun() throws InterruptedException { public Runnable possiblyInterruptedRunnable(final long timeoutMillis) { return new CheckedRunnable() { + @Override protected void realRun() { try { delay(timeoutMillis); @@ -956,6 +986,7 @@ protected void realRun() { } public class MediumPossiblyInterruptedRunnable extends CheckedRunnable { + @Override protected void realRun() { try { delay(MEDIUM_DELAY_MS); @@ -965,6 +996,7 @@ protected void realRun() { } public class LongPossiblyInterruptedRunnable extends CheckedRunnable { + @Override protected void realRun() { try { delay(LONG_DELAY_MS); @@ -975,6 +1007,7 @@ protected void realRun() { /** For use as ThreadFactory in constructors */ public static class SimpleThreadFactory implements ThreadFactory { + @Override public Thread newThread(Runnable r) { return new Thread(r); } @@ -988,10 +1021,12 @@ public static TrackedRunnable trackedRunnable(final long timeoutMillis) { return new TrackedRunnable() { private volatile boolean done = false; + @Override public boolean isDone() { return done; } + @Override public void run() { try { delay(timeoutMillis); @@ -1005,6 +1040,7 @@ public void run() { public static class TrackedShortRunnable implements Runnable { public volatile boolean done = false; + @Override public void run() { try { delay(SHORT_DELAY_MS); @@ -1017,6 +1053,7 @@ public void run() { public static class TrackedSmallRunnable implements Runnable { public volatile boolean done = false; + @Override public void run() { try { delay(SMALL_DELAY_MS); @@ -1029,6 +1066,7 @@ public void run() { public static class TrackedMediumRunnable implements Runnable { public volatile boolean done = false; + @Override public void run() { try { delay(MEDIUM_DELAY_MS); @@ -1041,6 +1079,7 @@ public void run() { public static class TrackedLongRunnable implements Runnable { public volatile boolean done = false; + @Override public void run() { try { delay(LONG_DELAY_MS); @@ -1053,14 +1092,16 @@ public void run() { public static class TrackedNoOpRunnable implements Runnable { public volatile boolean done = false; + @Override public void run() { done = true; } } - public static class TrackedCallable implements Callable { + public static class TrackedCallable implements Callable { public volatile boolean done = false; + @Override public Object call() { try { delay(SMALL_DELAY_MS); @@ -1104,6 +1145,7 @@ public Object call() { /** For use as RejectedExecutionHandler in constructors */ public static class NoOpREHandler implements RejectedExecutionHandler { + @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {} } @@ -1116,6 +1158,7 @@ public CheckedBarrier(int parties) { super(parties); } + @Override public int await() { try { return super.await(2 * LONG_DELAY_MS, MILLISECONDS); @@ -1129,7 +1172,7 @@ public int await() { } } - void checkEmpty(BlockingQueue q) { + void checkEmpty(BlockingQueue q) { try { assertTrue(q.isEmpty()); assertEquals(0, q.size()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/JdkFutureAdaptersTest.java b/android/guava-tests/test/com/google/common/util/concurrent/JdkFutureAdaptersTest.java index 3bb819a349fd..d762292408f9 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/JdkFutureAdaptersTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/JdkFutureAdaptersTest.java @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link JdkFutureAdapters}. @@ -40,6 +41,7 @@ * @author Sven Mawson * @author Kurt Alfred Kluever */ +@NullUnmarked public class JdkFutureAdaptersTest extends TestCase { private static final String DATA1 = "data"; @@ -100,7 +102,6 @@ public void testListenInPoolThreadIgnoresExecutorWhenDelegateIsDone() throws Exc assertTrue(listenableFuture.isDone()); } - public void testListenInPoolThreadUsesGivenExecutor() throws Exception { ExecutorService executorService = newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true).build()); @@ -125,7 +126,6 @@ public void testListenInPoolThreadUsesGivenExecutor() throws Exception { assertTrue(listenableFuture.isDone()); } - public void testListenInPoolThreadCustomExecutorInterrupted() throws Exception { final CountDownLatch submitSuccessful = new CountDownLatch(1); ExecutorService executorService = @@ -133,7 +133,7 @@ public void testListenInPoolThreadCustomExecutorInterrupted() throws Exception { 0, Integer.MAX_VALUE, 60L, - TimeUnit.SECONDS, + SECONDS, new SynchronousQueue(), new ThreadFactoryBuilder().setDaemon(true).build()) { @Override @@ -235,7 +235,6 @@ public synchronized void run() { } } - @SuppressWarnings("IsInstanceIncompatibleType") // intentional. public void testListenInPoolThreadRunsListenerAfterRuntimeException() throws Exception { RuntimeExceptionThrowingFuture input = new RuntimeExceptionThrowingFuture<>(); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTaskTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTaskTest.java index 8969a7574d04..4661e1df9a3f 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTaskTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTaskTest.java @@ -17,20 +17,23 @@ package com.google.common.util.concurrent; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test case for {@link ListenableFutureTask}. * * @author Sven Mawson */ +@NullUnmarked public class ListenableFutureTaskTest extends TestCase { private ExecutorService exec; @@ -80,7 +83,6 @@ protected void tearDown() throws Exception { super.tearDown(); } - public void testListenerDoesNotRunUntilTaskCompletes() throws Exception { // Test default state of not started. @@ -101,12 +103,11 @@ public void testListenerDoesNotRunUntilTaskCompletes() throws Exception { // listener to be called by blocking on the listener latch. taskLatch.countDown(); assertEquals(25, task.get().intValue()); - assertTrue(listenerLatch.await(5, TimeUnit.SECONDS)); + assertTrue(listenerLatch.await(5, SECONDS)); assertTrue(task.isDone()); assertFalse(task.isCancelled()); } - public void testListenerCalledOnException() throws Exception { throwException = true; @@ -115,14 +116,10 @@ public void testListenerCalledOnException() throws Exception { runLatch.await(); taskLatch.countDown(); - try { - task.get(5, TimeUnit.SECONDS); - fail("Should have propagated the failure."); - } catch (ExecutionException e) { - assertEquals(IllegalStateException.class, e.getCause().getClass()); - } + ExecutionException e = assertThrows(ExecutionException.class, () -> task.get(5, SECONDS)); + assertEquals(IllegalStateException.class, e.getCause().getClass()); - assertTrue(listenerLatch.await(5, TimeUnit.SECONDS)); + assertTrue(listenerLatch.await(5, SECONDS)); assertTrue(task.isDone()); assertFalse(task.isCancelled()); } @@ -134,7 +131,7 @@ public void testListenerCalledOnCancelFromNotRunning() throws Exception { assertEquals(1, runLatch.getCount()); // Wait for the listeners to be called, don't rely on the same-thread exec. - listenerLatch.await(5, TimeUnit.SECONDS); + listenerLatch.await(5, SECONDS); assertTrue(task.isDone()); assertTrue(task.isCancelled()); @@ -142,7 +139,6 @@ public void testListenerCalledOnCancelFromNotRunning() throws Exception { assertEquals(1, runLatch.getCount()); } - public void testListenerCalledOnCancelFromRunning() throws Exception { exec.execute(task); runLatch.await(); @@ -154,7 +150,7 @@ public void testListenerCalledOnCancelFromRunning() throws Exception { assertEquals(1, taskLatch.getCount()); // Wait for the listeners to be called. - listenerLatch.await(5, TimeUnit.SECONDS); + listenerLatch.await(5, SECONDS); assertTrue(task.isDone()); assertTrue(task.isCancelled()); assertEquals(1, taskLatch.getCount()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTest.java new file mode 100644 index 000000000000..e9efc76df802 --- /dev/null +++ b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.truth.Truth.assertWithMessage; + +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; + +/** Test for {@link ListenableFuture}. */ +@NullUnmarked +public class ListenableFutureTest extends TestCase { + public void testNoNewApis() throws Exception { + assertWithMessage( + "Do not add new methods to ListenableFuture. Its API needs to continue to match the" + + " version we released in a separate artifact com.google.guava:listenablefuture.") + .that(ListenableFuture.class.getDeclaredMethods()) + .asList() + .containsExactly( + ListenableFuture.class.getMethod("addListener", Runnable.class, Executor.class)); + assertWithMessage( + "Do not add new supertypes to ListenableFuture. Its API needs to continue to match the" + + " version we released in a separate artifact com.google.guava:listenablefuture.") + .that(ListenableFuture.class.getInterfaces()) + .asList() + .containsExactly(Future.class); + } +} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTester.java b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTester.java index 575ed06567dd..8cef591033a2 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTester.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ListenableFutureTester.java @@ -18,24 +18,27 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertThrows; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * Used to test listenable future implementations. * * @author Sven Mawson */ +@NullUnmarked public class ListenableFutureTester { private final ExecutorService exec; @@ -67,12 +70,12 @@ public void tearDown() { exec.shutdown(); } - public void testCompletedFuture(@CheckForNull Object expectedValue) + public void testCompletedFuture(@Nullable Object expectedValue) throws InterruptedException, ExecutionException { assertTrue(future.isDone()); assertFalse(future.isCancelled()); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(latch.await(5, SECONDS)); assertTrue(future.isDone()); assertFalse(future.isCancelled()); @@ -83,22 +86,18 @@ public void testCancelledFuture() throws InterruptedException, ExecutionExceptio assertTrue(future.isDone()); assertTrue(future.isCancelled()); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(latch.await(5, SECONDS)); assertTrue(future.isDone()); assertTrue(future.isCancelled()); - try { - future.get(); - fail("Future should throw CancellationException on cancel."); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> future.get()); } - public void testFailedFuture(@CheckForNull String message) throws InterruptedException { + public void testFailedFuture(@Nullable String message) throws InterruptedException { assertTrue(future.isDone()); assertFalse(future.isCancelled()); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(latch.await(5, SECONDS)); assertTrue(future.isDone()); assertFalse(future.isCancelled()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ListenerCallQueueTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ListenerCallQueueTest.java index 5fd9b950855b..7cf21dd72298 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ListenerCallQueueTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ListenerCallQueueTest.java @@ -31,8 +31,10 @@ import java.util.logging.Level; import java.util.logging.Logger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Tests for {@link ListenerCallQueue}. */ +@NullUnmarked public class ListenerCallQueueTest extends TestCase { private static final ListenerCallQueue.Event THROWING_EVENT = @@ -129,7 +131,6 @@ public void testEnqueueAndDispatch_withLabeledExceptions() { logHandler.getStoredLogRecords().get(0).getMessage()); } - public void testEnqueueAndDispatch_multithreaded() throws InterruptedException { Object listener = new Object(); ExecutorService service = Executors.newFixedThreadPool(4); @@ -153,7 +154,6 @@ public void testEnqueueAndDispatch_multithreaded() throws InterruptedException { } } - public void testEnqueueAndDispatch_multithreaded_withThrowingRunnable() throws InterruptedException { Object listener = new Object(); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/MonitorTestCase.java b/android/guava-tests/test/com/google/common/util/concurrent/MonitorTestCase.java index 6d620ffc24cd..88299c24c217 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/MonitorTestCase.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/MonitorTestCase.java @@ -20,13 +20,14 @@ import com.google.common.testing.TearDownStack; import java.util.Random; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Monitor}, either interruptible or uninterruptible. * * @author Justin T. Sampson */ - +@NullUnmarked public abstract class MonitorTestCase extends TestCase { public class TestGuard extends Monitor.Guard { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/MoreExecutorsTest.java b/android/guava-tests/test/com/google/common/util/concurrent/MoreExecutorsTest.java index fe4e7f540038..1d00fb2ce6a9 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/MoreExecutorsTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/MoreExecutorsTest.java @@ -36,8 +36,12 @@ import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; import static com.google.common.util.concurrent.MoreExecutors.renamingDecorator; import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -69,9 +73,10 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.mockito.InOrder; import org.mockito.Mockito; @@ -80,6 +85,7 @@ * * @author Kyle Littlefield (klittle) */ +@NullUnmarked public class MoreExecutorsTest extends JSR166TestCase { private static final Runnable EMPTY_RUNNABLE = @@ -88,7 +94,6 @@ public class MoreExecutorsTest extends JSR166TestCase { public void run() {} }; - public void testDirectExecutorServiceServiceInThreadExecution() throws Exception { final ListeningExecutorService executor = newDirectExecutorService(); final ThreadLocal threadLocalCount = @@ -168,7 +173,6 @@ public Integer call() { assertEquals(10, threadLocalCount.get().intValue()); } - public void testDirectExecutorServiceServiceTermination() throws Exception { final ExecutorService executor = newDirectExecutorService(); final CyclicBarrier barrier = new CyclicBarrier(2); @@ -187,19 +191,19 @@ public void run() { try { Future future = executor.submit( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { // WAIT #1 - barrier.await(1, TimeUnit.SECONDS); + barrier.await(1, SECONDS); // WAIT #2 - barrier.await(1, TimeUnit.SECONDS); + barrier.await(1, SECONDS); assertTrue(executor.isShutdown()); assertFalse(executor.isTerminated()); // WAIT #3 - barrier.await(1, TimeUnit.SECONDS); + barrier.await(1, SECONDS); return null; } }); @@ -215,35 +219,25 @@ public Void call() throws Exception { otherThread.start(); // WAIT #1 - barrier.await(1, TimeUnit.SECONDS); + barrier.await(1, SECONDS); assertFalse(executor.isShutdown()); assertFalse(executor.isTerminated()); executor.shutdown(); assertTrue(executor.isShutdown()); - try { - executor.submit(doNothingRunnable); - fail("Should have encountered RejectedExecutionException"); - } catch (RejectedExecutionException ex) { - // good to go - } + assertThrows(RejectedExecutionException.class, () -> executor.submit(doNothingRunnable)); assertFalse(executor.isTerminated()); // WAIT #2 - barrier.await(1, TimeUnit.SECONDS); - assertFalse(executor.awaitTermination(20, TimeUnit.MILLISECONDS)); + barrier.await(1, SECONDS); + assertFalse(executor.awaitTermination(20, MILLISECONDS)); // WAIT #3 - barrier.await(1, TimeUnit.SECONDS); - assertTrue(executor.awaitTermination(1, TimeUnit.SECONDS)); - assertTrue(executor.awaitTermination(0, TimeUnit.SECONDS)); + barrier.await(1, SECONDS); + assertTrue(executor.awaitTermination(1, SECONDS)); + assertTrue(executor.awaitTermination(0, SECONDS)); assertTrue(executor.isShutdown()); - try { - executor.submit(doNothingRunnable); - fail("Should have encountered RejectedExecutionException"); - } catch (RejectedExecutionException ex) { - // good to go - } + assertThrows(RejectedExecutionException.class, () -> executor.submit(doNothingRunnable)); assertTrue(executor.isTerminated()); otherThread.join(1000); @@ -259,15 +253,14 @@ public Void call() throws Exception { * Test for a bug where threads weren't getting signaled when shutdown was called, only when tasks * completed. */ - public void testDirectExecutorService_awaitTermination_missedSignal() { - final ExecutorService service = MoreExecutors.newDirectExecutorService(); + final ExecutorService service = newDirectExecutorService(); Thread waiter = new Thread() { @Override public void run() { try { - service.awaitTermination(1, TimeUnit.DAYS); + service.awaitTermination(1, DAYS); } catch (InterruptedException e) { return; } @@ -276,7 +269,7 @@ public void run() { waiter.start(); awaitTimedWaiting(waiter); service.shutdown(); - Uninterruptibles.joinUninterruptibly(waiter, 10, TimeUnit.SECONDS); + Uninterruptibles.joinUninterruptibly(waiter, 10, SECONDS); if (waiter.isAlive()) { waiter.interrupt(); fail("awaitTermination failed to trigger after shutdown()"); @@ -284,6 +277,7 @@ public void run() { } /** Wait for the given thread to reach the {@link State#TIMED_WAITING} thread state. */ + @SuppressWarnings("ThreadPriorityCheck") // TODO: b/175898629 - Consider onSpinWait. void awaitTimedWaiting(Thread thread) { while (true) { switch (thread.getState()) { @@ -296,7 +290,6 @@ void awaitTimedWaiting(Thread thread) { case TIMED_WAITING: return; case TERMINATED: - default: throw new AssertionError(); } } @@ -311,11 +304,7 @@ public void testDirectExecutorService_shutdownNow() { public void testExecuteAfterShutdown() { ExecutorService executor = newDirectExecutorService(); executor.shutdown(); - try { - executor.execute(EMPTY_RUNNABLE); - fail(); - } catch (RejectedExecutionException expected) { - } + assertThrows(RejectedExecutionException.class, () -> executor.execute(EMPTY_RUNNABLE)); } public void testListeningExecutorServiceInvokeAllJavadocCodeCompiles() throws Exception { @@ -342,6 +331,7 @@ public void testListeningDecorator() throws Exception { */ } + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testListeningDecorator_noWrapExecuteTask() { ExecutorService delegate = mock(ExecutorService.class); ListeningExecutorService service = listeningDecorator(delegate); @@ -354,7 +344,6 @@ public void run() {} verify(delegate).execute(task); } - public void testListeningDecorator_scheduleSuccess() throws Exception { final CountDownLatch completed = new CountDownLatch(1); ScheduledThreadPoolExecutor delegate = @@ -365,8 +354,7 @@ protected void afterExecute(Runnable r, Throwable t) { } }; ListeningScheduledExecutorService service = listeningDecorator(delegate); - ListenableFuture future = - service.schedule(Callables.returning(42), 1, TimeUnit.MILLISECONDS); + ListenableFuture future = service.schedule(Callables.returning(42), 1, MILLISECONDS); /* * Wait not just until the Future's value is set (as in future.get()) but @@ -380,18 +368,15 @@ protected void afterExecute(Runnable r, Throwable t) { assertEquals(0, delegate.getQueue().size()); } - public void testListeningDecorator_scheduleFailure() throws Exception { ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1); ListeningScheduledExecutorService service = listeningDecorator(delegate); RuntimeException ex = new RuntimeException(); - ListenableFuture future = - service.schedule(new ThrowingRunnable(0, ex), 1, TimeUnit.MILLISECONDS); + ListenableFuture future = service.schedule(new ThrowingRunnable(0, ex), 1, MILLISECONDS); assertExecutionException(future, ex); assertEquals(0, delegate.getQueue().size()); } - public void testListeningDecorator_schedulePeriodic() throws Exception { ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1); ListeningScheduledExecutorService service = listeningDecorator(delegate); @@ -400,19 +385,18 @@ public void testListeningDecorator_schedulePeriodic() throws Exception { ListenableFuture future; ThrowingRunnable runnable = new ThrowingRunnable(5, ex); - future = service.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.MILLISECONDS); + future = service.scheduleAtFixedRate(runnable, 1, 1, MILLISECONDS); assertExecutionException(future, ex); assertEquals(5, runnable.count); assertEquals(0, delegate.getQueue().size()); runnable = new ThrowingRunnable(5, ex); - future = service.scheduleWithFixedDelay(runnable, 1, 1, TimeUnit.MILLISECONDS); + future = service.scheduleWithFixedDelay(runnable, 1, 1, MILLISECONDS); assertExecutionException(future, ex); assertEquals(5, runnable.count); assertEquals(0, delegate.getQueue().size()); } - public void testListeningDecorator_cancelled() throws Exception { ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1); BlockingQueue delegateQueue = delegate.getQueue(); @@ -426,7 +410,7 @@ public void testListeningDecorator_cancelled() throws Exception { public void run() {} }; - future = service.schedule(runnable, 5, TimeUnit.MINUTES); + future = service.schedule(runnable, 5, MINUTES); future.cancel(true); assertTrue(future.isCancelled()); delegateFuture = (ScheduledFuture) delegateQueue.element(); @@ -434,7 +418,7 @@ public void run() {} delegateQueue.clear(); - future = service.scheduleAtFixedRate(runnable, 5, 5, TimeUnit.MINUTES); + future = service.scheduleAtFixedRate(runnable, 5, 5, MINUTES); future.cancel(true); assertTrue(future.isCancelled()); delegateFuture = (ScheduledFuture) delegateQueue.element(); @@ -442,7 +426,7 @@ public void run() {} delegateQueue.clear(); - future = service.scheduleWithFixedDelay(runnable, 5, 5, TimeUnit.MINUTES); + future = service.scheduleWithFixedDelay(runnable, 5, 5, MINUTES); future.cancel(true); assertTrue(future.isCancelled()); delegateFuture = (ScheduledFuture) delegateQueue.element(); @@ -481,7 +465,7 @@ private static void assertExecutionException(Future future, Exception expecte public void testInvokeAnyImpl_nullTasks() throws Exception { ListeningExecutorService e = newDirectExecutorService(); try { - invokeAnyImpl(e, null, false, 0, TimeUnit.NANOSECONDS); + invokeAnyImpl(e, null, false, 0, NANOSECONDS); fail(); } catch (NullPointerException success) { } finally { @@ -493,7 +477,7 @@ public void testInvokeAnyImpl_nullTasks() throws Exception { public void testInvokeAnyImpl_emptyTasks() throws Exception { ListeningExecutorService e = newDirectExecutorService(); try { - invokeAnyImpl(e, new ArrayList>(), false, 0, TimeUnit.NANOSECONDS); + invokeAnyImpl(e, new ArrayList>(), false, 0, NANOSECONDS); fail(); } catch (IllegalArgumentException success) { } finally { @@ -514,7 +498,7 @@ public Integer call() { }); l.add(null); try { - invokeAnyImpl(e, l, false, 0, TimeUnit.NANOSECONDS); + invokeAnyImpl(e, l, false, 0, NANOSECONDS); fail(); } catch (NullPointerException success) { } finally { @@ -528,7 +512,7 @@ public void testInvokeAnyImpl_noTaskCompletes() throws Exception { List> l = new ArrayList<>(); l.add(new NPETask()); try { - invokeAnyImpl(e, l, false, 0, TimeUnit.NANOSECONDS); + invokeAnyImpl(e, l, false, 0, NANOSECONDS); fail(); } catch (ExecutionException success) { assertThat(success).hasCauseThat().isInstanceOf(NullPointerException.class); @@ -544,7 +528,7 @@ public void testInvokeAnyImpl() throws Exception { List> l = new ArrayList<>(); l.add(new StringTask()); l.add(new StringTask()); - String result = invokeAnyImpl(e, l, false, 0, TimeUnit.NANOSECONDS); + String result = invokeAnyImpl(e, l, false, 0, NANOSECONDS); assertSame(TEST_STRING, result); } finally { joinPool(e); @@ -566,38 +550,37 @@ public void run() { } } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testAddDelayedShutdownHook_success() throws InterruptedException { TestApplication application = new TestApplication(); ExecutorService service = mock(ExecutorService.class); - application.addDelayedShutdownHook(service, 2, TimeUnit.SECONDS); + application.addDelayedShutdownHook(service, 2, SECONDS); verify(service, Mockito.never()).shutdown(); application.shutdown(); InOrder shutdownFirst = Mockito.inOrder(service); shutdownFirst.verify(service).shutdown(); - shutdownFirst.verify(service).awaitTermination(2, TimeUnit.SECONDS); + shutdownFirst.verify(service).awaitTermination(2, SECONDS); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testAddDelayedShutdownHook_interrupted() throws InterruptedException { TestApplication application = new TestApplication(); ExecutorService service = mock(ExecutorService.class); - application.addDelayedShutdownHook(service, 2, TimeUnit.SECONDS); - when(service.awaitTermination(2, TimeUnit.SECONDS)).thenThrow(new InterruptedException()); + application.addDelayedShutdownHook(service, 2, SECONDS); + when(service.awaitTermination(2, SECONDS)).thenThrow(new InterruptedException()); application.shutdown(); verify(service).shutdown(); } - public void testGetExitingExecutorService_executorSetToUseDaemonThreads() { TestApplication application = new TestApplication(); ThreadPoolExecutor executor = - new ThreadPoolExecutor(1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(1)); + new ThreadPoolExecutor(1, 2, 3, SECONDS, new ArrayBlockingQueue(1)); assertNotNull(application.getExitingExecutorService(executor)); assertTrue(executor.getThreadFactory().newThread(EMPTY_RUNNABLE).isDaemon()); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testGetExitingExecutorService_executorDelegatesToOriginal() { TestApplication application = new TestApplication(); ThreadPoolExecutor executor = mock(ThreadPoolExecutor.class); @@ -607,7 +590,7 @@ public void testGetExitingExecutorService_executorDelegatesToOriginal() { verify(executor).execute(EMPTY_RUNNABLE); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testGetExitingExecutorService_shutdownHookRegistered() throws InterruptedException { TestApplication application = new TestApplication(); ThreadPoolExecutor executor = mock(ThreadPoolExecutor.class); @@ -618,7 +601,6 @@ public void testGetExitingExecutorService_shutdownHookRegistered() throws Interr verify(executor).shutdown(); } - public void testGetExitingScheduledExecutorService_executorSetToUseDaemonThreads() { TestApplication application = new TestApplication(); ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); @@ -626,7 +608,7 @@ public void testGetExitingScheduledExecutorService_executorSetToUseDaemonThreads assertTrue(executor.getThreadFactory().newThread(EMPTY_RUNNABLE).isDaemon()); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testGetExitingScheduledExecutorService_executorDelegatesToOriginal() { TestApplication application = new TestApplication(); ScheduledThreadPoolExecutor executor = mock(ScheduledThreadPoolExecutor.class); @@ -636,7 +618,7 @@ public void testGetExitingScheduledExecutorService_executorDelegatesToOriginal() verify(executor).execute(EMPTY_RUNNABLE); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testGetScheduledExitingExecutorService_shutdownHookRegistered() throws InterruptedException { TestApplication application = new TestApplication(); @@ -669,7 +651,6 @@ public void run() { assertEquals(oldName, Thread.currentThread().getName()); } - public void testExecutors_nullCheck() throws Exception { new ClassSanityTester() .setDefault(RateLimiter.class, RateLimiter.create(1.0)) @@ -699,14 +680,13 @@ synchronized void shutdown() throws InterruptedException { /* Half of a 1-second timeout in nanoseconds */ private static final long HALF_SECOND_NANOS = NANOSECONDS.convert(1L, SECONDS) / 2; - public void testShutdownAndAwaitTermination_immediateShutdown() throws Exception { ExecutorService service = Executors.newSingleThreadExecutor(); assertTrue(shutdownAndAwaitTermination(service, 1L, SECONDS)); assertTrue(service.isTerminated()); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testShutdownAndAwaitTermination_immediateShutdownInternal() throws Exception { ExecutorService service = mock(ExecutorService.class); when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS)).thenReturn(true); @@ -716,7 +696,7 @@ public void testShutdownAndAwaitTermination_immediateShutdownInternal() throws E verify(service).awaitTermination(HALF_SECOND_NANOS, NANOSECONDS); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testShutdownAndAwaitTermination_forcedShutDownInternal() throws Exception { ExecutorService service = mock(ExecutorService.class); when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS)) @@ -729,7 +709,7 @@ public void testShutdownAndAwaitTermination_forcedShutDownInternal() throws Exce verify(service).shutdownNow(); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testShutdownAndAwaitTermination_nonTerminationInternal() throws Exception { ExecutorService service = mock(ExecutorService.class); when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS)) @@ -741,7 +721,7 @@ public void testShutdownAndAwaitTermination_nonTerminationInternal() throws Exce verify(service).shutdownNow(); } - + @AndroidIncompatible // Mocking ExecutorService is forbidden there. TODO(b/218700094): Don't mock. public void testShutdownAndAwaitTermination_interruptedInternal() throws Exception { final ExecutorService service = mock(ExecutorService.class); when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS)) diff --git a/android/guava-tests/test/com/google/common/util/concurrent/RateLimiterTest.java b/android/guava-tests/test/com/google/common/util/concurrent/RateLimiterTest.java index 40713545bf13..a73e127916c4 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/RateLimiterTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/RateLimiterTest.java @@ -16,11 +16,14 @@ package com.google.common.util.concurrent; +import static com.google.common.truth.Truth.assertThat; +import static java.lang.Math.max; import static java.lang.reflect.Modifier.isStatic; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableSet; @@ -35,7 +38,7 @@ import java.util.Random; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; -import org.easymock.EasyMock; +import org.jspecify.annotations.NullUnmarked; import org.mockito.Mockito; /** @@ -43,6 +46,7 @@ * * @author Dimitris Andreou */ +@NullUnmarked public class RateLimiterTest extends TestCase { private static final double EPSILON = 1e-8; @@ -72,54 +76,23 @@ public void testDoubleMinValueCanAcquireExactlyOnce() { public void testSimpleRateUpdate() { RateLimiter limiter = RateLimiter.create(5.0, 5, SECONDS); - assertEquals(5.0, limiter.getRate()); + assertThat(limiter.getRate()).isEqualTo(5.0); limiter.setRate(10.0); - assertEquals(10.0, limiter.getRate()); + assertThat(limiter.getRate()).isEqualTo(10.0); - try { - limiter.setRate(0.0); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.setRate(-10.0); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> limiter.setRate(0.0)); + assertThrows(IllegalArgumentException.class, () -> limiter.setRate(-10.0)); + assertThrows(IllegalArgumentException.class, () -> limiter.setRate(Double.NaN)); } public void testAcquireParameterValidation() { RateLimiter limiter = RateLimiter.create(999); - try { - limiter.acquire(0); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.acquire(-1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.tryAcquire(0); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.tryAcquire(-1); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.tryAcquire(0, 1, SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - limiter.tryAcquire(-1, 1, SECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> limiter.acquire(0)); + assertThrows(IllegalArgumentException.class, () -> limiter.acquire(-1)); + assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(0)); + assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(-1)); + assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(0, 1, SECONDS)); + assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(-1, 1, SECONDS)); } public void testSimpleWithWait() { @@ -133,20 +106,22 @@ public void testSimpleWithWait() { public void testSimpleAcquireReturnValues() { RateLimiter limiter = RateLimiter.create(5.0, stopwatch); - assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00 + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.0); // R0.00 stopwatch.sleepMillis(200); // U0.20, we are ready for the next request... - assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00, ...which is granted immediately - assertEquals(0.2, limiter.acquire(), EPSILON); // R0.20 + assertThat(limiter.acquire()) + .isWithin(EPSILON) + .of(0.0); // R0.00, ...which is granted immediately + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.2); // R0.20 assertEvents("R0.00", "U0.20", "R0.00", "R0.20"); } public void testSimpleAcquireEarliestAvailableIsInPast() { RateLimiter limiter = RateLimiter.create(5.0, stopwatch); - assertEquals(0.0, limiter.acquire(), EPSILON); + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.0); stopwatch.sleepMillis(400); - assertEquals(0.0, limiter.acquire(), EPSILON); - assertEquals(0.0, limiter.acquire(), EPSILON); - assertEquals(0.2, limiter.acquire(), EPSILON); + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.0); + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.0); + assertThat(limiter.acquire()).isWithin(EPSILON).of(0.2); } public void testOneSecondBurst() { @@ -170,17 +145,9 @@ public void testCreateWarmupParameterValidation() { unused = RateLimiter.create(1.0, 1, NANOSECONDS); unused = RateLimiter.create(1.0, 0, NANOSECONDS); - try { - RateLimiter.create(0.0, 1, NANOSECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> RateLimiter.create(0.0, 1, NANOSECONDS)); - try { - RateLimiter.create(1.0, -1, NANOSECONDS); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> RateLimiter.create(1.0, -1, NANOSECONDS)); } @AndroidIncompatible // difference in String.format rounding? @@ -373,7 +340,7 @@ public void testSimpleWeights() { assertEvents("R0.00", "R1.00", "R1.00", "R2.00", "R4.00", "R8.00"); } - public void testInfinity_Bursty() { + public void testInfinity_bursty() { RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, stopwatch); limiter.acquire(Integer.MAX_VALUE / 4); limiter.acquire(Integer.MAX_VALUE / 2); @@ -399,8 +366,8 @@ public void testInfinity_Bursty() { assertEvents("R0.50", "R0.00", "R0.00"); // we repay the last request (.5sec), then back to +oo } - /** https://code.google.com/p/guava-libraries/issues/detail?id=1791 */ - public void testInfinity_BustyTimeElapsed() { + /** https://github.com/google/guava/issues/1791 */ + public void testInfinity_bustyTimeElapsed() { RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, stopwatch); stopwatch.instant += 1000000; limiter.setRate(2.0); @@ -414,7 +381,7 @@ public void testInfinity_BustyTimeElapsed() { "R0.50"); } - public void testInfinity_WarmUp() { + public void testInfinity_warmUp() { RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, 10, SECONDS, 3.0, stopwatch); limiter.acquire(Integer.MAX_VALUE / 4); limiter.acquire(Integer.MAX_VALUE / 2); @@ -434,7 +401,7 @@ public void testInfinity_WarmUp() { assertEvents("R1.00", "R0.00", "R0.00"); } - public void testInfinity_WarmUpTimeElapsed() { + public void testInfinity_warmUpTimeElapsed() { RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, 10, SECONDS, 3.0, stopwatch); stopwatch.instant += 1000000; limiter.setRate(1.0); @@ -511,7 +478,7 @@ public void testVerySmallDoubleValues() throws Exception { private long measureTotalTimeMillis(RateLimiter rateLimiter, int permits, Random random) { long startTime = stopwatch.instant; while (permits > 0) { - int nextPermitsToAcquire = Math.max(1, random.nextInt(permits)); + int nextPermitsToAcquire = max(1, random.nextInt(permits)); permits -= nextPermitsToAcquire; rateLimiter.acquire(nextPermitsToAcquire); } @@ -564,24 +531,9 @@ public String toString() { } } - /* - * Note: Mockito appears to lose its ability to Mock doGetRate as of Android 21. If we start - * testing with that version or newer, we'll need to suppress this test (or see if Mockito can be - * changed to support this). - */ + @AndroidIncompatible // Mockito loses its ability to mock doGetRate as of Android 21 public void testMockingMockito() throws Exception { RateLimiter mock = Mockito.mock(RateLimiter.class); - doTestMocking(mock); - } - - @AndroidIncompatible // EasyMock Class Extension doesn't appear to work on Android. - public void testMockingEasyMock() throws Exception { - RateLimiter mock = EasyMock.createNiceMock(RateLimiter.class); - EasyMock.replay(mock); - doTestMocking(mock); - } - - private static void doTestMocking(RateLimiter mock) throws Exception { for (Method method : RateLimiter.class.getMethods()) { if (!isStatic(method.getModifiers()) && !NOT_WORKING_ON_MOCKS.contains(method.getName()) diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ReflectionFreeAssertThrows.java b/android/guava-tests/test/com/google/common/util/concurrent/ReflectionFreeAssertThrows.java new file mode 100644 index 000000000000..8bb5a7cc8010 --- /dev/null +++ b/android/guava-tests/test/com/google/common/util/concurrent/ReflectionFreeAssertThrows.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.TestExceptions.SomeCheckedException; +import com.google.common.util.concurrent.TestExceptions.SomeError; +import com.google.common.util.concurrent.TestExceptions.SomeOtherCheckedException; +import com.google.common.util.concurrent.TestExceptions.SomeUncheckedException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** Replacements for JUnit's {@code assertThrows} that work under GWT/J2CL. */ +@GwtCompatible(emulated = true) +@NullMarked +final class ReflectionFreeAssertThrows { + interface ThrowingRunnable { + void run() throws Throwable; + } + + interface ThrowingSupplier { + @Nullable Object get() throws Throwable; + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingSupplier supplier) { + return doAssertThrows(expectedThrowable, supplier, /* userPassedSupplier= */ true); + } + + @CanIgnoreReturnValue + static T assertThrows( + Class expectedThrowable, ThrowingRunnable runnable) { + return doAssertThrows( + expectedThrowable, + () -> { + runnable.run(); + return null; + }, + /* userPassedSupplier= */ false); + } + + private static T doAssertThrows( + Class expectedThrowable, ThrowingSupplier supplier, boolean userPassedSupplier) { + checkNotNull(expectedThrowable); + checkNotNull(supplier); + Predicate predicate = INSTANCE_OF.get(expectedThrowable); + if (predicate == null) { + throw new IllegalArgumentException( + expectedThrowable + + " is not yet supported by ReflectionFreeAssertThrows. Add an entry for it in the" + + " map in that class."); + } + Object result; + try { + result = supplier.get(); + } catch (Throwable t) { + if (predicate.apply(t)) { + // We are careful to set up INSTANCE_OF to match each Predicate to its target Class. + @SuppressWarnings("unchecked") + T caught = (T) t; + return caught; + } + throw new AssertionError( + "expected to throw " + expectedThrowable.getSimpleName() + " but threw " + t, t); + } + if (userPassedSupplier) { + throw new AssertionError( + "expected to throw " + + expectedThrowable.getSimpleName() + + " but returned result: " + + result); + } else { + throw new AssertionError("expected to throw " + expectedThrowable.getSimpleName()); + } + } + + private enum PlatformSpecificExceptionBatch { + PLATFORM { + @GwtIncompatible + @J2ktIncompatible + @Override + // returns the types available in "normal" environments + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of( + InvocationTargetException.class, + e -> e instanceof InvocationTargetException, + StackOverflowError.class, + e -> e instanceof StackOverflowError); + } + }; + + // used under GWT, etc., since the override of this method does not exist there + ImmutableMap, Predicate> exceptions() { + return ImmutableMap.of(); + } + } + + private static final ImmutableMap, Predicate> INSTANCE_OF = + ImmutableMap., Predicate>builder() + .put(ArithmeticException.class, e -> e instanceof ArithmeticException) + .put( + ArrayIndexOutOfBoundsException.class, + e -> e instanceof ArrayIndexOutOfBoundsException) + .put(ArrayStoreException.class, e -> e instanceof ArrayStoreException) + .put(AssertionFailedError.class, e -> e instanceof AssertionFailedError) + .put(CancellationException.class, e -> e instanceof CancellationException) + .put(ClassCastException.class, e -> e instanceof ClassCastException) + .put( + ConcurrentModificationException.class, + e -> e instanceof ConcurrentModificationException) + .put(ExecutionError.class, e -> e instanceof ExecutionError) + .put(ExecutionException.class, e -> e instanceof ExecutionException) + .put(IllegalArgumentException.class, e -> e instanceof IllegalArgumentException) + .put(IllegalStateException.class, e -> e instanceof IllegalStateException) + .put(IndexOutOfBoundsException.class, e -> e instanceof IndexOutOfBoundsException) + .put(NoSuchElementException.class, e -> e instanceof NoSuchElementException) + .put(NullPointerException.class, e -> e instanceof NullPointerException) + .put(NumberFormatException.class, e -> e instanceof NumberFormatException) + .put(RuntimeException.class, e -> e instanceof RuntimeException) + .put(SomeCheckedException.class, e -> e instanceof SomeCheckedException) + .put(SomeError.class, e -> e instanceof SomeError) + .put(SomeOtherCheckedException.class, e -> e instanceof SomeOtherCheckedException) + .put(SomeUncheckedException.class, e -> e instanceof SomeUncheckedException) + .put(TimeoutException.class, e -> e instanceof TimeoutException) + .put(UncheckedExecutionException.class, e -> e instanceof UncheckedExecutionException) + .put(UnsupportedCharsetException.class, e -> e instanceof UnsupportedCharsetException) + .put(UnsupportedOperationException.class, e -> e instanceof UnsupportedOperationException) + .put(VerifyException.class, e -> e instanceof VerifyException) + .putAll(PlatformSpecificExceptionBatch.PLATFORM.exceptions()) + .buildOrThrow(); + + private ReflectionFreeAssertThrows() {} +} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/RunnablesTest.java b/android/guava-tests/test/com/google/common/util/concurrent/RunnablesTest.java index c4bd1c7c4ec6..6203761caf15 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/RunnablesTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/RunnablesTest.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtCompatible; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit tests for {@link Runnables}. @@ -25,6 +26,7 @@ * @author Olivier Pernet */ @GwtCompatible +@NullUnmarked public class RunnablesTest extends TestCase { public void testDoNothingRunnableIsSingleton() { assertSame(Runnables.doNothing(), Runnables.doNothing()); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/SequentialExecutorTest.java b/android/guava-tests/test/com/google/common/util/concurrent/SequentialExecutorTest.java index 20209e8b80dc..3b4ac0383381 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/SequentialExecutorTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/SequentialExecutorTest.java @@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.newSequentialExecutor; import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -33,16 +35,17 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests {@link SequentialExecutor}. * * @author JJ Furman */ +@NullUnmarked public class SequentialExecutorTest extends TestCase { private static class FakeExecutor implements Executor { @@ -79,11 +82,7 @@ public void setUp() { } public void testConstructingWithNullExecutor_fails() { - try { - new SequentialExecutor(null); - fail("Should have failed with NullPointerException."); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> new SequentialExecutor(null)); } public void testBasics() { @@ -263,18 +262,21 @@ public void run() { numCalls.incrementAndGet(); } }; - try { - executor.execute(task); - fail(); - } catch (RejectedExecutionException expected) { - } + assertThrows(RejectedExecutionException.class, () -> executor.execute(task)); assertEquals(0, numCalls.get()); reject.set(false); executor.execute(task); assertEquals(1, numCalls.get()); } - + /* + * Under Android, MyError propagates up and fails the test? + * + * TODO(b/218700094): Does this matter to prod users, or is it just a feature of our testing + * environment? If the latter, maybe write a custom Executor that avoids failing the test when it + * sees an Error? + */ + @AndroidIncompatible public void testTaskThrowsError() throws Exception { class MyError extends Error {} final CyclicBarrier barrier = new CyclicBarrier(2); @@ -303,17 +305,16 @@ public void run() { executor.execute(errorTask); service.execute(barrierTask); // submit directly to the service // the barrier task runs after the error task so we know that the error has been observed by - // SequentialExecutor by the time the barrier is satified - barrier.await(1, TimeUnit.SECONDS); + // SequentialExecutor by the time the barrier is satisfied + barrier.await(1, SECONDS); executor.execute(barrierTask); // timeout means the second task wasn't even tried - barrier.await(1, TimeUnit.SECONDS); + barrier.await(1, SECONDS); } finally { service.shutdown(); } } - public void testRejectedExecutionThrownWithMultipleCalls() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final SettableFuture future = SettableFuture.create(); @@ -337,19 +338,12 @@ public void run() { executor.execute(Runnables.doNothing()); } }); - future.get(10, TimeUnit.SECONDS); - try { - executor.execute(Runnables.doNothing()); - fail(); - } catch (RejectedExecutionException expected) { - } + future.get(10, SECONDS); + assertThrows(RejectedExecutionException.class, () -> executor.execute(Runnables.doNothing())); latch.countDown(); - try { - first.get(10, TimeUnit.SECONDS); - fail(); - } catch (ExecutionException expected) { - assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> first.get(10, SECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(RejectedExecutionException.class); } public void testToString() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ServiceManagerTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ServiceManagerTest.java index 20ed3dea5461..be35f06ad917 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ServiceManagerTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ServiceManagerTest.java @@ -16,19 +16,25 @@ package com.google.common.util.concurrent; +import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION; +import static com.google.common.base.StandardSystemProperty.OS_NAME; import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Arrays.asList; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; import com.google.common.testing.TestLogHandler; import com.google.common.util.concurrent.Service.State; import com.google.common.util.concurrent.ServiceManager.Listener; +import java.time.Duration; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -42,6 +48,7 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link ServiceManager}. @@ -49,6 +56,7 @@ * @author Luke Sandberg * @author Chris Nokleberg */ +@NullUnmarked public class ServiceManagerTest extends TestCase { private static class NoOpService extends AbstractService { @@ -79,7 +87,7 @@ protected void doStart() { new Thread() { @Override public void run() { - Uninterruptibles.sleepUninterruptibly(delay, TimeUnit.MILLISECONDS); + Uninterruptibles.sleepUninterruptibly(delay, MILLISECONDS); notifyStarted(); } }.start(); @@ -90,7 +98,7 @@ protected void doStop() { new Thread() { @Override public void run() { - Uninterruptibles.sleepUninterruptibly(delay, TimeUnit.MILLISECONDS); + Uninterruptibles.sleepUninterruptibly(delay, MILLISECONDS); notifyStopped(); } }.start(); @@ -119,8 +127,11 @@ protected void doStop() { } } - public void testServiceStartupTimes() { + if (isWindows() && isJava8()) { + // Flaky there: https://github.com/google/guava/pull/6731#issuecomment-1736298607 + return; + } Service a = new NoOpDelayedService(150); Service b = new NoOpDelayedService(353); ServiceManager serviceManager = new ServiceManager(asList(a, b)); @@ -131,6 +142,20 @@ public void testServiceStartupTimes() { assertThat(startupTimes.get(b)).isAtLeast(353); } + public void testServiceStartupDurations() { + if (isWindows() && isJava8()) { + // Flaky there: https://github.com/google/guava/pull/6731#issuecomment-1736298607 + return; + } + Service a = new NoOpDelayedService(150); + Service b = new NoOpDelayedService(353); + ServiceManager serviceManager = new ServiceManager(asList(a, b)); + serviceManager.startAsync().awaitHealthy(); + ImmutableMap startupTimes = serviceManager.startupDurations(); + assertThat(startupTimes).hasSize(2); + assertThat(startupTimes.get(a)).isAtLeast(Duration.ofMillis(150)); + assertThat(startupTimes.get(b)).isAtLeast(Duration.ofMillis(353)); + } public void testServiceStartupTimes_selfStartingServices() { // This tests to ensure that: @@ -143,7 +168,7 @@ public void testServiceStartupTimes_selfStartingServices() { protected void doStart() { super.doStart(); // This will delay service listener execution at least 150 milliseconds - Uninterruptibles.sleepUninterruptibly(150, TimeUnit.MILLISECONDS); + Uninterruptibles.sleepUninterruptibly(150, MILLISECONDS); } }; Service a = @@ -166,7 +191,6 @@ protected void doStart() { assertThat(startupTimes.get(b)).isNotNull(); } - public void testServiceStartStop() { Service a = new NoOpService(); Service b = new NoOpService(); @@ -188,7 +212,6 @@ public void testServiceStartStop() { assertTrue(listener.failedServices.isEmpty()); } - public void testFailStart() throws Exception { Service a = new NoOpService(); Service b = new FailStartService(); @@ -199,11 +222,7 @@ public void testFailStart() throws Exception { RecordingListener listener = new RecordingListener(); manager.addListener(listener, directExecutor()); assertState(manager, Service.State.NEW, a, b, c, d, e); - try { - manager.startAsync().awaitHealthy(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> manager.startAsync().awaitHealthy()); assertFalse(listener.healthyCalled); assertState(manager, Service.State.RUNNING, a, c, e); assertEquals(ImmutableSet.of(b, d), listener.failedServices); @@ -216,7 +235,6 @@ public void testFailStart() throws Exception { assertTrue(listener.stoppedCalled); } - public void testFailRun() throws Exception { Service a = new NoOpService(); Service b = new FailRunService(); @@ -224,11 +242,7 @@ public void testFailRun() throws Exception { RecordingListener listener = new RecordingListener(); manager.addListener(listener, directExecutor()); assertState(manager, Service.State.NEW, a, b); - try { - manager.startAsync().awaitHealthy(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> manager.startAsync().awaitHealthy()); assertTrue(listener.healthyCalled); assertEquals(ImmutableSet.of(b), listener.failedServices); @@ -239,7 +253,6 @@ public void testFailRun() throws Exception { assertTrue(listener.stoppedCalled); } - public void testFailStop() throws Exception { Service a = new NoOpService(); Service b = new FailStopService(); @@ -268,24 +281,15 @@ public void testToString() throws Exception { assertThat(toString).contains("FailStartService"); } - public void testTimeouts() throws Exception { Service a = new NoOpDelayedService(50); ServiceManager manager = new ServiceManager(asList(a)); manager.startAsync(); - try { - manager.awaitHealthy(1, TimeUnit.MILLISECONDS); - fail(); - } catch (TimeoutException expected) { - } + assertThrows(TimeoutException.class, () -> manager.awaitHealthy(1, MILLISECONDS)); manager.awaitHealthy(5, SECONDS); // no exception thrown manager.stopAsync(); - try { - manager.awaitStopped(1, TimeUnit.MILLISECONDS); - fail(); - } catch (TimeoutException expected) { - } + assertThrows(TimeoutException.class, () -> manager.awaitStopped(1, MILLISECONDS)); manager.awaitStopped(5, SECONDS); // no exception thrown } @@ -298,11 +302,7 @@ public void testSingleFailedServiceCallsStopped() { ServiceManager manager = new ServiceManager(asList(a)); RecordingListener listener = new RecordingListener(); manager.addListener(listener, directExecutor()); - try { - manager.startAsync().awaitHealthy(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> manager.startAsync().awaitHealthy()); assertTrue(listener.stoppedCalled); } @@ -315,11 +315,7 @@ public void testFailStart_singleServiceCallsHealthy() { ServiceManager manager = new ServiceManager(asList(a)); RecordingListener listener = new RecordingListener(); manager.addListener(listener, directExecutor()); - try { - manager.startAsync().awaitHealthy(); - fail(); - } catch (IllegalStateException expected) { - } + assertThrows(IllegalStateException.class, () -> manager.startAsync().awaitHealthy()); assertFalse(listener.healthyCalled); } @@ -342,7 +338,7 @@ public void failure(Service service) { }, directExecutor()); manager.startAsync(); - manager.awaitStopped(10, TimeUnit.MILLISECONDS); + manager.awaitStopped(10, MILLISECONDS); } public void testDoCancelStart() throws TimeoutException { @@ -368,7 +364,7 @@ protected void doStop() { final ServiceManager manager = new ServiceManager(asList(a)); manager.startAsync(); manager.stopAsync(); - manager.awaitStopped(10, TimeUnit.MILLISECONDS); + manager.awaitStopped(10, MILLISECONDS); assertThat(manager.servicesByState().keySet()).containsExactly(Service.State.TERMINATED); } @@ -388,7 +384,7 @@ protected void doStop() { }; final ServiceManager manager = new ServiceManager(asList(a)); manager.startAsync(); - manager.awaitStopped(10, TimeUnit.MILLISECONDS); + manager.awaitStopped(10, MILLISECONDS); assertThat(manager.servicesByState().keySet()).containsExactly(Service.State.FAILED); } @@ -441,11 +437,41 @@ public String format(LogRecord record) { } } + public void testStartupFailureOutput() { + Logger logger = Logger.getLogger(ServiceManager.class.getName()); + logger.setLevel(Level.SEVERE); + TestLogHandler logHandler = new TestLogHandler(); + logger.addHandler(logHandler); + ServiceManager manager = + new ServiceManager(Arrays.asList(new FailRunService(), new FailStartService())); + // Due to the implementation of the two services we know that both are now failed. So the + // following awaitHealthy call is just to get the exception. + manager.startAsync(); + assertThat(manager.servicesByState().get(State.FAILED)).hasSize(2); + IllegalStateException e = + assertThrows(IllegalStateException.class, () -> manager.awaitHealthy()); + assertThat(e) + .hasMessageThat() + .contains( + "Expected to be healthy after starting. The following services are not " + "running:"); + + Throwable[] suppressed = e.getSuppressed(); + assertThat(suppressed).hasLength(2); + assertThat(suppressed[0]).hasCauseThat().isInstanceOf(IllegalStateException.class); + assertThat(suppressed[0]).hasCauseThat().hasMessageThat().isEqualTo("run failure"); + + assertThat(suppressed[1]).hasCauseThat().isInstanceOf(IllegalStateException.class); + assertThat(suppressed[1]).hasCauseThat().hasMessageThat().isEqualTo("start failure"); + LogRecord record = Iterables.getOnlyElement(logHandler.getStoredLogRecords()); + // We log failures that occur after startup + assertThat(record.getMessage()) + .contains("Service FailRunService [FAILED] has failed in the RUNNING state"); + } + /** * Tests that a ServiceManager can be fully shut down if one of its failure listeners is slow or * even permanently blocked. */ - public void testListenerDeadlock() throws InterruptedException { final CountDownLatch failEnter = new CountDownLatch(1); final CountDownLatch failLeave = new CountDownLatch(1); @@ -500,7 +526,7 @@ public void run() { } }; stoppingThread.start(); - // this should be super fast since the only non stopped service is a NoOpService + // this should be super fast since the only non-stopped service is a NoOpService stoppingThread.join(1000); assertFalse("stopAsync has deadlocked!.", stoppingThread.isAlive()); failLeave.countDown(); // release the background thread @@ -519,11 +545,7 @@ public void testPartiallyConstructedManager() { logger.addHandler(logHandler); NoOpService service = new NoOpService(); service.startAsync(); - try { - new ServiceManager(Arrays.asList(service)); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows(IllegalArgumentException.class, () -> new ServiceManager(Arrays.asList(service))); service.stopAsync(); // Nothing was logged! assertEquals(0, logHandler.getStoredLogRecords().size()); @@ -544,6 +566,7 @@ public final void addListener(Listener listener, Executor executor) { service1.startAsync(); delegate.addListener(listener, executor); } + // Delegates from here on down @Override public final Service startAsync() { @@ -590,12 +613,11 @@ public final Throwable failureCause() { return delegate.failureCause(); } }; - try { - new ServiceManager(Arrays.asList(service1, service2)); - fail(); - } catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).contains("started transitioning asynchronously"); - } + IllegalArgumentException expected = + assertThrows( + IllegalArgumentException.class, + () -> new ServiceManager(Arrays.asList(service1, service2))); + assertThat(expected).hasMessageThat().contains("started transitioning asynchronously"); } /** @@ -606,7 +628,6 @@ public final Throwable failureCause() { * *

    Before the bug was fixed this test would fail at least 30% of the time. */ - public void testTransitionRace() throws TimeoutException { for (int k = 0; k < 1000; k++) { List services = Lists.newArrayList(); @@ -615,12 +636,12 @@ public void testTransitionRace() throws TimeoutException { } ServiceManager manager = new ServiceManager(services); manager.startAsync().awaitHealthy(); - manager.stopAsync().awaitStopped(10, TimeUnit.SECONDS); + manager.stopAsync().awaitStopped(10, SECONDS); } } /** - * This service will shutdown very quickly after stopAsync is called and uses a background thread + * This service will shut down very quickly after stopAsync is called and uses a background thread * so that we know that the stopping() listeners will execute on a different thread than the * terminated() listeners. */ @@ -675,4 +696,12 @@ public void failure(Service service) { failedServices.add(service); } } + + private static boolean isWindows() { + return OS_NAME.value().startsWith("Windows"); + } + + private static boolean isJava8() { + return JAVA_SPECIFICATION_VERSION.value().equals("1.8"); + } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ServiceTest.java index 98b8033d901b..53e299c70dc3 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ServiceTest.java @@ -25,8 +25,10 @@ import java.util.Locale; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** Unit tests for {@link Service} */ +@NullUnmarked public class ServiceTest extends TestCase { /** Assert on the comparison ordering of the State enum since we guarantee it. */ diff --git a/android/guava-tests/test/com/google/common/util/concurrent/SettableFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/SettableFutureTest.java index 9b8b88f27167..23c39af8effe 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/SettableFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/SettableFutureTest.java @@ -17,18 +17,21 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test cases for {@link SettableFuture}. * * @author Sven Mawson */ +@NullUnmarked public class SettableFutureTest extends TestCase { private SettableFuture future; @@ -44,38 +47,26 @@ protected void setUp() throws Exception { } public void testDefaultState() throws Exception { - try { - future.get(5, TimeUnit.MILLISECONDS); - fail(); - } catch (TimeoutException expected) { - } + assertThrows(TimeoutException.class, () -> future.get(5, MILLISECONDS)); } - public void testSetValue() throws Exception { assertTrue(future.set("value")); tester.testCompletedFuture("value"); } - public void testSetFailure() throws Exception { assertTrue(future.setException(new Exception("failure"))); tester.testFailedFuture("failure"); } - public void testSetFailureNull() throws Exception { - try { - future.setException(null); - fail(); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> future.setException(null)); assertFalse(future.isDone()); assertTrue(future.setException(new Exception("failure"))); tester.testFailedFuture("failure"); } - public void testCancel() throws Exception { assertTrue(future.cancel(true)); tester.testCancelledFuture(); @@ -112,12 +103,8 @@ public void testSetException() throws Exception { // Check that the future has been set properly. assertTrue(future.isDone()); assertFalse(future.isCancelled()); - try { - future.get(); - fail("Expected ExecutionException"); - } catch (ExecutionException ee) { - assertThat(ee).hasCauseThat().isSameInstanceAs(e); - } + ExecutionException ee = assertThrows(ExecutionException.class, () -> future.get()); + assertThat(ee).hasCauseThat().isSameInstanceAs(e); } public void testSetFuture() throws Exception { @@ -131,12 +118,7 @@ public void testSetFuture() throws Exception { // Check that the future has been set properly. assertFalse(future.isDone()); assertFalse(future.isCancelled()); - try { - future.get(0, TimeUnit.MILLISECONDS); - fail("Expected TimeoutException"); - } catch (TimeoutException expected) { - /* expected */ - } + assertThrows(TimeoutException.class, () -> future.get(0, MILLISECONDS)); nested.set("foo"); assertTrue(future.isDone()); assertFalse(future.isCancelled()); @@ -158,12 +140,7 @@ public void testSetFuture_genericsHierarchy() throws Exception { // Check that the future has been set properly. assertFalse(future.isDone()); assertFalse(future.isCancelled()); - try { - future.get(0, TimeUnit.MILLISECONDS); - fail("Expected TimeoutException"); - } catch (TimeoutException expected) { - /* expected */ - } + assertThrows(TimeoutException.class, () -> future.get(0, MILLISECONDS)); FooChild value = new FooChild(); nested.set(value); assertTrue(future.isDone()); @@ -177,12 +154,7 @@ public void testCancel_innerCancelsAsync() throws Exception { async.setFuture(inner); inner.cancel(true); assertTrue(async.isCancelled()); - try { - async.get(); - fail("Expected CancellationException"); - } catch (CancellationException expected) { - /* expected */ - } + assertThrows(CancellationException.class, () -> async.get()); } public void testCancel_resultCancelsInner_interrupted() throws Exception { @@ -192,12 +164,7 @@ public void testCancel_resultCancelsInner_interrupted() throws Exception { async.cancel(true); assertTrue(inner.isCancelled()); assertTrue(inner.wasInterrupted()); - try { - inner.get(); - fail("Expected CancellationException"); - } catch (CancellationException expected) { - /* expected */ - } + assertThrows(CancellationException.class, () -> inner.get()); } public void testCancel_resultCancelsInner() throws Exception { @@ -207,12 +174,7 @@ public void testCancel_resultCancelsInner() throws Exception { async.cancel(false); assertTrue(inner.isCancelled()); assertFalse(inner.wasInterrupted()); - try { - inner.get(); - fail("Expected CancellationException"); - } catch (CancellationException expected) { - /* expected */ - } + assertThrows(CancellationException.class, () -> inner.get()); } public void testCancel_beforeSet() throws Exception { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/SimpleTimeLimiterTest.java b/android/guava-tests/test/com/google/common/util/concurrent/SimpleTimeLimiterTest.java index 04b824f823f9..d7a860d3fcd0 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/SimpleTimeLimiterTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/SimpleTimeLimiterTest.java @@ -18,15 +18,18 @@ import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.base.Stopwatch; import com.google.common.collect.Range; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Unit test for {@link SimpleTimeLimiter}. @@ -34,7 +37,7 @@ * @author kevinb * @author Jens Nyman */ - +@NullUnmarked public class SimpleTimeLimiterTest extends TestCase { private static final long DELAY_MS = 50; @@ -109,11 +112,7 @@ public void testNewProxy_goodMethodWithNotEnoughTime() throws Exception { Sample proxy = service.newProxy(target, Sample.class, NOT_ENOUGH_MS, MILLISECONDS); Stopwatch stopwatch = Stopwatch.createStarted(); - try { - proxy.sleepThenReturnInput("x"); - fail("no exception thrown"); - } catch (UncheckedTimeoutException expected) { - } + assertThrows(UncheckedTimeoutException.class, () -> proxy.sleepThenReturnInput("x")); assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(NOT_ENOUGH_MS, DELAY_MS * 2)); // Is it still computing away anyway? @@ -127,11 +126,7 @@ public void testNewProxy_badMethodWithEnoughTime() throws Exception { Sample proxy = service.newProxy(target, Sample.class, ENOUGH_MS, MILLISECONDS); Stopwatch stopwatch = Stopwatch.createStarted(); - try { - proxy.sleepThenThrowException(); - fail("no exception thrown"); - } catch (SampleException expected) { - } + assertThrows(SampleException.class, () -> proxy.sleepThenThrowException()); assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS)); } @@ -141,11 +136,7 @@ public void testNewProxy_badMethodWithNotEnoughTime() throws Exception { Sample proxy = service.newProxy(target, Sample.class, NOT_ENOUGH_MS, MILLISECONDS); Stopwatch stopwatch = Stopwatch.createStarted(); - try { - proxy.sleepThenThrowException(); - fail("no exception thrown"); - } catch (UncheckedTimeoutException expected) { - } + assertThrows(UncheckedTimeoutException.class, () -> proxy.sleepThenThrowException()); assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(NOT_ENOUGH_MS, DELAY_MS * 2)); } @@ -160,20 +151,17 @@ public void testCallWithTimeout_goodCallableWithEnoughTime() throws Exception { } public void testCallWithTimeout_goodCallableWithNotEnoughTime() throws Exception { - try { - service.callWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (TimeoutException expected) { - } + assertThrows( + TimeoutException.class, + () -> service.callWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS)); } public void testCallWithTimeout_badCallableWithEnoughTime() throws Exception { - try { - service.callWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(SampleException.class); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> service.callWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(SampleException.class); } public void testCallUninterruptiblyWithTimeout_goodCallableWithEnoughTime() throws Exception { @@ -186,20 +174,17 @@ public void testCallUninterruptiblyWithTimeout_goodCallableWithEnoughTime() thro } public void testCallUninterruptiblyWithTimeout_goodCallableWithNotEnoughTime() throws Exception { - try { - service.callUninterruptiblyWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (TimeoutException expected) { - } + assertThrows( + TimeoutException.class, + () -> service.callUninterruptiblyWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS)); } public void testCallUninterruptiblyWithTimeout_badCallableWithEnoughTime() throws Exception { - try { - service.callUninterruptiblyWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (ExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(SampleException.class); - } + ExecutionException expected = + assertThrows( + ExecutionException.class, + () -> service.callUninterruptiblyWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(SampleException.class); } public void testRunWithTimeout_goodRunnableWithEnoughTime() throws Exception { @@ -211,20 +196,17 @@ public void testRunWithTimeout_goodRunnableWithEnoughTime() throws Exception { } public void testRunWithTimeout_goodRunnableWithNotEnoughTime() throws Exception { - try { - service.runWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (TimeoutException expected) { - } + assertThrows( + TimeoutException.class, + () -> service.runWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS)); } public void testRunWithTimeout_badRunnableWithEnoughTime() throws Exception { - try { - service.runWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (UncheckedExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(SampleRuntimeException.class); - } + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, + () -> service.runWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(SampleRuntimeException.class); } public void testRunUninterruptiblyWithTimeout_goodRunnableWithEnoughTime() throws Exception { @@ -236,20 +218,17 @@ public void testRunUninterruptiblyWithTimeout_goodRunnableWithEnoughTime() throw } public void testRunUninterruptiblyWithTimeout_goodRunnableWithNotEnoughTime() throws Exception { - try { - service.runUninterruptiblyWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (TimeoutException expected) { - } + assertThrows( + TimeoutException.class, + () -> service.runUninterruptiblyWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS)); } public void testRunUninterruptiblyWithTimeout_badRunnableWithEnoughTime() throws Exception { - try { - service.runUninterruptiblyWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS); - fail("no exception thrown"); - } catch (UncheckedExecutionException expected) { - assertThat(expected.getCause()).isInstanceOf(SampleRuntimeException.class); - } + UncheckedExecutionException expected = + assertThrows( + UncheckedExecutionException.class, + () -> service.runUninterruptiblyWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS)); + assertThat(expected).hasCauseThat().isInstanceOf(SampleRuntimeException.class); } private interface Sample { @@ -272,6 +251,7 @@ private static class SampleImpl implements Sample { this.delayMillis = delayMillis; } + @CanIgnoreReturnValue @Override public String sleepThenReturnInput(String input) { try { @@ -279,7 +259,7 @@ public String sleepThenReturnInput(String input) { finished = true; return input; } catch (InterruptedException e) { - return null; + throw new AssertionError(); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/StripedTest.java b/android/guava-tests/test/com/google/common/util/concurrent/StripedTest.java index fa9d87f752f2..dadba8cc3ec5 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/StripedTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/StripedTest.java @@ -37,12 +37,14 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for Striped. * * @author Dimitris Andreou */ +@NullUnmarked public class StripedTest extends TestCase { private static List> strongImplementations() { return ImmutableList.of( @@ -50,22 +52,8 @@ private static List> strongImplementations() { Striped.readWriteLock(256), Striped.lock(100), Striped.lock(256), - Striped.custom( - 100, - new Supplier() { - @Override - public Lock get() { - return new ReentrantLock(true); - } - }), - Striped.custom( - 256, - new Supplier() { - @Override - public Lock get() { - return new ReentrantLock(true); - } - }), + Striped.custom(100, FAIR_LOCK_SUPPLER), + Striped.custom(256, FAIR_LOCK_SUPPLER), Striped.semaphore(100, 1), Striped.semaphore(256, 1)); } @@ -86,6 +74,14 @@ public Lock get() { } }; + private static final Supplier FAIR_LOCK_SUPPLER = + new Supplier() { + @Override + public Lock get() { + return new ReentrantLock(true); + } + }; + private static final Supplier SEMAPHORE_SUPPLER = new Supplier() { @Override @@ -108,6 +104,8 @@ private static List> weakImplementations() { .add(new Striped.SmallLazyStriped(64, SEMAPHORE_SUPPLER)) .add(new Striped.LargeLazyStriped(50, SEMAPHORE_SUPPLER)) .add(new Striped.LargeLazyStriped(64, SEMAPHORE_SUPPLER)) + .add(Striped.lazyWeakCustom(50, FAIR_LOCK_SUPPLER)) + .add(Striped.lazyWeakCustom(64, FAIR_LOCK_SUPPLER)) .build(); } @@ -129,7 +127,7 @@ public void testSizes() { assertTrue(Striped.lazyWeakLock(256).size() == 256); } - + @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. public void testWeakImplementations() { for (Striped striped : weakImplementations()) { WeakReference weakRef = new WeakReference<>(striped.get(new Object())); @@ -137,7 +135,7 @@ public void testWeakImplementations() { } } - + @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. public void testWeakReadWrite() { Striped striped = Striped.lazyWeakReadWriteLock(1000); Object key = new Object(); @@ -150,7 +148,7 @@ public void testWeakReadWrite() { readLock.unlock(); } - + @AndroidIncompatible // Presumably GC doesn't trigger, despite our efforts. public void testStrongImplementations() { for (Striped striped : strongImplementations()) { WeakReference weakRef = new WeakReference<>(striped.get(new Object())); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/SupplementalMonitorTest.java b/android/guava-tests/test/com/google/common/util/concurrent/SupplementalMonitorTest.java index 8a52ffeed591..1fcbf03b280d 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/SupplementalMonitorTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/SupplementalMonitorTest.java @@ -18,12 +18,14 @@ import static com.google.common.util.concurrent.GeneratedMonitorTest.startThread; import static com.google.common.util.concurrent.Uninterruptibles.joinUninterruptibly; +import static org.junit.Assert.assertThrows; import com.google.common.util.concurrent.GeneratedMonitorTest.FlagGuard; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Supplemental tests for {@link Monitor}. @@ -33,46 +35,30 @@ * * @author Justin T. Sampson */ - +@NullUnmarked public class SupplementalMonitorTest extends TestCase { public void testLeaveWithoutEnterThrowsIMSE() { Monitor monitor = new Monitor(); - try { - monitor.leave(); - fail("expected IllegalMonitorStateException"); - } catch (IllegalMonitorStateException expected) { - } + assertThrows(IllegalMonitorStateException.class, () -> monitor.leave()); } public void testGetWaitQueueLengthWithWrongMonitorThrowsIMSE() { Monitor monitor1 = new Monitor(); Monitor monitor2 = new Monitor(); FlagGuard guard = new FlagGuard(monitor2); - try { - monitor1.getWaitQueueLength(guard); - fail("expected IllegalMonitorStateException"); - } catch (IllegalMonitorStateException expected) { - } + assertThrows(IllegalMonitorStateException.class, () -> monitor1.getWaitQueueLength(guard)); } public void testHasWaitersWithWrongMonitorThrowsIMSE() { Monitor monitor1 = new Monitor(); Monitor monitor2 = new Monitor(); FlagGuard guard = new FlagGuard(monitor2); - try { - monitor1.hasWaiters(guard); - fail("expected IllegalMonitorStateException"); - } catch (IllegalMonitorStateException expected) { - } + assertThrows(IllegalMonitorStateException.class, () -> monitor1.hasWaiters(guard)); } public void testNullMonitorInGuardConstructorThrowsNPE() { - try { - new FlagGuard(null); - fail("expected NullPointerException"); - } catch (NullPointerException expected) { - } + assertThrows(NullPointerException.class, () -> new FlagGuard(null)); } public void testIsFair() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/TestExceptions.java b/android/guava-tests/test/com/google/common/util/concurrent/TestExceptions.java new file mode 100644 index 000000000000..3a0496818fbe --- /dev/null +++ b/android/guava-tests/test/com/google/common/util/concurrent/TestExceptions.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import org.jspecify.annotations.NullUnmarked; + +/** Exception classes for use in tests. */ +@GwtCompatible +@NullUnmarked +final class TestExceptions { + static class SomeError extends Error {} + + static class SomeCheckedException extends Exception {} + + static class SomeOtherCheckedException extends Exception {} + + static class YetAnotherCheckedException extends Exception {} + + static class SomeUncheckedException extends RuntimeException {} + + static class SomeChainingException extends RuntimeException { + public SomeChainingException(Throwable cause) { + super(cause); + } + } + + private TestExceptions() {} +} diff --git a/android/guava-tests/test/com/google/common/util/concurrent/TestPlatform.java b/android/guava-tests/test/com/google/common/util/concurrent/TestPlatform.java index 5c87fe552a05..c8fc26da8afe 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/TestPlatform.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/TestPlatform.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.util.concurrent.FuturesTest.failureWithCause; import static com.google.common.util.concurrent.FuturesTest.pseudoTimedGetUninterruptibly; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -30,7 +29,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; -import junit.framework.AssertionFailedError; +import org.jspecify.annotations.Nullable; /** Methods factored out so that they can be emulated differently in GWT. */ @GwtCompatible(emulated = true) @@ -42,7 +41,7 @@ static void verifyGetOnPendingFuture(Future future) { fail(); } catch (TimeoutException expected) { } catch (ExecutionException e) { - throw failureWithCause(e, ""); + throw new AssertionError(e); } } @@ -52,7 +51,7 @@ static void verifyTimedGetOnPendingFuture(Future future) { fail(); } catch (TimeoutException expected) { } catch (ExecutionException e) { - throw failureWithCause(e, ""); + throw new AssertionError(e); } } @@ -68,14 +67,13 @@ static void clearInterrupt() { * Retrieves the result of a {@code Future} known to be done but uses the {@code get(long, * TimeUnit)} overload in order to test that method. */ - static V getDoneFromTimeoutOverload(Future future) throws ExecutionException { + static V getDoneFromTimeoutOverload(Future future) + throws ExecutionException { checkState(future.isDone(), "Future was expected to be done: %s", future); try { return getUninterruptibly(future, 0, SECONDS); } catch (TimeoutException e) { - AssertionFailedError error = new AssertionFailedError(e.getMessage()); - error.initCause(e); - throw error; + throw new AssertionError(e); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/TestThread.java b/android/guava-tests/test/com/google/common/util/concurrent/TestThread.java index f85d3134dd25..e183f5b69f07 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/TestThread.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/TestThread.java @@ -17,6 +17,7 @@ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -26,10 +27,10 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.CheckForNull; import junit.framework.AssertionFailedError; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * A helper for concurrency testing. One or more {@code TestThread} instances are instantiated in a @@ -48,6 +49,7 @@ * @param the type of the lock-like object to be used * @author Justin T. Sampson */ +@NullUnmarked public final class TestThread extends Thread implements TearDown { private static final long DUE_DILIGENCE_MILLIS = 100; @@ -58,7 +60,7 @@ public final class TestThread extends Thread implements TearDown { private final SynchronousQueue requestQueue = new SynchronousQueue<>(); private final SynchronousQueue responseQueue = new SynchronousQueue<>(); - private Throwable uncaughtThrowable = null; + private @Nullable Throwable uncaughtThrowable = null; public TestThread(L lockLikeObject, String threadName) { super(threadName); @@ -77,9 +79,7 @@ public void tearDown() throws Exception { join(); if (uncaughtThrowable != null) { - throw (AssertionFailedError) - new AssertionFailedError("Uncaught throwable in " + getName()) - .initCause(uncaughtThrowable); + throw new AssertionError("Uncaught throwable in " + getName(), uncaughtThrowable); } } @@ -168,7 +168,7 @@ public void callAndAssertWaits(String methodName, Object conditionLikeObject) th * Asserts that a prior call that had caused this thread to block or wait has since returned * normally. */ - public void assertPriorCallReturns(@CheckForNull String methodName) throws Exception { + public void assertPriorCallReturns(@Nullable String methodName) throws Exception { assertEquals(null, getResponse(methodName).getResult()); } @@ -176,7 +176,7 @@ public void assertPriorCallReturns(@CheckForNull String methodName) throws Excep * Asserts that a prior call that had caused this thread to block or wait has since returned the * expected boolean value. */ - public void assertPriorCallReturns(boolean expected, @CheckForNull String methodName) + public void assertPriorCallReturns(boolean expected, @Nullable String methodName) throws Exception { assertEquals(expected, getResponse(methodName).getResult()); } @@ -188,8 +188,7 @@ public void assertPriorCallReturns(boolean expected, @CheckForNull String method * of time */ private void sendRequest(String methodName, Object... arguments) throws Exception { - if (!requestQueue.offer( - new Request(methodName, arguments), TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + if (!requestQueue.offer(new Request(methodName, arguments), TIMEOUT_MILLIS, MILLISECONDS)) { throw new TimeoutException(); } } @@ -203,7 +202,7 @@ private void sendRequest(String methodName, Object... arguments) throws Exceptio * this thread has called most recently */ private Response getResponse(String methodName) throws Exception { - Response response = responseQueue.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + Response response = responseQueue.poll(TIMEOUT_MILLIS, MILLISECONDS); if (response == null) { throw new TimeoutException(); } @@ -275,7 +274,7 @@ private static class Response { final Object result; final Throwable throwable; - Response(String methodName, Object result, Throwable throwable) { + Response(String methodName, @Nullable Object result, @Nullable Throwable throwable) { this.methodName = methodName; this.result = result; this.throwable = throwable; @@ -283,7 +282,7 @@ private static class Response { Object getResult() { if (throwable != null) { - throw (AssertionFailedError) new AssertionFailedError().initCause(throwable); + throw new AssertionError(throwable); } return result; } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/ThreadFactoryBuilderTest.java b/android/guava-tests/test/com/google/common/util/concurrent/ThreadFactoryBuilderTest.java index 7684b963376a..c29e8cb04f99 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/ThreadFactoryBuilderTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/ThreadFactoryBuilderTest.java @@ -17,6 +17,7 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.common.testing.NullPointerTester; import java.lang.Thread.UncaughtExceptionHandler; @@ -24,6 +25,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for ThreadFactoryBuilder. @@ -31,6 +33,7 @@ * @author Kurt Alfred Kluever * @author Martin Buchholz */ +@NullUnmarked public class ThreadFactoryBuilderTest extends TestCase { private final Runnable monitoredRunnable = new Runnable() { @@ -56,7 +59,6 @@ public void setUp() { builder = new ThreadFactoryBuilder(); } - public void testThreadFactoryBuilder_defaults() throws InterruptedException { ThreadFactory threadFactory = builder.build(); Thread thread = threadFactory.newThread(monitoredRunnable); @@ -93,7 +95,6 @@ private static void checkThreadPoolName(Thread thread, int threadId) { assertThat(thread.getName()).matches("^pool-\\d+-thread-" + threadId + "$"); } - public void testNameFormatWithPercentS_custom() { String format = "super-duper-thread-%s"; ThreadFactory factory = builder.setNameFormat(format).build(); @@ -102,7 +103,6 @@ public void testNameFormatWithPercentS_custom() { } } - public void testNameFormatWithPercentD_custom() { String format = "super-duper-thread-%d"; ThreadFactory factory = builder.setNameFormat(format).build(); @@ -111,21 +111,18 @@ public void testNameFormatWithPercentD_custom() { } } - public void testDaemon_false() { ThreadFactory factory = builder.setDaemon(false).build(); Thread thread = factory.newThread(monitoredRunnable); assertFalse(thread.isDaemon()); } - public void testDaemon_true() { ThreadFactory factory = builder.setDaemon(true).build(); Thread thread = factory.newThread(monitoredRunnable); assertTrue(thread.isDaemon()); } - public void testPriority_custom() { for (int i = Thread.MIN_PRIORITY; i <= Thread.MAX_PRIORITY; i++) { ThreadFactory factory = builder.setPriority(i).build(); @@ -135,22 +132,15 @@ public void testPriority_custom() { } public void testPriority_tooLow() { - try { - builder.setPriority(Thread.MIN_PRIORITY - 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> builder.setPriority(Thread.MIN_PRIORITY - 1)); } public void testPriority_tooHigh() { - try { - builder.setPriority(Thread.MAX_PRIORITY + 1); - fail(); - } catch (IllegalArgumentException expected) { - } + assertThrows( + IllegalArgumentException.class, () -> builder.setPriority(Thread.MAX_PRIORITY + 1)); } - public void testUncaughtExceptionHandler_custom() { assertEquals( UNCAUGHT_EXCEPTION_HANDLER, @@ -161,7 +151,6 @@ public void testUncaughtExceptionHandler_custom() { .getUncaughtExceptionHandler()); } - public void testBuildMutateBuild() { ThreadFactory factory1 = builder.setPriority(1).build(); assertEquals(1, factory1.newThread(monitoredRunnable).getPriority()); @@ -177,7 +166,6 @@ public void testBuildTwice() { unused = builder.build(); // this is *also* allowed } - public void testBuildMutate() { ThreadFactory factory1 = builder.setPriority(1).build(); assertEquals(1, factory1.newThread(monitoredRunnable).getPriority()); @@ -186,7 +174,6 @@ public void testBuildMutate() { assertEquals(1, factory1.newThread(monitoredRunnable).getPriority()); } - public void testThreadFactory() throws InterruptedException { final String THREAD_NAME = "ludicrous speed"; final int THREAD_PRIORITY = 1; diff --git a/android/guava-tests/test/com/google/common/util/concurrent/TrustedInputFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/TrustedInputFutureTest.java index 1f2eccad932d..bcd6e95adcaa 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/TrustedInputFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/TrustedInputFutureTest.java @@ -16,15 +16,16 @@ package com.google.common.util.concurrent; - import com.google.common.annotations.GwtCompatible; import com.google.common.util.concurrent.AbstractFuture.TrustedFuture; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link AbstractFuture} that use a {@link TrustedFuture} for {@link * AbstractFuture#setFuture} calls. */ @GwtCompatible +@NullUnmarked public class TrustedInputFutureTest extends AbstractAbstractFutureTest { @Override AbstractFuture newDelegate() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/TrustedListenableFutureTaskTest.java b/android/guava-tests/test/com/google/common/util/concurrent/TrustedListenableFutureTaskTest.java index 157afa79d8a6..397c41d8f526 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/TrustedListenableFutureTaskTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/TrustedListenableFutureTaskTest.java @@ -19,10 +19,12 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.Callables.returning; import static com.google.common.util.concurrent.Futures.getDone; +import static com.google.common.util.concurrent.ReflectionFreeAssertThrows.assertThrows; import static com.google.common.util.concurrent.TestPlatform.verifyThreadWasNotInterrupted; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; @@ -33,8 +35,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** Test case for {@link TrustedListenableFutureTask}. */ +@NullMarked @GwtCompatible(emulated = true) public class TrustedListenableFutureTaskTest extends TestCase { @@ -54,11 +59,7 @@ public void testCancelled() throws Exception { assertTrue(task.isDone()); assertTrue(task.isCancelled()); assertFalse(task.wasInterrupted()); - try { - getDone(task); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> getDone(task)); verifyThreadWasNotInterrupted(); } @@ -75,16 +76,13 @@ public Integer call() throws Exception { task.run(); assertTrue(task.isDone()); assertFalse(task.isCancelled()); - try { - getDone(task); - fail(); - } catch (ExecutionException executionException) { - assertThat(executionException).hasCauseThat().isEqualTo(e); - } + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> getDone(task)); + assertThat(executionException).hasCauseThat().isEqualTo(e); } + @J2ktIncompatible @GwtIncompatible // blocking wait - public void testCancel_interrupted() throws Exception { final AtomicBoolean interruptedExceptionThrown = new AtomicBoolean(); final CountDownLatch enterLatch = new CountDownLatch(1); @@ -125,17 +123,13 @@ public void run() { assertTrue(task.isDone()); assertTrue(task.isCancelled()); assertTrue(task.wasInterrupted()); - try { - task.get(); - fail(); - } catch (CancellationException expected) { - } + assertThrows(CancellationException.class, () -> task.get()); exitLatch.await(); assertTrue(interruptedExceptionThrown.get()); } + @J2ktIncompatible @GwtIncompatible // blocking wait - public void testRunIdempotency() throws Exception { final int numThreads = 10; final ExecutorService executor = Executors.newFixedThreadPool(numThreads); @@ -170,16 +164,16 @@ public void run() { executor.shutdown(); } + @J2ktIncompatible @GwtIncompatible // blocking wait - public void testToString() throws Exception { final CountDownLatch enterLatch = new CountDownLatch(1); final CountDownLatch exitLatch = new CountDownLatch(1); - final TrustedListenableFutureTask task = + final TrustedListenableFutureTask<@Nullable Void> task = TrustedListenableFutureTask.create( - new Callable() { + new Callable<@Nullable Void>() { @Override - public Void call() throws Exception { + public @Nullable Void call() throws Exception { enterLatch.countDown(); new CountDownLatch(1).await(); // wait forever return null; @@ -208,7 +202,8 @@ public void run() { exitLatch.await(); } - @GwtIncompatible // used only in GwtIncomaptible tests + @J2ktIncompatible + @GwtIncompatible // used only in GwtIncompatible tests private void awaitUnchecked(CyclicBarrier barrier) { try { barrier.await(); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UncaughtExceptionHandlersTest.java b/android/guava-tests/test/com/google/common/util/concurrent/UncaughtExceptionHandlersTest.java index eb8455b18323..92e629e1d4b0 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UncaughtExceptionHandlersTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UncaughtExceptionHandlersTest.java @@ -20,17 +20,21 @@ import static org.mockito.Mockito.verify; import com.google.common.util.concurrent.UncaughtExceptionHandlers.Exiter; +import com.google.common.util.concurrent.UncaughtExceptionHandlers.RuntimeWrapper; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; -/** @author Gregory Kick */ - +/** + * @author Gregory Kick + */ +@NullUnmarked public class UncaughtExceptionHandlersTest extends TestCase { - private Runtime runtimeMock; + private RuntimeWrapper runtimeMock; @Override protected void setUp() { - runtimeMock = mock(Runtime.class); + runtimeMock = mock(RuntimeWrapper.class); } public void testExiter() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UncheckedThrowingFuture.java b/android/guava-tests/test/com/google/common/util/concurrent/UncheckedThrowingFuture.java index 405772279512..52975562a499 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UncheckedThrowingFuture.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UncheckedThrowingFuture.java @@ -23,6 +23,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.NullUnmarked; /** * A {@link Future} implementation which always throws directly from calls to {@code get()} (i.e. @@ -34,6 +35,7 @@ * @author Anthony Zana */ @GwtCompatible +@NullUnmarked final class UncheckedThrowingFuture extends AbstractFuture { public static ListenableFuture throwingError(Error error) { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleFutureTest.java index 0d24266074b0..512de121e264 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleFutureTest.java @@ -18,8 +18,10 @@ import static com.google.common.util.concurrent.InterruptionUtil.repeatedlyInterruptTestThread; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertThrows; import com.google.common.testing.TearDown; import com.google.common.testing.TearDownStack; @@ -29,9 +31,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; // TODO(cpovirk): Should this be merged into UninterruptiblesTest? /** @@ -40,6 +42,7 @@ * @author Kevin Bourrillion * @author Chris Povirk */ +@NullUnmarked public class UninterruptibleFutureTest extends TestCase { private SleepingRunnable sleeper; private Future delayedFuture; @@ -77,7 +80,6 @@ protected void tearDown() { * This first test doesn't test anything in Uninterruptibles, just demonstrates some normal * behavior of futures so that you can contrast the next test with it. */ - public void testRegularFutureInterrupted() throws ExecutionException { /* @@ -93,11 +95,11 @@ public void testRegularFutureInterrupted() throws ExecutionException { * 7. We expect get() to return this result. * 8. We expect the test thread's interrupt state to be false. */ - InterruptionUtil.requestInterruptIn(200, TimeUnit.MILLISECONDS); + InterruptionUtil.requestInterruptIn(200, MILLISECONDS); assertFalse(Thread.interrupted()); try { - delayedFuture.get(20000, TimeUnit.MILLISECONDS); + delayedFuture.get(20000, MILLISECONDS); fail("expected to be interrupted"); } catch (InterruptedException expected) { } catch (TimeoutException e) { @@ -116,17 +118,13 @@ public void testRegularFutureInterrupted() throws ExecutionException { assertTrue(sleeper.completed); } - public void testMakeUninterruptible_timeoutPreservedThroughInterruption() throws ExecutionException { repeatedlyInterruptTestThread(100, tearDownStack); - try { - getUninterruptibly(delayedFuture, 500, TimeUnit.MILLISECONDS); - fail("expected to time out"); - } catch (TimeoutException expected) { - } + assertThrows( + TimeoutException.class, () -> getUninterruptibly(delayedFuture, 500, MILLISECONDS)); assertTrue(Thread.interrupted()); // clears the interrupt state, too assertFalse(sleeper.completed); @@ -155,32 +153,26 @@ public void run() { } } - public void testMakeUninterruptible_untimed_uninterrupted() throws Exception { runUntimedInterruptsTest(0); } - public void testMakeUninterruptible_untimed_interrupted() throws Exception { runUntimedInterruptsTest(1); } - public void testMakeUninterruptible_untimed_multiplyInterrupted() throws Exception { runUntimedInterruptsTest(38); } - public void testMakeUninterruptible_timed_uninterrupted() throws Exception { runTimedInterruptsTest(0); } - public void testMakeUninterruptible_timed_interrupted() throws Exception { runTimedInterruptsTest(1); } - public void testMakeUninterruptible_timed_multiplyInterrupted() throws Exception { runTimedInterruptsTest(38); } @@ -218,7 +210,6 @@ private static void runNInterruptsTest( /** * Confirms that the test code triggers {@link InterruptedException} in a standard {@link Future}. */ - public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception { SettableFuture future = SettableFuture.create(); FutureTask wasInterrupted = untimedInterruptReporter(future, true); @@ -226,16 +217,11 @@ public void testMakeUninterruptible_plainFutureSanityCheck() throws Exception { Thread waitingThread = new Thread(wasInterrupted); waitingThread.start(); waitingThread.interrupt(); - try { - wasInterrupted.get(); - fail(); - } catch (ExecutionException expected) { - assertTrue( - expected.getCause().toString(), expected.getCause() instanceof InterruptedException); - } + ExecutionException expected = + assertThrows(ExecutionException.class, () -> wasInterrupted.get()); + assertTrue(expected.getCause().toString(), expected.getCause() instanceof InterruptedException); } - public void testMakeUninterruptible_timedGetZeroTimeoutAttempted() throws TimeoutException, ExecutionException { SettableFuture future = SettableFuture.create(); @@ -248,7 +234,6 @@ public void testMakeUninterruptible_timedGetZeroTimeoutAttempted() assertEquals(RESULT, getUninterruptibly(future, 0, SECONDS)); } - public void testMakeUninterruptible_timedGetNegativeTimeoutAttempted() throws TimeoutException, ExecutionException { SettableFuture future = SettableFuture.create(); diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleMonitorTest.java b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleMonitorTest.java index 59bf80878849..29c881126056 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleMonitorTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptibleMonitorTest.java @@ -16,13 +16,14 @@ package com.google.common.util.concurrent; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Monitor}'s uninterruptible methods. * * @author Justin T. Sampson */ - +@NullUnmarked public class UninterruptibleMonitorTest extends MonitorTestCase { public UninterruptibleMonitorTest() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptiblesTest.java b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptiblesTest.java index e58cf6a60ef1..2d5ff758ec8b 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UninterruptiblesTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UninterruptiblesTest.java @@ -16,6 +16,7 @@ package com.google.common.util.concurrent; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.InterruptionUtil.repeatedlyInterruptTestThread; import static com.google.common.util.concurrent.Uninterruptibles.awaitTerminationUninterruptibly; import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly; @@ -34,6 +35,7 @@ import com.google.common.testing.TearDown; import com.google.common.testing.TearDownStack; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.Date; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -48,13 +50,14 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link Uninterruptibles}. * * @author Anthony Zana */ - +@NullUnmarked public class UninterruptiblesTest extends TestCase { private static final String EXPECTED_TAKE = "expectedTake"; @@ -469,6 +472,26 @@ public void testTryAcquireTimeoutMultiInterruptExpiredMultiPermit() { } // executor.awaitTermination Testcases + public void testTryAwaitTerminationUninterruptiblyDuration_success() { + ExecutorService executor = newFixedThreadPool(1); + requestInterruptIn(500); + executor.execute(new SleepTask(1000)); + executor.shutdown(); + assertTrue(awaitTerminationUninterruptibly(executor, Duration.ofMillis(LONG_DELAY_MS))); + assertTrue(executor.isTerminated()); + assertInterrupted(); + } + + public void testTryAwaitTerminationUninterruptiblyDuration_failure() { + ExecutorService executor = newFixedThreadPool(1); + requestInterruptIn(500); + executor.execute(new SleepTask(10000)); + executor.shutdown(); + assertFalse(awaitTerminationUninterruptibly(executor, Duration.ofSeconds(1))); + assertFalse(executor.isTerminated()); + assertInterrupted(); + } + public void testTryAwaitTerminationUninterruptiblyLongTimeUnit_success() { ExecutorService executor = newFixedThreadPool(1); requestInterruptIn(500); @@ -761,7 +784,7 @@ void joinSuccessfully(long timeoutMillis) { void joinUnsuccessfully(long timeoutMillis) { Uninterruptibles.joinUninterruptibly(thread, timeoutMillis, MILLISECONDS); completed.assertCompletionNotExpected(timeoutMillis); - assertFalse(Thread.State.TERMINATED.equals(thread.getState())); + assertThat(thread.getState()).isNotEqualTo(Thread.State.TERMINATED); } } diff --git a/android/guava-tests/test/com/google/common/util/concurrent/UntrustedInputFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/UntrustedInputFutureTest.java index a6241d2e7120..44ee313c7656 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/UntrustedInputFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/UntrustedInputFutureTest.java @@ -16,15 +16,16 @@ package com.google.common.util.concurrent; - import com.google.common.annotations.GwtCompatible; import com.google.common.util.concurrent.AbstractFuture.TrustedFuture; +import org.jspecify.annotations.NullUnmarked; /** * Tests for {@link AbstractFuture} that use a non-{@link TrustedFuture} for {@link * AbstractFuture#setFuture} calls. */ @GwtCompatible +@NullUnmarked public class UntrustedInputFutureTest extends AbstractAbstractFutureTest { @Override AbstractFuture newDelegate() { diff --git a/android/guava-tests/test/com/google/common/util/concurrent/WrappingExecutorServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/WrappingExecutorServiceTest.java index fdb2c54e6792..68edc7b7691e 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/WrappingExecutorServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/WrappingExecutorServiceTest.java @@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; import static com.google.common.util.concurrent.Runnables.doNothing; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -34,19 +36,22 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test for {@link WrappingExecutorService} * * @author Chris Nokleberg */ +@NullUnmarked public class WrappingExecutorServiceTest extends TestCase { private static final String RESULT_VALUE = "ran"; + // Uninteresting delegations public void testDelegations() throws InterruptedException { MockExecutor mock = new MockExecutor(); TestExecutor testExecutor = new TestExecutor(mock); - assertFalse(testExecutor.awaitTermination(10, TimeUnit.MILLISECONDS)); + assertFalse(testExecutor.awaitTermination(10, MILLISECONDS)); mock.assertLastMethodCalled("awaitTermination"); assertFalse(testExecutor.isTerminated()); mock.assertLastMethodCalled("isTerminated"); @@ -102,7 +107,7 @@ public void testInvokeAll() throws InterruptedException, ExecutionException { } { MockExecutor mock = new MockExecutor(); - TimeUnit unit = TimeUnit.SECONDS; + TimeUnit unit = SECONDS; long timeout = 5; TestExecutor testExecutor = new TestExecutor(mock); List> futures = testExecutor.invokeAll(tasks, timeout, unit); @@ -122,7 +127,7 @@ public void testInvokeAny() throws InterruptedException, ExecutionException, Tim } { MockExecutor mock = new MockExecutor(); - TimeUnit unit = TimeUnit.SECONDS; + TimeUnit unit = SECONDS; long timeout = 5; TestExecutor testExecutor = new TestExecutor(mock); String s = testExecutor.invokeAny(tasks, timeout, unit); @@ -188,7 +193,7 @@ protected Runnable wrapTask(Runnable command) { } } - // TODO: If this test can ever depend on EasyMock or the like, use it instead. + // TODO: If this test can ever depend on Mockito or the like, use it instead. private static final class MockExecutor implements ExecutorService { private String lastMethodCalled = ""; private long lastTimeoutInMillis = -1; diff --git a/android/guava-tests/test/com/google/common/util/concurrent/WrappingScheduledExecutorServiceTest.java b/android/guava-tests/test/com/google/common/util/concurrent/WrappingScheduledExecutorServiceTest.java index 8d0183e3247c..548a45273a1b 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/WrappingScheduledExecutorServiceTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/WrappingScheduledExecutorServiceTest.java @@ -17,6 +17,8 @@ package com.google.common.util.concurrent; import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; import java.util.Collection; import java.util.List; @@ -29,12 +31,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Test for {@link WrappingScheduledExecutorService} * * @author Luke Sandberg */ +@NullUnmarked public class WrappingScheduledExecutorServiceTest extends TestCase { private static final Runnable DO_NOTHING = new Runnable() { @@ -47,13 +51,13 @@ public void testSchedule() { TestExecutor testExecutor = new TestExecutor(mock); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored - Future possiblyIgnoredError = testExecutor.schedule(DO_NOTHING, 10, TimeUnit.MINUTES); - mock.assertLastMethodCalled("scheduleRunnable", 10, TimeUnit.MINUTES); + Future possiblyIgnoredError = testExecutor.schedule(DO_NOTHING, 10, MINUTES); + mock.assertLastMethodCalled("scheduleRunnable", 10, MINUTES); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError1 = - testExecutor.schedule(Executors.callable(DO_NOTHING), 5, TimeUnit.SECONDS); - mock.assertLastMethodCalled("scheduleCallable", 5, TimeUnit.SECONDS); + testExecutor.schedule(Executors.callable(DO_NOTHING), 5, SECONDS); + mock.assertLastMethodCalled("scheduleCallable", 5, SECONDS); } public void testSchedule_repeating() { @@ -61,13 +65,12 @@ public void testSchedule_repeating() { TestExecutor testExecutor = new TestExecutor(mock); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored Future possiblyIgnoredError = - testExecutor.scheduleWithFixedDelay(DO_NOTHING, 100, 10, TimeUnit.MINUTES); - mock.assertLastMethodCalled("scheduleWithFixedDelay", 100, 10, TimeUnit.MINUTES); + testExecutor.scheduleWithFixedDelay(DO_NOTHING, 100, 10, MINUTES); + mock.assertLastMethodCalled("scheduleWithFixedDelay", 100, 10, MINUTES); @SuppressWarnings("unused") // https://errorprone.info/bugpattern/FutureReturnValueIgnored - Future possiblyIgnoredError1 = - testExecutor.scheduleAtFixedRate(DO_NOTHING, 3, 7, TimeUnit.SECONDS); - mock.assertLastMethodCalled("scheduleAtFixedRate", 3, 7, TimeUnit.SECONDS); + Future possiblyIgnoredError1 = testExecutor.scheduleAtFixedRate(DO_NOTHING, 3, 7, SECONDS); + mock.assertLastMethodCalled("scheduleAtFixedRate", 3, 7, SECONDS); } private static final class WrappedCallable implements Callable { diff --git a/android/guava-tests/test/com/google/common/xml/XmlEscapersTest.java b/android/guava-tests/test/com/google/common/xml/XmlEscapersTest.java index 00b5cf16b60b..d491115b19e1 100644 --- a/android/guava-tests/test/com/google/common/xml/XmlEscapersTest.java +++ b/android/guava-tests/test/com/google/common/xml/XmlEscapersTest.java @@ -22,6 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.escape.CharEscaper; import junit.framework.TestCase; +import org.jspecify.annotations.NullUnmarked; /** * Tests for the {@link XmlEscapers} class. @@ -30,6 +31,7 @@ * @author David Beaumont */ @GwtCompatible +@NullUnmarked public class XmlEscapersTest extends TestCase { public void testXmlContentEscaper() throws Exception { diff --git a/android/guava/javadoc-link/checker-framework/package-list b/android/guava/javadoc-link/checker-framework/package-list deleted file mode 100644 index ce4e9fb098e0..000000000000 --- a/android/guava/javadoc-link/checker-framework/package-list +++ /dev/null @@ -1,101 +0,0 @@ -android.annotation -android.support.annotation -com.sun.istack.internal -edu.umd.cs.findbugs.annotations -javax.annotation -javax.annotation.concurrent -javax.annotation.meta -javax.validation.constraints -lombok -net.jcip.annotations -org.checkerframework.checker.compilermsgs -org.checkerframework.checker.compilermsgs.qual -org.checkerframework.checker.fenum -org.checkerframework.checker.fenum.qual -org.checkerframework.checker.formatter -org.checkerframework.checker.formatter.qual -org.checkerframework.checker.guieffect -org.checkerframework.checker.guieffect.qual -org.checkerframework.checker.i18n -org.checkerframework.checker.i18n.qual -org.checkerframework.checker.i18nformatter -org.checkerframework.checker.i18nformatter.qual -org.checkerframework.checker.i18nformatter.unittests -org.checkerframework.checker.index -org.checkerframework.checker.index.lowerbound -org.checkerframework.checker.index.qual -org.checkerframework.checker.index.samelen -org.checkerframework.checker.index.searchindex -org.checkerframework.checker.index.substringindex -org.checkerframework.checker.index.upperbound -org.checkerframework.checker.initialization -org.checkerframework.checker.initialization.qual -org.checkerframework.checker.interning -org.checkerframework.checker.interning.qual -org.checkerframework.checker.linear -org.checkerframework.checker.linear.qual -org.checkerframework.checker.lock -org.checkerframework.checker.lock.qual -org.checkerframework.checker.nullness -org.checkerframework.checker.nullness.compatqual -org.checkerframework.checker.nullness.qual -org.checkerframework.checker.propkey -org.checkerframework.checker.propkey.qual -org.checkerframework.checker.regex -org.checkerframework.checker.regex.qual -org.checkerframework.checker.signature -org.checkerframework.checker.signature.qual -org.checkerframework.checker.signedness -org.checkerframework.checker.signedness.qual -org.checkerframework.checker.tainting -org.checkerframework.checker.tainting.qual -org.checkerframework.checker.units -org.checkerframework.checker.units.qual -org.checkerframework.common.aliasing -org.checkerframework.common.aliasing.qual -org.checkerframework.common.basetype -org.checkerframework.common.reflection -org.checkerframework.common.reflection.qual -org.checkerframework.common.subtyping -org.checkerframework.common.util -org.checkerframework.common.util.count -org.checkerframework.common.util.debug -org.checkerframework.common.util.report -org.checkerframework.common.util.report.qual -org.checkerframework.common.value -org.checkerframework.common.value.qual -org.checkerframework.common.value.util -org.checkerframework.common.wholeprograminference -org.checkerframework.dataflow.analysis -org.checkerframework.dataflow.cfg -org.checkerframework.dataflow.cfg.block -org.checkerframework.dataflow.cfg.node -org.checkerframework.dataflow.cfg.playground -org.checkerframework.dataflow.constantpropagation -org.checkerframework.dataflow.qual -org.checkerframework.dataflow.util -org.checkerframework.framework.flow -org.checkerframework.framework.qual -org.checkerframework.framework.source -org.checkerframework.framework.test -org.checkerframework.framework.test.diagnostics -org.checkerframework.framework.type -org.checkerframework.framework.type.treeannotator -org.checkerframework.framework.type.typeannotator -org.checkerframework.framework.type.visitor -org.checkerframework.framework.util -org.checkerframework.framework.util.defaults -org.checkerframework.framework.util.dependenttypes -org.checkerframework.framework.util.element -org.checkerframework.framework.util.typeinference -org.checkerframework.framework.util.typeinference.constraint -org.checkerframework.framework.util.typeinference.solver -org.checkerframework.javacutil -org.checkerframework.javacutil.dist -org.checkerframework.javacutil.trees -org.eclipse.jdt.annotation -org.eclipse.jgit.annotations -org.jetbrains.annotations -org.jmlspecs.annotation -org.netbeans.api.annotations.common -org.springframework.lang diff --git a/android/guava/javadoc-link/jsr305/package-list b/android/guava/javadoc-link/jsr305/package-list deleted file mode 100644 index cc08202c352c..000000000000 --- a/android/guava/javadoc-link/jsr305/package-list +++ /dev/null @@ -1,3 +0,0 @@ -javax.annotation -javax.annotation.concurrent -javax.annotation.meta diff --git a/android/guava/pom.xml b/android/guava/pom.xml index bfefec25cdc5..0010fa2d6fe6 100644 --- a/android/guava/pom.xml +++ b/android/guava/pom.xml @@ -1,11 +1,12 @@ + 4.0.0 com.google.guava guava-parent - HEAD-android-SNAPSHOT + 999.0.0-HEAD-android-SNAPSHOT guava bundle @@ -20,7 +21,7 @@ com.google.guava failureaccess - 1.0.1 + 1.0.2 com.google.guava @@ -28,12 +29,8 @@ 9999.0-empty-to-avoid-conflict-with-guava - com.google.code.findbugs - jsr305 - - - org.checkerframework - checker-qual + org.jspecify + jspecify com.google.errorprone @@ -43,11 +40,26 @@ com.google.j2objc j2objc-annotations - - + + + .. + + LICENSE + proguard/* + + META-INF + + + + org.mvnsearch + toolchains-maven-plugin + + + maven-toolchains-plugin + maven-jar-plugin @@ -62,7 +74,7 @@ true org.apache.felix maven-bundle-plugin - 2.5.0 + 5.1.8 bundle-manifest @@ -95,24 +107,6 @@ maven-source-plugin - - - maven-dependency-plugin - - - unpack-jdk-sources - generate-sources - unpack-dependencies - - srczip - ${project.build.directory}/jdk-sources - false - - **/module-info.java,**/java/io/FileDescriptor.java - - - - org.codehaus.mojo animal-sniffer-maven-plugin @@ -120,16 +114,11 @@ maven-javadoc-plugin - - - - ${project.build.sourceDirectory}:${project.build.directory}/jdk-sources - - com.google.common.base.internal,com.google.common.base.internal.*,com.google.thirdparty.publicsuffix,com.google.thirdparty.publicsuffix.*,com.oracle.*,com.sun.*,java.*,javax.*,jdk,jdk.*,org.*,sun.* + com.azul.tooling.in,com.google.common.base.internal,com.google.common.base.internal.*,com.google.thirdparty.publicsuffix,com.google.thirdparty.publicsuffix.*,com.oracle.*,com.sun.*,java.*,javax.*,jdk,jdk.*,org.*,sun.* @@ -160,94 +149,67 @@ - + false - - https://static.javadoc.io/com.google.code.findbugs/jsr305/3.0.1/ - ${project.basedir}/javadoc-link/jsr305 - - - https://static.javadoc.io/com.google.j2objc/j2objc-annotations/1.1/ + https://javadoc.io/doc/com.google.j2objc/j2objc-annotations/latest/ ${project.basedir}/javadoc-link/j2objc-annotations - - - https://docs.oracle.com/javase/9/docs/api/ - https://docs.oracle.com/javase/9/docs/api/ - - - - https://checkerframework.org/api/ - ${project.basedir}/javadoc-link/checker-framework - + https://docs.oracle.com/en/java/javase/21/docs/api/ https://errorprone.info/api/latest/ + https://jspecify.dev/docs/api/ + ../../overview.html + + + maven-resources-plugin - attach-docs + gradle-module-metadata + compile + + copy-resources + + + target/publish + + + ../../guava + + module.json + + true + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + - generate-javadoc-site-report - site - javadoc + attach-gradle-module-metadata + + attach-artifact + + + + + target/publish/module.json + module + + + - - - srczip-parent - - - ${java.home}/../src.zip - - - - - jdk - srczip - 999 - system - ${java.home}/../src.zip - true - - - - - srczip-lib - - - ${java.home}/lib/src.zip - - - - - jdk - srczip - 999 - system - ${java.home}/lib/src.zip - true - - - - - - maven-javadoc-plugin - - - ${project.build.sourceDirectory}:${project.build.directory}/jdk-sources/java.base - - - - - - diff --git a/android/guava/src/com/google/common/annotations/J2ktIncompatible.java b/android/guava/src/com/google/common/annotations/J2ktIncompatible.java new file mode 100644 index 000000000000..59511632e2af --- /dev/null +++ b/android/guava/src/com/google/common/annotations/J2ktIncompatible.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on an API indicates that the method may not be used with + * J2kt. + * + * @since 32.0.0 + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@GwtCompatible +public @interface J2ktIncompatible {} diff --git a/android/guava/src/com/google/common/annotations/VisibleForTesting.java b/android/guava/src/com/google/common/annotations/VisibleForTesting.java index e767afcdd3e2..24b4db5bde02 100644 --- a/android/guava/src/com/google/common/annotations/VisibleForTesting.java +++ b/android/guava/src/com/google/common/annotations/VisibleForTesting.java @@ -22,8 +22,8 @@ * bad design, and it does not prevent anyone from using the declaration---and experience has shown * that they will. If the method breaks the encapsulation of its class, then its internal * representation will be hard to change. Instead, use RestrictedApiChecker, which - * enforces fine-grained visibility policies. + * href="http://errorprone.info/bugpattern/RestrictedApi">RestrictedApiChecker, which enforces + * fine-grained visibility policies. * * @author Johannes Henkel */ diff --git a/android/guava/src/com/google/common/annotations/package-info.java b/android/guava/src/com/google/common/annotations/package-info.java index 9ad041ffeb60..3cff985b7f75 100644 --- a/android/guava/src/com/google/common/annotations/package-info.java +++ b/android/guava/src/com/google/common/annotations/package-info.java @@ -13,7 +13,7 @@ */ /** - * Common annotation types. This package is a part of the open-source Guava library. + * Annotation types. This package is a part of the open-source Guava library. */ package com.google.common.annotations; diff --git a/android/guava/src/com/google/common/base/Absent.java b/android/guava/src/com/google/common/base/Absent.java index f96136b5efac..f9aff2a3229d 100644 --- a/android/guava/src/com/google/common/base/Absent.java +++ b/android/guava/src/com/google/common/base/Absent.java @@ -17,13 +17,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Collections; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** Implementation of an {@link Optional} not containing a reference. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class Absent extends Optional { static final Absent INSTANCE = new Absent<>(); @@ -62,8 +63,7 @@ public T or(Supplier supplier) { } @Override - @CheckForNull - public T orNull() { + public @Nullable T orNull() { return null; } @@ -79,7 +79,7 @@ public Optional transform(Function function) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return object == this; } @@ -97,5 +97,5 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/base/AbstractIterator.java b/android/guava/src/com/google/common/base/AbstractIterator.java index bb0a1d324978..f46e12ecbe0b 100644 --- a/android/guava/src/com/google/common/base/AbstractIterator.java +++ b/android/guava/src/com/google/common/base/AbstractIterator.java @@ -21,15 +21,13 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Note this class is a copy of {@link com.google.common.collect.AbstractIterator} (for dependency * reasons). */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractIterator implements Iterator { private State state = State.NOT_READY; @@ -42,14 +40,12 @@ private enum State { FAILED, } - @CheckForNull private T next; + private @Nullable T next; - @CheckForNull - protected abstract T computeNext(); + protected abstract @Nullable T computeNext(); @CanIgnoreReturnValue - @CheckForNull - protected final T endOfData() { + protected final @Nullable T endOfData() { state = State.DONE; return null; } diff --git a/android/guava/src/com/google/common/base/Ascii.java b/android/guava/src/com/google/common/base/Ascii.java index 0c651bb27be4..d8f5dc5f9757 100644 --- a/android/guava/src/com/google/common/base/Ascii.java +++ b/android/guava/src/com/google/common/base/Ascii.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import java.nio.charset.StandardCharsets; /** * Static methods pertaining to ASCII characters (those in the range of values {@code 0x00} through @@ -27,7 +28,7 @@ * *
      * - *
    • {@link Charsets#US_ASCII} specifies the {@code Charset} of ASCII characters. + *
    • {@link StandardCharsets#US_ASCII} specifies the {@code Charset} of ASCII characters. *
    • {@link CharMatcher#ascii} matches ASCII characters and provides text processing methods * which operate only on the ASCII characters of a string. *
    @@ -37,7 +38,6 @@ * @since 7.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Ascii { private Ascii() {} diff --git a/android/guava/src/com/google/common/base/CaseFormat.java b/android/guava/src/com/google/common/base/CaseFormat.java index 7b393ebd7e0f..c3b1afe9cf3e 100644 --- a/android/guava/src/com/google/common/base/CaseFormat.java +++ b/android/guava/src/com/google/common/base/CaseFormat.java @@ -18,8 +18,10 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Utility class for converting between various ASCII case formats. Behavior is undefined for @@ -29,9 +31,11 @@ * @since 1.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public enum CaseFormat { - /** Hyphenated variable naming convention, e.g., "lower-hyphen". */ + /** + * Hyphenated variable naming convention, e.g., "lower-hyphen". This format is also colloquially + * known as "kebab case". + */ LOWER_HYPHEN(CharMatcher.is('-'), "-") { @Override String normalizeWord(String word) { @@ -151,7 +155,8 @@ String convert(CaseFormat format, String s) { } /** - * Returns a {@code Converter} that converts strings from this format to {@code targetFormat}. + * Returns a serializable {@code Converter} that converts strings from this format to {@code + * targetFormat}. * * @since 16.0 */ @@ -181,7 +186,7 @@ protected String doBackward(String s) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof StringConverter) { StringConverter that = (StringConverter) object; return sourceFormat.equals(that.sourceFormat) && targetFormat.equals(that.targetFormat); @@ -199,7 +204,7 @@ public String toString() { return sourceFormat + ".converterTo(" + targetFormat + ")"; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } abstract String normalizeWord(String word); diff --git a/android/guava/src/com/google/common/base/CharMatcher.java b/android/guava/src/com/google/common/base/CharMatcher.java index 2a2a89447890..f1afc2415f88 100644 --- a/android/guava/src/com/google/common/base/CharMatcher.java +++ b/android/guava/src/com/google/common/base/CharMatcher.java @@ -21,6 +21,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; +import com.google.errorprone.annotations.InlineMe; +import com.google.errorprone.annotations.InlineMeValidationDisabled; import java.util.Arrays; import java.util.BitSet; @@ -61,7 +63,6 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class CharMatcher implements Predicate { /* * N777777777NO @@ -133,7 +134,8 @@ public static CharMatcher none() { * illustrated here. * This is not the same definition used by other Java APIs. (See a comparison of several definitions of "whitespace".) + * href="https://docs.google.com/spreadsheets/d/1kq4ECwPjHX9B8QUCTPclgsDCXYaj7T-FlT4tB5q3ahk/edit">comparison + * of several definitions of "whitespace".) * *

    All Unicode White_Space characters are on the BMP and thus supported by this API. * @@ -292,7 +294,7 @@ public static CharMatcher singleWidth() { // Static factories /** Returns a {@code char} matcher that matches only one specified BMP character. */ - public static CharMatcher is(final char match) { + public static CharMatcher is(char match) { return new Is(match); } @@ -301,7 +303,7 @@ public static CharMatcher is(final char match) { * *

    To negate another {@code CharMatcher}, use {@link #negate()}. */ - public static CharMatcher isNot(final char match) { + public static CharMatcher isNot(char match) { return new IsNot(match); } @@ -309,7 +311,7 @@ public static CharMatcher isNot(final char match) { * Returns a {@code char} matcher that matches any BMP character present in the given character * sequence. Returns a bogus matcher if the sequence contains supplementary characters. */ - public static CharMatcher anyOf(final CharSequence sequence) { + public static CharMatcher anyOf(CharSequence sequence) { switch (sequence.length()) { case 0: return none(); @@ -339,7 +341,7 @@ public static CharMatcher noneOf(CharSequence sequence) { * * @throws IllegalArgumentException if {@code endInclusive < startInclusive} */ - public static CharMatcher inRange(final char startInclusive, final char endInclusive) { + public static CharMatcher inRange(char startInclusive, char endInclusive) { return new InRange(startInclusive, endInclusive); } @@ -347,7 +349,7 @@ public static CharMatcher inRange(final char startInclusive, final char endInclu * Returns a matcher with identical behavior to the given {@link Character}-based predicate, but * which operates on primitive {@code char} instances instead. */ - public static CharMatcher forPredicate(final Predicate predicate) { + public static CharMatcher forPredicate(Predicate predicate) { return predicate instanceof CharMatcher ? (CharMatcher) predicate : new ForPredicate(predicate); } @@ -367,7 +369,8 @@ protected CharMatcher() {} // Non-static factories /** Returns a matcher that matches any character not matched by this matcher. */ - // @Override under Java 8 but not under Java 7 + // This is not an override in java7, where Guava's Predicate does not extend the JDK's Predicate. + @SuppressWarnings("MissingOverride") public CharMatcher negate() { return new Negated(this); } @@ -388,12 +391,12 @@ public CharMatcher or(CharMatcher other) { /** * Returns a {@code char} matcher functionally equivalent to this one, but which may be faster to - * query than the original; your mileage may vary. Precomputation takes time and is likely to be - * worthwhile only if the precomputed matcher is queried many thousands of times. + * query than the original; your mileage may vary. Precomputation takes time and requires more + * memory, so it is only likely to be worthwhile if the precomputed matcher is queried very often. * *

    This method has no effect (returns {@code this}) when called in GWT: it's unclear whether a - * precomputed matcher is faster, but it certainly consumes more memory, which doesn't seem like a - * worthwhile tradeoff in a browser. + * precomputed matcher is faster, but it certainly would consume more memory (which doesn't seem + * like a worthwhile tradeoff in a browser). */ public CharMatcher precomputed() { return Platform.precomputeCharMatcher(this); @@ -413,7 +416,7 @@ public CharMatcher precomputed() { */ @GwtIncompatible // SmallCharMatcher CharMatcher precomputedInternal() { - final BitSet table = new BitSet(); + BitSet table = new BitSet(); setBits(table); int totalCharacters = table.cardinality(); if (totalCharacters * 2 <= DISTINCT_CHARS) { @@ -423,7 +426,7 @@ CharMatcher precomputedInternal() { table.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); int negatedCharacters = DISTINCT_CHARS - totalCharacters; String suffix = ".negate()"; - final String description = toString(); + String description = toString(); String negatedDescription = description.endsWith(suffix) ? description.substring(0, description.length() - suffix.length()) @@ -904,8 +907,16 @@ private String finishCollapseFrom( * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #matches} * instead. */ + @InlineMe(replacement = "this.matches(character)") @Deprecated @Override + /* + * We can't compatibly make this `final` now (even after devising a way for `ForPredicate`, which + * currently overrides it, to keep the null check that it inserts). + */ + @InlineMeValidationDisabled( + "While apply() is not final, the inlining is still safe because all known overrides of" + + " apply() call matches().") public boolean apply(Character character) { return matches(character); } @@ -965,7 +976,7 @@ public final String toString() { } /** Negation of a {@link FastMatcher}. */ - static class NegatedFastMatcher extends Negated { + private static class NegatedFastMatcher extends Negated { NegatedFastMatcher(CharMatcher original) { super(original); @@ -1008,7 +1019,7 @@ void setBits(BitSet bitSet) { /** Implementation of {@link #any()}. */ private static final class Any extends NamedFastMatcher { - static final Any INSTANCE = new Any(); + static final CharMatcher INSTANCE = new Any(); private Any() { super("CharMatcher.any()"); @@ -1105,7 +1116,7 @@ public CharMatcher negate() { /** Implementation of {@link #none()}. */ private static final class None extends NamedFastMatcher { - static final None INSTANCE = new None(); + static final CharMatcher INSTANCE = new None(); private None() { super("CharMatcher.none()"); @@ -1221,7 +1232,7 @@ static final class Whitespace extends NamedFastMatcher { static final int MULTIPLIER = 1682554634; static final int SHIFT = Integer.numberOfLeadingZeros(TABLE.length() - 1); - static final Whitespace INSTANCE = new Whitespace(); + static final CharMatcher INSTANCE = new Whitespace(); Whitespace() { super("CharMatcher.whitespace()"); @@ -1278,7 +1289,7 @@ public String toString() { /** Implementation of {@link #ascii()}. */ private static final class Ascii extends NamedFastMatcher { - static final Ascii INSTANCE = new Ascii(); + static final CharMatcher INSTANCE = new Ascii(); Ascii() { super("CharMatcher.ascii()"); @@ -1352,7 +1363,7 @@ private static char[] nines() { return nines; } - static final Digit INSTANCE = new Digit(); + static final CharMatcher INSTANCE = new Digit(); private Digit() { super("CharMatcher.digit()", zeroes(), nines()); @@ -1362,7 +1373,7 @@ private Digit() { /** Implementation of {@link #javaDigit()}. */ private static final class JavaDigit extends CharMatcher { - static final JavaDigit INSTANCE = new JavaDigit(); + static final CharMatcher INSTANCE = new JavaDigit(); @Override public boolean matches(char c) { @@ -1378,7 +1389,7 @@ public String toString() { /** Implementation of {@link #javaLetter()}. */ private static final class JavaLetter extends CharMatcher { - static final JavaLetter INSTANCE = new JavaLetter(); + static final CharMatcher INSTANCE = new JavaLetter(); @Override public boolean matches(char c) { @@ -1394,7 +1405,7 @@ public String toString() { /** Implementation of {@link #javaLetterOrDigit()}. */ private static final class JavaLetterOrDigit extends CharMatcher { - static final JavaLetterOrDigit INSTANCE = new JavaLetterOrDigit(); + static final CharMatcher INSTANCE = new JavaLetterOrDigit(); @Override public boolean matches(char c) { @@ -1410,7 +1421,7 @@ public String toString() { /** Implementation of {@link #javaUpperCase()}. */ private static final class JavaUpperCase extends CharMatcher { - static final JavaUpperCase INSTANCE = new JavaUpperCase(); + static final CharMatcher INSTANCE = new JavaUpperCase(); @Override public boolean matches(char c) { @@ -1426,7 +1437,7 @@ public String toString() { /** Implementation of {@link #javaLowerCase()}. */ private static final class JavaLowerCase extends CharMatcher { - static final JavaLowerCase INSTANCE = new JavaLowerCase(); + static final CharMatcher INSTANCE = new JavaLowerCase(); @Override public boolean matches(char c) { @@ -1442,7 +1453,7 @@ public String toString() { /** Implementation of {@link #javaIsoControl()}. */ private static final class JavaIsoControl extends NamedFastMatcher { - static final JavaIsoControl INSTANCE = new JavaIsoControl(); + static final CharMatcher INSTANCE = new JavaIsoControl(); private JavaIsoControl() { super("CharMatcher.javaIsoControl()"); @@ -1467,7 +1478,7 @@ private static final class Invisible extends RangesMatcher { "\u0020\u00a0\u00ad\u0605\u061c\u06dd\u070f\u0891\u08e2\u1680\u180e\u200f\u202f\u2064\u206f" + "\u3000\uf8ff\ufeff\ufffb"; - static final Invisible INSTANCE = new Invisible(); + static final CharMatcher INSTANCE = new Invisible(); private Invisible() { super("CharMatcher.invisible()", RANGE_STARTS.toCharArray(), RANGE_ENDS.toCharArray()); @@ -1477,7 +1488,7 @@ private Invisible() { /** Implementation of {@link #singleWidth()}. */ private static final class SingleWidth extends RangesMatcher { - static final SingleWidth INSTANCE = new SingleWidth(); + static final CharMatcher INSTANCE = new SingleWidth(); private SingleWidth() { super( @@ -1804,7 +1815,7 @@ public boolean matches(char c) { return predicate.apply(c); } - @SuppressWarnings("deprecation") // intentional; deprecation is for callers primarily + @Deprecated @Override public boolean apply(Character character) { return predicate.apply(checkNotNull(character)); diff --git a/android/guava/src/com/google/common/base/Charsets.java b/android/guava/src/com/google/common/base/Charsets.java index 7aebea826c78..aa371a5735b4 100644 --- a/android/guava/src/com/google/common/base/Charsets.java +++ b/android/guava/src/com/google/common/base/Charsets.java @@ -16,7 +16,9 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Contains constant definitions for the six standard {@link Charset} instances, which are @@ -31,73 +33,54 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Charsets { - private Charsets() {} /** * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#US_ASCII} instead. - * + * @deprecated Use {@link StandardCharsets#US_ASCII} instead. */ - @GwtIncompatible // Charset not supported by GWT - public static final Charset US_ASCII = Charset.forName("US-ASCII"); + @Deprecated @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset US_ASCII = StandardCharsets.US_ASCII; /** * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#ISO_8859_1} instead. - * + * @deprecated Use {@link StandardCharsets#ISO_8859_1} instead. */ - public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + @Deprecated public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; /** * UTF-8: eight-bit UCS Transformation Format. * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#UTF_8} instead. - * + * @deprecated Use {@link StandardCharsets#UTF_8} instead. */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); + @Deprecated public static final Charset UTF_8 = StandardCharsets.UTF_8; /** * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#UTF_16BE} instead. - * + * @deprecated Use {@link StandardCharsets#UTF_16BE} instead. */ - @GwtIncompatible // Charset not supported by GWT - public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); + @Deprecated @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16BE = StandardCharsets.UTF_16BE; /** * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#UTF_16LE} instead. - * + * @deprecated Use {@link StandardCharsets#UTF_16LE} instead. */ - @GwtIncompatible // Charset not supported by GWT - public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); + @Deprecated @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16LE = StandardCharsets.UTF_16LE; /** * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order * mark. * - *

    Note for Java 7 and later: this constant should be treated as deprecated; use {@link - * java.nio.charset.StandardCharsets#UTF_16} instead. - * + * @deprecated Use {@link StandardCharsets#UTF_16} instead. */ - @GwtIncompatible // Charset not supported by GWT - public static final Charset UTF_16 = Charset.forName("UTF-16"); + @Deprecated @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16 = StandardCharsets.UTF_16; - /* - * Please do not add new Charset references to this class, unless those character encodings are - * part of the set required to be supported by all Java platform implementations! Any Charsets - * initialized here may cause unexpected delays when this class is loaded. See the Charset - * Javadocs for the list of built-in character encodings. - */ + private Charsets() {} } diff --git a/android/guava/src/com/google/common/base/CommonMatcher.java b/android/guava/src/com/google/common/base/CommonMatcher.java index d63b46b5d48f..6d14c6bc2630 100644 --- a/android/guava/src/com/google/common/base/CommonMatcher.java +++ b/android/guava/src/com/google/common/base/CommonMatcher.java @@ -22,7 +22,6 @@ * javadoc for details. */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class CommonMatcher { public abstract boolean matches(); diff --git a/android/guava/src/com/google/common/base/CommonPattern.java b/android/guava/src/com/google/common/base/CommonPattern.java index c425d52609d6..6be5b01408aa 100644 --- a/android/guava/src/com/google/common/base/CommonPattern.java +++ b/android/guava/src/com/google/common/base/CommonPattern.java @@ -22,7 +22,6 @@ * javadoc for details. */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class CommonPattern { public abstract CommonMatcher matcher(CharSequence t); diff --git a/android/guava/src/com/google/common/base/Converter.java b/android/guava/src/com/google/common/base/Converter.java index f08df5d74357..270dcc1f75ee 100644 --- a/android/guava/src/com/google/common/base/Converter.java +++ b/android/guava/src/com/google/common/base/Converter.java @@ -18,13 +18,16 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.ForOverride; +import com.google.errorprone.annotations.InlineMe; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import java.io.Serializable; import java.util.Iterator; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A function from {@code A} to {@code B} with an associated reverse function from {@code B} @@ -69,7 +72,7 @@ * create a "fake" converter for a unit test. It is unnecessary (and confusing) to mock * the {@code Converter} type using a mocking framework. *

  • Extend this class and implement its {@link #doForward} and {@link #doBackward} methods. - *
  • Java 8 users: you may prefer to pass two lambda expressions or method references to + *
  • Java 8+ users: you may prefer to pass two lambda expressions or method references to * the {@link #from from} factory method. * * @@ -114,7 +117,6 @@ * @since 16.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault /* * 1. The type parameter is rather than so that we can use T in the * doForward and doBackward methods to indicate that the parameter cannot be null. (We also take @@ -143,7 +145,7 @@ public abstract class Converter implements Function { private final boolean handleNullAutomatically; // We lazily cache the reverse view to avoid allocating on every call to reverse(). - @LazyInit @RetainedWith @CheckForNull private transient Converter reverse; + @LazyInit @RetainedWith private transient @Nullable Converter reverse; /** Constructor for use by subclasses. */ protected Converter() { @@ -189,14 +191,11 @@ protected Converter() { * * @return the converted value; is null if and only if {@code a} is null */ - @CanIgnoreReturnValue - @CheckForNull - public final B convert(@CheckForNull A a) { + public final @Nullable B convert(@Nullable A a) { return correctedDoForward(a); } - @CheckForNull - B correctedDoForward(@CheckForNull A a) { + @Nullable B correctedDoForward(@Nullable A a) { if (handleNullAutomatically) { // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? return a == null ? null : checkNotNull(doForward(a)); @@ -205,8 +204,7 @@ B correctedDoForward(@CheckForNull A a) { } } - @CheckForNull - A correctedDoBackward(@CheckForNull B b) { + @Nullable A correctedDoBackward(@Nullable B b) { if (handleNullAutomatically) { // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? return b == null ? null : checkNotNull(doBackward(b)); @@ -241,13 +239,11 @@ A correctedDoBackward(@CheckForNull B b) { * LegacyConverter does violate the assumptions we make elsewhere. */ - @CheckForNull - private B unsafeDoForward(@CheckForNull A a) { + private @Nullable B unsafeDoForward(@Nullable A a) { return doForward(uncheckedCastNullableTToT(a)); } - @CheckForNull - private A unsafeDoBackward(@CheckForNull B b) { + private @Nullable A unsafeDoBackward(@Nullable B b) { return doBackward(uncheckedCastNullableTToT(b)); } @@ -259,7 +255,6 @@ private A unsafeDoBackward(@CheckForNull B b) { * a successful {@code remove()} call, {@code fromIterable} no longer contains the corresponding * element. */ - @CanIgnoreReturnValue /* * Just as Converter could implement `Function<@Nullable A, @Nullable B>` instead of `Function`, convertAll could accept and return iterables with nullable element types. In both cases, @@ -272,10 +267,8 @@ private A unsafeDoBackward(@CheckForNull B b) { */ public Iterable convertAll(Iterable fromIterable) { checkNotNull(fromIterable, "fromIterable"); - return new Iterable() { - @Override - public Iterator iterator() { - return new Iterator() { + return () -> + new Iterator() { private final Iterator fromIterator = fromIterable.iterator(); @Override @@ -284,8 +277,6 @@ public boolean hasNext() { } @Override - @SuppressWarnings("nullness") // See code comments on convertAll and Converter.apply. - @CheckForNull public B next() { return convert(fromIterator.next()); } @@ -295,8 +286,6 @@ public void remove() { fromIterator.remove(); } }; - } - }; } /** @@ -307,7 +296,7 @@ public void remove() { * *

    Note: you should not override this method. It is non-final for legacy reasons. */ - @CanIgnoreReturnValue + @CheckReturnValue public Converter reverse() { Converter result = reverse; return (result == null) ? reverse = new ReverseConverter<>(this) : result; @@ -339,14 +328,12 @@ protected B doBackward(A a) { } @Override - @CheckForNull - A correctedDoForward(@CheckForNull B b) { + @Nullable A correctedDoForward(@Nullable B b) { return original.correctedDoBackward(b); } @Override - @CheckForNull - B correctedDoBackward(@CheckForNull A a) { + @Nullable B correctedDoBackward(@Nullable A a) { return original.correctedDoForward(a); } @@ -356,7 +343,7 @@ public Converter reverse() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof ReverseConverter) { ReverseConverter that = (ReverseConverter) object; return this.original.equals(that.original); @@ -374,7 +361,7 @@ public String toString() { return original + ".reverse()"; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } /** @@ -421,19 +408,17 @@ protected A doBackward(C c) { } @Override - @CheckForNull - C correctedDoForward(@CheckForNull A a) { + @Nullable C correctedDoForward(@Nullable A a) { return second.correctedDoForward(first.correctedDoForward(a)); } @Override - @CheckForNull - A correctedDoBackward(@CheckForNull C c) { + @Nullable A correctedDoBackward(@Nullable C c) { return first.correctedDoBackward(second.correctedDoBackward(c)); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof ConverterComposition) { ConverterComposition that = (ConverterComposition) object; return this.first.equals(that.first) && this.second.equals(that.second); @@ -451,7 +436,7 @@ public String toString() { return first + ".andThen(" + second + ")"; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } /** @@ -459,40 +444,26 @@ public String toString() { */ @Deprecated @Override - @CanIgnoreReturnValue - /* - * Even though we implement `Function` instead of `Function<@Nullable A, @Nullable B>` (as - * discussed in a code comment at the top of the class), we declare our override of Function.apply - * to accept and return null. This requires a suppression, but it's safe: - * - * - Callers who use Converter as a Function will neither pass null nor have it returned to - * them. (Or, if they're not using nullness checking, they might be able to pass null and thus - * have null returned to them. But our signature isn't making their existing nullness type error - * any worse.) - * - In the relatively unlikely event that anyone calls Converter.apply directly, that caller is - * allowed to pass null but is also forced to deal with a potentially null return. - * - Perhaps more important than actual *callers* of this method are various tools that look at - * bytecode. Notably, NullPointerTester expects a method to throw NPE when passed null unless it - * is annotated in a way that identifies its parameter type as potentially including null. (And - * this method does not throw NPE -- nor do we want to enact a dangerous change to make it begin - * doing so.) We can even imagine tools that rewrite bytecode to insert null checks before and - * after calling methods with allegedly non-nullable parameters[*]. If we didn't annotate the - * parameter and return type here, then anyone who used such a tool (and managed to pass null to - * this method, presumably because that user doesn't run a normal nullness checker) could see - * NullPointerException. - * - * [*] Granted, such tools could conceivably be smart enough to recognize that the apply() method - * on a a Function should never allow null inputs and never produce null outputs even if - * this specific subclass claims otherwise. Such tools might still produce NPE for calls to this - * method. And that is one reason that we should be nervous about "lying" by extending Function in the first place. But for now, we're giving it a try, since extending Function<@Nullable - * A, @Nullable B> will cause issues *today*, whereas extending Function causes problems in - * various hypothetical futures. (Plus, a tool that were that smart would likely already introduce - * problems with LegacyConverter.) - */ - @SuppressWarnings("nullness") - @CheckForNull - public final B apply(@CheckForNull A a) { + @InlineMe(replacement = "this.convert(a)") + public final B apply(A a) { + /* + * Given that we declare this method as accepting and returning non-nullable values (because we + * implement Function, as discussed in a class-level comment), it would make some sense to + * perform runtime null checks on the input and output. (That would also make NullPointerTester + * happy!) However, since we didn't do that for many years, we're not about to start now. + * (Runtime checks could be particularly bad for users of LegacyConverter.) + * + * Luckily, our nullness checker is smart enough to realize that `convert` has @PolyNull-like + * behavior, so it knows that `convert(a)` returns a non-nullable value, and we don't need to + * perform even a cast, much less a runtime check. + * + * All that said, don't forget that everyone should call converter.convert() instead of + * converter.apply(), anyway. If clients use only converter.convert(), then their nullness + * checkers are unlikely to ever look at the annotations on this declaration. + * + * Historical note: At one point, we'd declared this method as accepting and returning nullable + * values. For details on that, see earlier revisions of this file. + */ return convert(a); } @@ -508,7 +479,7 @@ public final B apply(@CheckForNull A a) { * interchangeable. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return super.equals(object); } @@ -557,7 +528,7 @@ protected A doBackward(B b) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof FunctionBasedConverter) { FunctionBasedConverter that = (FunctionBasedConverter) object; return this.forwardFunction.equals(that.forwardFunction) @@ -588,7 +559,7 @@ public static Converter identity() { * "pass-through type". */ private static final class IdentityConverter extends Converter implements Serializable { - static final IdentityConverter INSTANCE = new IdentityConverter<>(); + static final Converter INSTANCE = new IdentityConverter<>(); @Override protected T doForward(T t) { @@ -624,6 +595,6 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } } diff --git a/android/guava/src/com/google/common/base/Defaults.java b/android/guava/src/com/google/common/base/Defaults.java index 5d12343ed241..8105badc59b3 100644 --- a/android/guava/src/com/google/common/base/Defaults.java +++ b/android/guava/src/com/google/common/base/Defaults.java @@ -17,7 +17,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * This class provides default values for all Java types, as defined by the JLS. @@ -25,8 +26,8 @@ * @author Ben Yu * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Defaults { private Defaults() {} @@ -39,8 +40,7 @@ private Defaults() {} * {@code void}, {@code null} is returned. */ @SuppressWarnings("unchecked") - @CheckForNull - public static T defaultValue(Class type) { + public static @Nullable T defaultValue(Class type) { checkNotNull(type); if (type.isPrimitive()) { if (type == boolean.class) { diff --git a/android/guava/src/com/google/common/base/Enums.java b/android/guava/src/com/google/common/base/Enums.java index 5c55b65942cd..02bb3efb50e3 100644 --- a/android/guava/src/com/google/common/base/Enums.java +++ b/android/guava/src/com/google/common/base/Enums.java @@ -16,8 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; @@ -25,7 +25,7 @@ import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Utility methods for working with {@link Enum} instances. @@ -33,8 +33,8 @@ * @author Steve McKay * @since 9.0 */ -@GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@GwtIncompatible +@J2ktIncompatible public final class Enums { private Enums() {} @@ -48,7 +48,8 @@ private Enums() {} */ @GwtIncompatible // reflection public static Field getField(Enum enumValue) { - Class clazz = enumValue.getDeclaringClass(); + Class + clazz = enumValue.getDeclaringClass(); try { return clazz.getDeclaredField(enumValue.name()); } catch (NoSuchFieldException impossible) { @@ -98,17 +99,19 @@ static > Map>> getEnum } /** - * Returns a converter that converts between strings and {@code enum} values of type {@code - * enumClass} using {@link Enum#valueOf(Class, String)} and {@link Enum#name()}. The converter - * will throw an {@code IllegalArgumentException} if the argument is not the name of any enum - * constant in the specified enum. + * Returns a serializable converter that converts between strings and {@code enum} values of type + * {@code enumClass} using {@link Enum#valueOf(Class, String)} and {@link Enum#name()}. The + * converter will throw an {@code IllegalArgumentException} if the argument is not the name of any + * enum constant in the specified enum. * * @since 16.0 */ + @GwtIncompatible public static > Converter stringConverter(Class enumClass) { return new StringConverter<>(enumClass); } + @GwtIncompatible private static final class StringConverter> extends Converter implements Serializable { @@ -129,7 +132,7 @@ protected String doBackward(T enumValue) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof StringConverter) { StringConverter that = (StringConverter) object; return this.enumClass.equals(that.enumClass); @@ -147,6 +150,6 @@ public String toString() { return "Enums.stringConverter(" + enumClass.getName() + ".class)"; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } } diff --git a/android/guava/src/com/google/common/base/Equivalence.java b/android/guava/src/com/google/common/base/Equivalence.java index 0ce901feb290..97c6c13739e1 100644 --- a/android/guava/src/com/google/common/base/Equivalence.java +++ b/android/guava/src/com/google/common/base/Equivalence.java @@ -17,10 +17,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.ForOverride; import java.io.Serializable; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A strategy for determining whether two instances are considered equivalent, and for computing @@ -39,7 +41,6 @@ * source-compatible since 4.0) */ @GwtCompatible -@ElementTypesAreNonnullByDefault /* * The type parameter is rather than so that we can use T in the * doEquivalent and doHash methods to indicate that the parameter cannot be null. @@ -65,7 +66,7 @@ protected Equivalence() {} *

    Note that all calls to {@code equivalent(x, y)} are expected to return the same result as * long as neither {@code x} nor {@code y} is modified. */ - public final boolean equivalent(@CheckForNull T a, @CheckForNull T b) { + public final boolean equivalent(@Nullable T a, @Nullable T b) { if (a == b) { return true; } @@ -76,7 +77,6 @@ public final boolean equivalent(@CheckForNull T a, @CheckForNull T b) { } /** - * * @since 10.0 (previously, subclasses would override equivalent()) */ @ForOverride @@ -99,7 +99,7 @@ public final boolean equivalent(@CheckForNull T a, @CheckForNull T b) { *

  • {@code hash(null)} is {@code 0}. * */ - public final int hash(@CheckForNull T t) { + public final int hash(@Nullable T t) { if (t == null) { return 0; } @@ -150,10 +150,13 @@ public final Equivalence onResultOf(FunctionThe returned object is serializable if both this {@code Equivalence} and {@code reference} + * are serializable (including when {@code reference} is null). + * * @since 10.0 */ public final Wrapper wrap(@ParametricNullness S reference) { - return new Wrapper(this, reference); + return new Wrapper<>(this, reference); } /** @@ -177,10 +180,19 @@ public final Equivalence onResultOf(Function implements Serializable { - private final Equivalence equivalence; + /* + * Equivalence's type argument is always non-nullable: Equivalence, never + * Equivalence<@Nullable Number>. That can still produce wrappers of various types -- + * Wrapper, Wrapper, Wrapper<@Nullable Integer>, etc. If we used just + * Equivalence below, no type could satisfy both that bound and T's own + * bound. With this type, they have some overlap: in our example, Equivalence + * and Equivalence. + */ + private final Equivalence equivalence; + @ParametricNullness private final T reference; - private Wrapper(Equivalence equivalence, @ParametricNullness T reference) { + private Wrapper(Equivalence equivalence, @ParametricNullness T reference) { this.equivalence = checkNotNull(equivalence); this.reference = reference; } @@ -197,7 +209,7 @@ public T get() { * equivalence. */ @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -232,7 +244,7 @@ public String toString() { return equivalence + ".wrap(" + reference + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -244,6 +256,8 @@ public String toString() { *

    Note that this method performs a similar function for equivalences as {@link * com.google.common.collect.Ordering#lexicographical} does for orderings. * + *

    The returned object is serializable if this object is serializable. + * * @since 10.0 */ @GwtCompatible(serializable = true) @@ -259,28 +273,28 @@ public String toString() { * * @since 10.0 */ - public final Predicate<@Nullable T> equivalentTo(@CheckForNull T target) { - return new EquivalentToPredicate(this, target); + public final Predicate<@Nullable T> equivalentTo(@Nullable T target) { + return new EquivalentToPredicate<>(this, target); } private static final class EquivalentToPredicate implements Predicate<@Nullable T>, Serializable { private final Equivalence equivalence; - @CheckForNull private final T target; + private final @Nullable T target; - EquivalentToPredicate(Equivalence equivalence, @CheckForNull T target) { + EquivalentToPredicate(Equivalence equivalence, @Nullable T target) { this.equivalence = checkNotNull(equivalence); this.target = target; } @Override - public boolean apply(@CheckForNull T input) { + public boolean apply(@Nullable T input) { return equivalence.equivalent(input, target); } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -301,7 +315,7 @@ public String toString() { return equivalence + ".equivalentTo(" + target + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -348,7 +362,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } static final class Identity extends Equivalence implements Serializable { @@ -369,6 +383,6 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } } diff --git a/android/guava/src/com/google/common/base/ExtraObjectsMethodsForWeb.java b/android/guava/src/com/google/common/base/ExtraObjectsMethodsForWeb.java index 677075522028..21cca2c109d6 100644 --- a/android/guava/src/com/google/common/base/ExtraObjectsMethodsForWeb.java +++ b/android/guava/src/com/google/common/base/ExtraObjectsMethodsForWeb.java @@ -21,5 +21,4 @@ * version. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ExtraObjectsMethodsForWeb {} diff --git a/android/guava/src/com/google/common/base/FinalizablePhantomReference.java b/android/guava/src/com/google/common/base/FinalizablePhantomReference.java index 4f9399669569..89b600f4fa13 100644 --- a/android/guava/src/com/google/common/base/FinalizablePhantomReference.java +++ b/android/guava/src/com/google/common/base/FinalizablePhantomReference.java @@ -15,9 +15,10 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Phantom reference with a {@code finalizeReferent()} method which a background thread invokes @@ -29,8 +30,8 @@ * @author Bob Lee * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class FinalizablePhantomReference extends PhantomReference implements FinalizableReference { /** @@ -39,7 +40,7 @@ public abstract class FinalizablePhantomReference extends PhantomReference * @param referent to phantom reference * @param queue that should finalize the referent */ - protected FinalizablePhantomReference(@CheckForNull T referent, FinalizableReferenceQueue queue) { + protected FinalizablePhantomReference(@Nullable T referent, FinalizableReferenceQueue queue) { super(referent, queue.queue); queue.cleanUp(); } diff --git a/android/guava/src/com/google/common/base/FinalizableReference.java b/android/guava/src/com/google/common/base/FinalizableReference.java index 73753c9b3520..d7e91e46e0ee 100644 --- a/android/guava/src/com/google/common/base/FinalizableReference.java +++ b/android/guava/src/com/google/common/base/FinalizableReference.java @@ -15,6 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.DoNotMock; /** @@ -25,8 +26,8 @@ * @since 2.0 */ @DoNotMock("Use an instance of one of the Finalizable*Reference classes") +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface FinalizableReference { /** * Invoked on a background thread after the referent has been garbage collected unless security diff --git a/android/guava/src/com/google/common/base/FinalizableReferenceQueue.java b/android/guava/src/com/google/common/base/FinalizableReferenceQueue.java index 7447b8051d31..e6059dc280b6 100644 --- a/android/guava/src/com/google/common/base/FinalizableReferenceQueue.java +++ b/android/guava/src/com/google/common/base/FinalizableReferenceQueue.java @@ -15,6 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.io.Closeable; import java.io.FileNotFoundException; @@ -27,11 +28,12 @@ import java.net.URLClassLoader; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A reference queue with an associated background thread that dequeues references and invokes - * {@link FinalizableReference#finalizeReferent()} on them. + * {@link FinalizableReference#finalizeReferent()} on them. Java 9+ users should prefer {@link + * java.lang.ref.Cleaner Cleaner}; see example below. * *

    Keep a strong reference to this object until all of the associated referents have been * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code @@ -61,8 +63,9 @@ * * public static MyServer create(...) { * MyServer myServer = new MyServer(...); - * final ServerSocket serverSocket = myServer.serverSocket; + * ServerSocket serverSocket = myServer.serverSocket; * Reference reference = new FinalizablePhantomReference(myServer, frq) { + * @Override * public void finalizeReferent() { * references.remove(this): * if (!serverSocket.isClosed()) { @@ -79,17 +82,62 @@ * return myServer; * } * - * public void close() { + * @Override + * public void close() throws IOException { * serverSocket.close(); * } * } * } * + *

    Here is how you might achieve the same thing using {@link java.lang.ref.Cleaner + * Cleaner}, if you are using a Java version where that is available: + * + *

    {@code
    + * public class MyServer implements Closeable {
    + *   private static final Cleaner cleaner = Cleaner.create();
    + *   // You might also share this between several objects.
    + *
    + *   private final ServerSocket serverSocket;
    + *   private final Cleaner.Cleanable cleanable;
    + *
    + *   public MyServer(...) {
    + *     ...
    + *     this.serverSocket = new ServerSocket(...);
    + *     this.cleanable = cleaner.register(this, closeServerSocketRunnable(serverSocket));
    + *     ...
    + *   }
    + *
    + *   private static Runnable closeServerSocketRunnable(ServerSocket serverSocket) {
    + *     return () -> {
    + *       if (!serverSocket.isClosed()) {
    + *         ...log a message about how nobody called close()...
    + *         try {
    + *           serverSocket.close();
    + *         } catch (IOException e) {
    + *           ...
    + *         }
    + *       }
    + *     };
    + *   }
    + *
    + *   @Override
    + *   public void close() throws IOException {
    + *     serverSocket.close();
    + *     cleanable.clean();
    + *   }
    + * }
    + * }
    + * + *

    Some care is needed when using {@code Cleaner} to ensure that the callback passed to {@code + * register} does not have a reference to the object (in this case, {@code MyServer}) that may be + * garbage-collected. That's why we are careful to make a {@code Runnable} that does not have a + * reference to any {@code MyServer} instance. + * * @author Bob Lee * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public class FinalizableReferenceQueue implements Closeable { /* * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a @@ -229,8 +277,7 @@ interface FinalizerLoader { * * @throws SecurityException if we don't have the appropriate privileges */ - @CheckForNull - Class loadFinalizer(); + @Nullable Class loadFinalizer(); } /** @@ -243,8 +290,7 @@ static class SystemLoader implements FinalizerLoader { @VisibleForTesting static boolean disabled; @Override - @CheckForNull - public Class loadFinalizer() { + public @Nullable Class loadFinalizer() { if (disabled) { return null; } @@ -281,8 +327,7 @@ static class DecoupledLoader implements FinalizerLoader { + "issue, or move Guava to your system class path."; @Override - @CheckForNull - public Class loadFinalizer() { + public @Nullable Class loadFinalizer() { try { /* * We use URLClassLoader because it's the only concrete class loader implementation in the diff --git a/android/guava/src/com/google/common/base/FinalizableSoftReference.java b/android/guava/src/com/google/common/base/FinalizableSoftReference.java index c0e9b6bae041..c4f6baa3c7de 100644 --- a/android/guava/src/com/google/common/base/FinalizableSoftReference.java +++ b/android/guava/src/com/google/common/base/FinalizableSoftReference.java @@ -15,9 +15,10 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Soft reference with a {@code finalizeReferent()} method which a background thread invokes after @@ -27,8 +28,8 @@ * @author Bob Lee * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class FinalizableSoftReference extends SoftReference implements FinalizableReference { /** @@ -37,7 +38,7 @@ public abstract class FinalizableSoftReference extends SoftReference * @param referent to softly reference * @param queue that should finalize the referent */ - protected FinalizableSoftReference(@CheckForNull T referent, FinalizableReferenceQueue queue) { + protected FinalizableSoftReference(@Nullable T referent, FinalizableReferenceQueue queue) { super(referent, queue.queue); queue.cleanUp(); } diff --git a/android/guava/src/com/google/common/base/FinalizableWeakReference.java b/android/guava/src/com/google/common/base/FinalizableWeakReference.java index 9cca92ed5310..aeea7c7f8508 100644 --- a/android/guava/src/com/google/common/base/FinalizableWeakReference.java +++ b/android/guava/src/com/google/common/base/FinalizableWeakReference.java @@ -15,9 +15,10 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Weak reference with a {@code finalizeReferent()} method which a background thread invokes after @@ -27,8 +28,8 @@ * @author Bob Lee * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class FinalizableWeakReference extends WeakReference implements FinalizableReference { /** @@ -37,7 +38,7 @@ public abstract class FinalizableWeakReference extends WeakReference * @param referent to weakly reference * @param queue that should finalize the referent */ - protected FinalizableWeakReference(@CheckForNull T referent, FinalizableReferenceQueue queue) { + protected FinalizableWeakReference(@Nullable T referent, FinalizableReferenceQueue queue) { super(referent, queue.queue); queue.cleanUp(); } diff --git a/android/guava/src/com/google/common/base/Function.java b/android/guava/src/com/google/common/base/Function.java index 7d633af21c61..ef0780eb380c 100644 --- a/android/guava/src/com/google/common/base/Function.java +++ b/android/guava/src/com/google/common/base/Function.java @@ -15,9 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Determines an output value based on an input value; a pre-Java-8 version of {@link @@ -45,7 +43,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Function { /** * Returns the result of applying this function to {@code input}. This method is generally @@ -61,7 +58,6 @@ public interface Function extends Equivalence implements Serializable { - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; - private final Function function; + private final Function function; private final Equivalence resultEquivalence; FunctionalEquivalence( - Function function, Equivalence resultEquivalence) { + Function function, Equivalence resultEquivalence) { this.function = checkNotNull(function); this.resultEquivalence = checkNotNull(resultEquivalence); } @@ -54,7 +53,7 @@ protected int doHash(F a) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/android/guava/src/com/google/common/base/Functions.java b/android/guava/src/com/google/common/base/Functions.java index 78785047eb82..f67859fcf59c 100644 --- a/android/guava/src/com/google/common/base/Functions.java +++ b/android/guava/src/com/google/common/base/Functions.java @@ -19,10 +19,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code com.google.common.base.Function} instances; see that @@ -38,7 +39,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Functions { private Functions() {} @@ -55,9 +55,9 @@ private Functions() {} * {@code equals}, {@code hashCode} or {@code toString} behavior of the returned function. A * future migration to {@code java.util.function} will not preserve this behavior. * - *

    For Java 8 users: use the method reference {@code Object::toString} instead. In the + *

    Java 8+ users: use the method reference {@code Object::toString} instead. In the * future, when this class requires Java 8, this method will be deprecated. See {@link Function} - * for more important information about the Java 8 transition. + * for more important information about the Java 8+ transition. */ public static Function toStringFunction() { return ToStringFunction.INSTANCE; @@ -79,7 +79,12 @@ public String toString() { } } - /** Returns the identity function. */ + /** + * Returns the identity function. + * + *

    Discouraged: Prefer using a lambda like {@code v -> v}, which is shorter and often + * more readable. + */ // implementation is "fully variant"; E has become a "pass-through" type @SuppressWarnings("unchecked") public static Function identity() { @@ -91,8 +96,7 @@ private enum IdentityFunction implements Function<@Nullable Object, @Nullable Ob INSTANCE; @Override - @CheckForNull - public Object apply(@CheckForNull Object o) { + public @Nullable Object apply(@Nullable Object o) { return o; } @@ -111,7 +115,7 @@ public String toString() { * can use {@link com.google.common.collect.Maps#asConverter Maps.asConverter} instead to get a * function that also supports reverse conversion. * - *

    Java 8 users: if you are okay with {@code null} being returned for an unrecognized + *

    Java 8+ users: if you are okay with {@code null} being returned for an unrecognized * key (instead of an exception being thrown), you can use the method reference {@code map::get} * instead. */ @@ -125,7 +129,7 @@ public String toString() { * this method returns {@code defaultValue} for all inputs that do not belong to the map's key * set. See also {@link #forMap(Map)}, which throws an exception in this case. * - *

    Java 8 users: you can just write the lambda expression {@code k -> + *

    Java 8+ users: you can just write the lambda expression {@code k -> * map.getOrDefault(k, defaultValue)} instead. * * @param map source map that determines the function behavior @@ -157,7 +161,7 @@ public V apply(@ParametricNullness K key) { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof FunctionForMapNoDefault) { FunctionForMapNoDefault that = (FunctionForMapNoDefault) o; return map.equals(that.map); @@ -175,7 +179,7 @@ public String toString() { return "Functions.forMap(" + map + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static class ForMapWithDefault @@ -199,7 +203,7 @@ public V apply(@ParametricNullness K key) { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof ForMapWithDefault) { ForMapWithDefault that = (ForMapWithDefault) o; return map.equals(that.map) && Objects.equal(defaultValue, that.defaultValue); @@ -218,14 +222,14 @@ public String toString() { return "Functions.forMap(" + map + ", defaultValue=" + defaultValue + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns the composition of two functions. For {@code f: A->B} and {@code g: B->C}, composition * is defined as the function h such that {@code h(a) == g(f(a))} for each {@code a}. * - *

    Java 8 users: use {@code g.compose(f)} or (probably clearer) {@code f.andThen(g)} + *

    Java 8+ users: use {@code g.compose(f)} or (probably clearer) {@code f.andThen(g)} * instead. * * @param g the second function to apply @@ -256,7 +260,7 @@ public C apply(@ParametricNullness A a) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof FunctionComposition) { FunctionComposition that = (FunctionComposition) obj; return f.equals(that.f) && g.equals(that.g); @@ -275,7 +279,7 @@ public String toString() { return g + "(" + f + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -284,14 +288,16 @@ public String toString() { *

    The returned function is consistent with equals (as documented at {@link * Function#apply}) if and only if {@code predicate} is itself consistent with equals. * - *

    Java 8 users: use the method reference {@code predicate::test} instead. + *

    Java 8+ users: use the method reference {@code predicate::test} instead. */ public static Function forPredicate( Predicate predicate) { return new PredicateFunction<>(predicate); } - /** @see Functions#forPredicate */ + /** + * @see Functions#forPredicate + */ private static class PredicateFunction implements Function, Serializable { private final Predicate predicate; @@ -306,7 +312,7 @@ public Boolean apply(@ParametricNullness T t) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof PredicateFunction) { PredicateFunction that = (PredicateFunction) obj; return predicate.equals(that.predicate); @@ -324,13 +330,13 @@ public String toString() { return "Functions.forPredicate(" + predicate + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns a function that ignores its input and always returns {@code value}. * - *

    Java 8 users: use the lambda expression {@code o -> value} instead. + *

    Java 8+ users: use the lambda expression {@code o -> value} instead. * * @param value the constant value for the function to return * @return a function that always returns {@code value} @@ -350,12 +356,12 @@ public ConstantFunction(@ParametricNullness E value) { @Override @ParametricNullness - public E apply(@CheckForNull Object from) { + public E apply(@Nullable Object from) { return value; } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof ConstantFunction) { ConstantFunction that = (ConstantFunction) obj; return Objects.equal(value, that.value); @@ -373,13 +379,13 @@ public String toString() { return "Functions.constant(" + value + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns a function that ignores its input and returns the result of {@code supplier.get()}. * - *

    Java 8 users: use the lambda expression {@code o -> supplier.get()} instead. + *

    Java 8+ users: use the lambda expression {@code o -> supplier.get()} instead. * * @since 10.0 */ @@ -388,7 +394,9 @@ public String toString() { return new SupplierFunction<>(supplier); } - /** @see Functions#forSupplier */ + /** + * @see Functions#forSupplier + */ private static class SupplierFunction implements Function, Serializable { @@ -405,7 +413,7 @@ public T apply(@ParametricNullness F input) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof SupplierFunction) { SupplierFunction that = (SupplierFunction) obj; return this.supplier.equals(that.supplier); @@ -423,6 +431,6 @@ public String toString() { return "Functions.forSupplier(" + supplier + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/base/IgnoreJRERequirement.java b/android/guava/src/com/google/common/base/IgnoreJRERequirement.java new file mode 100644 index 000000000000..4d6cfd48da94 --- /dev/null +++ b/android/guava/src/com/google/common/base/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.base; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/base/Internal.java b/android/guava/src/com/google/common/base/Internal.java new file mode 100644 index 000000000000..648d1c40638e --- /dev/null +++ b/android/guava/src/com/google/common/base/Internal.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.time.Duration; + +/** This class is for {@code com.google.common.base} use only! */ +@J2ktIncompatible +@GwtIncompatible // java.time.Duration +final class Internal { + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

    Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + @SuppressWarnings({ + // We use this method only for cases in which we need to decompose to primitives. + "GoodTime-ApiWithNumericTimeUnit", + "GoodTime-DecomposeToPrimitive", + // We use this method only from within APIs that require a Duration. + "Java7ApiChecker", + }) + @IgnoreJRERequirement + static long toNanosSaturated(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } + + private Internal() {} +} diff --git a/android/guava/src/com/google/common/base/Java8Compatibility.java b/android/guava/src/com/google/common/base/Java8Compatibility.java index edc8b73bdd10..d3ee13968bc2 100644 --- a/android/guava/src/com/google/common/base/Java8Compatibility.java +++ b/android/guava/src/com/google/common/base/Java8Compatibility.java @@ -15,14 +15,15 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.nio.Buffer; /** * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See * https://github.com/google/guava/issues/3990 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class Java8Compatibility { static void clear(Buffer b) { b.clear(); diff --git a/android/guava/src/com/google/common/base/JdkPattern.java b/android/guava/src/com/google/common/base/JdkPattern.java index 4788398b7c20..66bf460e8bdc 100644 --- a/android/guava/src/com/google/common/base/JdkPattern.java +++ b/android/guava/src/com/google/common/base/JdkPattern.java @@ -15,12 +15,12 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; /** A regex pattern implementation which is backed by the {@link Pattern}. */ -@ElementTypesAreNonnullByDefault @GwtIncompatible final class JdkPattern extends CommonPattern implements Serializable { private final Pattern pattern; @@ -87,5 +87,5 @@ public int start() { } } - private static final long serialVersionUID = 0; + @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/base/Joiner.java b/android/guava/src/com/google/common/base/Joiner.java index 8b29f68f8ad1..7f95b591907f 100644 --- a/android/guava/src/com/google/common/base/Joiner.java +++ b/android/guava/src/com/google/common/base/Joiner.java @@ -17,17 +17,16 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.AbstractList; import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a @@ -65,7 +64,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public class Joiner { /** Returns a joiner which automatically places {@code separator} between consecutive elements. */ public static Joiner on(String separator) { @@ -87,19 +85,12 @@ private Joiner(Joiner prototype) { this.separator = prototype.separator; } - /* - * In this file, we use instead of to work around a Kotlin bug - * (see b/189937072 until we file a bug against Kotlin itself). (The two should be equivalent, so - * we normally prefer the shorter one.) - */ - /** * Appends the string representation of each of {@code parts}, using the previously configured * separator between each, to {@code appendable}. */ @CanIgnoreReturnValue - public A appendTo(A appendable, Iterable parts) - throws IOException { + public A appendTo(A appendable, Iterable parts) throws IOException { return appendTo(appendable, parts.iterator()); } @@ -110,8 +101,7 @@ public A appendTo(A appendable, Iterable A appendTo(A appendable, Iterator parts) - throws IOException { + public A appendTo(A appendable, Iterator parts) throws IOException { checkNotNull(appendable); if (parts.hasNext()) { appendable.append(toString(parts.next())); @@ -130,16 +120,15 @@ public A appendTo(A appendable, Iterator A appendTo(A appendable, @Nullable Object[] parts) throws IOException { - return appendTo(appendable, Arrays.asList(parts)); + @SuppressWarnings("nullness") // TODO: b/316358623 - Remove suppression after fixing checker + List partsList = Arrays.<@Nullable Object>asList(parts); + return appendTo(appendable, partsList); } /** Appends to {@code appendable} the string representation of each of the remaining arguments. */ @CanIgnoreReturnValue public final A appendTo( - A appendable, - @CheckForNull Object first, - @CheckForNull Object second, - @Nullable Object... rest) + A appendable, @Nullable Object first, @Nullable Object second, @Nullable Object... rest) throws IOException { return appendTo(appendable, iterable(first, second, rest)); } @@ -150,8 +139,7 @@ public final A appendTo( * Iterable)}, except that it does not throw {@link IOException}. */ @CanIgnoreReturnValue - public final StringBuilder appendTo( - StringBuilder builder, Iterable parts) { + public final StringBuilder appendTo(StringBuilder builder, Iterable parts) { return appendTo(builder, parts.iterator()); } @@ -163,8 +151,7 @@ public final StringBuilder appendTo( * @since 11.0 */ @CanIgnoreReturnValue - public final StringBuilder appendTo( - StringBuilder builder, Iterator parts) { + public final StringBuilder appendTo(StringBuilder builder, Iterator parts) { try { appendTo((Appendable) builder, parts); } catch (IOException impossible) { @@ -180,7 +167,9 @@ public final StringBuilder appendTo( */ @CanIgnoreReturnValue public final StringBuilder appendTo(StringBuilder builder, @Nullable Object[] parts) { - return appendTo(builder, Arrays.asList(parts)); + @SuppressWarnings("nullness") // TODO: b/316358623 - Remove suppression after fixing checker + List partsList = Arrays.<@Nullable Object>asList(parts); + return appendTo(builder, partsList); } /** @@ -191,8 +180,8 @@ public final StringBuilder appendTo(StringBuilder builder, @Nullable Object[] pa @CanIgnoreReturnValue public final StringBuilder appendTo( StringBuilder builder, - @CheckForNull Object first, - @CheckForNull Object second, + @Nullable Object first, + @Nullable Object second, @Nullable Object... rest) { return appendTo(builder, iterable(first, second, rest)); } @@ -201,17 +190,27 @@ public final StringBuilder appendTo( * Returns a string containing the string representation of each of {@code parts}, using the * previously configured separator between each. */ - public final String join(Iterable parts) { + public String join(Iterable parts) { + // We don't use the same optimization here as in the JRE flavor. + // TODO: b/381289911 - Evaluate the performance impact of doing so. return join(parts.iterator()); } + /* + * TODO: b/381289911 - Make the Iterator overload use StringJoiner (including Android or not)—or + * some other optimization, given that StringJoiner can over-allocate: + * https://bugs.openjdk.org/browse/JDK-8305774 + */ + + // TODO: b/381289911 - Optimize MapJoiner similarly to Joiner (including Android or not). + /** * Returns a string containing the string representation of each of {@code parts}, using the * previously configured separator between each. * * @since 11.0 */ - public final String join(Iterator parts) { + public final String join(Iterator parts) { return appendTo(new StringBuilder(), parts).toString(); } @@ -220,7 +219,9 @@ public final String join(Iterator parts) { * previously configured separator between each. */ public final String join(@Nullable Object[] parts) { - return join(Arrays.asList(parts)); + @SuppressWarnings("nullness") // TODO: b/316358623 - Remove suppression after fixing checker + List partsList = Arrays.<@Nullable Object>asList(parts); + return join(partsList); } /** @@ -228,7 +229,7 @@ public final String join(@Nullable Object[] parts) { * configured separator between each. */ public final String join( - @CheckForNull Object first, @CheckForNull Object second, @Nullable Object... rest) { + @Nullable Object first, @Nullable Object second, @Nullable Object... rest) { return join(iterable(first, second, rest)); } @@ -240,7 +241,7 @@ public Joiner useForNull(String nullText) { checkNotNull(nullText); return new Joiner(this) { @Override - CharSequence toString(@CheckForNull Object part) { + CharSequence toString(@Nullable Object part) { return (part == null) ? nullText : Joiner.this.toString(part); } @@ -263,8 +264,13 @@ public Joiner skipNulls() { public Joiner skipNulls() { return new Joiner(this) { @Override - public A appendTo( - A appendable, Iterator parts) throws IOException { + @SuppressWarnings("JoinIterableIterator") // suggests infinite recursion + public String join(Iterable parts) { + return join(parts.iterator()); + } + + @Override + public A appendTo(A appendable, Iterator parts) throws IOException { checkNotNull(appendable, "appendable"); checkNotNull(parts, "parts"); while (parts.hasNext()) { @@ -366,7 +372,6 @@ public StringBuilder appendTo(StringBuilder builder, Map map) { * * @since 10.0 */ - @Beta @CanIgnoreReturnValue public A appendTo(A appendable, Iterable> entries) throws IOException { @@ -379,7 +384,6 @@ public A appendTo(A appendable, Iterable A appendTo(A appendable, Iterator> parts) throws IOException { @@ -407,7 +411,6 @@ public A appendTo(A appendable, Iterator> entries) { return appendTo(builder, entries.iterator()); @@ -420,7 +423,6 @@ public StringBuilder appendTo(StringBuilder builder, Iterable> entries) { try { @@ -445,7 +447,6 @@ public String join(Map map) { * * @since 10.0 */ - @Beta public String join(Iterable> entries) { return join(entries.iterator()); } @@ -456,7 +457,6 @@ public String join(Iterable> entries) { * * @since 11.0 */ - @Beta public String join(Iterator> entries) { return appendTo(new StringBuilder(), entries).toString(); } @@ -470,7 +470,8 @@ public MapJoiner useForNull(String nullText) { } } - CharSequence toString(@CheckForNull Object part) { + // TODO(cpovirk): Rename to "toCharSequence." + CharSequence toString(@Nullable Object part) { /* * requireNonNull is not safe: Joiner.on(...).join(somethingThatContainsNull) will indeed throw. * However, Joiner.on(...).useForNull(...).join(somethingThatContainsNull) *is* safe -- because @@ -493,7 +494,7 @@ CharSequence toString(@CheckForNull Object part) { } private static Iterable<@Nullable Object> iterable( - @CheckForNull Object first, @CheckForNull Object second, @Nullable Object[] rest) { + @Nullable Object first, @Nullable Object second, @Nullable Object[] rest) { checkNotNull(rest); return new AbstractList<@Nullable Object>() { @Override @@ -502,8 +503,7 @@ public int size() { } @Override - @CheckForNull - public Object get(int index) { + public @Nullable Object get(int index) { switch (index) { case 0: return first; @@ -515,4 +515,23 @@ public Object get(int index) { } }; } + + // cloned from ImmutableCollection + private static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new IllegalArgumentException("cannot store more than Integer.MAX_VALUE elements"); + } else if (minCapacity <= oldCapacity) { + return oldCapacity; + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; + // guaranteed to be >= newCapacity + } + return newCapacity; + } } diff --git a/android/guava/src/com/google/common/base/MoreObjects.java b/android/guava/src/com/google/common/base/MoreObjects.java index da976242479b..38d549663383 100644 --- a/android/guava/src/com/google/common/base/MoreObjects.java +++ b/android/guava/src/com/google/common/base/MoreObjects.java @@ -22,7 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Helper functions that operate on any {@code Object}, and are not already provided in {@link @@ -36,7 +36,6 @@ * @since 18.0 (since 2.0 as {@code Objects}) */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class MoreObjects { /** * Returns the first of two given parameters that is not {@code null}, if either is, or otherwise @@ -58,24 +57,7 @@ public final class MoreObjects { * @throws NullPointerException if both {@code first} and {@code second} are null * @since 18.0 (since 3.0 as {@code Objects.firstNonNull()}). */ - /* - * We annotate firstNonNull in a way that protects against NullPointerException at the cost of - * forbidding some reasonable calls. - * - * The more permissive signature would be to accept (@CheckForNull T first, @CheckForNull T - * second), since it's OK for `second` to be null as long as `first` is not also null. But we - * expect for that flexibility to be useful relatively rarely: The more common use case is to - * supply a clearly non-null default, like `firstNonNull(someString, "")`. And users who really - * know that `first` is guaranteed non-null when `second` is null can write the logic out - * longhand, including a requireNonNull call, which calls attention to the fact that the static - * analyzer can't prove that the operation is safe. - * - * This matches the signature we currently have for requireNonNullElse in our own checker. (And - * that in turn matches that method's signature under the Checker Framework.) As always, we could - * consider the more flexible signature if we judge it worth the risks. If we do, we would likely - * update both methods so that they continue to match. - */ - public static T firstNonNull(@CheckForNull T first, T second) { + public static T firstNonNull(@Nullable T first, @Nullable T second) { if (first != null) { return first; } @@ -186,13 +168,31 @@ public ToStringHelper omitNullValues() { return this; } + /** + * Configures the {@link ToStringHelper} so {@link #toString()} will ignore properties with + * empty values. The order of calling this method, relative to the {@code add()}/{@code + * addValue()} methods, is not significant. + * + *

    Note: in general, code should assume that the string form returned by {@code + * ToStringHelper} for a given object may change. In particular, the list of types which are + * checked for emptiness is subject to change. We currently check {@code CharSequence}s, {@code + * Collection}s, {@code Map}s, optionals (including Guava's), and arrays. + * + * @since 33.4.0 + */ + @CanIgnoreReturnValue + public ToStringHelper omitEmptyValues() { + omitEmptyValues = true; + return this; + } + /** * Adds a name/value pair to the formatted output in {@code name=value} format. If {@code value} * is {@code null}, the string {@code "null"} is used, unless {@link #omitNullValues()} is * called, in which case this name/value pair will not be added. */ @CanIgnoreReturnValue - public ToStringHelper add(String name, @CheckForNull Object value) { + public ToStringHelper add(String name, @Nullable Object value) { return addHolder(name, value); } @@ -263,7 +263,7 @@ public ToStringHelper add(String name, long value) { * readable name. */ @CanIgnoreReturnValue - public ToStringHelper addValue(@CheckForNull Object value) { + public ToStringHelper addValue(@Nullable Object value) { return addHolder(value); } @@ -346,7 +346,7 @@ public ToStringHelper addValue(long value) { } private static boolean isEmpty(Object value) { - // Put types estimated to be most frequent first. + // Put types estimated to be the most frequent first. if (value instanceof CharSequence) { return ((CharSequence) value).length() == 0; } else if (value instanceof Collection) { @@ -408,13 +408,15 @@ private ValueHolder addHolder() { return valueHolder; } - private ToStringHelper addHolder(@CheckForNull Object value) { + @CanIgnoreReturnValue + private ToStringHelper addHolder(@Nullable Object value) { ValueHolder valueHolder = addHolder(); valueHolder.value = value; return this; } - private ToStringHelper addHolder(String name, @CheckForNull Object value) { + @CanIgnoreReturnValue + private ToStringHelper addHolder(String name, @Nullable Object value) { ValueHolder valueHolder = addHolder(); valueHolder.value = value; valueHolder.name = checkNotNull(name); @@ -427,12 +429,14 @@ private UnconditionalValueHolder addUnconditionalHolder() { return valueHolder; } + @CanIgnoreReturnValue private ToStringHelper addUnconditionalHolder(Object value) { UnconditionalValueHolder valueHolder = addUnconditionalHolder(); valueHolder.value = value; return this; } + @CanIgnoreReturnValue private ToStringHelper addUnconditionalHolder(String name, Object value) { UnconditionalValueHolder valueHolder = addUnconditionalHolder(); valueHolder.value = value; @@ -441,10 +445,10 @@ private ToStringHelper addUnconditionalHolder(String name, Object value) { } // Holder object for values that might be null and/or empty. - private static class ValueHolder { - @CheckForNull String name; - @CheckForNull Object value; - @CheckForNull ValueHolder next; + static class ValueHolder { + @Nullable String name; + @Nullable Object value; + @Nullable ValueHolder next; } /** diff --git a/android/guava/src/com/google/common/base/NullnessCasts.java b/android/guava/src/com/google/common/base/NullnessCasts.java index 1ada6bf26148..5c99948ad47c 100644 --- a/android/guava/src/com/google/common/base/NullnessCasts.java +++ b/android/guava/src/com/google/common/base/NullnessCasts.java @@ -15,12 +15,10 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class NullnessCasts { /** * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that @@ -52,7 +50,7 @@ final class NullnessCasts { */ @ParametricNullness @SuppressWarnings("nullness") - static T uncheckedCastNullableTToT(@CheckForNull T t) { + static T uncheckedCastNullableTToT(@Nullable T t) { return t; } diff --git a/android/guava/src/com/google/common/base/Objects.java b/android/guava/src/com/google/common/base/Objects.java index bd6b0d94c5f0..114520c51d4b 100644 --- a/android/guava/src/com/google/common/base/Objects.java +++ b/android/guava/src/com/google/common/base/Objects.java @@ -16,8 +16,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Arrays; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Helper functions that can operate on any {@code Object}. @@ -30,7 +29,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Objects extends ExtraObjectsMethodsForWeb { private Objects() {} @@ -47,10 +45,10 @@ private Objects() {} *

    This assumes that any non-null objects passed to this function conform to the {@code * equals()} contract. * - *

    Note for Java 7 and later: This method should be treated as deprecated; use {@link + *

    Note: this method is now unnecessary and should be treated as deprecated; use {@link * java.util.Objects#equals} instead. */ - public static boolean equal(@CheckForNull Object a, @CheckForNull Object b) { + public static boolean equal(@Nullable Object a, @Nullable Object b) { return a == b || (a != null && a.equals(b)); } @@ -72,10 +70,10 @@ public static boolean equal(@CheckForNull Object a, @CheckForNull Object b) { *

    Warning: When a single object is supplied, the returned hash code does not equal the * hash code of that object. * - *

    Note for Java 7 and later: This method should be treated as deprecated; use {@link + *

    Note: this method is now unnecessary and should be treated as deprecated; use {@link * java.util.Objects#hash} instead. */ - public static int hashCode(@CheckForNull @Nullable Object... objects) { + public static int hashCode(@Nullable Object @Nullable ... objects) { return Arrays.hashCode(objects); } } diff --git a/android/guava/src/com/google/common/base/Optional.java b/android/guava/src/com/google/common/base/Optional.java index a7a0b3d72038..af97f4eacffa 100644 --- a/android/guava/src/com/google/common/base/Optional.java +++ b/android/guava/src/com/google/common/base/Optional.java @@ -16,13 +16,14 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.DoNotMock; import java.io.Serializable; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable object that may contain a non-null reference to another object. Each instance of @@ -52,6 +53,9 @@ *

    This class is not intended as a direct analogue of any existing "option" or "maybe" construct * from other programming environments, though it may bear some similarities. * + *

    An instance of this class is serializable if its reference is absent or is a serializable + * object. + * *

    Comparison to {@code java.util.Optional} (JDK 8 and higher): A new {@code Optional} * class was added for Java 8. The two classes are extremely similar, but incompatible (they cannot * share a common supertype). All known differences are listed either here or with the @@ -82,7 +86,6 @@ */ @DoNotMock("Use Optional.of(value) or Optional.absent()") @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault public abstract class Optional implements Serializable { /** * Returns an {@code Optional} instance with no contained reference. @@ -103,7 +106,7 @@ public static Optional absent() { * @throws NullPointerException if {@code reference} is null */ public static Optional of(T reference) { - return new Present(checkNotNull(reference)); + return new Present<>(checkNotNull(reference)); } /** @@ -113,10 +116,70 @@ public static Optional of(T reference) { *

    Comparison to {@code java.util.Optional}: this method is equivalent to Java 8's * {@code Optional.ofNullable}. */ - public static Optional fromNullable(@CheckForNull T nullableReference) { + public static Optional fromNullable(@Nullable T nullableReference) { return (nullableReference == null) ? Optional.absent() : new Present(nullableReference); } + /** + * Returns the equivalent {@code com.google.common.base.Optional} value to the given {@code + * java.util.Optional}, or {@code null} if the argument is null. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + @SuppressWarnings({ + "NullableOptional", // Null passthrough is reasonable for type conversions + "Java7ApiChecker", + }) + @IgnoreJRERequirement // Users will use this only if they're already using Optional. + public static @Nullable Optional fromJavaUtil( + java.util.@Nullable Optional javaUtilOptional) { + return (javaUtilOptional == null) ? null : fromNullable(javaUtilOptional.orElse(null)); + } + + /** + * Returns the equivalent {@code java.util.Optional} value to the given {@code + * com.google.common.base.Optional}, or {@code null} if the argument is null. + * + *

    If {@code googleOptional} is known to be non-null, use {@code googleOptional.toJavaUtil()} + * instead. + * + *

    Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it + * could refer to either the static or instance version of this method. Write out the lambda + * expression {@code o -> Optional.toJavaUtil(o)} instead. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + @SuppressWarnings({ + "AmbiguousMethodReference", // We chose the name despite knowing this risk. + "NullableOptional", // Null passthrough is reasonable for type conversions + "Java7ApiChecker", + }) + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Optional calls + @IgnoreJRERequirement + public static java.util.@Nullable Optional toJavaUtil( + @Nullable Optional googleOptional) { + return googleOptional == null ? null : googleOptional.toJavaUtil(); + } + + /** + * Returns the equivalent {@code java.util.Optional} value to this optional. + * + *

    Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it + * could refer to either the static or instance version of this method. Write out the lambda + * expression {@code o -> o.toJavaUtil()} instead. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + @SuppressWarnings({ + "AmbiguousMethodReference", // We chose the name despite knowing this risk. + "Java7ApiChecker", + }) + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Optional calls + @IgnoreJRERequirement + public java.util.Optional toJavaUtil() { + return java.util.Optional.ofNullable(orNull()); + } + Optional() {} /** @@ -131,7 +194,7 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { * {@link #or(Object)} or {@link #orNull} instead. * *

    Comparison to {@code java.util.Optional}: when the value is absent, this method - * throws {@link IllegalStateException}, whereas the Java 8 counterpart throws {@link + * throws {@link IllegalStateException}, whereas the {@code java.util} counterpart throws {@link * java.util.NoSuchElementException NoSuchElementException}. * * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns {@code @@ -192,12 +255,11 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { * *

    Comparison to {@code java.util.Optional}: this method is similar to Java 8's {@code * Optional.orElseGet}, except when {@code supplier} returns {@code null}. In this case this - * method throws an exception, whereas the Java 8 method returns the {@code null} to the caller. + * method throws an exception, whereas the Java 8+ method returns the {@code null} to the caller. * * @throws NullPointerException if this optional's value is absent and the supplier returns {@code * null} */ - @Beta public abstract T or(Supplier supplier); /** @@ -207,8 +269,7 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { *

    Comparison to {@code java.util.Optional}: this method is equivalent to Java 8's * {@code Optional.orElse(null)}. */ - @CheckForNull - public abstract T orNull(); + public abstract @Nullable T orNull(); /** * Returns an immutable singleton {@link Set} whose only element is the contained instance if it @@ -241,7 +302,7 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { * *

    Comparison to {@code java.util.Optional}: this method is similar to Java 8's {@code * Optional.map}, except when {@code function} returns {@code null}. In this case this method - * throws an exception, whereas the Java 8 method returns {@code Optional.absent()}. + * throws an exception, whereas the Java 8+ method returns {@code Optional.absent()}. * * @throws NullPointerException if the function returns {@code null} * @since 12.0 @@ -256,13 +317,13 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { *

    Comparison to {@code java.util.Optional}: no differences. */ @Override - public abstract boolean equals(@CheckForNull Object object); + public abstract boolean equals(@Nullable Object object); /** * Returns a hash code for this instance. * *

    Comparison to {@code java.util.Optional}: this class leaves the specific choice of - * hash code unspecified, unlike the Java 8 equivalent. + * hash code unspecified, unlike the Java 8+ equivalent. */ @Override public abstract int hashCode(); @@ -271,7 +332,7 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { * Returns a string representation for this instance. * *

    Comparison to {@code java.util.Optional}: this class leaves the specific string - * representation unspecified, unlike the Java 8 equivalent. + * representation unspecified, unlike the Java 8+ equivalent. */ @Override public abstract String toString(); @@ -289,20 +350,16 @@ public static Optional fromNullable(@CheckForNull T nullableReference) { * * @since 11.0 (generics widened in 13.0) */ - @Beta public static Iterable presentInstances( - final Iterable> optionals) { + Iterable> optionals) { checkNotNull(optionals); - return new Iterable() { - @Override - public Iterator iterator() { - return new AbstractIterator() { + return () -> + new AbstractIterator() { private final Iterator> iterator = checkNotNull(optionals.iterator()); @Override - @CheckForNull - protected T computeNext() { + protected @Nullable T computeNext() { while (iterator.hasNext()) { Optional optional = iterator.next(); if (optional.isPresent()) { @@ -312,9 +369,7 @@ protected T computeNext() { return endOfData(); } }; - } - }; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/base/PairwiseEquivalence.java b/android/guava/src/com/google/common/base/PairwiseEquivalence.java index 74be27fd7c6f..bb4ffd068fb7 100644 --- a/android/guava/src/com/google/common/base/PairwiseEquivalence.java +++ b/android/guava/src/com/google/common/base/PairwiseEquivalence.java @@ -15,13 +15,13 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class PairwiseEquivalence extends Equivalence> implements Serializable { final Equivalence elementEquivalence; @@ -54,9 +54,10 @@ protected int doHash(Iterable iterable) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof PairwiseEquivalence) { - PairwiseEquivalence that = (PairwiseEquivalence) object; + @SuppressWarnings("unchecked") + PairwiseEquivalence that = (PairwiseEquivalence) object; return this.elementEquivalence.equals(that.elementEquivalence); } @@ -73,5 +74,5 @@ public String toString() { return elementEquivalence + ".pairwise()"; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } diff --git a/android/guava/src/com/google/common/base/ParametricNullness.java b/android/guava/src/com/google/common/base/ParametricNullness.java index 5b595adb3ffb..cdec346f42b5 100644 --- a/android/guava/src/com/google/common/base/ParametricNullness.java +++ b/android/guava/src/com/google/common/base/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/base/PatternCompiler.java b/android/guava/src/com/google/common/base/PatternCompiler.java index 72a45faae963..90a565b1e470 100644 --- a/android/guava/src/com/google/common/base/PatternCompiler.java +++ b/android/guava/src/com/google/common/base/PatternCompiler.java @@ -15,6 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; +import com.google.errorprone.annotations.RestrictedApi; /** * Pluggable interface for compiling a regex pattern. By default this package uses the {@code @@ -22,18 +23,23 @@ * java.util.ServiceLoader} mechanism. */ @GwtIncompatible -@ElementTypesAreNonnullByDefault interface PatternCompiler { /** * Compiles the given pattern. * * @throws IllegalArgumentException if the pattern is invalid */ + @RestrictedApi( + explanation = "PatternCompiler is an implementation detail of com.google.common.base", + allowedOnPath = ".*/com/google/common/base/.*") CommonPattern compile(String pattern); /** * Returns {@code true} if the regex implementation behaves like Perl -- notably, by supporting * possessive quantifiers but also being susceptible to catastrophic backtracking. */ + @RestrictedApi( + explanation = "PatternCompiler is an implementation detail of com.google.common.base", + allowedOnPath = ".*/com/google/common/base/.*") boolean isPcreLike(); } diff --git a/android/guava/src/com/google/common/base/Platform.java b/android/guava/src/com/google/common/base/Platform.java index 703b8605b1d5..02cf59fb6727 100644 --- a/android/guava/src/com/google/common/base/Platform.java +++ b/android/guava/src/com/google/common/base/Platform.java @@ -17,11 +17,8 @@ import com.google.common.annotations.GwtCompatible; import java.lang.ref.WeakReference; import java.util.Locale; -import java.util.ServiceConfigurationError; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Methods factored out so that they can be emulated differently in GWT. @@ -29,33 +26,35 @@ * @author Jesse Wilson */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class Platform { - private static final Logger logger = Logger.getLogger(Platform.class.getName()); private static final PatternCompiler patternCompiler = loadPatternCompiler(); private Platform() {} - /** Calls {@link System#nanoTime()}. */ - @SuppressWarnings("GoodTime") // reading system time without TimeSource - static long systemNanoTime() { - return System.nanoTime(); - } - static CharMatcher precomputeCharMatcher(CharMatcher matcher) { return matcher.precomputedInternal(); } static > Optional getEnumIfPresent(Class enumClass, String value) { WeakReference> ref = Enums.getEnumConstants(enumClass).get(value); - return ref == null ? Optional.absent() : Optional.of(enumClass.cast(ref.get())); + /* + * We use `fromNullable` instead of `of` because `WeakReference.get()` has a nullable return + * type. + * + * In practice, we are very unlikely to see `null`: The `WeakReference` to the enum constant + * won't be cleared as long as the enum constant is referenced somewhere, and the enum constant + * is referenced somewhere for as long as the enum class is loaded. *Maybe in theory* the enum + * class could be unloaded after the above call to `getEnumConstants` but before we call + * `get()`, but that is vanishingly unlikely. + */ + return ref == null ? Optional.absent() : Optional.fromNullable(enumClass.cast(ref.get())); } static String formatCompact4Digits(double value) { return String.format(Locale.ROOT, "%.4g", value); } - static boolean stringIsNullOrEmpty(@CheckForNull String string) { + static boolean stringIsNullOrEmpty(@Nullable String string) { return string == null || string.isEmpty(); } @@ -65,7 +64,7 @@ static boolean stringIsNullOrEmpty(@CheckForNull String string) { * @param string the string to test and possibly return * @return {@code string} if it is not null; {@code ""} otherwise */ - static String nullToEmpty(@CheckForNull String string) { + static String nullToEmpty(@Nullable String string) { return (string == null) ? "" : string; } @@ -75,11 +74,18 @@ static String nullToEmpty(@CheckForNull String string) { * @param string the string to test and possibly return * @return {@code string} if it is not empty; {@code null} otherwise */ - @CheckForNull - static String emptyToNull(@CheckForNull String string) { + static @Nullable String emptyToNull(@Nullable String string) { return stringIsNullOrEmpty(string) ? null : string; } + static String lenientFormat(@Nullable String template, @Nullable Object @Nullable ... args) { + return Strings.lenientFormat(template, args); + } + + static String stringValueOf(@Nullable Object o) { + return String.valueOf(o); + } + static CommonPattern compilePattern(String pattern) { Preconditions.checkNotNull(pattern); return patternCompiler.compile(pattern); @@ -98,10 +104,6 @@ private static PatternCompiler loadPatternCompiler() { return new JdkPatternCompiler(); } - private static void logPatternCompilerError(ServiceConfigurationError e) { - logger.log(Level.WARNING, "Error loading regex compiler, falling back to next option", e); - } - private static final class JdkPatternCompiler implements PatternCompiler { @Override public CommonPattern compile(String pattern) { @@ -113,6 +115,4 @@ public boolean isPcreLike() { return true; } } - - static void checkGwtRpcEnabled() {} } diff --git a/android/guava/src/com/google/common/base/Preconditions.java b/android/guava/src/com/google/common/base/Preconditions.java index 13ff77b7f78d..50e78691f0b1 100644 --- a/android/guava/src/com/google/common/base/Preconditions.java +++ b/android/guava/src/com/google/common/base/Preconditions.java @@ -18,8 +18,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static convenience methods that help a method or constructor check whether it was invoked @@ -114,12 +113,9 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Preconditions { private Preconditions() {} - // TODO(cpovirk): Standardize parameter names (expression vs. b, reference vs. obj). - /** * Ensures the truth of an expression involving one or more parameters to the calling method. * @@ -140,9 +136,9 @@ public static void checkArgument(boolean expression) { * string using {@link String#valueOf(Object)} * @throws IllegalArgumentException if {@code expression} is false */ - public static void checkArgument(boolean expression, @CheckForNull Object errorMessage) { + public static void checkArgument(boolean expression, @Nullable Object errorMessage) { if (!expression) { - throw new IllegalArgumentException(String.valueOf(errorMessage)); + throw new IllegalArgumentException(Platform.stringValueOf(errorMessage)); } } @@ -162,9 +158,10 @@ public static void checkArgument(boolean expression, @CheckForNull Object errorM public static void checkArgument( boolean expression, String errorMessageTemplate, - @CheckForNull @Nullable Object... errorMessageArgs) { + @Nullable Object @Nullable ... errorMessageArgs) { if (!expression) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + throw new IllegalArgumentException( + Platform.lenientFormat(errorMessageTemplate, errorMessageArgs)); } } @@ -175,8 +172,8 @@ public static void checkArgument( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, char p1) { - if (!b) { + public static void checkArgument(boolean expression, String errorMessageTemplate, char p1) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); } } @@ -188,8 +185,8 @@ public static void checkArgument(boolean b, String errorMessageTemplate, char p1 * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, int p1) { - if (!b) { + public static void checkArgument(boolean expression, String errorMessageTemplate, int p1) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); } } @@ -201,8 +198,8 @@ public static void checkArgument(boolean b, String errorMessageTemplate, int p1) * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, long p1) { - if (!b) { + public static void checkArgument(boolean expression, String errorMessageTemplate, long p1) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); } } @@ -215,9 +212,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, long p1 * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, @CheckForNull Object p1) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); + boolean expression, String errorMessageTemplate, @Nullable Object p1) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1)); } } @@ -228,8 +225,9 @@ public static void checkArgument( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, char p1, char p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, char p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -241,8 +239,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, char p1 * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, char p1, int p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, int p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -254,8 +253,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, char p1 * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, char p1, long p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, long p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -268,9 +268,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, char p1 * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, char p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, char p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -281,8 +281,9 @@ public static void checkArgument( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, int p1, char p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, char p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -294,8 +295,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, int p1, * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, int p1, int p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, int p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -307,8 +309,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, int p1, * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, int p1, long p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, long p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -321,9 +324,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, int p1, * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, int p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, int p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -334,8 +337,9 @@ public static void checkArgument( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, long p1, char p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, char p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -347,8 +351,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, long p1 * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, long p1, int p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, int p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -360,8 +365,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, long p1 * * @since 20.0 (varargs overload since 2.0) */ - public static void checkArgument(boolean b, String errorMessageTemplate, long p1, long p2) { - if (!b) { + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, long p2) { + if (!expression) { throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -374,9 +380,9 @@ public static void checkArgument(boolean b, String errorMessageTemplate, long p1 * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, long p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, long p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -388,9 +394,9 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, char p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, char p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -402,9 +408,9 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, int p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, int p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -416,9 +422,9 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, long p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, long p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -430,9 +436,13 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, + // TODO: cl/604933487 - Make errorMessageTemplate consistently @Nullable across overloads. + @Nullable String errorMessageTemplate, + @Nullable Object p1, + @Nullable Object p2) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -444,13 +454,13 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, + boolean expression, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3) { + if (!expression) { + throw new IllegalArgumentException(Platform.lenientFormat(errorMessageTemplate, p1, p2, p3)); } } @@ -462,14 +472,15 @@ public static void checkArgument( * @since 20.0 (varargs overload since 2.0) */ public static void checkArgument( - boolean b, + boolean expression, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3, - @CheckForNull Object p4) { - if (!b) { - throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3, + @Nullable Object p4) { + if (!expression) { + throw new IllegalArgumentException( + Platform.lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); } } @@ -497,9 +508,9 @@ public static void checkState(boolean expression) { * @throws IllegalStateException if {@code expression} is false * @see Verify#verify Verify.verify() */ - public static void checkState(boolean expression, @CheckForNull Object errorMessage) { + public static void checkState(boolean expression, @Nullable Object errorMessage) { if (!expression) { - throw new IllegalStateException(String.valueOf(errorMessage)); + throw new IllegalStateException(Platform.stringValueOf(errorMessage)); } } @@ -521,17 +532,18 @@ public static void checkState(boolean expression, @CheckForNull Object errorMess public static void checkState( boolean expression, /* - * TODO(cpovirk): Consider removing @CheckForNull here, as we've done with the other methods' - * errorMessageTemplate parameters: It it unlikely that callers intend for their string + * TODO(cpovirk): Consider removing @Nullable here, as we've done with the other methods' + * errorMessageTemplate parameters: It is unlikely that callers intend for their string * template to be null (though we do handle that case gracefully at runtime). I've left this * one as it is because one of our users has defined a wrapper API around Preconditions, * declaring a checkState method that accepts a possibly null template. So we'd need to update * that user first. */ - @CheckForNull String errorMessageTemplate, - @CheckForNull @Nullable Object... errorMessageArgs) { + @Nullable String errorMessageTemplate, + @Nullable Object @Nullable ... errorMessageArgs) { if (!expression) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + throw new IllegalStateException( + Platform.lenientFormat(errorMessageTemplate, errorMessageArgs)); } } @@ -543,8 +555,8 @@ public static void checkState( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, char p1) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, char p1) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); } } @@ -557,8 +569,8 @@ public static void checkState(boolean b, String errorMessageTemplate, char p1) { * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, int p1) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, int p1) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); } } @@ -571,8 +583,8 @@ public static void checkState(boolean b, String errorMessageTemplate, int p1) { * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, long p1) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, long p1) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); } } @@ -585,9 +597,10 @@ public static void checkState(boolean b, String errorMessageTemplate, long p1) { * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, @CheckForNull Object p1) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); + public static void checkState( + boolean expression, String errorMessageTemplate, @Nullable Object p1) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1)); } } @@ -599,8 +612,8 @@ public static void checkState(boolean b, String errorMessageTemplate, @CheckForN * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, char p1, char p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, char p1, char p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -613,8 +626,8 @@ public static void checkState(boolean b, String errorMessageTemplate, char p1, c * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, char p1, int p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, char p1, int p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -627,8 +640,8 @@ public static void checkState(boolean b, String errorMessageTemplate, char p1, i * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, char p1, long p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, char p1, long p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -642,9 +655,9 @@ public static void checkState(boolean b, String errorMessageTemplate, char p1, l * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, char p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, char p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -656,8 +669,8 @@ public static void checkState( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, int p1, char p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, int p1, char p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -670,8 +683,8 @@ public static void checkState(boolean b, String errorMessageTemplate, int p1, ch * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, int p1, int p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, int p1, int p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -684,8 +697,8 @@ public static void checkState(boolean b, String errorMessageTemplate, int p1, in * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, int p1, long p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, int p1, long p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -699,9 +712,9 @@ public static void checkState(boolean b, String errorMessageTemplate, int p1, lo * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, int p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, int p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -713,8 +726,8 @@ public static void checkState( * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, long p1, char p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, long p1, char p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -727,8 +740,8 @@ public static void checkState(boolean b, String errorMessageTemplate, long p1, c * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, long p1, int p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, long p1, int p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -741,8 +754,8 @@ public static void checkState(boolean b, String errorMessageTemplate, long p1, i * * @since 20.0 (varargs overload since 2.0) */ - public static void checkState(boolean b, String errorMessageTemplate, long p1, long p2) { - if (!b) { + public static void checkState(boolean expression, String errorMessageTemplate, long p1, long p2) { + if (!expression) { throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -756,9 +769,9 @@ public static void checkState(boolean b, String errorMessageTemplate, long p1, l * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, long p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, long p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -771,9 +784,9 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, char p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, char p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -786,9 +799,9 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, int p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, int p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -801,9 +814,9 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, long p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, long p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -816,9 +829,9 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, String errorMessageTemplate, @CheckForNull Object p1, @CheckForNull Object p2) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + boolean expression, String errorMessageTemplate, @Nullable Object p1, @Nullable Object p2) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } } @@ -831,13 +844,13 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, + boolean expression, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2, p3)); } } @@ -850,14 +863,14 @@ public static void checkState( * @since 20.0 (varargs overload since 2.0) */ public static void checkState( - boolean b, + boolean expression, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3, - @CheckForNull Object p4) { - if (!b) { - throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3, + @Nullable Object p4) { + if (!expression) { + throw new IllegalStateException(Platform.lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); } } @@ -884,7 +897,7 @@ public static void checkState( * @see Verify#verifyNotNull Verify.verifyNotNull() */ @CanIgnoreReturnValue - public static T checkNotNull(@CheckForNull T reference) { + public static T checkNotNull(@Nullable T reference) { if (reference == null) { throw new NullPointerException(); } @@ -902,9 +915,9 @@ public static T checkNotNull(@CheckForNull T reference) { * @see Verify#verifyNotNull Verify.verifyNotNull() */ @CanIgnoreReturnValue - public static T checkNotNull(@CheckForNull T reference, @CheckForNull Object errorMessage) { + public static T checkNotNull(@Nullable T reference, @Nullable Object errorMessage) { if (reference == null) { - throw new NullPointerException(String.valueOf(errorMessage)); + throw new NullPointerException(Platform.stringValueOf(errorMessage)); } return reference; } @@ -926,11 +939,12 @@ public static T checkNotNull(@CheckForNull T reference, @CheckForNull Object */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T reference, + @Nullable T reference, String errorMessageTemplate, - @CheckForNull @Nullable Object... errorMessageArgs) { + @Nullable Object @Nullable ... errorMessageArgs) { if (reference == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + throw new NullPointerException( + Platform.lenientFormat(errorMessageTemplate, errorMessageArgs)); } return reference; } @@ -943,11 +957,11 @@ public static T checkNotNull( * @since 20.0 (varargs overload since 2.0) */ @CanIgnoreReturnValue - public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplate, char p1) { - if (obj == null) { + public static T checkNotNull(@Nullable T reference, String errorMessageTemplate, char p1) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); } - return obj; + return reference; } /** @@ -958,11 +972,11 @@ public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplat * @since 20.0 (varargs overload since 2.0) */ @CanIgnoreReturnValue - public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplate, int p1) { - if (obj == null) { + public static T checkNotNull(@Nullable T reference, String errorMessageTemplate, int p1) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); } - return obj; + return reference; } /** @@ -973,11 +987,11 @@ public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplat * @since 20.0 (varargs overload since 2.0) */ @CanIgnoreReturnValue - public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplate, long p1) { - if (obj == null) { + public static T checkNotNull(@Nullable T reference, String errorMessageTemplate, long p1) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); } - return obj; + return reference; } /** @@ -989,11 +1003,11 @@ public static T checkNotNull(@CheckForNull T obj, String errorMessageTemplat */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, @CheckForNull Object p1) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); + @Nullable T reference, String errorMessageTemplate, @Nullable Object p1) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1)); } - return obj; + return reference; } /** @@ -1005,11 +1019,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, char p1, char p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, char p1, char p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1021,11 +1035,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, char p1, int p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, char p1, int p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1037,11 +1051,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, char p1, long p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, char p1, long p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1053,11 +1067,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, char p1, @CheckForNull Object p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, char p1, @Nullable Object p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1069,11 +1083,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, int p1, char p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, int p1, char p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1085,11 +1099,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, int p1, int p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, int p1, int p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1101,11 +1115,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, int p1, long p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, int p1, long p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1117,11 +1131,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, int p1, @CheckForNull Object p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, int p1, @Nullable Object p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1133,11 +1147,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, long p1, char p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, long p1, char p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1149,11 +1163,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, long p1, int p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, long p1, int p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1165,11 +1179,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, long p1, long p2) { - if (obj == null) { + @Nullable T reference, String errorMessageTemplate, long p1, long p2) { + if (reference == null) { throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1181,11 +1195,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, long p1, @CheckForNull Object p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, long p1, @Nullable Object p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1197,11 +1211,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, @CheckForNull Object p1, char p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, @Nullable Object p1, char p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1213,11 +1227,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, @CheckForNull Object p1, int p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, @Nullable Object p1, int p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1229,11 +1243,11 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, String errorMessageTemplate, @CheckForNull Object p1, long p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable T reference, String errorMessageTemplate, @Nullable Object p1, long p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1245,14 +1259,14 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, + @Nullable T reference, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + @Nullable Object p1, + @Nullable Object p2) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2)); } - return obj; + return reference; } /** @@ -1264,15 +1278,15 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, + @Nullable T reference, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2, p3)); } - return obj; + return reference; } /** @@ -1284,16 +1298,16 @@ public static T checkNotNull( */ @CanIgnoreReturnValue public static T checkNotNull( - @CheckForNull T obj, + @Nullable T reference, String errorMessageTemplate, - @CheckForNull Object p1, - @CheckForNull Object p2, - @CheckForNull Object p3, - @CheckForNull Object p4) { - if (obj == null) { - throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); - } - return obj; + @Nullable Object p1, + @Nullable Object p2, + @Nullable Object p3, + @Nullable Object p4) { + if (reference == null) { + throw new NullPointerException(Platform.lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + } + return reference; } /* diff --git a/android/guava/src/com/google/common/base/Predicate.java b/android/guava/src/com/google/common/base/Predicate.java index 35d57a6018a2..54438f36419a 100644 --- a/android/guava/src/com/google/common/base/Predicate.java +++ b/android/guava/src/com/google/common/base/Predicate.java @@ -15,9 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Determines a true or false value for a given input; a pre-Java-8 version of {@link @@ -46,10 +44,9 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Predicate { /** - * Returns the result of applying this predicate to {@code input} (Java 8 users, see notes in the + * Returns the result of applying this predicate to {@code input} (Java 8+ users, see notes in the * class documentation above). This method is generally expected, but not absolutely * required, to have the following properties: * @@ -63,7 +60,6 @@ public interface Predicate { * @throws NullPointerException if {@code input} is null and this predicate does not accept null * arguments */ - @CanIgnoreReturnValue boolean apply(@ParametricNullness T input); /** @@ -77,5 +73,5 @@ public interface Predicate { * predicates are known not to be interchangeable. */ @Override - boolean equals(@CheckForNull Object object); + boolean equals(@Nullable Object object); } diff --git a/android/guava/src/com/google/common/base/Predicates.java b/android/guava/src/com/google/common/base/Predicates.java index dc487fc56ffa..6e3fce2938e6 100644 --- a/android/guava/src/com/google/common/base/Predicates.java +++ b/android/guava/src/com/google/common/base/Predicates.java @@ -16,17 +16,16 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code Predicate} instances. @@ -40,7 +39,6 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Predicates { private Predicates() {} @@ -195,8 +193,8 @@ private Predicates() {} * * @since 20.0 (since 10.0 under the incorrect name {@code assignableFrom}) */ + @J2ktIncompatible @GwtIncompatible // Class.isAssignableFrom - @Beta public static Predicate> subtypeOf(Class clazz) { return new SubtypeOfPredicate(clazz); } @@ -212,6 +210,7 @@ public static Predicate> subtypeOf(Class clazz) { * * @param target the collection that may contain the function input */ + @SuppressWarnings("NoHardKeywords") // We're stuck with the name for compatibility reasons. public static Predicate in(Collection target) { return new InPredicate<>(target); } @@ -256,10 +255,12 @@ public static Predicate contains(Pattern pattern) { // Package private for GWT serialization. enum ObjectPredicate implements Predicate<@Nullable Object> { - /** @see Predicates#alwaysTrue() */ + /** + * @see Predicates#alwaysTrue() + */ ALWAYS_TRUE { @Override - public boolean apply(@CheckForNull Object o) { + public boolean apply(@Nullable Object o) { return true; } @@ -268,10 +269,12 @@ public String toString() { return "Predicates.alwaysTrue()"; } }, - /** @see Predicates#alwaysFalse() */ + /** + * @see Predicates#alwaysFalse() + */ ALWAYS_FALSE { @Override - public boolean apply(@CheckForNull Object o) { + public boolean apply(@Nullable Object o) { return false; } @@ -280,10 +283,12 @@ public String toString() { return "Predicates.alwaysFalse()"; } }, - /** @see Predicates#isNull() */ + /** + * @see Predicates#isNull() + */ IS_NULL { @Override - public boolean apply(@CheckForNull Object o) { + public boolean apply(@Nullable Object o) { return o == null; } @@ -292,10 +297,12 @@ public String toString() { return "Predicates.isNull()"; } }, - /** @see Predicates#notNull() */ + /** + * @see Predicates#notNull() + */ NOT_NULL { @Override - public boolean apply(@CheckForNull Object o) { + public boolean apply(@Nullable Object o) { return o != null; } @@ -311,7 +318,9 @@ public String toString() { } } - /** @see Predicates#not(Predicate) */ + /** + * @see Predicates#not(Predicate) + */ private static class NotPredicate implements Predicate, Serializable { final Predicate predicate; @@ -331,7 +340,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NotPredicate) { NotPredicate that = (NotPredicate) obj; return predicate.equals(that.predicate); @@ -344,10 +353,12 @@ public String toString() { return "Predicates.not(" + predicate + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#and(Iterable) */ + /** + * @see Predicates#and(Iterable) + */ private static class AndPredicate implements Predicate, Serializable { private final List> components; @@ -374,7 +385,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof AndPredicate) { AndPredicate that = (AndPredicate) obj; return components.equals(that.components); @@ -387,10 +398,12 @@ public String toString() { return toStringHelper("and", components); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#or(Iterable) */ + /** + * @see Predicates#or(Iterable) + */ private static class OrPredicate implements Predicate, Serializable { private final List> components; @@ -417,7 +430,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof OrPredicate) { OrPredicate that = (OrPredicate) obj; return components.equals(that.components); @@ -430,7 +443,7 @@ public String toString() { return toStringHelper("or", components); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static String toStringHelper(String methodName, Iterable components) { @@ -446,7 +459,9 @@ private static String toStringHelper(String methodName, Iterable components) return builder.append(')').toString(); } - /** @see Predicates#equalTo(Object) */ + /** + * @see Predicates#equalTo(Object) + */ private static class IsEqualToPredicate implements Predicate<@Nullable Object>, Serializable { private final Object target; @@ -455,7 +470,7 @@ private IsEqualToPredicate(Object target) { } @Override - public boolean apply(@CheckForNull Object o) { + public boolean apply(@Nullable Object o) { return target.equals(o); } @@ -465,7 +480,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof IsEqualToPredicate) { IsEqualToPredicate that = (IsEqualToPredicate) obj; return target.equals(that.target); @@ -478,7 +493,7 @@ public String toString() { return "Predicates.equalTo(" + target + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; @SuppressWarnings("unchecked") // safe contravariant cast Predicate withNarrowedType() { @@ -486,7 +501,9 @@ public String toString() { } } - /** @see Predicates#instanceOf(Class) */ + /** + * @see Predicates#instanceOf(Class) + */ @GwtIncompatible // Class.isInstance private static class InstanceOfPredicate implements Predicate, Serializable { @@ -507,7 +524,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof InstanceOfPredicate) { InstanceOfPredicate that = (InstanceOfPredicate) obj; return clazz == that.clazz; @@ -520,10 +537,13 @@ public String toString() { return "Predicates.instanceOf(" + clazz.getName() + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#subtypeOf(Class) */ + /** + * @see Predicates#subtypeOf(Class) + */ + @J2ktIncompatible @GwtIncompatible // Class.isAssignableFrom private static class SubtypeOfPredicate implements Predicate>, Serializable { private final Class clazz; @@ -543,7 +563,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof SubtypeOfPredicate) { SubtypeOfPredicate that = (SubtypeOfPredicate) obj; return clazz == that.clazz; @@ -556,10 +576,12 @@ public String toString() { return "Predicates.subtypeOf(" + clazz.getName() + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#in(Collection) */ + /** + * @see Predicates#in(Collection) + */ private static class InPredicate implements Predicate, Serializable { private final Collection target; @@ -578,7 +600,13 @@ public boolean apply(@ParametricNullness T t) { } @Override - public boolean equals(@CheckForNull Object obj) { + /* + * We should probably not have implemented equals() at all, but given that we did, we can't + * provide a better implementation than the input Collection, at least without dramatic changes + * like copying it to a new Set—which might then test for element equality differently. + */ + @SuppressWarnings("UndefinedEquals") + public boolean equals(@Nullable Object obj) { if (obj instanceof InPredicate) { InPredicate that = (InPredicate) obj; return target.equals(that.target); @@ -596,10 +624,12 @@ public String toString() { return "Predicates.in(" + target + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#compose(Predicate, Function) */ + /** + * @see Predicates#compose(Predicate, Function) + */ private static class CompositionPredicate implements Predicate, Serializable { final Predicate p; @@ -616,7 +646,7 @@ public boolean apply(@ParametricNullness A a) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof CompositionPredicate) { CompositionPredicate that = (CompositionPredicate) obj; return f.equals(that.f) && p.equals(that.p); @@ -635,10 +665,12 @@ public String toString() { return p + "(" + f + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#contains(Pattern) */ + /** + * @see Predicates#contains(Pattern) + */ @GwtIncompatible // Only used by other GWT-incompatible code. private static class ContainsPatternPredicate implements Predicate, Serializable { final CommonPattern pattern; @@ -661,7 +693,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof ContainsPatternPredicate) { ContainsPatternPredicate that = (ContainsPatternPredicate) obj; @@ -683,10 +715,12 @@ public String toString() { return "Predicates.contains(" + patternString + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Predicates#containsPattern(String) */ + /** + * @see Predicates#containsPattern(String) + */ @GwtIncompatible // Only used by other GWT-incompatible code. private static class ContainsPatternFromStringPredicate extends ContainsPatternPredicate { @@ -699,7 +733,7 @@ public String toString() { return "Predicates.containsPattern(" + pattern.pattern() + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static List> asList( diff --git a/android/guava/src/com/google/common/base/Present.java b/android/guava/src/com/google/common/base/Present.java index 4e62da29e93b..dcf6623aaba8 100644 --- a/android/guava/src/com/google/common/base/Present.java +++ b/android/guava/src/com/google/common/base/Present.java @@ -17,13 +17,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Collections; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** Implementation of an {@link Optional} containing a reference. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class Present extends Optional { private final T reference; @@ -78,7 +79,7 @@ public Optional transform(Function function) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Present) { Present other = (Present) object; return reference.equals(other.reference); @@ -96,5 +97,5 @@ public String toString() { return "Optional.of(" + reference + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/base/SmallCharMatcher.java b/android/guava/src/com/google/common/base/SmallCharMatcher.java index f0e801b67118..c92fb7065e99 100644 --- a/android/guava/src/com/google/common/base/SmallCharMatcher.java +++ b/android/guava/src/com/google/common/base/SmallCharMatcher.java @@ -26,7 +26,6 @@ * @author Christopher Swenson */ @GwtIncompatible // no precomputation is done in GWT -@ElementTypesAreNonnullByDefault final class SmallCharMatcher extends NamedFastMatcher { static final int MAX_SIZE = 1023; private final char[] table; @@ -56,7 +55,7 @@ static int smear(int hashCode) { } private boolean checkFilter(int c) { - return 1 == (1 & (filter >> c)); + return ((filter >> c) & 1) == 1; } // This is all essentially copied from ImmutableSet, but we have to duplicate because diff --git a/android/guava/src/com/google/common/base/SneakyThrows.java b/android/guava/src/com/google/common/base/SneakyThrows.java new file mode 100644 index 000000000000..33e2714ce43c --- /dev/null +++ b/android/guava/src/com/google/common/base/SneakyThrows.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +/** Static utility method for unchecked throwing of any {@link Throwable}. */ +@GwtCompatible +final class SneakyThrows { + /** + * Throws {@code t} as if it were an unchecked {@link Throwable}. + * + *

    This method is useful primarily when we make a reflective call to a method with no {@code + * throws} clause: Java forces us to handle an arbitrary {@link Throwable} from that method, + * rather than just the {@link RuntimeException} or {@link Error} that should be possible. (And in + * fact the static type of {@link Throwable} is occasionally justified even for a method with no + * {@code throws} clause: Some such methods can in fact throw a checked exception (e.g., by + * calling code written in Kotlin).) Typically, we want to let a {@link Throwable} from such a + * method propagate untouched, just as we'd typically let it do for a non-reflective call. + * However, we can't usually write {@code throw t;} when {@code t} has a static type of {@link + * Throwable}. But we can write {@code sneakyThrow(t);}. + * + *

    We sometimes also use {@code sneakyThrow} for testing how our code responds to + * sneaky checked exception. + * + * @return never; this method declares a return type of {@link Error} only so that callers can + * write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always + * throw. + */ + @CanIgnoreReturnValue + static Error sneakyThrow(Throwable t) { + throw new SneakyThrows().throwIt(t); + } + + @SuppressWarnings("unchecked") // not really safe, but that's the point + private Error throwIt(Throwable t) throws T { + throw (T) t; + } + + private SneakyThrows() {} +} diff --git a/android/guava/src/com/google/common/base/Splitter.java b/android/guava/src/com/google/common/base/Splitter.java index bde2e0ec42f8..2bec65f6dd1a 100644 --- a/android/guava/src/com/google/common/base/Splitter.java +++ b/android/guava/src/com/google/common/base/Splitter.java @@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import java.util.ArrayList; @@ -27,7 +26,9 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import javax.annotation.CheckForNull; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; /** * Extracts non-overlapping substrings from an input string, typically by recognizing appearances of @@ -98,7 +99,6 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Splitter { private final CharMatcher trimmer; private final boolean omitEmptyStrings; @@ -137,14 +137,12 @@ public static Splitter on(char separator) { * separator * @return a splitter, with default settings, that uses this matcher */ - public static Splitter on(final CharMatcher separatorMatcher) { + public static Splitter on(CharMatcher separatorMatcher) { checkNotNull(separatorMatcher); return new Splitter( - new Strategy() { - @Override - public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) { - return new SplittingIterator(splitter, toSplit) { + (splitter, toSplit) -> + new SplittingIterator(splitter, toSplit) { @Override int separatorStart(int start) { return separatorMatcher.indexIn(toSplit, start); @@ -154,9 +152,7 @@ int separatorStart(int start) { int separatorEnd(int separatorPosition) { return separatorPosition + 1; } - }; - } - }); + }); } /** @@ -167,16 +163,14 @@ int separatorEnd(int separatorPosition) { * @param separator the literal, nonempty string to recognize as a separator * @return a splitter, with default settings, that recognizes that separator */ - public static Splitter on(final String separator) { + public static Splitter on(String separator) { checkArgument(separator.length() != 0, "The separator may not be the empty string."); if (separator.length() == 1) { return Splitter.on(separator.charAt(0)); } return new Splitter( - new Strategy() { - @Override - public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { - return new SplittingIterator(splitter, toSplit) { + (splitter, toSplit) -> + new SplittingIterator(splitter, toSplit) { @Override public int separatorStart(int start) { int separatorLength = separator.length(); @@ -197,9 +191,7 @@ public int separatorStart(int start) { public int separatorEnd(int separatorPosition) { return separatorPosition + separator.length(); } - }; - } - }); + }); } /** @@ -214,32 +206,30 @@ public int separatorEnd(int separatorPosition) { */ @GwtIncompatible // java.util.regex public static Splitter on(Pattern separatorPattern) { - return on(new JdkPattern(separatorPattern)); + return onPatternInternal(new JdkPattern(separatorPattern)); } - private static Splitter on(final CommonPattern separatorPattern) { + /** Internal utility; see {@link #on(Pattern)} instead. */ + static Splitter onPatternInternal(CommonPattern separatorPattern) { checkArgument( !separatorPattern.matcher("").matches(), "The pattern may not match the empty string: %s", separatorPattern); return new Splitter( - new Strategy() { - @Override - public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { - final CommonMatcher matcher = separatorPattern.matcher(toSplit); - return new SplittingIterator(splitter, toSplit) { - @Override - public int separatorStart(int start) { - return matcher.find(start) ? matcher.start() : -1; - } - - @Override - public int separatorEnd(int separatorPosition) { - return matcher.end(); - } - }; - } + (splitter, toSplit) -> { + CommonMatcher matcher = separatorPattern.matcher(toSplit); + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + return matcher.find(start) ? matcher.start() : -1; + } + + @Override + public int separatorEnd(int separatorPosition) { + return matcher.end(); + } + }; }); } @@ -257,7 +247,7 @@ public int separatorEnd(int separatorPosition) { */ @GwtIncompatible // java.util.regex public static Splitter onPattern(String separatorPattern) { - return on(Platform.compilePattern(separatorPattern)); + return onPatternInternal(Platform.compilePattern(separatorPattern)); } /** @@ -278,14 +268,12 @@ public static Splitter onPattern(String separatorPattern) { * @return a splitter, with default settings, that can split into fixed sized pieces * @throws IllegalArgumentException if {@code length} is zero or negative */ - public static Splitter fixedLength(final int length) { + public static Splitter fixedLength(int length) { checkArgument(length > 0, "The length may not be less than 1"); return new Splitter( - new Strategy() { - @Override - public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { - return new SplittingIterator(splitter, toSplit) { + (splitter, toSplit) -> + new SplittingIterator(splitter, toSplit) { @Override public int separatorStart(int start) { int nextChunkStart = start + length; @@ -296,9 +284,7 @@ public int separatorStart(int start) { public int separatorEnd(int separatorPosition) { return separatorPosition; } - }; - } - }); + }); } /** @@ -329,7 +315,7 @@ public Splitter omitEmptyStrings() { *

    For example, {@code Splitter.on(',').limit(3).split("a,b,c,d")} returns an iterable * containing {@code ["a", "b", "c,d"]}. When omitting empty strings, the omitted strings do not * count. Hence, {@code Splitter.on(',').limit(3).omitEmptyStrings().split("a,,,b,,,c,d")} returns - * an iterable containing {@code ["a", "b", "c,d"}. When trim is requested, all entries are + * an iterable containing {@code ["a", "b", "c,d"]}. When trim is requested, all entries are * trimmed, including the last. Hence {@code Splitter.on(',').limit(3).trimResults().split(" a , b * , c , d ")} results in {@code ["a", "b", "c , d"]}. * @@ -379,7 +365,7 @@ public Splitter trimResults(CharMatcher trimmer) { * @param sequence the sequence of characters to split * @return an iteration over the segments split from the parameter */ - public Iterable split(final CharSequence sequence) { + public Iterable split(CharSequence sequence) { checkNotNull(sequence); return new Iterable() { @@ -423,13 +409,29 @@ public List splitToList(CharSequence sequence) { return Collections.unmodifiableList(result); } + /** + * Splits {@code sequence} into string components and makes them available through an {@link + * Stream}, which may be lazily evaluated. If you want an eagerly computed {@link List}, use + * {@link #splitToList(CharSequence)}. + * + * @param sequence the sequence of characters to split + * @return a stream over the segments split from the parameter + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Stream calls. + @IgnoreJRERequirement + public Stream splitToStream(CharSequence sequence) { + // Can't use Streams.stream() from base + return StreamSupport.stream(split(sequence).spliterator(), false); + } + /** * Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries * into keys and values using the specified separator. * * @since 10.0 */ - @Beta public MapSplitter withKeyValueSeparator(String separator) { return withKeyValueSeparator(on(separator)); } @@ -440,7 +442,6 @@ public MapSplitter withKeyValueSeparator(String separator) { * * @since 14.0 */ - @Beta public MapSplitter withKeyValueSeparator(char separator) { return withKeyValueSeparator(on(separator)); } @@ -464,7 +465,6 @@ public MapSplitter withKeyValueSeparator(char separator) { * * @since 10.0 */ - @Beta public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) { return new MapSplitter(this, keyValueSplitter); } @@ -477,7 +477,6 @@ public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) { * * @since 10.0 */ - @Beta public static final class MapSplitter { private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry"; private final Splitter outerSplitter; @@ -549,9 +548,8 @@ protected SplittingIterator(Splitter splitter, CharSequence toSplit) { this.toSplit = toSplit; } - @CheckForNull @Override - protected String computeNext() { + protected @Nullable String computeNext() { /* * The returned string will be from the end of the last match to the beginning of the next * one. nextStart is the start position of the returned substring, while offset is the place diff --git a/android/guava/src/com/google/common/base/StandardSystemProperty.java b/android/guava/src/com/google/common/base/StandardSystemProperty.java index dc29792de782..29f62a28eff2 100644 --- a/android/guava/src/com/google/common/base/StandardSystemProperty.java +++ b/android/guava/src/com/google/common/base/StandardSystemProperty.java @@ -15,7 +15,8 @@ package com.google.common.base; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Represents a {@linkplain System#getProperties() standard system property}. @@ -23,8 +24,8 @@ * @author Kurt Alfred Kluever * @since 15.0 */ +@J2ktIncompatible @GwtIncompatible // java.lang.System#getProperty -@ElementTypesAreNonnullByDefault public enum StandardSystemProperty { /** Java Runtime Environment version. */ @@ -125,7 +126,7 @@ public enum StandardSystemProperty { this.key = key; } - /** Returns the key used to lookup this system property. */ + /** Returns the key used to look up this system property. */ public String key() { return key; } @@ -153,8 +154,7 @@ public String key() { *

  • {@code jdk.module.*} (added in Java 9, optional) * */ - @CheckForNull - public String value() { + public @Nullable String value() { return System.getProperty(key); } diff --git a/android/guava/src/com/google/common/base/Stopwatch.java b/android/guava/src/com/google/common/base/Stopwatch.java index f76c098613a9..48d6250a0411 100644 --- a/android/guava/src/com/google/common/base/Stopwatch.java +++ b/android/guava/src/com/google/common/base/Stopwatch.java @@ -25,7 +25,11 @@ import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.j2objc.annotations.J2ObjCIncompatible; +import java.time.Duration; import java.util.concurrent.TimeUnit; /** @@ -33,12 +37,11 @@ * successive readings of "now" in the same process. * *

    In contrast, wall time is a reading of "now" as given by a method like - * {@link System#currentTimeMillis()}, best represented as an {@link Instant}. Such values - * - *

    can be subtracted to obtain a {@code Duration} (such as by {@code Duration.between}), - * but doing so does not give a reliable measurement of elapsed time, because wall time - * readings are inherently approximate, routinely affected by periodic clock corrections. Because - * this class (by default) uses {@link System#nanoTime}, it is unaffected by these changes. + * {@link System#currentTimeMillis()}, best represented as an {@link java.time.Instant}. Such values + * can be subtracted to obtain a {@code Duration} (such as by {@code Duration.between}), but + * doing so does not give a reliable measurement of elapsed time, because wall time readings + * are inherently approximate, routinely affected by periodic clock corrections. Because this class + * (by default) uses {@link System#nanoTime}, it is unaffected by these changes. * *

    Use this class instead of direct calls to {@link System#nanoTime} for two reasons: * @@ -49,6 +52,12 @@ * performance reasons, without affecting most of your code. * * + *

    The one downside of {@code Stopwatch} relative to {@link System#nanoTime()} is that {@code + * Stopwatch} requires object allocation and additional method calls, which can reduce the accuracy + * of the elapsed times reported. {@code Stopwatch} is still suitable for logging and metrics where + * reasonably accurate values are sufficient. If the uncommon case that you need to maximize + * accuracy, use {@code System.nanoTime()} directly instead. + * *

    Basic usage: * *

    {@code
    @@ -56,7 +65,7 @@
      * doSomething();
      * stopwatch.stop(); // optional
      *
    - * long millis = stopwatch.elapsed(MILLISECONDS);
    + * Duration duration = stopwatch.elapsed();
      *
      * log.info("time: " + stopwatch); // formatted string like "12.3 ms"
      * }
    @@ -87,7 +96,6 @@ */ @GwtCompatible(emulated = true) @SuppressWarnings("GoodTime") // lots of violations -@ElementTypesAreNonnullByDefault public final class Stopwatch { private final Ticker ticker; private boolean isRunning; @@ -196,8 +204,12 @@ private long elapsedNanos() { * Returns the current elapsed time shown on this stopwatch, expressed in the desired time unit, * with any fraction rounded down. * - *

    Note that the overhead of measurement can be more than a microsecond, so it is generally not - * useful to specify {@link TimeUnit#NANOSECONDS} precision here. + *

    Note: the overhead of measurement can be more than a microsecond, so it is generally + * not useful to specify {@link TimeUnit#NANOSECONDS} precision here. + * + *

    It is generally not a good idea to use an ambiguous, unitless {@code long} to represent + * elapsed time. Therefore, we recommend using {@link #elapsed()} instead, which returns a + * strongly-typed {@code Duration} instance. * * @since 14.0 (since 10.0 as {@code elapsedTime()}) */ @@ -205,6 +217,27 @@ public long elapsed(TimeUnit desiredUnit) { return desiredUnit.convert(elapsedNanos(), NANOSECONDS); } + /** + * Returns the current elapsed time shown on this stopwatch as a {@link Duration}. Unlike {@link + * #elapsed(TimeUnit)}, this method does not lose any precision due to rounding. + * + *

    Warning: do not call this method from Android code unless you are on Android API + * level 26+ or you opt in to + * library desugaring. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Duration calls + @IgnoreJRERequirement + @J2ktIncompatible + @GwtIncompatible + @J2ObjCIncompatible + public Duration elapsed() { + return Duration.ofNanos(elapsedNanos()); + } + /** Returns a string representation of the current elapsed time. */ @Override public String toString() { @@ -255,8 +288,7 @@ private static String abbreviate(TimeUnit unit) { return "h"; case DAYS: return "d"; - default: - throw new AssertionError(); } + throw new AssertionError(); } } diff --git a/android/guava/src/com/google/common/base/Strings.java b/android/guava/src/com/google/common/base/Strings.java index fa3626648d10..0f26ec985fbb 100644 --- a/android/guava/src/com/google/common/base/Strings.java +++ b/android/guava/src/com/google/common/base/Strings.java @@ -16,13 +16,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.min; import static java.util.logging.Level.WARNING; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code String} or {@code CharSequence} instances. @@ -31,7 +31,6 @@ * @since 3.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Strings { private Strings() {} @@ -41,7 +40,7 @@ private Strings() {} * @param string the string to test and possibly return * @return {@code string} itself if it is non-null; {@code ""} if it is null */ - public static String nullToEmpty(@CheckForNull String string) { + public static String nullToEmpty(@Nullable String string) { return Platform.nullToEmpty(string); } @@ -51,8 +50,7 @@ public static String nullToEmpty(@CheckForNull String string) { * @param string the string to test and possibly return * @return {@code string} itself if it is nonempty; {@code null} if it is empty or null */ - @CheckForNull - public static String emptyToNull(@CheckForNull String string) { + public static @Nullable String emptyToNull(@Nullable String string) { return Platform.emptyToNull(string); } @@ -67,7 +65,7 @@ public static String emptyToNull(@CheckForNull String string) { * @param string a string reference to check * @return {@code true} if the string is null or is the empty string */ - public static boolean isNullOrEmpty(@CheckForNull String string) { + public static boolean isNullOrEmpty(@Nullable String string) { return Platform.stringIsNullOrEmpty(string); } @@ -152,14 +150,14 @@ public static String repeat(String string, int count) { } // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark - final int len = string.length(); - final long longSize = (long) len * (long) count; - final int size = (int) longSize; + int len = string.length(); + long longSize = (long) len * (long) count; + int size = (int) longSize; if (size != longSize) { throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize); } - final char[] array = new char[size]; + char[] array = new char[size]; string.getChars(0, len, array, 0); int n; for (n = len; n < size - n; n <<= 1) { @@ -180,7 +178,7 @@ public static String commonPrefix(CharSequence a, CharSequence b) { checkNotNull(a); checkNotNull(b); - int maxPrefixLength = Math.min(a.length(), b.length()); + int maxPrefixLength = min(a.length(), b.length()); int p = 0; while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { p++; @@ -202,7 +200,7 @@ public static String commonSuffix(CharSequence a, CharSequence b) { checkNotNull(a); checkNotNull(b); - int maxSuffixLength = Math.min(a.length(), b.length()); + int maxSuffixLength = min(a.length(), b.length()); int s = 0; while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) { s++; @@ -260,7 +258,7 @@ static boolean validSurrogatePairAt(CharSequence string, int index) { */ // TODO(diamondm) consider using Arrays.toString() for array parameters public static String lenientFormat( - @CheckForNull String template, @CheckForNull @Nullable Object... args) { + @Nullable String template, @Nullable Object @Nullable ... args) { template = String.valueOf(template); // null -> "null" if (args == null) { @@ -300,13 +298,14 @@ public static String lenientFormat( return builder.toString(); } - private static String lenientToString(@CheckForNull Object o) { + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception + private static String lenientToString(@Nullable Object o) { if (o == null) { return "null"; } try { return o.toString(); - } catch (Exception e) { + } catch (Exception e) { // sneaky checked exception // Default toString() behavior - see Object.toString() String objectToString = o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o)); diff --git a/android/guava/src/com/google/common/base/Supplier.java b/android/guava/src/com/google/common/base/Supplier.java index f9e1e34f17a0..85a05229aa0a 100644 --- a/android/guava/src/com/google/common/base/Supplier.java +++ b/android/guava/src/com/google/common/base/Supplier.java @@ -15,8 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A class that can supply objects of a single type; a pre-Java-8 version of {@link @@ -46,7 +45,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Supplier { /** * Retrieves an instance of the appropriate type. The returned object may or may not be a new @@ -54,7 +52,6 @@ public interface Supplier { * * @return an instance of the appropriate type */ - @CanIgnoreReturnValue @ParametricNullness T get(); } diff --git a/android/guava/src/com/google/common/base/Suppliers.java b/android/guava/src/com/google/common/base/Suppliers.java index 6ced905b988d..28cf8e2a4b6d 100644 --- a/android/guava/src/com/google/common/base/Suppliers.java +++ b/android/guava/src/com/google/common/base/Suppliers.java @@ -14,17 +14,21 @@ package com.google.common.base; +import static com.google.common.base.Internal.toNanosSaturated; import static com.google.common.base.NullnessCasts.uncheckedCastNullableTToT; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; +import java.time.Duration; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Useful suppliers. @@ -35,8 +39,7 @@ * @author Harry Heymann * @since 2.0 */ -@GwtCompatible -@ElementTypesAreNonnullByDefault +@GwtCompatible(emulated = true) public final class Suppliers { private Suppliers() {} @@ -68,7 +71,7 @@ public T get() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof SupplierComposition) { SupplierComposition that = (SupplierComposition) obj; return function.equals(that.function) && supplier.equals(that.supplier); @@ -86,7 +89,7 @@ public String toString() { return "Suppliers.compose(" + function + ", " + supplier + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -97,7 +100,7 @@ public String toString() { *

    The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at * most once unless the underlying {@code get()} throws an exception. The supplier's serialized * form does not contain the cached value, which will be recalculated when {@code get()} is called - * on the reserialized instance. + * on the deserialized instance. * *

    When the underlying delegate throws an exception then this memoizing supplier will keep * delegating calls until it returns valid data. @@ -117,11 +120,13 @@ public String toString() { @VisibleForTesting static class MemoizingSupplier implements Supplier, Serializable { + private transient Object lock = new Object(); + final Supplier delegate; transient volatile boolean initialized; // "value" does not need to be volatile; visibility piggy-backs // on volatile read of "initialized". - @CheckForNull transient T value; + transient @Nullable T value; MemoizingSupplier(Supplier delegate) { this.delegate = checkNotNull(delegate); @@ -129,10 +134,12 @@ static class MemoizingSupplier implements Supplier implements Supplier { - @CheckForNull volatile Supplier delegate; - volatile boolean initialized; - // "value" does not need to be volatile; visibility piggy-backs - // on volatile read of "initialized". - @CheckForNull T value; + private final Object lock = new Object(); + + @SuppressWarnings("UnnecessaryLambda") // Must be a fixed singleton object + private static final Supplier<@Nullable Void> SUCCESSFULLY_COMPUTED = + () -> { + throw new IllegalStateException(); // Should never get called. + }; + + private volatile Supplier delegate; + // "value" does not need to be volatile; visibility piggy-backs on volatile read of "delegate". + private @Nullable T value; NonSerializableMemoizingSupplier(Supplier delegate) { this.delegate = checkNotNull(delegate); @@ -169,27 +189,20 @@ static class NonSerializableMemoizingSupplier implem @Override @ParametricNullness + @SuppressWarnings("unchecked") // Cast from Supplier to Supplier is always valid public T get() { - // A 2-field variant of Double Checked Locking. - if (!initialized) { - synchronized (this) { - if (!initialized) { - /* - * requireNonNull is safe because we read and write `delegate` under synchronization. - * - * TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton - * `Supplier` that always throws an exception. - */ - T t = requireNonNull(delegate).get(); + // Because Supplier is read-heavy, we use the "double-checked locking" pattern. + if (delegate != SUCCESSFULLY_COMPUTED) { + synchronized (lock) { + if (delegate != SUCCESSFULLY_COMPUTED) { + T t = delegate.get(); value = t; - initialized = true; - // Release the delegate to GC. - delegate = null; + delegate = (Supplier) SUCCESSFULLY_COMPUTED; return t; } } } - // This is safe because we checked `initialized.` + // This is safe because we checked `delegate`. return uncheckedCastNullableTToT(value); } @@ -197,7 +210,9 @@ public T get() { public String toString() { Supplier delegate = this.delegate; return "Suppliers.memoize(" - + (delegate == null ? "" : delegate) + + (delegate == SUCCESSFULLY_COMPUTED + ? "" + : delegate) + ")"; } } @@ -223,30 +238,68 @@ public String toString() { * @throws IllegalArgumentException if {@code duration} is not positive * @since 2.0 */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // Prefer the Duration overload public static Supplier memoizeWithExpiration( Supplier delegate, long duration, TimeUnit unit) { - return new ExpiringMemoizingSupplier<>(delegate, duration, unit); + checkNotNull(delegate); + checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit); + return new ExpiringMemoizingSupplier<>(delegate, unit.toNanos(duration)); + } + + /** + * Returns a supplier that caches the instance supplied by the delegate and removes the cached + * value after the specified time has passed. Subsequent calls to {@code get()} return the cached + * value if the expiration time has not passed. After the expiration time, a new value is + * retrieved, cached, and returned. See: memoization + * + *

    The returned supplier is thread-safe. The supplier's serialized form does not contain the + * cached value, which will be recalculated when {@code get()} is called on the reserialized + * instance. The actual memoization does not happen when the underlying delegate throws an + * exception. + * + *

    When the underlying delegate throws an exception then this memoizing supplier will keep + * delegating calls until it returns valid data. + * + * @param duration the length of time after a value is created that it should stop being returned + * by subsequent {@code get()} calls + * @throws IllegalArgumentException if {@code duration} is not positive + * @since 33.1.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @SuppressWarnings("Java7ApiChecker") // no more dangerous that wherever the user got the Duration + @IgnoreJRERequirement + public static Supplier memoizeWithExpiration( + Supplier delegate, Duration duration) { + checkNotNull(delegate); + // The alternative of `duration.compareTo(Duration.ZERO) > 0` causes J2ObjC trouble. + checkArgument( + !duration.isNegative() && !duration.isZero(), "duration (%s) must be > 0", duration); + return new ExpiringMemoizingSupplier<>(delegate, toNanosSaturated(duration)); } @VisibleForTesting @SuppressWarnings("GoodTime") // lots of violations static class ExpiringMemoizingSupplier implements Supplier, Serializable { + private transient Object lock = new Object(); + final Supplier delegate; final long durationNanos; - @CheckForNull transient volatile T value; + transient volatile @Nullable T value; // The special value 0 means "not yet initialized". transient volatile long expirationNanos; - ExpiringMemoizingSupplier(Supplier delegate, long duration, TimeUnit unit) { - this.delegate = checkNotNull(delegate); - this.durationNanos = unit.toNanos(duration); - checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit); + ExpiringMemoizingSupplier(Supplier delegate, long durationNanos) { + this.delegate = delegate; + this.durationNanos = durationNanos; } @Override @ParametricNullness + // We set the field only once (during construction or deserialization). + @SuppressWarnings("SynchronizeOnNonFinalField") public T get() { // Another variant of Double Checked Locking. // @@ -255,9 +308,9 @@ public T get() { // the extra memory consumption and indirection are more // expensive than the extra volatile reads. long nanos = expirationNanos; - long now = Platform.systemNanoTime(); + long now = System.nanoTime(); if (nanos == 0 || now - nanos >= 0) { - synchronized (this) { + synchronized (lock) { if (nanos == expirationNanos) { // recheck for lost race T t = delegate.get(); value = t; @@ -269,7 +322,7 @@ public T get() { } } } - // This is safe because we checked `expirationNanos.` + // This is safe because we checked `expirationNanos`. return uncheckedCastNullableTToT(value); } @@ -280,7 +333,14 @@ public String toString() { return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)"; } - private static final long serialVersionUID = 0; + @GwtIncompatible // serialization + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + lock = new Object(); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** Returns a supplier that always supplies {@code instance}. */ @@ -304,7 +364,7 @@ public T get() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof SupplierOfInstance) { SupplierOfInstance that = (SupplierOfInstance) obj; return Objects.equal(instance, that.instance); @@ -322,18 +382,20 @@ public String toString() { return "Suppliers.ofInstance(" + instance + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling * it, making it thread-safe. */ + @J2ktIncompatible public static Supplier synchronizedSupplier( Supplier delegate) { return new ThreadSafeSupplier<>(delegate); } + @J2ktIncompatible private static class ThreadSafeSupplier implements Supplier, Serializable { final Supplier delegate; @@ -355,14 +417,14 @@ public String toString() { return "Suppliers.synchronizedSupplier(" + delegate + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns a function that accepts a supplier and returns the result of invoking {@link * Supplier#get} on that supplier. * - *

    Java 8 users: use the method reference {@code Supplier::get} instead. + *

    Java 8+ users: use the method reference {@code Supplier::get} instead. * * @since 8.0 */ @@ -379,8 +441,7 @@ private enum SupplierFunctionImpl implements SupplierFunction<@Nullable Object> // Note: This makes T a "pass-through type" @Override - @CheckForNull - public Object apply(Supplier<@Nullable Object> input) { + public @Nullable Object apply(Supplier<@Nullable Object> input) { return input.get(); } diff --git a/android/guava/src/com/google/common/base/Throwables.java b/android/guava/src/com/google/common/base/Throwables.java index 772825eedb4f..319828d8990b 100644 --- a/android/guava/src/com/google/common/base/Throwables.java +++ b/android/guava/src/com/google/common/base/Throwables.java @@ -19,9 +19,9 @@ import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -33,7 +33,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to instances of {@link Throwable}. @@ -46,7 +46,6 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Throwables { private Throwables() {} @@ -99,9 +98,10 @@ public static void throwIfInstanceOf( * null}. */ @Deprecated + @J2ktIncompatible @GwtIncompatible // throwIfInstanceOf public static void propagateIfInstanceOf( - @CheckForNull Throwable throwable, Class declaredType) throws X { + @Nullable Throwable throwable, Class declaredType) throws X { if (throwable != null) { throwIfInstanceOf(throwable, declaredType); } @@ -138,25 +138,15 @@ public static void throwIfUnchecked(Throwable throwable) { /** * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link - * RuntimeException} or {@link Error}. Example usage: - * - *

    -   * try {
    -   *   someMethodThatCouldThrowAnything();
    -   * } catch (IKnowWhatToDoWithThisException e) {
    -   *   handle(e);
    -   * } catch (Throwable t) {
    -   *   Throwables.propagateIfPossible(t);
    -   *   throw new RuntimeException("unexpected", t);
    -   * }
    -   * 
    + * RuntimeException} or {@link Error}. * * @deprecated Use {@link #throwIfUnchecked}, which has the same behavior but rejects {@code * null}. */ @Deprecated + @J2ktIncompatible @GwtIncompatible - public static void propagateIfPossible(@CheckForNull Throwable throwable) { + public static void propagateIfPossible(@Nullable Throwable throwable) { if (throwable != null) { throwIfUnchecked(throwable); } @@ -164,43 +154,41 @@ public static void propagateIfPossible(@CheckForNull Throwable throwable) { /** * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link - * RuntimeException}, {@link Error}, or {@code declaredType}. Example usage: + * RuntimeException}, {@link Error}, or {@code declaredType}. * - *
    -   * try {
    -   *   someMethodThatCouldThrowAnything();
    -   * } catch (IKnowWhatToDoWithThisException e) {
    -   *   handle(e);
    -   * } catch (Throwable t) {
    -   *   Throwables.propagateIfPossible(t, OtherException.class);
    -   *   throw new RuntimeException("unexpected", t);
    -   * }
    -   * 
    + *

    Discouraged in favor of calling {@link #throwIfInstanceOf} and {@link + * #throwIfUnchecked}. * * @param throwable the Throwable to possibly propagate * @param declaredType the single checked exception type declared by the calling method + * @deprecated Use a combination of {@link #throwIfInstanceOf} and {@link #throwIfUnchecked}, + * which togther provide the same behavior except that they reject {@code null}. */ + @Deprecated + @J2ktIncompatible @GwtIncompatible // propagateIfInstanceOf public static void propagateIfPossible( - @CheckForNull Throwable throwable, Class declaredType) throws X { + @Nullable Throwable throwable, Class declaredType) throws X { propagateIfInstanceOf(throwable, declaredType); propagateIfPossible(throwable); } /** * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link - * RuntimeException}, {@link Error}, {@code declaredType1}, or {@code declaredType2}. In the - * unlikely case that you have three or more declared checked exception types, you can handle them - * all by invoking these methods repeatedly. See usage example in {@link - * #propagateIfPossible(Throwable, Class)}. + * RuntimeException}, {@link Error}, {@code declaredType1}, or {@code declaredType2}. * * @param throwable the Throwable to possibly propagate * @param declaredType1 any checked exception type declared by the calling method * @param declaredType2 any other checked exception type declared by the calling method + * @deprecated Use a combination of two calls to {@link #throwIfInstanceOf} and one call to {@link + * #throwIfUnchecked}, which togther provide the same behavior except that they reject {@code + * null}. */ + @Deprecated + @J2ktIncompatible @GwtIncompatible // propagateIfInstanceOf public static void propagateIfPossible( - @CheckForNull Throwable throwable, Class declaredType1, Class declaredType2) + @Nullable Throwable throwable, Class declaredType1, Class declaredType2) throws X1, X2 { checkNotNull(declaredType2); propagateIfInstanceOf(throwable, declaredType1); @@ -230,12 +218,15 @@ public static void propagateIfPossi * @param throwable the Throwable to propagate * @return nothing will ever be returned; this return type is only for your convenience, as * illustrated in the example above - * @deprecated Use {@code throw e} or {@code throw new RuntimeException(e)} directly, or use a - * combination of {@link #throwIfUnchecked} and {@code throw new RuntimeException(e)}. For - * background on the deprecation, read Why we deprecated - * {@code Throwables.propagate}. + * @deprecated To preserve behavior, use {@code throw e} or {@code throw new RuntimeException(e)} + * directly, or use a combination of {@link #throwIfUnchecked} and {@code throw new + * RuntimeException(e)}. But consider whether users would be better off if your API threw a + * different type of exception. For background on the deprecation, read Why we + * deprecated {@code Throwables.propagate}. */ @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible @Deprecated public static RuntimeException propagate(Throwable throwable) { @@ -290,7 +281,6 @@ public static Throwable getRootCause(Throwable throwable) { * @return an unmodifiable list containing the cause chain starting with {@code throwable} * @throws IllegalArgumentException if there is a loop in the causal chain */ - @Beta // TODO(kevinb): decide best return type public static List getCausalChain(Throwable throwable) { checkNotNull(throwable); List causes = new ArrayList<>(4); @@ -330,10 +320,8 @@ public static List getCausalChain(Throwable throwable) { * ClassCastException}'s cause is {@code throwable}. * @since 22.0 */ - @Beta @GwtIncompatible // Class.cast(Object) - @CheckForNull - public static X getCauseAs( + public static @Nullable X getCauseAs( Throwable throwable, Class expectedCauseType) { try { return expectedCauseType.cast(throwable.getCause()); @@ -382,11 +370,13 @@ public static String getStackTraceAsString(Throwable throwable) { * exception's creation. * * @since 19.0 + * @deprecated This method is equivalent to {@link Throwable#getStackTrace()} on JDK versions past + * JDK 8 and on all Android versions. Use {@link Throwable#getStackTrace()} directly, or where + * possible use the {@code java.lang.StackWalker.walk} method introduced in JDK 9. */ - // TODO(cpovirk): Say something about the possibility that List access could fail at runtime? - @Beta + @Deprecated + @J2ktIncompatible @GwtIncompatible // lazyStackTraceIsLazy, jlaStackTrace - // TODO(cpovirk): Consider making this available under GWT (slow implementation only). public static List lazyStackTrace(Throwable throwable) { return lazyStackTraceIsLazy() ? jlaStackTrace(throwable) @@ -398,13 +388,17 @@ public static List lazyStackTrace(Throwable throwable) { * documentation. * * @since 19.0 + * @deprecated This method always returns false on JDK versions past JDK 8 and on all Android + * versions. */ - @Beta + @Deprecated + @J2ktIncompatible @GwtIncompatible // getStackTraceElementMethod public static boolean lazyStackTraceIsLazy() { return getStackTraceElementMethod != null && getStackTraceDepthMethod != null; } + @J2ktIncompatible @GwtIncompatible // invokeAccessibleNonThrowingMethod private static List jlaStackTrace(Throwable t) { checkNotNull(t); @@ -435,6 +429,7 @@ public int size() { }; } + @J2ktIncompatible @GwtIncompatible // java.lang.reflect private static Object invokeAccessibleNonThrowingMethod( Method method, Object receiver, Object... params) { @@ -448,42 +443,43 @@ private static Object invokeAccessibleNonThrowingMethod( } /** JavaLangAccess class name to load using reflection */ - @GwtIncompatible // not used by GWT emulation + @J2ktIncompatible @GwtIncompatible // not used by GWT emulation private static final String JAVA_LANG_ACCESS_CLASSNAME = "sun.misc.JavaLangAccess"; /** SharedSecrets class name to load using reflection */ + @J2ktIncompatible @GwtIncompatible // not used by GWT emulation @VisibleForTesting static final String SHARED_SECRETS_CLASSNAME = "sun.misc.SharedSecrets"; /** Access to some fancy internal JVM internals. */ - @GwtIncompatible // java.lang.reflect - @CheckForNull - private static final Object jla = getJLA(); + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final @Nullable Object jla = getJla(); /** * The "getStackTraceElementMethod" method, only available on some JDKs so we use reflection to * find it when available. When this is null, use the slow way. */ - @GwtIncompatible // java.lang.reflect - @CheckForNull - private static final Method getStackTraceElementMethod = (jla == null) ? null : getGetMethod(); + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final @Nullable Method getStackTraceElementMethod = + (jla == null) ? null : getGetMethod(); /** * The "getStackTraceDepth" method, only available on some JDKs so we use reflection to find it * when available. When this is null, use the slow way. */ - @GwtIncompatible // java.lang.reflect - @CheckForNull - private static final Method getStackTraceDepthMethod = (jla == null) ? null : getSizeMethod(jla); + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final @Nullable Method getStackTraceDepthMethod = + (jla == null) ? null : getSizeMethod(jla); /** * Returns the JavaLangAccess class that is present in all Sun JDKs. It is not allowed in * AppEngine, and not present in non-Sun JDKs. */ + @SuppressWarnings("removal") // b/318391980 + @J2ktIncompatible @GwtIncompatible // java.lang.reflect - @CheckForNull - private static Object getJLA() { + private static @Nullable Object getJla() { try { /* * We load sun.misc.* classes using reflection since Android doesn't support these classes and @@ -507,24 +503,24 @@ private static Object getJLA() { * Returns the Method that can be used to resolve an individual StackTraceElement, or null if that * method cannot be found (it is only to be found in fairly recent JDKs). */ + @J2ktIncompatible @GwtIncompatible // java.lang.reflect - @CheckForNull - private static Method getGetMethod() { + private static @Nullable Method getGetMethod() { return getJlaMethod("getStackTraceElement", Throwable.class, int.class); } /** * Returns the Method that can be used to return the size of a stack, or null if that method * cannot be found (it is only to be found in fairly recent JDKs). Tries to test method {@link - * sun.misc.JavaLangAccess#getStackTraceDepth(Throwable)} getStackTraceDepth} prior to return it + * sun.misc.JavaLangAccess#getStackTraceDepth(Throwable) getStackTraceDepth} prior to return it * (might fail some JDKs). * *

    See Throwables#lazyStackTrace throws * UnsupportedOperationException. */ + @J2ktIncompatible @GwtIncompatible // java.lang.reflect - @CheckForNull - private static Method getSizeMethod(Object jla) { + private static @Nullable Method getSizeMethod(Object jla) { try { Method getStackTraceDepth = getJlaMethod("getStackTraceDepth", Throwable.class); if (getStackTraceDepth == null) { @@ -537,9 +533,11 @@ private static Method getSizeMethod(Object jla) { } } + @SuppressWarnings("removal") // b/318391980 + @J2ktIncompatible @GwtIncompatible // java.lang.reflect - @CheckForNull - private static Method getJlaMethod(String name, Class... parameterTypes) throws ThreadDeath { + private static @Nullable Method getJlaMethod(String name, Class... parameterTypes) + throws ThreadDeath { try { return Class.forName(JAVA_LANG_ACCESS_CLASSNAME, false, null).getMethod(name, parameterTypes); } catch (ThreadDeath death) { diff --git a/android/guava/src/com/google/common/base/Ticker.java b/android/guava/src/com/google/common/base/Ticker.java index d898735c028f..e327a4cc907d 100644 --- a/android/guava/src/com/google/common/base/Ticker.java +++ b/android/guava/src/com/google/common/base/Ticker.java @@ -28,7 +28,6 @@ * source-compatible since 9.0) */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class Ticker { /** Constructor for use by subclasses. */ protected Ticker() {} @@ -48,8 +47,9 @@ public static Ticker systemTicker() { private static final Ticker SYSTEM_TICKER = new Ticker() { @Override + @SuppressWarnings("GoodTime") // reading system time without TimeSource public long read() { - return Platform.systemNanoTime(); + return System.nanoTime(); } }; } diff --git a/android/guava/src/com/google/common/base/Utf8.java b/android/guava/src/com/google/common/base/Utf8.java index bb945a35f095..e3f392877c8b 100644 --- a/android/guava/src/com/google/common/base/Utf8.java +++ b/android/guava/src/com/google/common/base/Utf8.java @@ -18,7 +18,6 @@ import static java.lang.Character.MAX_SURROGATE; import static java.lang.Character.MIN_SURROGATE; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -36,9 +35,7 @@ * @author Clément Roux * @since 16.0 */ -@Beta @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Utf8 { /** * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, this @@ -87,7 +84,7 @@ private static int encodedLengthGeneral(CharSequence sequence, int start) { utf8Length += (0x7f - c) >>> 31; // branch free! } else { utf8Length += 2; - // jdk7+: if (Character.isSurrogate(c)) { + // We can't use Character.isSurrogate(c) here and below because of GWT. if (MIN_SURROGATE <= c && c <= MAX_SURROGATE) { // Check that we have a well-formed surrogate pair. if (Character.codePointAt(sequence, i) == c) { @@ -167,7 +164,7 @@ private static boolean isWellFormedSlowPath(byte[] bytes, int off, int end) { // Overlong? 5 most significant bits must not all be zero. || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) // Check for illegal surrogate codepoints. - || (byte1 == (byte) 0xED && (byte) 0xA0 <= byte2) + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) // Third byte trailing-byte test. || bytes[index++] > (byte) 0xBF) { return false; diff --git a/android/guava/src/com/google/common/base/Verify.java b/android/guava/src/com/google/common/base/Verify.java index b2e9f5f04faf..5ff06d11fead 100644 --- a/android/guava/src/com/google/common/base/Verify.java +++ b/android/guava/src/com/google/common/base/Verify.java @@ -18,8 +18,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static convenience methods that serve the same purpose as Java language T verifyNotNull(@CheckForNull T reference) { + public static T verifyNotNull(@Nullable T reference) { return verifyNotNull(reference, "expected a non-null reference"); } @@ -496,9 +490,9 @@ public static T verifyNotNull(@CheckForNull T reference) { */ @CanIgnoreReturnValue public static T verifyNotNull( - @CheckForNull T reference, + @Nullable T reference, String errorMessageTemplate, - @CheckForNull @Nullable Object... errorMessageArgs) { + @Nullable Object @Nullable ... errorMessageArgs) { if (reference == null) { throw new VerifyException(lenientFormat(errorMessageTemplate, errorMessageArgs)); } diff --git a/android/guava/src/com/google/common/base/VerifyException.java b/android/guava/src/com/google/common/base/VerifyException.java index 10b99dee722b..e40f5f1294bc 100644 --- a/android/guava/src/com/google/common/base/VerifyException.java +++ b/android/guava/src/com/google/common/base/VerifyException.java @@ -15,7 +15,7 @@ package com.google.common.base; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Exception thrown upon the failure of a bigThreadConstructor = getBigThreadConstructor(); + private static final @Nullable Constructor bigThreadConstructor = + getBigThreadConstructor(); - @CheckForNull - private static final Field inheritableThreadLocals = + private static final @Nullable Field inheritableThreadLocals = (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null; /** Constructs a new finalizer thread. */ @@ -153,47 +151,67 @@ public void run() { } /** - * Cleans up a single reference. Catches and logs all throwables. + * Cleans up the given reference and any other references already in the queue. Catches and logs + * all throwables. * - * @return true if the caller should continue, false if the associated FinalizableReferenceQueue - * is no longer referenced. + * @return true if the caller should continue to wait for more references to be added to the + * queue, false if the associated FinalizableReferenceQueue is no longer referenced. */ - private boolean cleanUp(Reference reference) { + private boolean cleanUp(Reference firstReference) { Method finalizeReferentMethod = getFinalizeReferentMethod(); if (finalizeReferentMethod == null) { return false; } - do { - /* - * This is for the benefit of phantom references. Weak and soft references will have already - * been cleared by this point. - */ - reference.clear(); - if (reference == frqReference) { - /* - * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. - */ + if (!finalizeReference(firstReference, finalizeReferentMethod)) { + return false; + } + + /* + * Loop as long as we have references available so as not to waste CPU looking up the Method + * over and over again. + */ + while (true) { + Reference furtherReference = queue.poll(); + if (furtherReference == null) { + return true; + } + if (!finalizeReference(furtherReference, finalizeReferentMethod)) { return false; } + } + } - try { - finalizeReferentMethod.invoke(reference); - } catch (Throwable t) { - logger.log(Level.SEVERE, "Error cleaning up after reference.", t); - } + /** + * Cleans up the given reference. Catches and logs all throwables. + * + * @return true if the caller should continue to clean up references from the queue, false if the + * associated FinalizableReferenceQueue is no longer referenced. + */ + private boolean finalizeReference(Reference reference, Method finalizeReferentMethod) { + /* + * This is for the benefit of phantom references. Weak and soft references will have already + * been cleared by this point. + */ + reference.clear(); + if (reference == frqReference) { /* - * Loop as long as we have references available so as not to waste CPU looking up the Method - * over and over again. + * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. */ - } while ((reference = queue.poll()) != null); + return false; + } + + try { + finalizeReferentMethod.invoke(reference); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", t); + } return true; } /** Looks up FinalizableReference.finalizeReferent() method. */ - @CheckForNull - private Method getFinalizeReferentMethod() { + private @Nullable Method getFinalizeReferentMethod() { Class finalizableReferenceClass = finalizableReferenceClassReference.get(); if (finalizableReferenceClass == null) { /* @@ -211,8 +229,7 @@ private Method getFinalizeReferentMethod() { } } - @CheckForNull - private static Field getInheritableThreadLocalsField() { + private static @Nullable Field getInheritableThreadLocalsField() { try { Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); inheritableThreadLocals.setAccessible(true); @@ -226,8 +243,7 @@ private static Field getInheritableThreadLocalsField() { } } - @CheckForNull - private static Constructor getBigThreadConstructor() { + private static @Nullable Constructor getBigThreadConstructor() { try { return Thread.class.getConstructor( ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); diff --git a/android/guava/src/com/google/common/base/package-info.java b/android/guava/src/com/google/common/base/package-info.java index f2218562e82b..c73391c3b4e4 100644 --- a/android/guava/src/com/google/common/base/package-info.java +++ b/android/guava/src/com/google/common/base/package-info.java @@ -15,49 +15,50 @@ /** * Basic utility libraries and interfaces. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. * *

    Contents

    * - *

    String-related utilities

    + * The classes in this package that are most commonly useful are: + * + *

    String utilities

    * *
      - *
    • {@link com.google.common.base.Ascii} - *
    • {@link com.google.common.base.CaseFormat} - *
    • {@link com.google.common.base.CharMatcher} - *
    • {@link com.google.common.base.Charsets} - *
    • {@link com.google.common.base.Joiner} - *
    • {@link com.google.common.base.Splitter} - *
    • {@link com.google.common.base.Strings} + *
    • {@link Ascii} + *
    • {@link CaseFormat} + *
    • {@link CharMatcher} + *
    • {@link Splitter} + *
    • {@link Strings} *
    * *

    Function types

    * *
      - *
    • {@link com.google.common.base.Function}, {@link com.google.common.base.Functions} - *
    • {@link com.google.common.base.Predicate}, {@link com.google.common.base.Predicates} - *
    • {@link com.google.common.base.Equivalence} - *
    • {@link com.google.common.base.Converter} - *
    • {@link com.google.common.base.Supplier}, {@link com.google.common.base.Suppliers} + *
    • {@link Converter} + *
    • {@link Equivalence} *
    * *

    Other

    * *
      - *
    • {@link com.google.common.base.Defaults} - *
    • {@link com.google.common.base.Enums} - *
    • {@link com.google.common.base.Objects} - *
    • {@link com.google.common.base.Optional} - *
    • {@link com.google.common.base.Preconditions} - *
    • {@link com.google.common.base.Stopwatch} - *
    • {@link com.google.common.base.Throwables} + *
    • {@link Enums} + *
    • {@link MoreObjects} + *
    • {@link Preconditions} + *
    • {@link StandardSystemProperty} + *
    • {@link Stopwatch} + *
    • {@link Throwables} + *
    • {@link Verify} *
    * + *

    The rest

    + * + * This package also contains some classes with niche use cases (e.g., {@link Utf8} and {@link + * Defaults}), as well as a number of classes that have been superseded by additions to the JDK. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.base; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/cache/AbstractCache.java b/android/guava/src/com/google/common/cache/AbstractCache.java index eec5fdc34f56..bb5596ff58d5 100644 --- a/android/guava/src/com/google/common/cache/AbstractCache.java +++ b/android/guava/src/com/google/common/cache/AbstractCache.java @@ -38,13 +38,14 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class AbstractCache implements Cache { /** Constructor for use by subclasses. */ protected AbstractCache() {} - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public V get(K key, Callable valueLoader) throws ExecutionException { throw new UnsupportedOperationException(); @@ -79,13 +80,17 @@ public ImmutableMap getAllPresent(Iterable keys) { return ImmutableMap.copyOf(result); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public void put(K key, V value) { throw new UnsupportedOperationException(); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override public void putAll(Map m) { for (Entry entry : m.entrySet()) { @@ -106,7 +111,9 @@ public void invalidate(Object key) { throw new UnsupportedOperationException(); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override // For discussion of , see getAllPresent. public void invalidateAll(Iterable keys) { @@ -210,13 +217,17 @@ public static final class SimpleStatsCounter implements StatsCounter { /** Constructs an instance with all counts initialized to zero. */ public SimpleStatsCounter() {} - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public void recordHits(int count) { hitCount.add(count); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public void recordMisses(int count) { missCount.add(count); diff --git a/android/guava/src/com/google/common/cache/AbstractLoadingCache.java b/android/guava/src/com/google/common/cache/AbstractLoadingCache.java index 489597c51843..cc8df3faafef 100644 --- a/android/guava/src/com/google/common/cache/AbstractLoadingCache.java +++ b/android/guava/src/com/google/common/cache/AbstractLoadingCache.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -38,13 +39,13 @@ * @since 11.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class AbstractLoadingCache extends AbstractCache implements LoadingCache { /** Constructor for use by subclasses. */ protected AbstractLoadingCache() {} + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this? @Override public V getUnchecked(K key) { try { diff --git a/android/guava/src/com/google/common/cache/Cache.java b/android/guava/src/com/google/common/cache/Cache.java index 1234bb6a3e96..2a6925d31f1d 100644 --- a/android/guava/src/com/google/common/cache/Cache.java +++ b/android/guava/src/com/google/common/cache/Cache.java @@ -18,14 +18,14 @@ import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ExecutionError; import com.google.common.util.concurrent.UncheckedExecutionException; -import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CompatibleWith; import com.google.errorprone.annotations.DoNotMock; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A semi-persistent mapping from keys to values. Cache entries are manually added using {@link @@ -42,7 +42,6 @@ */ @DoNotMock("Use CacheBuilder.newBuilder().build()") @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Cache { /** @@ -51,8 +50,8 @@ public interface Cache { * * @since 11.0 */ - @CheckForNull - V getIfPresent(@CompatibleWith("K") Object key); + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this? + @Nullable V getIfPresent(@CompatibleWith("K") Object key); /** * Returns the value associated with {@code key} in this cache, obtaining that value from {@code @@ -100,6 +99,7 @@ public interface Cache { * @throws ExecutionError if an error was thrown while loading the value * @since 11.0 */ + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this V get(K key, Callable loader) throws ExecutionException; /** @@ -150,7 +150,6 @@ public interface Cache { void invalidateAll(); /** Returns the approximate number of entries in this cache. */ - @CheckReturnValue long size(); /** @@ -164,7 +163,6 @@ public interface Cache { * all values is returned. * */ - @CheckReturnValue CacheStats stats(); /** @@ -180,7 +178,6 @@ public interface Cache { * {@code ConcurrentMap} documentation. They will not function correctly and it is impossible for * Guava to fix them until Guava is ready to require Java 8 for all users. */ - @CheckReturnValue ConcurrentMap asMap(); /** diff --git a/android/guava/src/com/google/common/cache/CacheBuilder.java b/android/guava/src/com/google/common/cache/CacheBuilder.java index 227b2c22dcd1..957cbf04a928 100644 --- a/android/guava/src/com/google/common/cache/CacheBuilder.java +++ b/android/guava/src/com/google/common/cache/CacheBuilder.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -29,17 +30,18 @@ import com.google.common.cache.AbstractCache.SimpleStatsCounter; import com.google.common.cache.AbstractCache.StatsCounter; import com.google.common.cache.LocalCache.Strength; -import com.google.errorprone.annotations.CheckReturnValue; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.j2objc.annotations.J2ObjCIncompatible; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A builder of {@link LoadingCache} and {@link Cache} instances. @@ -49,8 +51,10 @@ * *

    The successor to Guava's caching API is Caffeine. Its API is designed to make it a - * nearly drop-in replacement -- though it requires Java 8 APIs and is not available for Android or - * GWT/j2cl. Its equivalent to {@code CacheBuilder} is its different (usually better) + * behavior when multiple threads attempt concurrent mutations. Its equivalent to {@code + * CacheBuilder} is its {@code * Caffeine} class. Caffeine offers better performance, more features (including asynchronous * loading), and fewer accumulation of cache access statistics * * - *

    These features are all optional; caches can be created using all or none of them. By default + *

    These features are all optional; caches can be created using all or none of them. By default, * cache instances created by {@code CacheBuilder} will not perform any type of eviction. * *

    Usage example: @@ -103,7 +107,7 @@ *

    {@code
      * LoadingCache graphs = CacheBuilder.newBuilder()
      *     .maximumSize(10000)
    - *     .expireAfterWrite(10, TimeUnit.MINUTES)
    + *     .expireAfterWrite(Duration.ofMinutes(10))
      *     .removalListener(MY_LISTENER)
      *     .build(
      *         new CacheLoader() {
    @@ -129,13 +133,11 @@
      *         });
      * }
    * - *

    The returned cache is implemented as a hash table with similar performance characteristics to - * {@link ConcurrentHashMap}. It implements all optional operations of the {@link LoadingCache} and - * {@link Cache} interfaces. The {@code asMap} view (and its collection views) have weakly - * consistent iterators. This means that they are safe for concurrent use, but if other threads - * modify the cache after the iterator is created, it is undefined which of these changes, if any, - * are reflected in that iterator. These iterators never throw {@link - * ConcurrentModificationException}. + *

    The returned cache implements all optional operations of the {@link LoadingCache} and {@link + * Cache} interfaces. The {@code asMap} view (and its collection views) have weakly consistent + * iterators. This means that they are safe for concurrent use, but if other threads modify the + * cache after the iterator is created, it is undefined which of these changes, if any, are + * reflected in that iterator. These iterators never throw {@link ConcurrentModificationException}. * *

    Note: by default, the returned cache uses equality comparisons (the {@link * Object#equals equals} method) to determine equality for keys or values. However, if {@link @@ -143,34 +145,33 @@ * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the cache uses identity * comparisons for values. * - *

    Entries are automatically evicted from the cache when any of {@linkplain #maximumSize(long) - * maximumSize}, {@linkplain #maximumWeight(long) maximumWeight}, {@linkplain #expireAfterWrite - * expireAfterWrite}, {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys - * weakKeys}, {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} are - * requested. + *

    Entries are automatically evicted from the cache when any of {@link #maximumSize(long) + * maximumSize}, {@link #maximumWeight(long) maximumWeight}, {@link #expireAfterWrite + * expireAfterWrite}, {@link #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, + * {@link #weakValues weakValues}, or {@link #softValues softValues} are requested. * - *

    If {@linkplain #maximumSize(long) maximumSize} or {@linkplain #maximumWeight(long) - * maximumWeight} is requested entries may be evicted on each cache modification. + *

    If {@link #maximumSize(long) maximumSize} or {@link #maximumWeight(long) maximumWeight} is + * requested entries may be evicted on each cache modification. * - *

    If {@linkplain #expireAfterWrite expireAfterWrite} or {@linkplain #expireAfterAccess - * expireAfterAccess} is requested entries may be evicted on each cache modification, on occasional - * cache accesses, or on calls to {@link Cache#cleanUp}. Expired entries may be counted by {@link - * Cache#size}, but will never be visible to read or write operations. + *

    If {@link #expireAfterWrite expireAfterWrite} or {@link #expireAfterAccess expireAfterAccess} + * is requested entries may be evicted on each cache modification, on occasional cache accesses, or + * on calls to {@link Cache#cleanUp}. Expired entries may be counted by {@link Cache#size}, but will + * never be visible to read or write operations. * - *

    If {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or {@linkplain - * #softValues softValues} are requested, it is possible for a key or value present in the cache to - * be reclaimed by the garbage collector. Entries with reclaimed keys or values may be removed from - * the cache on each cache modification, on occasional cache accesses, or on calls to {@link - * Cache#cleanUp}; such entries may be counted in {@link Cache#size}, but will never be visible to - * read or write operations. + *

    If {@link #weakKeys weakKeys}, {@link #weakValues weakValues}, or {@link #softValues + * softValues} are requested, it is possible for a key or value present in the cache to be reclaimed + * by the garbage collector. Entries with reclaimed keys or values may be removed from the cache on + * each cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}; such + * entries may be counted in {@link Cache#size}, but will never be visible to read or write + * operations. * *

    Certain cache configurations will result in the accrual of periodic maintenance tasks which * will be performed during write operations, or during occasional read operations in the absence of * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but - * calling it should not be necessary with a high throughput cache. Only caches built with - * {@linkplain #removalListener removalListener}, {@linkplain #expireAfterWrite expireAfterWrite}, - * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys}, {@linkplain - * #weakValues weakValues}, or {@linkplain #softValues softValues} perform periodic maintenance. + * calling it should not be necessary with a high throughput cache. Only caches built with {@link + * #removalListener removalListener}, {@link #expireAfterWrite expireAfterWrite}, {@link + * #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, {@link #weakValues + * weakValues}, or {@link #softValues softValues} perform periodic maintenance. * *

    The caches produced by {@code CacheBuilder} are serializable, and the deserialized caches * retain all the configuration properties of the original cache. Note that the serialized form does @@ -181,25 +182,24 @@ * explanation. * * @param the most general key type this builder will be able to create caches for. This is - * normally {@code Object} unless it is constrained by using a method like {@code + * normally {@code Object} unless it is constrained by using a method like {@link * #removalListener}. Cache keys may not be null. * @param the most general value type this builder will be able to create caches for. This is - * normally {@code Object} unless it is constrained by using a method like {@code + * normally {@code Object} unless it is constrained by using a method like {@link * #removalListener}. Cache values may not be null. * @author Charles Fry * @author Kevin Bourrillion * @since 10.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class CacheBuilder { private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final int DEFAULT_CONCURRENCY_LEVEL = 4; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration private static final int DEFAULT_EXPIRATION_NANOS = 0; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration private static final int DEFAULT_REFRESH_NANOS = 0; static final Supplier NULL_STATS_COUNTER = @@ -229,6 +229,18 @@ public CacheStats snapshot() { }); static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); + /* + * We avoid using a method reference or lambda here for now: + * + * - method reference: Inside Google, CacheBuilder is used from the implementation of a custom + * ClassLoader that is sometimes used as a system classloader. That's a problem because + * method-reference linking tries to look up the system classloader, and it fails because there + * isn't one yet. + * + * - lambda: Outside Google, we got a report of a similar problem in + * https://github.com/google/guava/issues/6565 + */ + @SuppressWarnings("AnonymousToLambda") static final Supplier CACHE_STATS_COUNTER = new Supplier() { @Override @@ -261,7 +273,10 @@ public long read() { } }; - private static final Logger logger = Logger.getLogger(CacheBuilder.class.getName()); + // We use a holder class to delay initialization: https://github.com/google/guava/issues/6566 + private static final class LoggerHolder { + static final Logger logger = Logger.getLogger(CacheBuilder.class.getName()); + } static final int UNSET_INT = -1; @@ -276,13 +291,13 @@ public long read() { @Nullable Strength keyStrength; @Nullable Strength valueStrength; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long expireAfterWriteNanos = UNSET_INT; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long expireAfterAccessNanos = UNSET_INT; - @SuppressWarnings("GoodTime") // should be a java.time.Duration + @SuppressWarnings("GoodTime") // should be a Duration long refreshNanos = UNSET_INT; @Nullable Equivalence keyEquivalence; @@ -302,7 +317,6 @@ private CacheBuilder() {} *

    Note that while this return type is {@code CacheBuilder}, type parameters on * the {@link #build} methods allow you to create a cache of any key and value type desired. */ - @CheckReturnValue public static CacheBuilder newBuilder() { return new CacheBuilder<>(); } @@ -313,7 +327,6 @@ public static CacheBuilder newBuilder() { * @since 12.0 */ @GwtIncompatible // To be supported - @CheckReturnValue public static CacheBuilder from(CacheBuilderSpec spec) { return spec.toCacheBuilder().lenientParsing(); } @@ -326,7 +339,6 @@ public static CacheBuilder from(CacheBuilderSpec spec) { * @since 12.0 */ @GwtIncompatible // To be supported - @CheckReturnValue public static CacheBuilder from(String spec) { return from(CacheBuilderSpec.parse(spec)); } @@ -337,6 +349,7 @@ public static CacheBuilder from(String spec) { * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported + @CanIgnoreReturnValue CacheBuilder lenientParsing() { strictParsing = false; return this; @@ -351,6 +364,7 @@ CacheBuilder lenientParsing() { * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported + @CanIgnoreReturnValue CacheBuilder keyEquivalence(Equivalence equivalence) { checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence); keyEquivalence = checkNotNull(equivalence); @@ -371,6 +385,7 @@ Equivalence getKeyEquivalence() { * @return this {@code CacheBuilder} instance (for chaining) */ @GwtIncompatible // To be supported + @CanIgnoreReturnValue CacheBuilder valueEquivalence(Equivalence equivalence) { checkState( valueEquivalence == null, "value equivalence was already set to %s", valueEquivalence); @@ -393,6 +408,7 @@ Equivalence getValueEquivalence() { * @throws IllegalArgumentException if {@code initialCapacity} is negative * @throws IllegalStateException if an initial capacity was already set */ + @CanIgnoreReturnValue public CacheBuilder initialCapacity(int initialCapacity) { checkState( this.initialCapacity == UNSET_INT, @@ -438,6 +454,7 @@ int getInitialCapacity() { * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive * @throws IllegalStateException if a concurrency level was already set */ + @CanIgnoreReturnValue public CacheBuilder concurrencyLevel(int concurrencyLevel) { checkState( this.concurrencyLevel == UNSET_INT, @@ -473,6 +490,7 @@ int getConcurrencyLevel() { * @throws IllegalArgumentException if {@code maximumSize} is negative * @throws IllegalStateException if a maximum size or weight was already set */ + @CanIgnoreReturnValue public CacheBuilder maximumSize(long maximumSize) { checkState( this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); @@ -514,6 +532,7 @@ public CacheBuilder maximumSize(long maximumSize) { * @since 11.0 */ @GwtIncompatible // To be supported + @CanIgnoreReturnValue public CacheBuilder maximumWeight(long maximumWeight) { checkState( this.maximumWeight == UNSET_INT, @@ -551,18 +570,19 @@ public CacheBuilder maximumWeight(long maximumWeight) { * * @param weigher the weigher to use in calculating the weight of cache entries * @return this {@code CacheBuilder} instance (for chaining) - * @throws IllegalArgumentException if {@code size} is negative - * @throws IllegalStateException if a maximum size was already set + * @throws IllegalStateException if a weigher was already set or {@link #maximumSize(long)} was + * previously called * @since 11.0 */ @GwtIncompatible // To be supported + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this public CacheBuilder weigher( Weigher weigher) { checkState(this.weigher == null); if (strictParsing) { checkState( this.maximumSize == UNSET_INT, - "weigher can not be combined with maximum size", + "weigher can not be combined with maximum size (%s provided)", this.maximumSize); } @@ -603,10 +623,12 @@ Weigher getWeigher() { * @throws IllegalStateException if the key strength was already set */ @GwtIncompatible // java.lang.ref.WeakReference + @CanIgnoreReturnValue public CacheBuilder weakKeys() { return setKeyStrength(Strength.WEAK); } + @CanIgnoreReturnValue CacheBuilder setKeyStrength(Strength strength) { checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); keyStrength = checkNotNull(strength); @@ -635,6 +657,7 @@ Strength getKeyStrength() { * @throws IllegalStateException if the value strength was already set */ @GwtIncompatible // java.lang.ref.WeakReference + @CanIgnoreReturnValue public CacheBuilder weakValues() { return setValueStrength(Strength.WEAK); } @@ -660,10 +683,12 @@ public CacheBuilder weakValues() { * @throws IllegalStateException if the value strength was already set */ @GwtIncompatible // java.lang.ref.SoftReference + @CanIgnoreReturnValue public CacheBuilder softValues() { return setValueStrength(Strength.SOFT); } + @CanIgnoreReturnValue CacheBuilder setValueStrength(Strength strength) { checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); valueStrength = checkNotNull(strength); @@ -688,12 +713,49 @@ Strength getValueStrength() { * * @param duration the length of time after an entry is created that it should be automatically * removed + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterWrite} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 33.3.0 (but since 25.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + @CanIgnoreReturnValue + public CacheBuilder expireAfterWrite(Duration duration) { + return expireAfterWrite(toNanosSaturated(duration), NANOSECONDS); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

    When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

    Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + *

    If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed * @param unit the unit that {@code duration} is expressed in * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterWrite} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration + @CanIgnoreReturnValue public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { checkState( expireAfterWriteNanos == UNSET_INT, @@ -709,6 +771,44 @@ long getExpireAfterWriteNanos() { return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; } + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code + * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}}. So, + * for example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for + * the entries you retrieve. + * + *

    When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

    Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterAccess} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 33.3.0 (but since 25.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + @CanIgnoreReturnValue + public CacheBuilder expireAfterAccess(Duration duration) { + return expireAfterAccess(toNanosSaturated(duration), NANOSECONDS); + } + /** * Specifies that each entry should be automatically removed from the cache once a fixed duration * has elapsed after the entry's creation, the most recent replacement of its value, or its last @@ -726,6 +826,9 @@ long getExpireAfterWriteNanos() { * write operations. Expired entries are cleaned up as part of the routine maintenance described * in the class javadoc. * + *

    If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #expireAfterAccess(Duration)} instead. + * * @param duration the length of time after an entry is last accessed that it should be * automatically removed * @param unit the unit that {@code duration} is expressed in @@ -733,7 +836,8 @@ long getExpireAfterWriteNanos() { * @throws IllegalArgumentException if {@code duration} is negative * @throws IllegalStateException if {@link #expireAfterAccess} was already set */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration + @CanIgnoreReturnValue public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { checkState( expireAfterAccessNanos == UNSET_INT, @@ -751,6 +855,46 @@ long getExpireAfterAccessNanos() { : expireAfterAccessNanos; } + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

    As the default implementation of {@link CacheLoader#reload} is synchronous, it is + * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous + * implementation; otherwise refreshes will be performed during unrelated cache read and write + * operations. + * + *

    Currently automatic refreshes are performed when the first stale request for an entry + * occurs. The request triggering refresh will make a synchronous call to {@link + * CacheLoader#reload} + * to obtain a future of the new value. If the returned future is already complete, it is returned + * immediately. Otherwise, the old value is returned. + * + *

    Note: all exceptions thrown during refresh will be logged and then swallowed. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #refreshAfterWrite} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 33.3.0 (but since 25.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + @CanIgnoreReturnValue + public CacheBuilder refreshAfterWrite(Duration duration) { + return refreshAfterWrite(toNanosSaturated(duration), NANOSECONDS); + } + /** * Specifies that active entries are eligible for automatic refresh once a fixed duration has * elapsed after the entry's creation, or the most recent replacement of its value. The semantics @@ -770,6 +914,9 @@ long getExpireAfterAccessNanos() { * *

    Note: all exceptions thrown during refresh will be logged and then swallowed. * + *

    If you can represent the duration as a {@link Duration} (which should be preferred when + * feasible), use {@link #refreshAfterWrite(Duration)} instead. + * * @param duration the length of time after an entry is created that it should be considered * stale, and thus eligible for refresh * @param unit the unit that {@code duration} is expressed in @@ -779,7 +926,8 @@ long getExpireAfterAccessNanos() { * @since 11.0 */ @GwtIncompatible // To be supported (synchronously). - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("GoodTime") // should accept a Duration + @CanIgnoreReturnValue public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { checkNotNull(unit); checkState(refreshNanos == UNSET_INT, "refresh was already set to %s ns", refreshNanos); @@ -803,6 +951,7 @@ long getRefreshNanos() { * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if a ticker was already set */ + @CanIgnoreReturnValue public CacheBuilder ticker(Ticker ticker) { checkState(this.ticker == null); this.ticker = checkNotNull(ticker); @@ -823,21 +972,19 @@ Ticker getTicker(boolean recordsTime) { * *

    Warning: after invoking this method, do not continue to use this cache builder * reference; instead use the reference this method returns. At runtime, these point to the - * same instance, but only the returned reference has the correct generic type information so as - * to ensure type safety. For best results, use the standard method-chaining idiom illustrated in - * the class documentation above, configuring a builder and building your cache in a single - * statement. Failure to heed this advice can result in a {@link ClassCastException} being thrown - * by a cache operation at some undefined point in the future. + * same instance, but only the returned reference has the correct generic type information to + * ensure type safety. For best results, use the standard method-chaining idiom illustrated in the + * class documentation above, configuring a builder and building your cache in a single statement. + * Failure to heed this advice can result in a {@link ClassCastException} being thrown by a cache + * operation at some undefined point in the future. * *

    Warning: any exception thrown by {@code listener} will not be propagated to * the {@code Cache} user, only logged via a {@link Logger}. * * @return the cache builder reference that should be used instead of {@code this} for any * remaining configuration and cache building - * @return this {@code CacheBuilder} instance (for chaining) * @throws IllegalStateException if a removal listener was already set */ - @CheckReturnValue public CacheBuilder removalListener( RemovalListener listener) { checkState(this.removalListener == null); @@ -865,6 +1012,7 @@ RemovalListener getRemovalListener() { * @return this {@code CacheBuilder} instance (for chaining) * @since 12.0 (previously, stats collection was automatic) */ + @CanIgnoreReturnValue public CacheBuilder recordStats() { statsCounterSupplier = CACHE_STATS_COUNTER; return this; @@ -890,7 +1038,6 @@ Supplier getStatsCounterSupplier() { * @param loader the cache loader used to obtain new values * @return a cache having the requested features */ - @CheckReturnValue public LoadingCache build( CacheLoader loader) { checkWeightWithWeigher(); @@ -909,7 +1056,6 @@ public LoadingCache build( * @return a cache having the requested features * @since 11.0 */ - @CheckReturnValue public Cache build() { checkWeightWithWeigher(); checkNonLoadingCache(); @@ -928,7 +1074,8 @@ private void checkWeightWithWeigher() { checkState(maximumWeight != UNSET_INT, "weigher requires maximumWeight"); } else { if (maximumWeight == UNSET_INT) { - logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight"); + LoggerHolder.logger.log( + Level.WARNING, "ignoring weigher specified without maximumWeight"); } } } @@ -976,4 +1123,27 @@ public String toString() { } return s.toString(); } + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

    Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + @GwtIncompatible // Duration + @SuppressWarnings({ + "GoodTime", // Duration decomposition + "Java7ApiChecker", + }) + @IgnoreJRERequirement // No more dangerous than wherever the caller got the Duration from + private static long toNanosSaturated(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } } diff --git a/android/guava/src/com/google/common/cache/CacheBuilderSpec.java b/android/guava/src/com/google/common/cache/CacheBuilderSpec.java index 64b5ad261990..800058fb5a28 100644 --- a/android/guava/src/com/google/common/cache/CacheBuilderSpec.java +++ b/android/guava/src/com/google/common/cache/CacheBuilderSpec.java @@ -16,6 +16,10 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; @@ -28,8 +32,7 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A specification of a {@link CacheBuilder} configuration. @@ -80,11 +83,10 @@ */ @SuppressWarnings("GoodTime") // lots of violations (nanosecond math) @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class CacheBuilderSpec { /** Parses a single value. */ private interface ValueParser { - void parse(CacheBuilderSpec spec, String key, @CheckForNull String value); + void parse(CacheBuilderSpec spec, String key, @Nullable String value); } /** Splits each key-value pair. */ @@ -108,21 +110,22 @@ private interface ValueParser { .put("expireAfterWrite", new WriteDurationParser()) .put("refreshAfterWrite", new RefreshDurationParser()) .put("refreshInterval", new RefreshDurationParser()) - .build(); - - @VisibleForTesting @CheckForNull Integer initialCapacity; - @VisibleForTesting @CheckForNull Long maximumSize; - @VisibleForTesting @CheckForNull Long maximumWeight; - @VisibleForTesting @CheckForNull Integer concurrencyLevel; - @VisibleForTesting @CheckForNull Strength keyStrength; - @VisibleForTesting @CheckForNull Strength valueStrength; - @VisibleForTesting @CheckForNull Boolean recordStats; + .buildOrThrow(); + + @VisibleForTesting @Nullable Integer initialCapacity; + @VisibleForTesting @Nullable Long maximumSize; + @VisibleForTesting @Nullable Long maximumWeight; + @VisibleForTesting @Nullable Integer concurrencyLevel; + @VisibleForTesting @Nullable Strength keyStrength; + @VisibleForTesting @Nullable Strength valueStrength; + @VisibleForTesting @Nullable Boolean recordStats; @VisibleForTesting long writeExpirationDuration; - @VisibleForTesting @CheckForNull TimeUnit writeExpirationTimeUnit; + @VisibleForTesting @Nullable TimeUnit writeExpirationTimeUnit; @VisibleForTesting long accessExpirationDuration; - @VisibleForTesting @CheckForNull TimeUnit accessExpirationTimeUnit; + @VisibleForTesting @Nullable TimeUnit accessExpirationTimeUnit; @VisibleForTesting long refreshDuration; - @VisibleForTesting @CheckForNull TimeUnit refreshTimeUnit; + @VisibleForTesting @Nullable TimeUnit refreshTimeUnit; + /** Specification; used for toParseableString(). */ private final String specification; @@ -251,7 +254,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -281,8 +284,7 @@ public boolean equals(@CheckForNull Object obj) { * Converts an expiration duration/unit pair into a single Long for hashing and equality. Uses * nanos to match CacheBuilder implementation. */ - @CheckForNull - private static Long durationInNanos(long duration, @CheckForNull TimeUnit unit) { + private static @Nullable Long durationInNanos(long duration, @Nullable TimeUnit unit) { return (unit == null) ? null : unit.toNanos(duration); } @@ -328,7 +330,7 @@ static class InitialCapacityParser extends IntegerParser { protected void parseInteger(CacheBuilderSpec spec, int value) { checkArgument( spec.initialCapacity == null, - "initial capacity was already set to ", + "initial capacity was already set to %s", spec.initialCapacity); spec.initialCapacity = value; } @@ -338,9 +340,10 @@ protected void parseInteger(CacheBuilderSpec spec, int value) { static class MaximumSizeParser extends LongParser { @Override protected void parseLong(CacheBuilderSpec spec, long value) { - checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize); checkArgument( - spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight); + spec.maximumSize == null, "maximum size was already set to %s", spec.maximumSize); + checkArgument( + spec.maximumWeight == null, "maximum weight was already set to %s", spec.maximumWeight); spec.maximumSize = value; } } @@ -350,8 +353,9 @@ static class MaximumWeightParser extends LongParser { @Override protected void parseLong(CacheBuilderSpec spec, long value) { checkArgument( - spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight); - checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize); + spec.maximumWeight == null, "maximum weight was already set to %s", spec.maximumWeight); + checkArgument( + spec.maximumSize == null, "maximum size was already set to %s", spec.maximumSize); spec.maximumWeight = value; } } @@ -362,7 +366,7 @@ static class ConcurrencyLevelParser extends IntegerParser { protected void parseInteger(CacheBuilderSpec spec, int value) { checkArgument( spec.concurrencyLevel == null, - "concurrency level was already set to ", + "concurrency level was already set to %s", spec.concurrencyLevel); spec.concurrencyLevel = value; } @@ -377,7 +381,7 @@ public KeyStrengthParser(Strength strength) { } @Override - public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) { + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { checkArgument(value == null, "key %s does not take values", key); checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength); spec.keyStrength = strength; @@ -393,7 +397,7 @@ public ValueStrengthParser(Strength strength) { } @Override - public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) { + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { checkArgument(value == null, "key %s does not take values", key); checkArgument( spec.valueStrength == null, "%s was already set to %s", key, spec.valueStrength); @@ -406,7 +410,7 @@ public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) static class RecordStatsParser implements ValueParser { @Override - public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) { + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { checkArgument(value == null, "recordStats does not take values"); checkArgument(spec.recordStats == null, "recordStats already set"); spec.recordStats = true; @@ -418,7 +422,7 @@ abstract static class DurationParser implements ValueParser { protected abstract void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit); @Override - public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) { + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { if (isNullOrEmpty(value)) { throw new IllegalArgumentException("value of key " + key + " omitted"); } @@ -427,16 +431,16 @@ public void parse(CacheBuilderSpec spec, String key, @CheckForNull String value) TimeUnit timeUnit; switch (lastChar) { case 'd': - timeUnit = TimeUnit.DAYS; + timeUnit = DAYS; break; case 'h': - timeUnit = TimeUnit.HOURS; + timeUnit = HOURS; break; case 'm': - timeUnit = TimeUnit.MINUTES; + timeUnit = MINUTES; break; case 's': - timeUnit = TimeUnit.SECONDS; + timeUnit = SECONDS; break; default: throw new IllegalArgumentException( diff --git a/android/guava/src/com/google/common/cache/CacheLoader.java b/android/guava/src/com/google/common/cache/CacheLoader.java index bb544cdcdc01..23462307776f 100644 --- a/android/guava/src/com/google/common/cache/CacheLoader.java +++ b/android/guava/src/com/google/common/cache/CacheLoader.java @@ -15,18 +15,17 @@ package com.google.common.cache; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Futures.immediateFuture; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Supplier; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFutureTask; -import com.google.errorprone.annotations.CheckReturnValue; import java.io.Serializable; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.Executor; /** @@ -57,7 +56,6 @@ * @since 10.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class CacheLoader { /** Constructor for use by subclasses. */ protected CacheLoader() {} @@ -99,7 +97,7 @@ protected CacheLoader() {} public ListenableFuture reload(K key, V oldValue) throws Exception { checkNotNull(key); checkNotNull(oldValue); - return Futures.immediateFuture(load(key)); + return immediateFuture(load(key)); } /** @@ -135,10 +133,11 @@ public Map loadAll(Iterable keys) throws Exception { * reloading or bulk loading. This is most useful when you can pass a lambda expression. Otherwise * it is useful mostly when you already have an existing function instance. * + *

    The returned object is serializable if {@code function} is serializable. + * * @param function the function to be used for loading values; must never return {@code null} * @return a cache loader that loads values by passing each key to {@code function} */ - @CheckReturnValue public static CacheLoader from(Function function) { return new FunctionToCacheLoader<>(function); } @@ -148,13 +147,14 @@ public static CacheLoader from(Function function) { * to create a new supplier just to pass it in here; just subclass {@code CacheLoader} and * implement {@link #load load} instead. * + *

    The returned object is serializable if {@code supplier} is serializable. + * * @param supplier the supplier to be used for loading values; must never return {@code null} * @return a cache loader that loads values by calling {@link Supplier#get}, irrespective of the * key */ - @CheckReturnValue public static CacheLoader from(Supplier supplier) { - return new SupplierToCacheLoader(supplier); + return new SupplierToCacheLoader<>(supplier); } private static final class FunctionToCacheLoader extends CacheLoader @@ -170,7 +170,7 @@ public V load(K key) { return computingFunction.apply(checkNotNull(key)); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -182,10 +182,9 @@ public V load(K key) { * * @since 17.0 */ - @CheckReturnValue @GwtIncompatible // Executor + Futures public static CacheLoader asyncReloading( - final CacheLoader loader, final Executor executor) { + CacheLoader loader, Executor executor) { checkNotNull(loader); checkNotNull(executor); return new CacheLoader() { @@ -195,15 +194,9 @@ public V load(K key) throws Exception { } @Override - public ListenableFuture reload(final K key, final V oldValue) throws Exception { + public ListenableFuture reload(K key, V oldValue) { ListenableFutureTask task = - ListenableFutureTask.create( - new Callable() { - @Override - public V call() throws Exception { - return loader.reload(key, oldValue).get(); - } - }); + ListenableFutureTask.create(() -> loader.reload(key, oldValue).get()); executor.execute(task); return task; } @@ -229,7 +222,7 @@ public V load(Object key) { return computingSupplier.get(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** diff --git a/android/guava/src/com/google/common/cache/CacheStats.java b/android/guava/src/com/google/common/cache/CacheStats.java index 8307e942a818..f9761489ee3c 100644 --- a/android/guava/src/com/google/common/cache/CacheStats.java +++ b/android/guava/src/com/google/common/cache/CacheStats.java @@ -17,12 +17,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.math.LongMath.saturatedAdd; import static com.google.common.math.LongMath.saturatedSubtract; +import static java.lang.Math.max; import com.google.common.annotations.GwtCompatible; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import java.util.concurrent.Callable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Statistics about the performance of a {@link Cache}. Instances of this class are immutable. @@ -57,7 +58,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class CacheStats { private final long hitCount; private final long missCount; @@ -140,7 +140,7 @@ public long missCount() { * requestCount}, or {@code 0.0} when {@code requestCount == 0}. Note that {@code hitRate + * missRate =~ 1.0}. Cache misses include all requests which weren't cache hits, including * requests which resulted in either successful or failed loading attempts, and requests which - * waited for other threads to finish loading. It is thus the case that {@code missCount >= + * waited for other threads to finish loading. It is thus the case that {@code missCount >= * loadSuccessCount + loadExceptionCount}. Multiple concurrent misses for the same key will result * in a single load operation. */ @@ -151,8 +151,8 @@ public double missRate() { /** * Returns the total number of times that {@link Cache} lookup methods attempted to load new - * values. This includes both successful load operations, as well as those that threw exceptions. - * This is defined as {@code loadSuccessCount + loadExceptionCount}. + * values. This includes both successful load operations and those that threw exceptions. This is + * defined as {@code loadSuccessCount + loadExceptionCount}. * *

    Note: the values of the metrics are undefined in case of overflow (though it is * guaranteed not to throw an exception). If you require specific handling, we recommend @@ -242,12 +242,12 @@ public long evictionCount() { */ public CacheStats minus(CacheStats other) { return new CacheStats( - Math.max(0, saturatedSubtract(hitCount, other.hitCount)), - Math.max(0, saturatedSubtract(missCount, other.missCount)), - Math.max(0, saturatedSubtract(loadSuccessCount, other.loadSuccessCount)), - Math.max(0, saturatedSubtract(loadExceptionCount, other.loadExceptionCount)), - Math.max(0, saturatedSubtract(totalLoadTime, other.totalLoadTime)), - Math.max(0, saturatedSubtract(evictionCount, other.evictionCount))); + max(0, saturatedSubtract(hitCount, other.hitCount)), + max(0, saturatedSubtract(missCount, other.missCount)), + max(0, saturatedSubtract(loadSuccessCount, other.loadSuccessCount)), + max(0, saturatedSubtract(loadExceptionCount, other.loadExceptionCount)), + max(0, saturatedSubtract(totalLoadTime, other.totalLoadTime)), + max(0, saturatedSubtract(evictionCount, other.evictionCount))); } /** @@ -277,7 +277,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof CacheStats) { CacheStats other = (CacheStats) object; return hitCount == other.hitCount diff --git a/android/guava/src/com/google/common/cache/ForwardingCache.java b/android/guava/src/com/google/common/cache/ForwardingCache.java index f118977b09a3..be7df89a3566 100644 --- a/android/guava/src/com/google/common/cache/ForwardingCache.java +++ b/android/guava/src/com/google/common/cache/ForwardingCache.java @@ -22,7 +22,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A cache which forwards all its method calls to another cache. Subclasses should override one or @@ -33,7 +33,6 @@ * @since 10.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingCache extends ForwardingObject implements Cache { /** Constructor for use by subclasses. */ @@ -42,20 +41,25 @@ protected ForwardingCache() {} @Override protected abstract Cache delegate(); - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override - @CheckForNull - public V getIfPresent(Object key) { + public @Nullable V getIfPresent(Object key) { return delegate().getIfPresent(key); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public V get(K key, Callable valueLoader) throws ExecutionException { return delegate().get(key, valueLoader); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override /* * is mostly the same as to plain Java. But to nullness checkers, they @@ -65,13 +69,17 @@ public ImmutableMap getAllPresent(Iterable keys) { return delegate().getAllPresent(keys); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override public void put(K key, V value) { delegate().put(key, value); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override public void putAll(Map m) { delegate().putAll(m); @@ -82,7 +90,9 @@ public void invalidate(Object key) { delegate().invalidate(key); } - /** @since 11.0 */ + /** + * @since 11.0 + */ @Override // For discussion of , see getAllPresent. public void invalidateAll(Iterable keys) { diff --git a/android/guava/src/com/google/common/cache/ForwardingLoadingCache.java b/android/guava/src/com/google/common/cache/ForwardingLoadingCache.java index ecd44ca8d8a9..296c44f484e2 100644 --- a/android/guava/src/com/google/common/cache/ForwardingLoadingCache.java +++ b/android/guava/src/com/google/common/cache/ForwardingLoadingCache.java @@ -17,6 +17,7 @@ import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.ExecutionException; /** @@ -31,7 +32,6 @@ * @since 11.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingLoadingCache extends ForwardingCache implements LoadingCache { @@ -41,16 +41,19 @@ protected ForwardingLoadingCache() {} @Override protected abstract LoadingCache delegate(); + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this @Override public V get(K key) throws ExecutionException { return delegate().get(key); } + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this @Override public V getUnchecked(K key) { return delegate().getUnchecked(key); } + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this @Override public ImmutableMap getAll(Iterable keys) throws ExecutionException { return delegate().getAll(keys); diff --git a/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java b/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java new file mode 100644 index 000000000000..ea6a7b733dec --- /dev/null +++ b/android/guava/src/com/google/common/cache/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.cache; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/cache/LoadingCache.java b/android/guava/src/com/google/common/cache/LoadingCache.java index e338ac4c3733..d60e9df8720b 100644 --- a/android/guava/src/com/google/common/cache/LoadingCache.java +++ b/android/guava/src/com/google/common/cache/LoadingCache.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ExecutionError; import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -39,7 +40,6 @@ * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface LoadingCache extends Cache, Function { /** @@ -67,6 +67,7 @@ public interface LoadingCache extends Cache, Function { * value * @throws ExecutionError if an error was thrown while loading the value */ + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this? V get(K key) throws ExecutionException; /** @@ -93,6 +94,7 @@ public interface LoadingCache extends Cache, Function { * explained in the last paragraph above, this should be an unchecked exception only.) * @throws ExecutionError if an error was thrown while loading the value */ + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this? V getUnchecked(K key); /** @@ -119,6 +121,7 @@ public interface LoadingCache extends Cache, Function { * @throws ExecutionError if an error was thrown while loading the values * @since 11.0 */ + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this ImmutableMap getAll(Iterable keys) throws ExecutionException; /** diff --git a/android/guava/src/com/google/common/cache/LocalCache.java b/android/guava/src/com/google/common/cache/LocalCache.java index d74db646d0dc..c4009847e70d 100644 --- a/android/guava/src/com/google/common/cache/LocalCache.java +++ b/android/guava/src/com/google/common/cache/LocalCache.java @@ -18,16 +18,20 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.cache.CacheBuilder.NULL_TICKER; import static com.google.common.cache.CacheBuilder.UNSET_INT; +import static com.google.common.util.concurrent.Futures.immediateFailedFuture; +import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.transform; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; +import static java.lang.Math.min; +import static java.util.Collections.unmodifiableSet; import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; -import com.google.common.base.Function; import com.google.common.base.Stopwatch; import com.google.common.base.Ticker; import com.google.common.cache.AbstractCache.SimpleStatsCounter; @@ -36,23 +40,25 @@ import com.google.common.cache.CacheBuilder.OneWeigher; import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException; +import com.google.common.cache.LocalCache.AbstractCacheSet; import com.google.common.collect.AbstractSequentialIterator; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import com.google.common.util.concurrent.ExecutionError; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.Uninterruptibles; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.GuardedBy; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import com.google.j2objc.annotations.Weak; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.lang.ref.Reference; @@ -63,11 +69,9 @@ import java.util.AbstractMap; import java.util.AbstractQueue; import java.util.AbstractSet; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; @@ -75,13 +79,13 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * The concurrent hash map implementation built by {@link CacheBuilder}. @@ -98,7 +102,7 @@ "nullness", // too much trouble for the payoff }) @GwtCompatible(emulated = true) -// TODO(cpovirk): Annotate for nullness. +@NullUnmarked // TODO(cpovirk): Annotate for nullness. class LocalCache extends AbstractMap implements ConcurrentMap { /* @@ -230,14 +234,14 @@ class LocalCache extends AbstractMap implements ConcurrentMap final StatsCounter globalStatsCounter; /** The default cache loader to use on loading operations. */ - @CheckForNull final CacheLoader defaultLoader; + final @Nullable CacheLoader defaultLoader; /** * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. */ LocalCache( - CacheBuilder builder, @CheckForNull CacheLoader loader) { - concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + CacheBuilder builder, @Nullable CacheLoader loader) { + concurrencyLevel = min(builder.getConcurrencyLevel(), MAX_SEGMENTS); keyStrength = builder.getKeyStrength(); valueStrength = builder.getValueStrength(); @@ -254,17 +258,17 @@ class LocalCache extends AbstractMap implements ConcurrentMap removalListener = builder.getRemovalListener(); removalNotificationQueue = (removalListener == NullListener.INSTANCE) - ? LocalCache.>discardingQueue() - : new ConcurrentLinkedQueue>(); + ? LocalCache.discardingQueue() + : new ConcurrentLinkedQueue<>(); ticker = builder.getTicker(recordsTime()); entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries()); globalStatsCounter = builder.getStatsCounterSupplier().get(); defaultLoader = loader; - int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); + int initialCapacity = min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); if (evictsBySize() && !customWeigher()) { - initialCapacity = (int) Math.min(initialCapacity, maxWeight); + initialCapacity = (int) min(initialCapacity, maxWeight); } // Find the lowest power-of-two segmentCount that exceeds concurrencyLevel, unless @@ -274,7 +278,8 @@ class LocalCache extends AbstractMap implements ConcurrentMap // will result in random eviction behavior. int segmentShift = 0; int segmentCount = 1; - while (segmentCount < concurrencyLevel && (!evictsBySize() || segmentCount * 20 <= maxWeight)) { + while (segmentCount < concurrencyLevel + && (!evictsBySize() || segmentCount * 20L <= maxWeight)) { ++segmentShift; segmentCount <<= 1; } @@ -440,21 +445,24 @@ enum EntryFactory { STRONG { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new StrongEntry<>(key, hash, next); } }, STRONG_ACCESS { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new StrongAccessEntry<>(key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyAccessEntry(original, newEntry); return newEntry; } @@ -462,14 +470,17 @@ ReferenceEntry copyEntry( STRONG_WRITE { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new StrongWriteEntry<>(key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyWriteEntry(original, newEntry); return newEntry; } @@ -477,14 +488,17 @@ ReferenceEntry copyEntry( STRONG_ACCESS_WRITE { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new StrongAccessWriteEntry<>(key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyAccessEntry(original, newEntry); copyWriteEntry(original, newEntry); return newEntry; @@ -493,21 +507,24 @@ ReferenceEntry copyEntry( WEAK { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new WeakEntry<>(segment.keyReferenceQueue, key, hash, next); } }, WEAK_ACCESS { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new WeakAccessEntry<>(segment.keyReferenceQueue, key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyAccessEntry(original, newEntry); return newEntry; } @@ -515,14 +532,17 @@ ReferenceEntry copyEntry( WEAK_WRITE { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new WeakWriteEntry<>(segment.keyReferenceQueue, key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyWriteEntry(original, newEntry); return newEntry; } @@ -530,14 +550,17 @@ ReferenceEntry copyEntry( WEAK_ACCESS_WRITE { @Override ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next) { + Segment segment, K key, int hash, @Nullable ReferenceEntry next) { return new WeakAccessWriteEntry<>(segment.keyReferenceQueue, key, hash, next); } @Override ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); copyAccessEntry(original, newEntry); copyWriteEntry(original, newEntry); return newEntry; @@ -580,18 +603,23 @@ static EntryFactory getFactory( * @param next entry in the same bucket */ abstract ReferenceEntry newEntry( - Segment segment, K key, int hash, @CheckForNull ReferenceEntry next); + Segment segment, K key, int hash, @Nullable ReferenceEntry next); /** * Copies an entry, assigning it a new {@code next} entry. * - * @param original the entry to copy + * @param original the entry to copy. But avoid calling {@code getKey} on it: Instead, use the + * {@code key} parameter. That way, we prevent the key from being garbage collected in the + * case of weak keys. If we create a new entry with a key that is null at construction time, + * we're not sure if entry will necessarily ever be garbage collected. * @param newNext entry in the same bucket + * @param key the key to copy from the original entry to the new one. Use this in preference to + * {@code original.getKey()}. */ // Guarded By Segment.this ReferenceEntry copyEntry( - Segment segment, ReferenceEntry original, ReferenceEntry newNext) { - return newEntry(segment, original.getKey(), original.getHash(), newNext); + Segment segment, ReferenceEntry original, ReferenceEntry newNext, K key) { + return newEntry(segment, key, original.getHash(), newNext); } // Guarded By Segment.this @@ -622,8 +650,7 @@ void copyWriteEntry(ReferenceEntry original, ReferenceEntry n /** A reference to a value. */ interface ValueReference { /** Returns the value. Does not block or throw exceptions. */ - @CheckForNull - V get(); + @Nullable V get(); /** * Waits for a value that may still be loading. Unlike get(), this method can block (in the case @@ -641,8 +668,7 @@ interface ValueReference { * Returns the entry associated with this value reference, or {@code null} if this value * reference is independent of any entry. */ - @CheckForNull - ReferenceEntry getEntry(); + @Nullable ReferenceEntry getEntry(); /** * Creates a copy of this reference for the given entry. @@ -650,17 +676,17 @@ interface ValueReference { *

    {@code value} may be null only for a loading reference. */ ValueReference copyFor( - ReferenceQueue queue, @CheckForNull V value, ReferenceEntry entry); + ReferenceQueue queue, @Nullable V value, ReferenceEntry entry); /** * Notify pending loads that a new value was set. This is only relevant to loading value * references. */ - void notifyNewValue(@CheckForNull V newValue); + void notifyNewValue(@Nullable V newValue); /** - * Returns true if a new value is currently loading, regardless of whether or not there is an - * existing value. It is assumed that the return value of this method is constant for any given + * Returns true if a new value is currently loading, regardless of whether there is an existing + * value. It is assumed that the return value of this method is constant for any given * ValueReference instance. */ boolean isLoading(); @@ -679,7 +705,7 @@ ValueReference copyFor( static final ValueReference UNSET = new ValueReference() { @Override - public Object get() { + public @Nullable Object get() { return null; } @@ -689,14 +715,14 @@ public int getWeight() { } @Override - public ReferenceEntry getEntry() { + public @Nullable ReferenceEntry getEntry() { return null; } @Override public ValueReference copyFor( ReferenceQueue queue, - @CheckForNull Object value, + @Nullable Object value, ReferenceEntry entry) { return this; } @@ -712,7 +738,7 @@ public boolean isActive() { } @Override - public Object waitForValue() { + public @Nullable Object waitForValue() { return null; } @@ -730,7 +756,7 @@ private enum NullEntry implements ReferenceEntry { INSTANCE; @Override - public ValueReference getValueReference() { + public @Nullable ValueReference getValueReference() { return null; } @@ -738,7 +764,7 @@ public ValueReference getValueReference() { public void setValueReference(ValueReference valueReference) {} @Override - public ReferenceEntry getNext() { + public @Nullable ReferenceEntry getNext() { return null; } @@ -748,7 +774,7 @@ public int getHash() { } @Override - public Object getKey() { + public @Nullable Object getKey() { return null; } @@ -901,12 +927,12 @@ public boolean offer(Object o) { } @Override - public Object peek() { + public @Nullable Object peek() { return null; } @Override - public Object poll() { + public @Nullable Object poll() { return null; } @@ -939,7 +965,7 @@ static Queue discardingQueue() { static class StrongEntry extends AbstractReferenceEntry { final K key; - StrongEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + StrongEntry(K key, int hash, @Nullable ReferenceEntry next) { this.key = key; this.hash = hash; this.next = next; @@ -953,7 +979,7 @@ public K getKey() { // The code below is exactly the same for each entry type. final int hash; - @CheckForNull final ReferenceEntry next; + final @Nullable ReferenceEntry next; volatile ValueReference valueReference = unset(); @Override @@ -978,7 +1004,7 @@ public ReferenceEntry getNext() { } static final class StrongAccessEntry extends StrongEntry { - StrongAccessEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + StrongAccessEntry(K key, int hash, @Nullable ReferenceEntry next) { super(key, hash, next); } @@ -1024,7 +1050,7 @@ public void setPreviousInAccessQueue(ReferenceEntry previous) { } static final class StrongWriteEntry extends StrongEntry { - StrongWriteEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + StrongWriteEntry(K key, int hash, @Nullable ReferenceEntry next) { super(key, hash, next); } @@ -1070,7 +1096,7 @@ public void setPreviousInWriteQueue(ReferenceEntry previous) { } static final class StrongAccessWriteEntry extends StrongEntry { - StrongAccessWriteEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + StrongAccessWriteEntry(K key, int hash, @Nullable ReferenceEntry next) { super(key, hash, next); } @@ -1157,7 +1183,7 @@ public void setPreviousInWriteQueue(ReferenceEntry previous) { /** Used for weakly-referenced keys. */ static class WeakEntry extends WeakReference implements ReferenceEntry { - WeakEntry(ReferenceQueue queue, K key, int hash, @CheckForNull ReferenceEntry next) { + WeakEntry(ReferenceQueue queue, K key, int hash, @Nullable ReferenceEntry next) { super(key, queue); this.hash = hash; this.next = next; @@ -1240,7 +1266,7 @@ public void setPreviousInWriteQueue(ReferenceEntry previous) { // The code below is exactly the same for each entry type. final int hash; - @CheckForNull final ReferenceEntry next; + final @Nullable ReferenceEntry next; volatile ValueReference valueReference = unset(); @Override @@ -1265,8 +1291,7 @@ public ReferenceEntry getNext() { } static final class WeakAccessEntry extends WeakEntry { - WeakAccessEntry( - ReferenceQueue queue, K key, int hash, @CheckForNull ReferenceEntry next) { + WeakAccessEntry(ReferenceQueue queue, K key, int hash, @Nullable ReferenceEntry next) { super(queue, key, hash, next); } @@ -1312,8 +1337,7 @@ public void setPreviousInAccessQueue(ReferenceEntry previous) { } static final class WeakWriteEntry extends WeakEntry { - WeakWriteEntry( - ReferenceQueue queue, K key, int hash, @CheckForNull ReferenceEntry next) { + WeakWriteEntry(ReferenceQueue queue, K key, int hash, @Nullable ReferenceEntry next) { super(queue, key, hash, next); } @@ -1360,7 +1384,7 @@ public void setPreviousInWriteQueue(ReferenceEntry previous) { static final class WeakAccessWriteEntry extends WeakEntry { WeakAccessWriteEntry( - ReferenceQueue queue, K key, int hash, @CheckForNull ReferenceEntry next) { + ReferenceQueue queue, K key, int hash, @Nullable ReferenceEntry next) { super(queue, key, hash, next); } @@ -1664,7 +1688,7 @@ static int rehash(int h) { * This method is a convenience for testing. Code should call {@link Segment#newEntry} directly. */ @VisibleForTesting - ReferenceEntry newEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + ReferenceEntry newEntry(K key, int hash, @Nullable ReferenceEntry next) { Segment segment = segmentFor(hash); segment.lock(); try { @@ -1695,7 +1719,7 @@ ValueReference newValueReference(ReferenceEntry entry, V value, int return valueStrength.referenceValue(segmentFor(hash), entry, checkNotNull(value), weight); } - int hash(@CheckForNull Object key) { + int hash(@Nullable Object key) { int h = keyEquivalence.hash(key); return rehash(h); } @@ -1738,12 +1762,11 @@ Segment createSegment( /** * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, - * loading, or expired. Unlike {@link Segment#getLiveValue} this method does not attempt to - * cleanup stale entries. As such it should only be called outside of a segment context, such as - * during iteration. + * loading, or expired. Unlike {@link Segment#getLiveValue} this method does not attempt to clean + * up stale entries. As such it should only be called outside a segment context, such as during + * iteration. */ - @CheckForNull - V getLiveValue(ReferenceEntry entry, long now) { + @Nullable V getLiveValue(ReferenceEntry entry, long now) { if (entry.getKey() == null) { return null; } @@ -1818,7 +1841,7 @@ void processPendingNotifications() { @SuppressWarnings("unchecked") final Segment[] newSegmentArray(int ssize) { - return new Segment[ssize]; + return (Segment[]) new Segment[ssize]; } // Inner Classes @@ -1887,7 +1910,7 @@ static class Segment extends ReentrantLock { int threshold; /** The per-segment table. */ - @CheckForNull volatile AtomicReferenceArray> table; + volatile @Nullable AtomicReferenceArray> table; /** The maximum weight of this segment. UNSET_INT if there is no maximum. */ final long maxSegmentWeight; @@ -1896,13 +1919,13 @@ static class Segment extends ReentrantLock { * The key reference queue contains entries whose keys have been garbage collected, and which * need to be cleaned up internally. */ - @CheckForNull final ReferenceQueue keyReferenceQueue; + final @Nullable ReferenceQueue keyReferenceQueue; /** * The value reference queue contains value references whose values have been garbage collected, * and which need to be cleaned up internally. */ - @CheckForNull final ReferenceQueue valueReferenceQueue; + final @Nullable ReferenceQueue valueReferenceQueue; /** * The recency queue is used to record which entries were accessed for updating the access @@ -1944,24 +1967,16 @@ static class Segment extends ReentrantLock { this.statsCounter = checkNotNull(statsCounter); initTable(newEntryArray(initialCapacity)); - keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue() : null; + keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue<>() : null; - valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue() : null; + valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue<>() : null; recencyQueue = - map.usesAccessQueue() - ? new ConcurrentLinkedQueue>() - : LocalCache.>discardingQueue(); + map.usesAccessQueue() ? new ConcurrentLinkedQueue<>() : LocalCache.discardingQueue(); - writeQueue = - map.usesWriteQueue() - ? new WriteQueue() - : LocalCache.>discardingQueue(); + writeQueue = map.usesWriteQueue() ? new WriteQueue<>() : LocalCache.discardingQueue(); - accessQueue = - map.usesAccessQueue() - ? new AccessQueue() - : LocalCache.>discardingQueue(); + accessQueue = map.usesAccessQueue() ? new AccessQueue<>() : LocalCache.discardingQueue(); } AtomicReferenceArray> newEntryArray(int size) { @@ -1978,7 +1993,7 @@ void initTable(AtomicReferenceArray> newTable) { } @GuardedBy("this") - ReferenceEntry newEntry(K key, int hash, @CheckForNull ReferenceEntry next) { + ReferenceEntry newEntry(K key, int hash, @Nullable ReferenceEntry next) { return map.entryFactory.newEntry(this, checkNotNull(key), hash, next); } @@ -1987,8 +2002,10 @@ ReferenceEntry newEntry(K key, int hash, @CheckForNull ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newNext) { - if (original.getKey() == null) { + @Nullable ReferenceEntry copyEntry( + ReferenceEntry original, ReferenceEntry newNext) { + K key = original.getKey(); + if (key == null) { // key collected return null; } @@ -2000,7 +2017,7 @@ ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newEntry = map.entryFactory.copyEntry(this, original, newNext); + ReferenceEntry newEntry = map.entryFactory.copyEntry(this, original, newNext, key); newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); return newEntry; } @@ -2021,6 +2038,7 @@ void setValue(ReferenceEntry entry, K key, V value, long now) { // loading + @CanIgnoreReturnValue V get(K key, int hash, CacheLoader loader) throws ExecutionException { checkNotNull(key); checkNotNull(loader); @@ -2058,8 +2076,7 @@ V get(K key, int hash, CacheLoader loader) throws ExecutionExcepti } } - @CheckForNull - V get(Object key, int hash) { + @Nullable V get(Object key, int hash) { try { if (count != 0) { // read-volatile long now = map.ticker.read(); @@ -2200,21 +2217,18 @@ V loadSync( } ListenableFuture loadAsync( - final K key, - final int hash, - final LoadingValueReference loadingValueReference, + K key, + int hash, + LoadingValueReference loadingValueReference, CacheLoader loader) { - final ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); loadingFuture.addListener( - new Runnable() { - @Override - public void run() { - try { - getAndRecordStats(key, hash, loadingValueReference, loadingFuture); - } catch (Throwable t) { - logger.log(Level.WARNING, "Exception thrown during refresh", t); - loadingValueReference.setException(t); - } + () -> { + try { + getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown during refresh", t); + loadingValueReference.setException(t); } }, directExecutor()); @@ -2222,6 +2236,7 @@ public void run() { } /** Waits uninterruptibly for {@code newValue} to be loaded, and then records loading stats. */ + @CanIgnoreReturnValue V getAndRecordStats( K key, int hash, @@ -2269,9 +2284,9 @@ V scheduleRefresh( * {@code null} if another thread is performing the refresh or if an error occurs during * refresh. */ - @CheckForNull - V refresh(K key, int hash, CacheLoader loader, boolean checkTime) { - final LoadingValueReference loadingValueReference = + @CanIgnoreReturnValue + @Nullable V refresh(K key, int hash, CacheLoader loader, boolean checkTime) { + LoadingValueReference loadingValueReference = insertLoadingValueReference(key, hash, checkTime); if (loadingValueReference == null) { return null; @@ -2292,9 +2307,8 @@ V refresh(K key, int hash, CacheLoader loader, boolean checkTime) * Returns a newly inserted {@code LoadingValueReference}, or null if the live value reference * is already loading. */ - @CheckForNull - LoadingValueReference insertLoadingValueReference( - final K key, final int hash, boolean checkTime) { + @Nullable LoadingValueReference insertLoadingValueReference( + K key, int hash, boolean checkTime) { ReferenceEntry e = null; lock(); try { @@ -2480,7 +2494,7 @@ void drainRecencyQueue() { // An entry may be in the recency queue despite it being removed from // the map . This can occur when the entry was concurrently read while a // writer is removing it from the segment or after a clear has removed - // all of the segment's entries. + // all the segment's entries. if (accessQueue.contains(e)) { accessQueue.add(e); } @@ -2522,7 +2536,7 @@ void expireEntries(long now) { @GuardedBy("this") void enqueueNotification( - @CheckForNull K key, int hash, @CheckForNull V value, int weight, RemovalCause cause) { + @Nullable K key, int hash, @Nullable V value, int weight, RemovalCause cause) { totalWeight -= weight; if (cause.wasEvicted()) { statsCounter.recordEviction(); @@ -2584,8 +2598,7 @@ ReferenceEntry getFirst(int hash) { // Specialized implementations of map methods - @CheckForNull - ReferenceEntry getEntry(Object key, int hash) { + @Nullable ReferenceEntry getEntry(Object key, int hash) { for (ReferenceEntry e = getFirst(hash); e != null; e = e.getNext()) { if (e.getHash() != hash) { continue; @@ -2605,8 +2618,7 @@ ReferenceEntry getEntry(Object key, int hash) { return null; } - @CheckForNull - ReferenceEntry getLiveEntry(Object key, int hash, long now) { + @Nullable ReferenceEntry getLiveEntry(Object key, int hash, long now) { ReferenceEntry e = getEntry(key, hash); if (e == null) { return null; @@ -2686,8 +2698,8 @@ boolean containsValue(Object value) { } } - @CheckForNull - V put(K key, int hash, V value, boolean onlyIfAbsent) { + @CanIgnoreReturnValue + @Nullable V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { long now = map.ticker.read(); @@ -2892,8 +2904,7 @@ boolean replace(K key, int hash, V oldValue, V newValue) { } } - @CheckForNull - V replace(K key, int hash, V newValue) { + @Nullable V replace(K key, int hash, V newValue) { lock(); try { long now = map.ticker.read(); @@ -2947,8 +2958,7 @@ V replace(K key, int hash, V newValue) { } } - @CheckForNull - V remove(Object key, int hash) { + @Nullable V remove(Object key, int hash) { lock(); try { long now = map.ticker.read(); @@ -3040,6 +3050,7 @@ boolean remove(Object key, int hash, Object value) { } } + @CanIgnoreReturnValue boolean storeLoadedValue( K key, int hash, LoadingValueReference oldValueReference, V newValue) { lock(); @@ -3139,11 +3150,10 @@ void clear() { } @GuardedBy("this") - @CheckForNull - ReferenceEntry removeValueFromChain( + @Nullable ReferenceEntry removeValueFromChain( ReferenceEntry first, ReferenceEntry entry, - @CheckForNull K key, + @Nullable K key, int hash, V value, ValueReference valueReference, @@ -3161,8 +3171,7 @@ ReferenceEntry removeValueFromChain( } @GuardedBy("this") - @CheckForNull - ReferenceEntry removeEntryFromChain( + @Nullable ReferenceEntry removeEntryFromChain( ReferenceEntry first, ReferenceEntry entry) { int newCount = count; ReferenceEntry newFirst = entry.getNext(); @@ -3192,6 +3201,7 @@ void removeCollectedEntry(ReferenceEntry entry) { } /** Removes an entry whose key has been garbage collected. */ + @CanIgnoreReturnValue boolean reclaimKey(ReferenceEntry entry, int hash) { lock(); try { @@ -3227,6 +3237,7 @@ boolean reclaimKey(ReferenceEntry entry, int hash) { } /** Removes an entry whose value has been garbage collected. */ + @CanIgnoreReturnValue boolean reclaimValue(K key, int hash, ValueReference valueReference) { lock(); try { @@ -3264,12 +3275,13 @@ boolean reclaimValue(K key, int hash, ValueReference valueReference) { return false; } finally { unlock(); - if (!isHeldByCurrentThread()) { // don't cleanup inside of put + if (!isHeldByCurrentThread()) { // don't clean up inside of put postWriteCleanup(); } } } + @CanIgnoreReturnValue boolean removeLoadingValue(K key, int hash, LoadingValueReference valueReference) { lock(); try { @@ -3305,6 +3317,7 @@ boolean removeLoadingValue(K key, int hash, LoadingValueReference valueRef @VisibleForTesting @GuardedBy("this") + @CanIgnoreReturnValue boolean removeEntry(ReferenceEntry entry, int hash, RemovalCause cause) { int newCount = this.count - 1; AtomicReferenceArray> table = this.table; @@ -3396,6 +3409,11 @@ public LoadingValueReference() { this(LocalCache.unset()); } + /* + * TODO(cpovirk): Consider making this implementation closer to the mainline implementation. + * (The difference was introduced as part of Java-8-specific changes in cl/132882204, but we + * could probably make *some* of those changes here in the backport, too.) + */ public LoadingValueReference(ValueReference oldValue) { this.oldValue = oldValue; } @@ -3415,20 +3433,22 @@ public int getWeight() { return oldValue.getWeight(); } - public boolean set(@CheckForNull V newValue) { + @CanIgnoreReturnValue + public boolean set(@Nullable V newValue) { return futureValue.set(newValue); } + @CanIgnoreReturnValue public boolean setException(Throwable t) { return futureValue.setException(t); } private ListenableFuture fullyFailedFuture(Throwable t) { - return Futures.immediateFailedFuture(t); + return immediateFailedFuture(t); } @Override - public void notifyNewValue(@CheckForNull V newValue) { + public void notifyNewValue(@Nullable V newValue) { if (newValue != null) { // The pending load was clobbered by a manual write. // Unblock all pending gets, and have them return the new value. @@ -3447,22 +3467,19 @@ public ListenableFuture loadFuture(K key, CacheLoader loader) { V previousValue = oldValue.get(); if (previousValue == null) { V newValue = loader.load(key); - return set(newValue) ? futureValue : Futures.immediateFuture(newValue); + return set(newValue) ? futureValue : immediateFuture(newValue); } ListenableFuture newValue = loader.reload(key, previousValue); if (newValue == null) { - return Futures.immediateFuture(null); + return immediateFuture(null); } // To avoid a race, make sure the refreshed value is set into loadingValueReference // *before* returning newValue from the cache query. return transform( newValue, - new Function() { - @Override - public V apply(V newValue) { - LoadingValueReference.this.set(newValue); - return newValue; - } + newResult -> { + LoadingValueReference.this.set(newResult); + return newResult; }, directExecutor()); } catch (Throwable t) { @@ -3499,7 +3516,7 @@ public ReferenceEntry getEntry() { @Override public ValueReference copyFor( - ReferenceQueue queue, @CheckForNull V value, ReferenceEntry entry) { + ReferenceQueue queue, @Nullable V value, ReferenceEntry entry) { return this; } } @@ -3569,13 +3586,13 @@ public boolean offer(ReferenceEntry entry) { } @Override - public ReferenceEntry peek() { + public @Nullable ReferenceEntry peek() { ReferenceEntry next = head.getNextInWriteQueue(); return (next == head) ? null : next; } @Override - public ReferenceEntry poll() { + public @Nullable ReferenceEntry poll() { ReferenceEntry next = head.getNextInWriteQueue(); if (next == head) { return null; @@ -3587,6 +3604,7 @@ public ReferenceEntry poll() { @Override @SuppressWarnings("unchecked") + @CanIgnoreReturnValue public boolean remove(Object o) { ReferenceEntry e = (ReferenceEntry) o; ReferenceEntry previous = e.getPreviousInWriteQueue(); @@ -3637,7 +3655,7 @@ public void clear() { public Iterator> iterator() { return new AbstractSequentialIterator>(peek()) { @Override - protected ReferenceEntry computeNext(ReferenceEntry previous) { + protected @Nullable ReferenceEntry computeNext(ReferenceEntry previous) { ReferenceEntry next = previous.getNextInWriteQueue(); return (next == head) ? null : next; } @@ -3708,13 +3726,13 @@ public boolean offer(ReferenceEntry entry) { } @Override - public ReferenceEntry peek() { + public @Nullable ReferenceEntry peek() { ReferenceEntry next = head.getNextInAccessQueue(); return (next == head) ? null : next; } @Override - public ReferenceEntry poll() { + public @Nullable ReferenceEntry poll() { ReferenceEntry next = head.getNextInAccessQueue(); if (next == head) { return null; @@ -3726,6 +3744,7 @@ public ReferenceEntry poll() { @Override @SuppressWarnings("unchecked") + @CanIgnoreReturnValue public boolean remove(Object o) { ReferenceEntry e = (ReferenceEntry) o; ReferenceEntry previous = e.getPreviousInAccessQueue(); @@ -3776,7 +3795,7 @@ public void clear() { public Iterator> iterator() { return new AbstractSequentialIterator>(peek()) { @Override - protected ReferenceEntry computeNext(ReferenceEntry previous) { + protected @Nullable ReferenceEntry computeNext(ReferenceEntry previous) { ReferenceEntry next = previous.getNextInAccessQueue(); return (next == head) ? null : next; } @@ -3805,19 +3824,19 @@ public boolean isEmpty() { */ long sum = 0L; Segment[] segments = this.segments; - for (int i = 0; i < segments.length; ++i) { - if (segments[i].count != 0) { + for (Segment segment : segments) { + if (segment.count != 0) { return false; } - sum += segments[i].modCount; + sum += segment.modCount; } if (sum != 0L) { // recheck unless no modifications - for (int i = 0; i < segments.length; ++i) { - if (segments[i].count != 0) { + for (Segment segment : segments) { + if (segment.count != 0) { return false; } - sum -= segments[i].modCount; + sum -= segment.modCount; } return sum == 0L; } @@ -3827,8 +3846,8 @@ public boolean isEmpty() { long longSize() { Segment[] segments = this.segments; long sum = 0; - for (int i = 0; i < segments.length; ++i) { - sum += Math.max(0, segments[i].count); // see https://github.com/google/guava/issues/2108 + for (Segment segment : segments) { + sum += Math.max(0, segment.count); // see https://github.com/google/guava/issues/2108 } return sum; } @@ -3838,9 +3857,9 @@ public int size() { return Ints.saturatedCast(longSize()); } + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { if (key == null) { return null; } @@ -3848,13 +3867,13 @@ public V get(@CheckForNull Object key) { return segmentFor(hash).get(key, hash); } + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this V get(K key, CacheLoader loader) throws ExecutionException { int hash = hash(checkNotNull(key)); return segmentFor(hash).get(key, hash, loader); } - @CheckForNull - public V getIfPresent(Object key) { + public @Nullable V getIfPresent(Object key) { int hash = hash(checkNotNull(key)); V value = segmentFor(hash).get(key, hash); if (value == null) { @@ -3865,9 +3884,8 @@ public V getIfPresent(Object key) { return value; } - @SuppressWarnings("MissingOverride") // Supermethod will not exist if we build with --release 7. - @CheckForNull - public V getOrDefault(@CheckForNull Object key, @CheckForNull V defaultValue) { + @Override + public @Nullable V getOrDefault(@Nullable Object key, @Nullable V defaultValue) { V result = get(key); return (result != null) ? result : defaultValue; } @@ -3880,7 +3898,7 @@ ImmutableMap getAllPresent(Iterable keys) { int hits = 0; int misses = 0; - Map result = Maps.newLinkedHashMap(); + ImmutableMap.Builder result = ImmutableMap.builder(); for (Object key : keys) { V value = get(key); if (value == null) { @@ -3895,7 +3913,7 @@ ImmutableMap getAllPresent(Iterable keys) { } globalStatsCounter.recordHits(hits); globalStatsCounter.recordMisses(misses); - return ImmutableMap.copyOf(result); + return result.buildKeepingLast(); } ImmutableMap getAll(Iterable keys) throws ExecutionException { @@ -3920,7 +3938,7 @@ ImmutableMap getAll(Iterable keys) throws ExecutionException try { if (!keysToLoad.isEmpty()) { try { - Map newEntries = loadAll(keysToLoad, defaultLoader); + Map newEntries = loadAll(unmodifiableSet(keysToLoad), defaultLoader); for (K key : keysToLoad) { V value = newEntries.get(key); if (value == null) { @@ -3947,8 +3965,7 @@ ImmutableMap getAll(Iterable keys) throws ExecutionException * Returns the result of calling {@link CacheLoader#loadAll}, or null if {@code loader} doesn't * implement {@code loadAll}. */ - @CheckForNull - Map loadAll(Set keys, CacheLoader loader) + @Nullable Map loadAll(Set keys, CacheLoader loader) throws ExecutionException { checkNotNull(loader); checkNotNull(keys); @@ -4011,7 +4028,7 @@ Map loadAll(Set keys, CacheLoader loader) * Returns the internal entry for the specified key. The entry may be loading, expired, or * partially collected. */ - ReferenceEntry getEntry(@CheckForNull Object key) { + @Nullable ReferenceEntry getEntry(@Nullable Object key) { // does not impact recency ordering if (key == null) { return null; @@ -4026,7 +4043,7 @@ void refresh(K key) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { // does not impact recency ordering if (key == null) { return false; @@ -4036,7 +4053,7 @@ public boolean containsKey(@CheckForNull Object key) { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { // does not impact recency ordering if (value == null) { return false; @@ -4048,7 +4065,7 @@ public boolean containsValue(@CheckForNull Object value) { // in time it was present somewhere int the map. This becomes increasingly unlikely as // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. long now = ticker.read(); - final Segment[] segments = this.segments; + Segment[] segments = this.segments; long last = -1L; for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { long sum = 0L; @@ -4075,8 +4092,9 @@ public boolean containsValue(@CheckForNull Object value) { return false; } + @CanIgnoreReturnValue @Override - public V put(K key, V value) { + public @Nullable V put(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -4084,7 +4102,7 @@ public V put(K key, V value) { } @Override - public V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -4098,8 +4116,9 @@ public void putAll(Map m) { } } + @CanIgnoreReturnValue @Override - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { if (key == null) { return null; } @@ -4107,8 +4126,9 @@ public V remove(@CheckForNull Object key) { return segmentFor(hash).remove(key, hash); } + @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { if (key == null || value == null) { return false; } @@ -4116,8 +4136,9 @@ public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { return segmentFor(hash).remove(key, hash, value); } + @CanIgnoreReturnValue @Override - public boolean replace(K key, @CheckForNull V oldValue, V newValue) { + public boolean replace(K key, @Nullable V oldValue, V newValue) { checkNotNull(key); checkNotNull(newValue); if (oldValue == null) { @@ -4127,8 +4148,9 @@ public boolean replace(K key, @CheckForNull V oldValue, V newValue) { return segmentFor(hash).replace(key, hash, oldValue, newValue); } + @CanIgnoreReturnValue @Override - public V replace(K key, V value) { + public @Nullable V replace(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -4149,7 +4171,7 @@ void invalidateAll(Iterable keys) { } } - @RetainedWith @CheckForNull Set keySet; + @LazyInit @RetainedWith @Nullable Set keySet; @Override public Set keySet() { @@ -4158,7 +4180,7 @@ public Set keySet() { return (ks != null) ? ks : (keySet = new KeySet()); } - @RetainedWith @CheckForNull Collection values; + @LazyInit @RetainedWith @Nullable Collection values; @Override public Collection values() { @@ -4167,7 +4189,7 @@ public Collection values() { return (vs != null) ? vs : (values = new Values()); } - @RetainedWith @CheckForNull Set> entrySet; + @LazyInit @RetainedWith @Nullable Set> entrySet; @Override @GwtIncompatible // Not supported. @@ -4183,11 +4205,11 @@ abstract class HashIterator implements Iterator { int nextSegmentIndex; int nextTableIndex; - @CheckForNull Segment currentSegment; - @CheckForNull AtomicReferenceArray> currentTable; - @CheckForNull ReferenceEntry nextEntry; - @CheckForNull WriteThroughEntry nextExternal; - @CheckForNull WriteThroughEntry lastReturned; + @Nullable Segment currentSegment; + @Nullable AtomicReferenceArray> currentTable; + @Nullable ReferenceEntry nextEntry; + @Nullable WriteThroughEntry nextExternal; + @Nullable WriteThroughEntry lastReturned; HashIterator() { nextSegmentIndex = segments.length - 1; @@ -4328,7 +4350,7 @@ public V getValue() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { // Cannot use key and value equivalence if (object instanceof Entry) { Entry that = (Entry) object; @@ -4379,26 +4401,6 @@ public boolean isEmpty() { public void clear() { LocalCache.this.clear(); } - - // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. - // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 - - @Override - public Object[] toArray() { - return toArrayList(this).toArray(); - } - - @Override - public E[] toArray(E[] a) { - return toArrayList(this).toArray(a); - } - } - - private static ArrayList toArrayList(Collection c) { - // Avoid calling ArrayList(Collection), which may call back into toArray. - ArrayList result = new ArrayList(c.size()); - Iterators.addAll(result, c.iterator()); - return result; } final class KeySet extends AbstractCacheSet { @@ -4444,19 +4446,6 @@ public Iterator iterator() { public boolean contains(Object o) { return LocalCache.this.containsValue(o); } - - // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. - // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 - - @Override - public Object[] toArray() { - return toArrayList(this).toArray(); - } - - @Override - public E[] toArray(E[] a) { - return toArrayList(this).toArray(a); - } } final class EntrySet extends AbstractCacheSet> { @@ -4504,7 +4493,7 @@ public boolean remove(Object o) { */ static class ManualSerializationProxy extends ForwardingCache implements Serializable { - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; final Strength keyStrength; final Strength valueStrength; @@ -4516,10 +4505,10 @@ static class ManualSerializationProxy extends ForwardingCache final Weigher weigher; final int concurrencyLevel; final RemovalListener removalListener; - @CheckForNull final Ticker ticker; + final @Nullable Ticker ticker; final CacheLoader loader; - @CheckForNull transient Cache delegate; + transient @Nullable Cache delegate; ManualSerializationProxy(LocalCache cache) { this( @@ -4575,13 +4564,13 @@ CacheBuilder recreateCacheBuilder() { .removalListener(removalListener); builder.strictParsing = false; if (expireAfterWriteNanos > 0) { - builder.expireAfterWrite(expireAfterWriteNanos, TimeUnit.NANOSECONDS); + builder.expireAfterWrite(expireAfterWriteNanos, NANOSECONDS); } if (expireAfterAccessNanos > 0) { - builder.expireAfterAccess(expireAfterAccessNanos, TimeUnit.NANOSECONDS); + builder.expireAfterAccess(expireAfterAccessNanos, NANOSECONDS); } if (weigher != OneWeigher.INSTANCE) { - builder.weigher(weigher); + Object unused = builder.weigher(weigher); if (maxWeight != UNSET_INT) { builder.maximumWeight(maxWeight); } @@ -4621,10 +4610,10 @@ protected Cache delegate() { * the proxy must be able to behave as the cache itself. */ static final class LoadingSerializationProxy extends ManualSerializationProxy - implements LoadingCache, Serializable { - private static final long serialVersionUID = 1; + implements LoadingCache { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; - @CheckForNull transient LoadingCache autoDelegate; + transient @Nullable LoadingCache autoDelegate; LoadingSerializationProxy(LocalCache cache) { super(cache); @@ -4652,7 +4641,7 @@ public ImmutableMap getAll(Iterable keys) throws ExecutionExc } @Override - public final V apply(K key) { + public V apply(K key) { return autoDelegate.apply(key); } @@ -4670,7 +4659,7 @@ static class LocalManualCache implements Cache, Serializable { final LocalCache localCache; LocalManualCache(CacheBuilder builder) { - this(new LocalCache(builder, null)); + this(new LocalCache<>(builder, null)); } private LocalManualCache(LocalCache localCache) { @@ -4680,13 +4669,12 @@ private LocalManualCache(LocalCache localCache) { // Cache methods @Override - @CheckForNull - public V getIfPresent(Object key) { + public @Nullable V getIfPresent(Object key) { return localCache.getIfPresent(key); } @Override - public V get(K key, final Callable valueLoader) throws ExecutionException { + public V get(K key, Callable valueLoader) throws ExecutionException { checkNotNull(valueLoader); return localCache.get( key, @@ -4756,11 +4744,15 @@ public void cleanUp() { // Serialization Support - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; Object writeReplace() { return new ManualSerializationProxy<>(localCache); } + + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use ManualSerializationProxy"); + } } static class LocalLoadingCache extends LocalManualCache @@ -4768,7 +4760,7 @@ static class LocalLoadingCache extends LocalManualCache LocalLoadingCache( CacheBuilder builder, CacheLoader loader) { - super(new LocalCache(builder, checkNotNull(loader))); + super(new LocalCache<>(builder, checkNotNull(loader))); } // LoadingCache methods @@ -4778,6 +4770,7 @@ public V get(K key) throws ExecutionException { return localCache.getOrLoad(key); } + @CanIgnoreReturnValue // TODO(b/27479612): consider removing this @Override public V getUnchecked(K key) { try { @@ -4804,11 +4797,15 @@ public final V apply(K key) { // Serialization Support - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; @Override Object writeReplace() { return new LoadingSerializationProxy<>(localCache); } + + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use LoadingSerializationProxy"); + } } } diff --git a/android/guava/src/com/google/common/cache/LongAddable.java b/android/guava/src/com/google/common/cache/LongAddable.java index 9851052d6817..eaa641467738 100644 --- a/android/guava/src/com/google/common/cache/LongAddable.java +++ b/android/guava/src/com/google/common/cache/LongAddable.java @@ -22,7 +22,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault interface LongAddable { void increment(); diff --git a/android/guava/src/com/google/common/cache/LongAddables.java b/android/guava/src/com/google/common/cache/LongAddables.java index b0f9e2b2177b..e5da7c8b772a 100644 --- a/android/guava/src/com/google/common/cache/LongAddables.java +++ b/android/guava/src/com/google/common/cache/LongAddables.java @@ -24,14 +24,14 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class LongAddables { private static final Supplier SUPPLIER; static { Supplier supplier; try { - new LongAdder(); // trigger static initialization of the LongAdder class, which may fail + // trigger static initialization of the LongAdder class, which may fail + LongAdder unused = new LongAdder(); supplier = new Supplier() { @Override diff --git a/android/guava/src/com/google/common/cache/LongAdder.java b/android/guava/src/com/google/common/cache/LongAdder.java index 7ead7e8eccd3..b9dc05291dd9 100644 --- a/android/guava/src/com/google/common/cache/LongAdder.java +++ b/android/guava/src/com/google/common/cache/LongAdder.java @@ -12,6 +12,8 @@ package com.google.common.cache; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -40,8 +42,8 @@ * @author Doug Lea */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class LongAdder extends Striped64 implements Serializable, LongAddable { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 7249069246863182397L; /** Version of plus for use in retryUpdate */ diff --git a/android/guava/src/com/google/common/cache/ParametricNullness.java b/android/guava/src/com/google/common/cache/ParametricNullness.java index 64682de55333..affbfc511840 100644 --- a/android/guava/src/com/google/common/cache/ParametricNullness.java +++ b/android/guava/src/com/google/common/cache/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *
      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/cache/ReferenceEntry.java b/android/guava/src/com/google/common/cache/ReferenceEntry.java index 8ff2e6c9b4ff..3c78679dba19 100644 --- a/android/guava/src/com/google/common/cache/ReferenceEntry.java +++ b/android/guava/src/com/google/common/cache/ReferenceEntry.java @@ -16,7 +16,7 @@ import com.google.common.annotations.GwtIncompatible; import com.google.common.cache.LocalCache.ValueReference; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An entry in a reference map. @@ -39,25 +39,21 @@ * */ @GwtIncompatible -@ElementTypesAreNonnullByDefault interface ReferenceEntry { /** Returns the value reference from this entry. */ - @CheckForNull - ValueReference getValueReference(); + @Nullable ValueReference getValueReference(); /** Sets the value reference for this entry. */ void setValueReference(ValueReference valueReference); /** Returns the next entry in the chain. */ - @CheckForNull - ReferenceEntry getNext(); + @Nullable ReferenceEntry getNext(); /** Returns the entry's hash. */ int getHash(); /** Returns the key for this entry. */ - @CheckForNull - K getKey(); + @Nullable K getKey(); /* * Used by entries that use access order. Access entries are maintained in a doubly-linked list. @@ -91,8 +87,8 @@ interface ReferenceEntry { * expired from the head of the list. */ - @SuppressWarnings("GoodTime") /** Returns the time that this entry was last written, in ns. */ + @SuppressWarnings("GoodTime") long getWriteTime(); /** Sets the entry write time in ns. */ diff --git a/android/guava/src/com/google/common/cache/RemovalCause.java b/android/guava/src/com/google/common/cache/RemovalCause.java index 2e68e68b582c..8ecc1d662736 100644 --- a/android/guava/src/com/google/common/cache/RemovalCause.java +++ b/android/guava/src/com/google/common/cache/RemovalCause.java @@ -26,7 +26,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public enum RemovalCause { /** * The entry was manually removed by the user. This can result from the user invoking {@link diff --git a/android/guava/src/com/google/common/cache/RemovalListener.java b/android/guava/src/com/google/common/cache/RemovalListener.java index a7472d74c6c7..5e738bfa3c19 100644 --- a/android/guava/src/com/google/common/cache/RemovalListener.java +++ b/android/guava/src/com/google/common/cache/RemovalListener.java @@ -33,7 +33,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface RemovalListener { /** * Notifies the listener that a removal occurred at some point in the past. diff --git a/android/guava/src/com/google/common/cache/RemovalListeners.java b/android/guava/src/com/google/common/cache/RemovalListeners.java index 38edf35287af..e5999a4e80e4 100644 --- a/android/guava/src/com/google/common/cache/RemovalListeners.java +++ b/android/guava/src/com/google/common/cache/RemovalListeners.java @@ -26,7 +26,6 @@ * @since 10.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class RemovalListeners { private RemovalListeners() {} diff --git a/android/guava/src/com/google/common/cache/RemovalNotification.java b/android/guava/src/com/google/common/cache/RemovalNotification.java index dab7fe533ec2..e95c5e202140 100644 --- a/android/guava/src/com/google/common/cache/RemovalNotification.java +++ b/android/guava/src/com/google/common/cache/RemovalNotification.java @@ -17,9 +17,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.AbstractMap.SimpleImmutableEntry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A notification of the removal of a single entry. The key and/or value may be null if they were @@ -33,7 +34,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class RemovalNotification extends SimpleImmutableEntry<@Nullable K, @Nullable V> { private final RemovalCause cause; @@ -46,11 +46,11 @@ public final class RemovalNotification * @since 19.0 */ public static RemovalNotification create( - @CheckForNull K key, @CheckForNull V value, RemovalCause cause) { - return new RemovalNotification(key, value, cause); + @Nullable K key, @Nullable V value, RemovalCause cause) { + return new RemovalNotification<>(key, value, cause); } - private RemovalNotification(@CheckForNull K key, @CheckForNull V value, RemovalCause cause) { + private RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) { super(key, value); this.cause = checkNotNull(cause); } @@ -68,5 +68,5 @@ public boolean wasEvicted() { return cause.wasEvicted(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/cache/Striped64.java b/android/guava/src/com/google/common/cache/Striped64.java index 0d2d75b9f384..a5241528f035 100644 --- a/android/guava/src/com/google/common/cache/Striped64.java +++ b/android/guava/src/com/google/common/cache/Striped64.java @@ -12,17 +12,21 @@ package com.google.common.cache; import com.google.common.annotations.GwtIncompatible; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Random; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; +import sun.misc.Unsafe; /** * A package-local class holding common representation and mechanics for classes supporting dynamic * striping on 64bit values. The class extends Number so that concrete subclasses must publicly do * so. */ +@SuppressWarnings("SunApi") // b/345822163 @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class Striped64 extends Number { /* * This class maintains a lazily-initialized table of atomically @@ -104,18 +108,18 @@ static final class Cell { } final boolean cas(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, cmp, val); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long valueOffset; + private static final Unsafe UNSAFE; + private static final long VALUE_OFFSET; static { try { UNSAFE = getUnsafe(); Class ak = Cell.class; - valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); + VALUE_OFFSET = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); } catch (Exception e) { throw new Error(e); } @@ -136,7 +140,7 @@ final boolean cas(long cmp, long val) { static final int NCPU = Runtime.getRuntime().availableProcessors(); /** Table of cells. When non-null, size is a power of 2. */ - @CheckForNull transient volatile Cell[] cells; + transient volatile Cell @Nullable [] cells; /** * Base value, used mainly when there is no contention, but also as a fallback during table @@ -152,12 +156,12 @@ final boolean cas(long cmp, long val) { /** CASes the base field. */ final boolean casBase(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + return UNSAFE.compareAndSwapLong(this, BASE_OFFSET, cmp, val); } /** CASes the busy field from 0 to 1 to acquire lock. */ final boolean casBusy() { - return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + return UNSAFE.compareAndSwapInt(this, BUSY_OFFSET, 0, 1); } /** @@ -179,7 +183,7 @@ final boolean casBusy() { * @param hc the hash code holder * @param wasUncontended false if CAS failed before call */ - final void retryUpdate(long x, @CheckForNull int[] hc, boolean wasUncontended) { + final void retryUpdate(long x, int @Nullable [] hc, boolean wasUncontended) { int h; if (hc == null) { threadHashCode.set(hc = new int[1]); // Initialize randomly @@ -266,16 +270,16 @@ final void internalReset(long initialValue) { } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long baseOffset; - private static final long busyOffset; + private static final Unsafe UNSAFE; + private static final long BASE_OFFSET; + private static final long BUSY_OFFSET; static { try { UNSAFE = getUnsafe(); Class sk = Striped64.class; - baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); - busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + BASE_OFFSET = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + BUSY_OFFSET = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); } catch (Exception e) { throw new Error(e); } @@ -287,18 +291,18 @@ final void internalReset(long initialValue) { * * @return a sun.misc.Unsafe */ - private static sun.misc.Unsafe getUnsafe() { + private static Unsafe getUnsafe() { try { - return sun.misc.Unsafe.getUnsafe(); + return Unsafe.getUnsafe(); } catch (SecurityException tryReflectionInstead) { } try { - return java.security.AccessController.doPrivileged( - new java.security.PrivilegedExceptionAction() { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { + public Unsafe run() throws Exception { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { f.setAccessible(true); Object x = f.get(null); if (k.isInstance(x)) return k.cast(x); @@ -306,7 +310,7 @@ public sun.misc.Unsafe run() throws Exception { throw new NoSuchFieldError("the Unsafe"); } }); - } catch (java.security.PrivilegedActionException e) { + } catch (PrivilegedActionException e) { throw new RuntimeException("Could not initialize intrinsics", e.getCause()); } } diff --git a/android/guava/src/com/google/common/cache/Weigher.java b/android/guava/src/com/google/common/cache/Weigher.java index d5bcd5b21472..cf552d44ec11 100644 --- a/android/guava/src/com/google/common/cache/Weigher.java +++ b/android/guava/src/com/google/common/cache/Weigher.java @@ -23,7 +23,6 @@ * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Weigher { /** diff --git a/android/guava/src/com/google/common/cache/package-info.java b/android/guava/src/com/google/common/cache/package-info.java index a7791de494a3..5bd416f5cfc5 100644 --- a/android/guava/src/com/google/common/cache/package-info.java +++ b/android/guava/src/com/google/common/cache/package-info.java @@ -13,23 +13,24 @@ */ /** - * This package contains caching utilities. + * {@linkplain CacheBuilder Discouraged} (in favor of Caffeine) caching utilities. * - *

    The core interface used to represent caches is {@link com.google.common.cache.Cache}. - * In-memory caches can be configured and created using {@link - * com.google.common.cache.CacheBuilder}, with cache entries being loaded by {@link - * com.google.common.cache.CacheLoader}. Statistics about cache performance are exposed using {@link - * com.google.common.cache.CacheStats}. + *

    The core interface used to represent caches is {@link Cache}. In-memory caches can be + * configured and created using {@link CacheBuilder}, with cache entries being loaded by {@link + * CacheLoader}. Statistics about cache performance are exposed using {@link CacheStats}. * *

    See the Guava User Guide article on caches. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. * * @author Charles Fry */ -@ParametersAreNonnullByDefault +@CheckReturnValue +@NullMarked package com.google.common.cache; -import javax.annotation.ParametersAreNonnullByDefault; +import com.google.errorprone.annotations.CheckReturnValue; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/collect/AbstractBiMap.java b/android/guava/src/com/google/common/collect/AbstractBiMap.java index 151764be8491..62cfa4b710e0 100644 --- a/android/guava/src/com/google/common/collect/AbstractBiMap.java +++ b/android/guava/src/com/google/common/collect/AbstractBiMap.java @@ -19,11 +19,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import com.google.j2objc.annotations.WeakOuter; import java.io.IOException; @@ -34,8 +37,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A general-purpose bimap implementation using any two backing {@code Map} instances. @@ -47,12 +49,15 @@ * @author Mike Bostock */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class AbstractBiMap extends ForwardingMap implements BiMap, Serializable { + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (lateinit) private transient Map delegate; - @RetainedWith transient AbstractBiMap inverse; + + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (lateinit) + @RetainedWith + transient AbstractBiMap inverse; /** Package-private constructor for creating a map-backed bimap. */ AbstractBiMap(Map forward, Map backward) { @@ -109,7 +114,7 @@ void setInverse(AbstractBiMap inverse) { // Query Operations (optimizations) @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return inverse.containsKey(value); } @@ -117,20 +122,18 @@ public boolean containsValue(@CheckForNull Object value) { @CanIgnoreReturnValue @Override - @CheckForNull - public V put(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { return putInBothMaps(key, value, false); } @CanIgnoreReturnValue @Override - @CheckForNull - public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) { return putInBothMaps(key, value, true); } - @CheckForNull - private V putInBothMaps(@ParametricNullness K key, @ParametricNullness V value, boolean force) { + private @Nullable V putInBothMaps( + @ParametricNullness K key, @ParametricNullness V value, boolean force) { checkKey(key); checkValue(value); boolean containedKey = containsKey(key); @@ -150,7 +153,7 @@ private V putInBothMaps(@ParametricNullness K key, @ParametricNullness V value, private void updateInverseMap( @ParametricNullness K key, boolean containedKey, - @CheckForNull V oldValue, + @Nullable V oldValue, @ParametricNullness V newValue) { if (containedKey) { // The cast is safe because of the containedKey check. @@ -161,14 +164,13 @@ private void updateInverseMap( @CanIgnoreReturnValue @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { return containsKey(key) ? removeFromBothMaps(key) : null; } @CanIgnoreReturnValue @ParametricNullness - private V removeFromBothMaps(@CheckForNull Object key) { + private V removeFromBothMaps(@Nullable Object key) { // The cast is safe because the callers of this method first check that the key is present. V oldValue = uncheckedCastNullableTToT(delegate.remove(key)); removeFromInverseMap(oldValue); @@ -201,7 +203,7 @@ public BiMap inverse() { return inverse; } - @CheckForNull private transient Set keySet; + @LazyInit private transient @Nullable Set keySet; @Override public Set keySet() { @@ -222,7 +224,7 @@ public void clear() { } @Override - public boolean remove(@CheckForNull Object key) { + public boolean remove(@Nullable Object key) { if (!contains(key)) { return false; } @@ -246,7 +248,7 @@ public Iterator iterator() { } } - @CheckForNull private transient Set valueSet; + @LazyInit private transient @Nullable Set valueSet; @Override public Set values() { @@ -289,7 +291,7 @@ public String toString() { } } - @CheckForNull private transient Set> entrySet; + @LazyInit private transient @Nullable Set> entrySet; @Override public Set> entrySet() { @@ -327,9 +329,9 @@ public V setValue(V value) { } Iterator> entrySetIterator() { - final Iterator> iterator = delegate.entrySet().iterator(); + Iterator> iterator = delegate.entrySet().iterator(); return new Iterator>() { - @CheckForNull Entry entry; + @Nullable Entry entry; @Override public boolean hasNext() { @@ -370,7 +372,7 @@ public void clear() { } @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { /* * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our * nullness checker. @@ -398,15 +400,8 @@ public Iterator> iterator() { // See java.util.Collections.CheckedEntrySet for details on attacks. @Override - public Object[] toArray() { - /* - * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it can - * be used with collections that may contain null. This collection never contains nulls, so we - * can treat it as a plain `Object[]`. - */ - @SuppressWarnings("nullness") - Object[] result = standardToArray(); - return result; + public @Nullable Object[] toArray() { + return standardToArray(); } @Override @@ -416,7 +411,7 @@ public Object[] toArray() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return Maps.containsEntryImpl(delegate(), o); } @@ -464,29 +459,32 @@ V checkValue(@ParametricNullness V value) { return inverse.checkKey(value); } - /** @serialData the forward bimap */ + /** + * @serialData the forward bimap + */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(inverse()); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - setInverse((AbstractBiMap) stream.readObject()); + setInverse((AbstractBiMap) requireNonNull(stream.readObject())); } @GwtIncompatible // Not needed in the emulated source. + @J2ktIncompatible Object readResolve() { return inverse().inverse(); } - @GwtIncompatible // Not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - @GwtIncompatible // Not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/AbstractIndexedListIterator.java b/android/guava/src/com/google/common/collect/AbstractIndexedListIterator.java index abb3960b261e..552a1bc2ca9e 100644 --- a/android/guava/src/com/google/common/collect/AbstractIndexedListIterator.java +++ b/android/guava/src/com/google/common/collect/AbstractIndexedListIterator.java @@ -21,7 +21,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.ListIterator; import java.util.NoSuchElementException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of the {@link ListIterator} interface across a @@ -31,7 +31,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractIndexedListIterator extends UnmodifiableListIterator { private final int size; diff --git a/android/guava/src/com/google/common/collect/AbstractIterator.java b/android/guava/src/com/google/common/collect/AbstractIterator.java index 66273f438da5..452e260ffbbc 100644 --- a/android/guava/src/com/google/common/collect/AbstractIterator.java +++ b/android/guava/src/com/google/common/collect/AbstractIterator.java @@ -22,8 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of the {@code Iterator} interface, to make this @@ -63,7 +62,6 @@ // When making changes to this class, please also update the copy at // com.google.common.base.AbstractIterator @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class AbstractIterator extends UnmodifiableIterator { private State state = State.NOT_READY; @@ -84,7 +82,7 @@ private enum State { FAILED, } - @CheckForNull private T next; + private @Nullable T next; /** * Returns the next element. Note: the implementation must call {@link #endOfData()} when @@ -110,8 +108,7 @@ private enum State { * this method. Any further attempts to use the iterator will result in an {@link * IllegalStateException}. */ - @CheckForNull - protected abstract T computeNext(); + protected abstract @Nullable T computeNext(); /** * Implementations of {@link #computeNext} must invoke this method when there are no @@ -121,13 +118,11 @@ private enum State { * simple statement {@code return endOfData();} */ @CanIgnoreReturnValue - @CheckForNull - protected final T endOfData() { + protected final @Nullable T endOfData() { state = State.DONE; return null; } - @CanIgnoreReturnValue // TODO(kak): Should we remove this? Some people are using it to prefetch? @Override public final boolean hasNext() { checkState(state != State.FAILED); diff --git a/android/guava/src/com/google/common/collect/AbstractListMultimap.java b/android/guava/src/com/google/common/collect/AbstractListMultimap.java index 46c4ee2448db..6cb094381af2 100644 --- a/android/guava/src/com/google/common/collect/AbstractListMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractListMultimap.java @@ -16,14 +16,17 @@ package com.google.common.collect; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of the {@link ListMultimap} interface. It's a wrapper around {@link @@ -34,7 +37,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractListMultimap extends AbstractMapBasedMultimap implements ListMultimap { /** @@ -51,13 +53,13 @@ protected AbstractListMultimap(Map> map) { @Override List createUnmodifiableEmptyCollection() { - return Collections.emptyList(); + return emptyList(); } @Override Collection unmodifiableCollectionSubclass( Collection collection) { - return Collections.unmodifiableList((List) collection); + return unmodifiableList((List) collection); } @Override @@ -88,7 +90,7 @@ public List get(@ParametricNullness K key) { */ @CanIgnoreReturnValue @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { return (List) super.removeAll(key); } @@ -136,9 +138,10 @@ public Map> asMap() { * in the same order. If the value orderings disagree, the multimaps will not be considered equal. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return super.equals(object); } + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 6588350623831699109L; } diff --git a/android/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java b/android/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java index 72cf8f1eea5e..1096f1900a83 100644 --- a/android/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java @@ -19,10 +19,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Maps.safeGet; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Maps.ViewCachingAbstractMap; import com.google.j2objc.annotations.WeakOuter; import java.io.Serializable; @@ -42,8 +46,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of the {@link Multimap} interface. This class represents a multimap as a map @@ -84,7 +87,7 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("WrongCommentType") // false positive abstract class AbstractMapBasedMultimap extends AbstractMultimap implements Serializable { /* @@ -175,7 +178,7 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } @@ -247,7 +250,7 @@ public Collection replaceValues(@ParametricNullness K key, IterableThe returned collection is immutable. */ @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { Collection collection = map.remove(key); if (collection == null) { @@ -302,7 +305,7 @@ Collection wrapCollection(@ParametricNullness K key, Collection collection } final List wrapList( - @ParametricNullness K key, List list, @CheckForNull WrappedCollection ancestor) { + @ParametricNullness K key, List list, @Nullable WrappedCollection ancestor) { return (list instanceof RandomAccess) ? new RandomAccessWrappedList(key, list, ancestor) : new WrappedList(key, list, ancestor); @@ -327,13 +330,11 @@ final List wrapList( class WrappedCollection extends AbstractCollection { @ParametricNullness final K key; Collection delegate; - @CheckForNull final WrappedCollection ancestor; - @CheckForNull final Collection ancestorDelegate; + final @Nullable WrappedCollection ancestor; + final @Nullable Collection ancestorDelegate; WrappedCollection( - @ParametricNullness K key, - Collection delegate, - @CheckForNull WrappedCollection ancestor) { + @ParametricNullness K key, Collection delegate, @Nullable WrappedCollection ancestor) { this.key = key; this.delegate = delegate; this.ancestor = ancestor; @@ -399,7 +400,14 @@ public int size() { } @Override - public boolean equals(@CheckForNull Object object) { + /* + * Most Multimap implementations use a List or Set (or even Multiset) for their values, in which + * case Multimap equality works as expected. Users who use a Collection type that does not + * implement equals(), such as most Queue implementations, will get the same behavior from our + * value-collection wrappers (and from Multimap.equals) as from the underlying Collection. + */ + @SuppressWarnings("UndefinedEquals") + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -492,8 +500,7 @@ public boolean add(@ParametricNullness V value) { return changed; } - @CheckForNull - WrappedCollection getAncestor() { + @Nullable WrappedCollection getAncestor() { return ancestor; } @@ -517,7 +524,7 @@ public boolean addAll(Collection collection) { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { refreshIfEmpty(); return delegate.contains(o); } @@ -540,7 +547,7 @@ public void clear() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { refreshIfEmpty(); boolean changed = delegate.remove(o); if (changed) { @@ -617,9 +624,7 @@ public boolean removeAll(Collection c) { @WeakOuter class WrappedSortedSet extends WrappedCollection implements SortedSet { WrappedSortedSet( - @ParametricNullness K key, - SortedSet delegate, - @CheckForNull WrappedCollection ancestor) { + @ParametricNullness K key, SortedSet delegate, @Nullable WrappedCollection ancestor) { super(key, delegate, ancestor); } @@ -628,8 +633,7 @@ SortedSet getSortedSetDelegate() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return getSortedSetDelegate().comparator(); } @@ -678,9 +682,7 @@ public SortedSet tailSet(@ParametricNullness V fromElement) { @WeakOuter class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { WrappedNavigableSet( - @ParametricNullness K key, - NavigableSet delegate, - @CheckForNull WrappedCollection ancestor) { + @ParametricNullness K key, NavigableSet delegate, @Nullable WrappedCollection ancestor) { super(key, delegate, ancestor); } @@ -690,38 +692,32 @@ NavigableSet getSortedSetDelegate() { } @Override - @CheckForNull - public V lower(@ParametricNullness V v) { + public @Nullable V lower(@ParametricNullness V v) { return getSortedSetDelegate().lower(v); } @Override - @CheckForNull - public V floor(@ParametricNullness V v) { + public @Nullable V floor(@ParametricNullness V v) { return getSortedSetDelegate().floor(v); } @Override - @CheckForNull - public V ceiling(@ParametricNullness V v) { + public @Nullable V ceiling(@ParametricNullness V v) { return getSortedSetDelegate().ceiling(v); } @Override - @CheckForNull - public V higher(@ParametricNullness V v) { + public @Nullable V higher(@ParametricNullness V v) { return getSortedSetDelegate().higher(v); } @Override - @CheckForNull - public V pollFirst() { + public @Nullable V pollFirst() { return Iterators.pollNext(iterator()); } @Override - @CheckForNull - public V pollLast() { + public @Nullable V pollLast() { return Iterators.pollNext(descendingIterator()); } @@ -763,8 +759,7 @@ public NavigableSet tailSet(@ParametricNullness V fromElement, boolean inclus /** List decorator that stays in sync with the multimap values for a key. */ @WeakOuter class WrappedList extends WrappedCollection implements List { - WrappedList( - @ParametricNullness K key, List delegate, @CheckForNull WrappedCollection ancestor) { + WrappedList(@ParametricNullness K key, List delegate, @Nullable WrappedCollection ancestor) { super(key, delegate, ancestor); } @@ -825,13 +820,13 @@ public V remove(int index) { } @Override - public int indexOf(@CheckForNull Object o) { + public int indexOf(@Nullable Object o) { refreshIfEmpty(); return getListDelegate().indexOf(o); } @Override - public int lastIndexOf(@CheckForNull Object o) { + public int lastIndexOf(@Nullable Object o) { refreshIfEmpty(); return getListDelegate().lastIndexOf(o); } @@ -913,7 +908,7 @@ public void add(@ParametricNullness V value) { */ private class RandomAccessWrappedList extends WrappedList implements RandomAccess { RandomAccessWrappedList( - @ParametricNullness K key, List delegate, @CheckForNull WrappedCollection ancestor) { + @ParametricNullness K key, List delegate, @Nullable WrappedCollection ancestor) { super(key, delegate, ancestor); } } @@ -935,15 +930,15 @@ final Set createMaybeNavigableKeySet() { @WeakOuter private class KeySet extends Maps.KeySet> { - KeySet(final Map> subMap) { + KeySet(Map> subMap) { super(subMap); } @Override public Iterator iterator() { - final Iterator>> entryIterator = map().entrySet().iterator(); + Iterator>> entryIterator = map().entrySet().iterator(); return new Iterator() { - @CheckForNull Entry> entry; + @Nullable Entry> entry; @Override public boolean hasNext() { @@ -972,7 +967,7 @@ public void remove() { // The following methods are included for better performance. @Override - public boolean remove(@CheckForNull Object key) { + public boolean remove(@Nullable Object key) { int count = 0; Collection collection = map().remove(key); if (collection != null) { @@ -994,7 +989,7 @@ public boolean containsAll(Collection c) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return this == object || this.map().keySet().equals(object); } @@ -1016,8 +1011,7 @@ SortedMap> sortedMap() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return sortedMap().comparator(); } @@ -1050,7 +1044,7 @@ public SortedSet tailSet(@ParametricNullness K fromElement) { } @WeakOuter - class NavigableKeySet extends SortedKeySet implements NavigableSet { + private final class NavigableKeySet extends SortedKeySet implements NavigableSet { NavigableKeySet(NavigableMap> subMap) { super(subMap); } @@ -1061,38 +1055,32 @@ NavigableMap> sortedMap() { } @Override - @CheckForNull - public K lower(@ParametricNullness K k) { + public @Nullable K lower(@ParametricNullness K k) { return sortedMap().lowerKey(k); } @Override - @CheckForNull - public K floor(@ParametricNullness K k) { + public @Nullable K floor(@ParametricNullness K k) { return sortedMap().floorKey(k); } @Override - @CheckForNull - public K ceiling(@ParametricNullness K k) { + public @Nullable K ceiling(@ParametricNullness K k) { return sortedMap().ceilingKey(k); } @Override - @CheckForNull - public K higher(@ParametricNullness K k) { + public @Nullable K higher(@ParametricNullness K k) { return sortedMap().higherKey(k); } @Override - @CheckForNull - public K pollFirst() { + public @Nullable K pollFirst() { return Iterators.pollNext(iterator()); } @Override - @CheckForNull - public K pollLast() { + public @Nullable K pollLast() { return Iterators.pollNext(descendingIterator()); } @@ -1144,7 +1132,7 @@ public NavigableSet tailSet(@ParametricNullness K fromElement, boolean inclus } /** Removes all values for the provided key. */ - private void removeValuesForKey(@CheckForNull Object key) { + private void removeValuesForKey(@Nullable Object key) { Collection collection = Maps.safeRemove(map, key); if (collection != null) { @@ -1156,8 +1144,8 @@ private void removeValuesForKey(@CheckForNull Object key) { private abstract class Itr implements Iterator { final Iterator>> keyIterator; - @CheckForNull K key; - @CheckForNull Collection collection; + @Nullable K key; + @Nullable Collection collection; Iterator valueIterator; Itr() { @@ -1175,6 +1163,7 @@ public boolean hasNext() { } @Override + @ParametricNullness public T next() { if (!valueIterator.hasNext()) { Entry> mapEntry = keyIterator.next(); @@ -1277,7 +1266,7 @@ Iterator> entryIterator() { return new Itr>() { @Override Entry output(@ParametricNullness K key, @ParametricNullness V value) { - return Maps.immutableEntry(key, value); + return immutableEntry(key, value); } }; } @@ -1317,14 +1306,13 @@ protected Set>> createEntrySet() { // The following methods are included for performance. @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return Maps.safeContainsKey(submap, key); } @Override - @CheckForNull - public Collection get(@CheckForNull Object key) { - Collection collection = Maps.safeGet(submap, key); + public @Nullable Collection get(@Nullable Object key) { + Collection collection = safeGet(submap, key); if (collection == null) { return null; } @@ -1344,8 +1332,7 @@ public int size() { } @Override - @CheckForNull - public Collection remove(@CheckForNull Object key) { + public @Nullable Collection remove(@Nullable Object key) { Collection collection = submap.remove(key); if (collection == null) { return null; @@ -1359,7 +1346,7 @@ public Collection remove(@CheckForNull Object key) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return this == object || submap.equals(object); } @@ -1384,7 +1371,7 @@ public void clear() { Entry> wrapEntry(Entry> entry) { K key = entry.getKey(); - return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); + return immutableEntry(key, wrapCollection(key, entry.getValue())); } @WeakOuter @@ -1402,12 +1389,12 @@ public Iterator>> iterator() { // The following methods are included for performance. @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return Collections2.safeContains(submap.entrySet(), o); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (!contains(o)) { return false; } @@ -1421,7 +1408,7 @@ public boolean remove(@CheckForNull Object o) { /** Iterator across all keys and value collections. */ class AsMapIterator implements Iterator>> { final Iterator>> delegateIterator = submap.entrySet().iterator(); - @CheckForNull Collection collection; + @Nullable Collection collection; @Override public boolean hasNext() { @@ -1457,8 +1444,7 @@ SortedMap> sortedMap() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return sortedMap().comparator(); } @@ -1490,7 +1476,7 @@ public SortedMap> tailMap(@ParametricNullness K fromKey) { return new SortedAsMap(sortedMap().tailMap(fromKey)); } - @CheckForNull SortedSet sortedKeySet; + @Nullable SortedSet sortedKeySet; // returns a SortedSet, even though returning a Set would be sufficient to // satisfy the SortedMap.keySet() interface @@ -1506,7 +1492,7 @@ SortedSet createKeySet() { } } - class NavigableAsMap extends SortedAsMap implements NavigableMap> { + private final class NavigableAsMap extends SortedAsMap implements NavigableMap> { NavigableAsMap(NavigableMap> submap) { super(submap); @@ -1518,85 +1504,73 @@ NavigableMap> sortedMap() { } @Override - @CheckForNull - public Entry> lowerEntry(@ParametricNullness K key) { + public @Nullable Entry> lowerEntry(@ParametricNullness K key) { Entry> entry = sortedMap().lowerEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return sortedMap().lowerKey(key); } @Override - @CheckForNull - public Entry> floorEntry(@ParametricNullness K key) { + public @Nullable Entry> floorEntry(@ParametricNullness K key) { Entry> entry = sortedMap().floorEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return sortedMap().floorKey(key); } @Override - @CheckForNull - public Entry> ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry> ceilingEntry(@ParametricNullness K key) { Entry> entry = sortedMap().ceilingEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return sortedMap().ceilingKey(key); } @Override - @CheckForNull - public Entry> higherEntry(@ParametricNullness K key) { + public @Nullable Entry> higherEntry(@ParametricNullness K key) { Entry> entry = sortedMap().higherEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return sortedMap().higherKey(key); } @Override - @CheckForNull - public Entry> firstEntry() { + public @Nullable Entry> firstEntry() { Entry> entry = sortedMap().firstEntry(); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public Entry> lastEntry() { + public @Nullable Entry> lastEntry() { Entry> entry = sortedMap().lastEntry(); return (entry == null) ? null : wrapEntry(entry); } @Override - @CheckForNull - public Entry> pollFirstEntry() { + public @Nullable Entry> pollFirstEntry() { return pollAsMapEntry(entrySet().iterator()); } @Override - @CheckForNull - public Entry> pollLastEntry() { + public @Nullable Entry> pollLastEntry() { return pollAsMapEntry(descendingMap().entrySet().iterator()); } - @CheckForNull - Entry> pollAsMapEntry(Iterator>> entryIterator) { + @Nullable Entry> pollAsMapEntry( + Iterator>> entryIterator) { if (!entryIterator.hasNext()) { return null; } @@ -1604,7 +1578,7 @@ Entry> pollAsMapEntry(Iterator>> entryIt Collection output = createCollection(); output.addAll(entry.getValue()); entryIterator.remove(); - return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); + return immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); } @Override @@ -1669,5 +1643,6 @@ public NavigableMap> tailMap( } } + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 2447537837011683357L; } diff --git a/android/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java b/android/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java index 8c71e682da31..c46700b18921 100644 --- a/android/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java +++ b/android/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java @@ -22,6 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -31,8 +32,7 @@ import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of {@code Multiset} backed by an instance of {@code @@ -44,7 +44,6 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class AbstractMapBasedMultiset extends AbstractMultiset implements Serializable { @@ -58,7 +57,7 @@ abstract class AbstractMapBasedMultiset extends Abst abstract ObjectCountHashMap newBackingMap(int distinctElements); @Override - public final int count(@CheckForNull Object element) { + public final int count(@Nullable Object element) { return backingMap.get(element); } @@ -93,7 +92,7 @@ public final int add(@ParametricNullness E element, int occurrences) { @CanIgnoreReturnValue @Override - public final int remove(@CheckForNull Object element, int occurrences) { + public final int remove(@Nullable Object element, int occurrences) { if (occurrences == 0) { return count(element); } @@ -255,12 +254,14 @@ public final int size() { * its count, and so on */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Serialization.writeMultiset(this, stream); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int distinctElements = Serialization.readCount(stream); @@ -268,6 +269,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo Serialization.populateMultiset(this, stream, distinctElements); } - @GwtIncompatible // Not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/AbstractMapEntry.java b/android/guava/src/com/google/common/collect/AbstractMapEntry.java index e9accf0592c2..6b87b90b1641 100644 --- a/android/guava/src/com/google/common/collect/AbstractMapEntry.java +++ b/android/guava/src/com/google/common/collect/AbstractMapEntry.java @@ -19,8 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} methods of {@code @@ -29,7 +28,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractMapEntry implements Entry { @@ -48,7 +46,7 @@ public V setValue(@ParametricNullness V value) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Entry) { Entry that = (Entry) object; return Objects.equal(this.getKey(), that.getKey()) diff --git a/android/guava/src/com/google/common/collect/AbstractMultimap.java b/android/guava/src/com/google/common/collect/AbstractMultimap.java index 17e84a62a855..cca1bf9cea15 100644 --- a/android/guava/src/com/google/common/collect/AbstractMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractMultimap.java @@ -28,8 +28,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. @@ -37,7 +36,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractMultimap implements Multimap { @Override @@ -46,7 +44,7 @@ public boolean isEmpty() { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { for (Collection collection : asMap().values()) { if (collection.contains(value)) { return true; @@ -57,14 +55,14 @@ public boolean containsValue(@CheckForNull Object value) { } @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { Collection collection = asMap().get(key); return collection != null && collection.contains(value); } @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { Collection collection = asMap().get(key); return collection != null && collection.remove(value); } @@ -109,7 +107,7 @@ public Collection replaceValues(@ParametricNullness K key, Iterable> entries; + @LazyInit private transient @Nullable Collection> entries; @Override public Collection> entries() { @@ -140,14 +138,14 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return Sets.equalsImpl(this, obj); } } abstract Iterator> entryIterator(); - @LazyInit @CheckForNull private transient Set keySet; + @LazyInit private transient @Nullable Set keySet; @Override public Set keySet() { @@ -157,7 +155,7 @@ public Set keySet() { abstract Set createKeySet(); - @LazyInit @CheckForNull private transient Multiset keys; + @LazyInit private transient @Nullable Multiset keys; @Override public Multiset keys() { @@ -167,7 +165,7 @@ public Multiset keys() { abstract Multiset createKeys(); - @LazyInit @CheckForNull private transient Collection values; + @LazyInit private transient @Nullable Collection values; @Override public Collection values() { @@ -190,7 +188,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return AbstractMultimap.this.containsValue(o); } @@ -204,7 +202,7 @@ Iterator valueIterator() { return Maps.valueIterator(entries().iterator()); } - @LazyInit @CheckForNull private transient Map> asMap; + @LazyInit private transient @Nullable Map> asMap; @Override public Map> asMap() { @@ -217,7 +215,7 @@ public Map> asMap() { // Comparison and hashing @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return Multimaps.equalsImpl(this, object); } diff --git a/android/guava/src/com/google/common/collect/AbstractMultiset.java b/android/guava/src/com/google/common/collect/AbstractMultiset.java index 8203e44598a0..55848b24b496 100644 --- a/android/guava/src/com/google/common/collect/AbstractMultiset.java +++ b/android/guava/src/com/google/common/collect/AbstractMultiset.java @@ -26,8 +26,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of the {@link Multiset} interface. A new multiset @@ -43,7 +42,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractMultiset extends AbstractCollection implements Multiset { // Query Operations @@ -54,7 +52,7 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return count(element) > 0; } @@ -74,13 +72,13 @@ public int add(@ParametricNullness E element, int occurrences) { @CanIgnoreReturnValue @Override - public final boolean remove(@CheckForNull Object element) { + public final boolean remove(@Nullable Object element) { return remove(element, 1) > 0; } @CanIgnoreReturnValue @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { throw new UnsupportedOperationException(); } @@ -127,7 +125,7 @@ public final boolean retainAll(Collection elementsToRetain) { // Views - @LazyInit @CheckForNull private transient Set elementSet; + @LazyInit private transient @Nullable Set elementSet; @Override public Set elementSet() { @@ -161,7 +159,7 @@ public Iterator iterator() { abstract Iterator elementIterator(); - @LazyInit @CheckForNull private transient Set> entrySet; + @LazyInit private transient @Nullable Set> entrySet; @Override public Set> entrySet() { @@ -207,7 +205,7 @@ Set> createEntrySet() { * and if, for each element, the two multisets have the same count. */ @Override - public final boolean equals(@CheckForNull Object object) { + public final boolean equals(@Nullable Object object) { return Multisets.equalsImpl(this, object); } diff --git a/android/guava/src/com/google/common/collect/AbstractNavigableMap.java b/android/guava/src/com/google/common/collect/AbstractNavigableMap.java index 47048d06ac49..ef2f20b04fa4 100644 --- a/android/guava/src/com/google/common/collect/AbstractNavigableMap.java +++ b/android/guava/src/com/google/common/collect/AbstractNavigableMap.java @@ -24,8 +24,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Skeletal implementation of {@link NavigableMap}. @@ -33,35 +32,29 @@ * @author Louis Wasserman */ @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class AbstractNavigableMap extends IteratorBasedAbstractMap implements NavigableMap { @Override - @CheckForNull - public abstract V get(@CheckForNull Object key); + public abstract @Nullable V get(@Nullable Object key); @Override - @CheckForNull - public Entry firstEntry() { - return Iterators.getNext(entryIterator(), null); + public @Nullable Entry firstEntry() { + return Iterators.<@Nullable Entry>getNext(entryIterator(), null); } @Override - @CheckForNull - public Entry lastEntry() { - return Iterators.getNext(descendingEntryIterator(), null); + public @Nullable Entry lastEntry() { + return Iterators.<@Nullable Entry>getNext(descendingEntryIterator(), null); } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return Iterators.pollNext(entryIterator()); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return Iterators.pollNext(descendingEntryIterator()); } @@ -88,50 +81,42 @@ public K lastKey() { } @Override - @CheckForNull - public Entry lowerEntry(@ParametricNullness K key) { + public @Nullable Entry lowerEntry(@ParametricNullness K key) { return headMap(key, false).lastEntry(); } @Override - @CheckForNull - public Entry floorEntry(@ParametricNullness K key) { + public @Nullable Entry floorEntry(@ParametricNullness K key) { return headMap(key, true).lastEntry(); } @Override - @CheckForNull - public Entry ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry ceilingEntry(@ParametricNullness K key) { return tailMap(key, true).firstEntry(); } @Override - @CheckForNull - public Entry higherEntry(@ParametricNullness K key) { + public @Nullable Entry higherEntry(@ParametricNullness K key) { return tailMap(key, false).firstEntry(); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return Maps.keyOrNull(lowerEntry(key)); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return Maps.keyOrNull(floorEntry(key)); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return Maps.keyOrNull(ceilingEntry(key)); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return Maps.keyOrNull(higherEntry(key)); } diff --git a/android/guava/src/com/google/common/collect/AbstractRangeSet.java b/android/guava/src/com/google/common/collect/AbstractRangeSet.java index 032be3d5b03e..7a879a4a885a 100644 --- a/android/guava/src/com/google/common/collect/AbstractRangeSet.java +++ b/android/guava/src/com/google/common/collect/AbstractRangeSet.java @@ -15,15 +15,15 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A skeletal implementation of {@code RangeSet}. * * @author Louis Wasserman */ +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class AbstractRangeSet implements RangeSet { AbstractRangeSet() {} @@ -33,8 +33,7 @@ public boolean contains(C value) { } @Override - @CheckForNull - public abstract Range rangeContaining(C value); + public abstract @Nullable Range rangeContaining(C value); @Override public boolean isEmpty() { @@ -104,7 +103,7 @@ public boolean intersects(Range otherRange) { public abstract boolean encloses(Range otherRange); @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } else if (obj instanceof RangeSet) { diff --git a/android/guava/src/com/google/common/collect/AbstractSequentialIterator.java b/android/guava/src/com/google/common/collect/AbstractSequentialIterator.java index 172fe356cb78..8c2d24be124b 100644 --- a/android/guava/src/com/google/common/collect/AbstractSequentialIterator.java +++ b/android/guava/src/com/google/common/collect/AbstractSequentialIterator.java @@ -18,7 +18,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of the {@code Iterator} interface for sequences @@ -40,15 +40,14 @@ * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class AbstractSequentialIterator extends UnmodifiableIterator { - @CheckForNull private T nextOrNull; + private @Nullable T nextOrNull; /** * Creates a new iterator with the given first element, or, if {@code firstOrNull} is null, * creates a new empty iterator. */ - protected AbstractSequentialIterator(@CheckForNull T firstOrNull) { + protected AbstractSequentialIterator(@Nullable T firstOrNull) { this.nextOrNull = firstOrNull; } @@ -57,8 +56,7 @@ protected AbstractSequentialIterator(@CheckForNull T firstOrNull) { * remain. This method is invoked during each call to {@link #next()} in order to compute the * result of a future call to {@code next()}. */ - @CheckForNull - protected abstract T computeNext(T previous); + protected abstract @Nullable T computeNext(T previous); @Override public final boolean hasNext() { diff --git a/android/guava/src/com/google/common/collect/AbstractSetMultimap.java b/android/guava/src/com/google/common/collect/AbstractSetMultimap.java index 90aa9dcb2f79..e46360997300 100644 --- a/android/guava/src/com/google/common/collect/AbstractSetMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractSetMultimap.java @@ -16,15 +16,18 @@ package com.google.common.collect; +import static java.util.Collections.emptySet; +import static java.util.Collections.unmodifiableSet; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of the {@link SetMultimap} interface. It's a wrapper around {@link @@ -34,7 +37,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractSetMultimap extends AbstractMapBasedMultimap implements SetMultimap { /** @@ -51,13 +53,13 @@ protected AbstractSetMultimap(Map> map) { @Override Set createUnmodifiableEmptyCollection() { - return Collections.emptySet(); + return emptySet(); } @Override Collection unmodifiableCollectionSubclass( Collection collection) { - return Collections.unmodifiableSet((Set) collection); + return unmodifiableSet((Set) collection); } @Override @@ -97,7 +99,7 @@ public Set> entries() { */ @CanIgnoreReturnValue @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { return (Set) super.removeAll(key); } @@ -147,9 +149,10 @@ public boolean put(@ParametricNullness K key, @ParametricNullness V value) { * Equality does not depend on the ordering of keys or values. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return super.equals(object); } + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 7431625294878419160L; } diff --git a/android/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/android/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java index 676936f0578e..b07e226df411 100644 --- a/android/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java @@ -21,7 +21,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. @@ -32,7 +32,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractSortedKeySortedSetMultimap< K extends @Nullable Object, V extends @Nullable Object> extends AbstractSortedSetMultimap { diff --git a/android/guava/src/com/google/common/collect/AbstractSortedMultiset.java b/android/guava/src/com/google/common/collect/AbstractSortedMultiset.java index 621e8f98a5e2..27ab0dd4213e 100644 --- a/android/guava/src/com/google/common/collect/AbstractSortedMultiset.java +++ b/android/guava/src/com/google/common/collect/AbstractSortedMultiset.java @@ -17,12 +17,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.util.Comparator; import java.util.Iterator; import java.util.NavigableSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of the {@link SortedMultiset} interface. @@ -34,7 +34,6 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class AbstractSortedMultiset extends AbstractMultiset implements SortedMultiset { @GwtTransient final Comparator comparator; @@ -65,22 +64,19 @@ public Comparator comparator() { } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { Iterator> entryIterator = entryIterator(); return entryIterator.hasNext() ? entryIterator.next() : null; } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { Iterator> entryIterator = descendingEntryIterator(); return entryIterator.hasNext() ? entryIterator.next() : null; } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { Iterator> entryIterator = entryIterator(); if (entryIterator.hasNext()) { Entry result = entryIterator.next(); @@ -92,8 +88,7 @@ public Entry pollFirstEntry() { } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { Iterator> entryIterator = descendingEntryIterator(); if (entryIterator.hasNext()) { Entry result = entryIterator.next(); @@ -122,7 +117,7 @@ Iterator descendingIterator() { return Multisets.iteratorImpl(descendingMultiset()); } - @CheckForNull private transient SortedMultiset descendingMultiset; + @LazyInit private transient @Nullable SortedMultiset descendingMultiset; @Override public SortedMultiset descendingMultiset() { diff --git a/android/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java b/android/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java index 32316133a051..6441cde91b09 100644 --- a/android/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java +++ b/android/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java @@ -16,15 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.Sets.unmodifiableNavigableSet; +import static java.util.Collections.unmodifiableSortedSet; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.NavigableSet; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Basic implementation of the {@link SortedSetMultimap} interface. It's a wrapper around {@link @@ -34,7 +37,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractSortedSetMultimap extends AbstractSetMultimap implements SortedSetMultimap { /** @@ -58,9 +60,9 @@ SortedSet createUnmodifiableEmptyCollection() { SortedSet unmodifiableCollectionSubclass( Collection collection) { if (collection instanceof NavigableSet) { - return Sets.unmodifiableNavigableSet((NavigableSet) collection); + return unmodifiableNavigableSet((NavigableSet) collection); } else { - return Collections.unmodifiableSortedSet((SortedSet) collection); + return unmodifiableSortedSet((SortedSet) collection); } } @@ -99,7 +101,7 @@ public SortedSet get(@ParametricNullness K key) { */ @CanIgnoreReturnValue @Override - public SortedSet removeAll(@CheckForNull Object key) { + public SortedSet removeAll(@Nullable Object key) { return (SortedSet) super.removeAll(key); } @@ -147,5 +149,6 @@ public Collection values() { return super.values(); } + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 430848587173315748L; } diff --git a/android/guava/src/com/google/common/collect/AbstractTable.java b/android/guava/src/com/google/common/collect/AbstractTable.java index fe1d4e77fedf..9f71fdfecd16 100644 --- a/android/guava/src/com/google/common/collect/AbstractTable.java +++ b/android/guava/src/com/google/common/collect/AbstractTable.java @@ -14,6 +14,9 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; +import static com.google.common.collect.Maps.safeGet; + import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.LazyInit; @@ -24,8 +27,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Skeletal, implementation-agnostic implementation of the {@link Table} interface. @@ -33,18 +35,17 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class AbstractTable< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> implements Table { @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { return Maps.safeContainsKey(rowMap(), rowKey); } @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { return Maps.safeContainsKey(columnMap(), columnKey); } @@ -59,7 +60,7 @@ public Set columnKeySet() { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { for (Map row : rowMap().values()) { if (row.containsValue(value)) { return true; @@ -69,16 +70,15 @@ public boolean containsValue(@CheckForNull Object value) { } @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { - Map row = Maps.safeGet(rowMap(), rowKey); + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = safeGet(rowMap(), rowKey); return row != null && Maps.safeContainsKey(row, columnKey); } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { - Map row = Maps.safeGet(rowMap(), rowKey); - return (row == null) ? null : Maps.safeGet(row, columnKey); + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = safeGet(rowMap(), rowKey); + return (row == null) ? null : safeGet(row, columnKey); } @Override @@ -93,16 +93,14 @@ public void clear() { @CanIgnoreReturnValue @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { - Map row = Maps.safeGet(rowMap(), rowKey); + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = safeGet(rowMap(), rowKey); return (row == null) ? null : Maps.safeRemove(row, columnKey); } @CanIgnoreReturnValue @Override - @CheckForNull - public V put( + public @Nullable V put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { return row(rowKey).put(columnKey, value); } @@ -114,7 +112,7 @@ public void putAll(Table table) { } } - @LazyInit @CheckForNull private transient Set> cellSet; + @LazyInit private transient @Nullable Set> cellSet; @Override public Set> cellSet() { @@ -131,25 +129,25 @@ Set> createCellSet() { @WeakOuter class CellSet extends AbstractSet> { @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Cell) { Cell cell = (Cell) o; - Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + Map row = safeGet(rowMap(), cell.getRowKey()); return row != null && Collections2.safeContains( - row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + row.entrySet(), immutableEntry(cell.getColumnKey(), cell.getValue())); } return false; } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o instanceof Cell) { Cell cell = (Cell) o; - Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + Map row = safeGet(rowMap(), cell.getRowKey()); return row != null && Collections2.safeRemove( - row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + row.entrySet(), immutableEntry(cell.getColumnKey(), cell.getValue())); } return false; } @@ -170,7 +168,7 @@ public int size() { } } - @LazyInit @CheckForNull private transient Collection values; + @LazyInit private transient @Nullable Collection values; @Override public Collection values() { @@ -200,7 +198,7 @@ public Iterator iterator() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return containsValue(o); } @@ -216,7 +214,7 @@ public int size() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return Tables.equalsImpl(this, obj); } diff --git a/android/guava/src/com/google/common/collect/AllEqualOrdering.java b/android/guava/src/com/google/common/collect/AllEqualOrdering.java index f6ca6faff797..e6d515c95fcc 100644 --- a/android/guava/src/com/google/common/collect/AllEqualOrdering.java +++ b/android/guava/src/com/google/common/collect/AllEqualOrdering.java @@ -17,10 +17,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An ordering that treats all references as equals, even nulls. @@ -28,12 +29,12 @@ * @author Emily Soldal */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class AllEqualOrdering extends Ordering<@Nullable Object> implements Serializable { static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); @Override - public int compare(@CheckForNull Object left, @CheckForNull Object right) { + @SuppressWarnings("UnusedVariable") // intentionally weird Comparator + public int compare(@Nullable Object left, @Nullable Object right) { return 0; } @@ -43,8 +44,7 @@ public int compare(@CheckForNull Object left, @CheckForNull Object right) { } @Override - @SuppressWarnings("nullness") // unsafe: see supertype - public ImmutableList immutableSortedCopy(Iterable iterable) { + public ImmutableList immutableSortedCopy(Iterable iterable) { return ImmutableList.copyOf(iterable); } @@ -63,5 +63,5 @@ public String toString() { return "Ordering.allEqual()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ArrayListMultimap.java b/android/guava/src/com/google/common/collect/ArrayListMultimap.java index 33f4c75397f1..fa87ffbcf1e8 100644 --- a/android/guava/src/com/google/common/collect/ArrayListMultimap.java +++ b/android/guava/src/com/google/common/collect/ArrayListMultimap.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.io.ObjectInputStream; @@ -29,7 +30,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Multimap} that uses an {@code ArrayList} to store the values for a given @@ -53,14 +54,12 @@ * with a call to {@link Multimaps#synchronizedListMultimap}. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public final class ArrayListMultimap extends ArrayListMultimapGwtSerializationDependencies { // Default from ArrayList @@ -71,8 +70,9 @@ public final class ArrayListMultimapThis method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys().arrayListValues().build()}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys().arrayListValues().build()}, which provides more control over the + * underlying data structure. */ public static ArrayListMultimap create() { @@ -83,8 +83,9 @@ ArrayListMultimap create() { * Constructs an empty {@code ArrayListMultimap} with enough capacity to hold the specified * numbers of keys and values without resizing. * - *

    This method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys(expectedKeys).arrayListValues(expectedValuesPerKey).build()}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys(expectedKeys).arrayListValues(expectedValuesPerKey).build()}, which + * provides more control over the underlying data structure. * * @param expectedKeys the expected number of distinct keys * @param expectedValuesPerKey the expected average number of values per key @@ -99,8 +100,9 @@ ArrayListMultimap create(int expectedKeys, int expectedValuesPerKey) { /** * Constructs an {@code ArrayListMultimap} with the same mappings as the specified multimap. * - *

    This method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys().arrayListValues().build(multimap)}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys().arrayListValues().build(multimap)}, which provides more control over + * the underlying data structure. * * @param multimap the multimap whose contents are copied to this multimap */ @@ -133,7 +135,7 @@ private ArrayListMultimap(Multimap multimap) { */ @Override List createCollection() { - return new ArrayList(expectedValuesPerKey); + return new ArrayList<>(expectedValuesPerKey); } /** @@ -156,12 +158,14 @@ public void trimToSize() { * key, number of values for that key, and the key's values */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Serialization.writeMultimap(this, stream); } @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; @@ -171,6 +175,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo Serialization.populateMultimap(this, stream, distinctKeys); } - @GwtIncompatible // Not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java b/android/guava/src/com/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java index 9a8cdfbdbd13..6d0eef163409 100644 --- a/android/guava/src/com/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java +++ b/android/guava/src/com/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.Nullable; /** * A dummy superclass to support GWT serialization of the element types of an {@link @@ -30,7 +31,8 @@ *

    TODO(cpovirk): Consider applying this subclass approach to our other types. */ @GwtCompatible(emulated = true) -abstract class ArrayListMultimapGwtSerializationDependencies +abstract class ArrayListMultimapGwtSerializationDependencies< + K extends @Nullable Object, V extends @Nullable Object> extends AbstractListMultimap { ArrayListMultimapGwtSerializationDependencies(Map> map) { super(map); diff --git a/android/guava/src/com/google/common/collect/ArrayTable.java b/android/guava/src/com/google/common/collect/ArrayTable.java index ebc9fa06a362..1a8e9081e829 100644 --- a/android/guava/src/com/google/common/collect/ArrayTable.java +++ b/android/guava/src/com/google/common/collect/ArrayTable.java @@ -19,15 +19,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.System.arraycopy; import static java.util.Collections.emptyMap; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.collect.Maps.IteratorBasedAbstractMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.io.Serializable; import java.lang.reflect.Array; @@ -36,8 +38,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Fixed-size {@link Table} implementation backed by a two-dimensional array. @@ -83,14 +84,12 @@ * thread that reads from another. * *

    See the Guava User Guide article on {@code Table}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#table">{@code Table}. * * @author Jared Levy * @since 10.0 */ -@Beta @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class ArrayTable extends AbstractTable implements Serializable { @@ -132,6 +131,7 @@ public static ArrayTable create( * * @throws NullPointerException if {@code table} has a null key */ + @SuppressWarnings("unchecked") // TODO(cpovirk): Make constructor accept wildcard types? public static ArrayTable create(Table table) { return (table instanceof ArrayTable) ? new ArrayTable((ArrayTable) table) @@ -161,8 +161,7 @@ private ArrayTable(Iterable rowKeys, Iterable columnKe columnKeyToIndex = Maps.indexMap(columnList); @SuppressWarnings("unchecked") - @Nullable - V[][] tmpArray = (@Nullable V[][]) new Object[rowList.size()][columnList.size()]; + @Nullable V[][] tmpArray = (@Nullable V[][]) new Object[rowList.size()][columnList.size()]; array = tmpArray; // Necessary because in GWT the arrays are initialized with "undefined" instead of null. eraseAll(); @@ -179,11 +178,10 @@ private ArrayTable(ArrayTable table) { rowKeyToIndex = table.rowKeyToIndex; columnKeyToIndex = table.columnKeyToIndex; @SuppressWarnings("unchecked") - @Nullable - V[][] copy = (@Nullable V[][]) new Object[rowList.size()][columnList.size()]; + @Nullable V[][] copy = (@Nullable V[][]) new Object[rowList.size()][columnList.size()]; array = copy; for (int i = 0; i < rowList.size(); i++) { - System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); + arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); } } @@ -222,7 +220,7 @@ public boolean isEmpty() { return keyIndex.isEmpty(); } - Entry getEntry(final int index) { + Entry getEntry(int index) { checkElementIndex(index, size()); return new AbstractMapEntry() { @Override @@ -248,7 +246,7 @@ public V setValue(@ParametricNullness V value) { Iterator> entryIterator() { return new AbstractIndexedListIterator>(size()) { @Override - protected Entry get(final int index) { + protected Entry get(int index) { return getEntry(index); } }; @@ -257,13 +255,12 @@ protected Entry get(final int index) { // TODO(lowasser): consider an optimized values() implementation @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return keyIndex.containsKey(key); } - @CheckForNull @Override - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { Integer index = keyIndex.get(key); if (index == null) { return null; @@ -273,8 +270,7 @@ public V get(@CheckForNull Object key) { } @Override - @CheckForNull - public V put(K key, @ParametricNullness V value) { + public @Nullable V put(K key, @ParametricNullness V value) { Integer index = keyIndex.get(key); if (index == null) { throw new IllegalArgumentException( @@ -284,8 +280,7 @@ public V put(K key, @ParametricNullness V value) { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -323,8 +318,7 @@ public ImmutableList columnKeyList() { * or equal to the number of allowed row keys, or {@code columnIndex} is greater than or equal * to the number of allowed column keys */ - @CheckForNull - public V at(int rowIndex, int columnIndex) { + public @Nullable V at(int rowIndex, int columnIndex) { // In GWT array access never throws IndexOutOfBoundsException. checkElementIndex(rowIndex, rowList.size()); checkElementIndex(columnIndex, columnList.size()); @@ -345,8 +339,7 @@ public V at(int rowIndex, int columnIndex) { * to the number of allowed column keys */ @CanIgnoreReturnValue - @CheckForNull - public V set(int rowIndex, int columnIndex, @CheckForNull V value) { + public @Nullable V set(int rowIndex, int columnIndex, @Nullable V value) { // In GWT array access never throws IndexOutOfBoundsException. checkElementIndex(rowIndex, rowList.size()); checkElementIndex(columnIndex, columnList.size()); @@ -367,10 +360,10 @@ public V set(int rowIndex, int columnIndex, @CheckForNull V value) { @GwtIncompatible // reflection public @Nullable V[][] toArray(Class valueClass) { @SuppressWarnings("unchecked") // TODO: safe? - @Nullable - V[][] copy = (@Nullable V[][]) Array.newInstance(valueClass, rowList.size(), columnList.size()); + @Nullable V[][] copy = + (@Nullable V[][]) Array.newInstance(valueClass, rowList.size(), columnList.size()); for (int i = 0; i < rowList.size(); i++) { - System.arraycopy(array[i], 0, copy[i], 0, array[i].length); + arraycopy(array[i], 0, copy[i], 0, array[i].length); } return copy; } @@ -400,7 +393,7 @@ public void eraseAll() { * constructed. */ @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return containsRow(rowKey) && containsColumn(columnKey); } @@ -409,7 +402,7 @@ public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object column * table was constructed. */ @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { return columnKeyToIndex.containsKey(columnKey); } @@ -418,12 +411,12 @@ public boolean containsColumn(@CheckForNull Object columnKey) { * constructed. */ @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { return rowKeyToIndex.containsKey(rowKey); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { for (@Nullable V[] row : array) { for (V element : row) { if (Objects.equal(value, element)) { @@ -435,8 +428,7 @@ public boolean containsValue(@CheckForNull Object value) { } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); return (rowIndex == null || columnIndex == null) ? null : at(rowIndex, columnIndex); @@ -458,8 +450,7 @@ public boolean isEmpty() { */ @CanIgnoreReturnValue @Override - @CheckForNull - public V put(R rowKey, C columnKey, @CheckForNull V value) { + public @Nullable V put(R rowKey, C columnKey, @Nullable V value) { checkNotNull(rowKey); checkNotNull(columnKey); Integer rowIndex = rowKeyToIndex.get(rowKey); @@ -499,8 +490,7 @@ public void putAll(Table table) @CanIgnoreReturnValue @Override @Deprecated - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { throw new UnsupportedOperationException(); } @@ -518,8 +508,7 @@ public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { * for the keys */ @CanIgnoreReturnValue - @CheckForNull - public V erase(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V erase(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); if (rowIndex == null || columnIndex == null) { @@ -555,13 +544,13 @@ public int size() { Iterator> cellIterator() { return new AbstractIndexedListIterator>(size()) { @Override - protected Cell get(final int index) { + protected Cell get(int index) { return getCell(index); } }; } - private Cell getCell(final int index) { + private Cell getCell(int index) { return new Tables.AbstractCell() { final int rowIndex = index / columnList.size(); final int columnIndex = index % columnList.size(); @@ -577,15 +566,13 @@ public C getColumnKey() { } @Override - @CheckForNull - public V getValue() { + public @Nullable V getValue() { return at(rowIndex, columnIndex); } }; } - @CheckForNull - private V getValue(int index) { + private @Nullable V getValue(int index) { int rowIndex = index / columnList.size(); int columnIndex = index % columnList.size(); return at(rowIndex, columnIndex); @@ -627,14 +614,12 @@ String getKeyRole() { } @Override - @CheckForNull - V getValue(int index) { + @Nullable V getValue(int index) { return at(index, columnIndex); } @Override - @CheckForNull - V setValue(int index, @CheckForNull V newValue) { + @Nullable V setValue(int index, @Nullable V newValue) { return set(index, columnIndex, newValue); } } @@ -650,7 +635,7 @@ public ImmutableSet columnKeySet() { return columnKeyToIndex.keySet(); } - @CheckForNull private transient ColumnMap columnMap; + @LazyInit private transient @Nullable ColumnMap columnMap; @Override public Map> columnMap() { @@ -680,8 +665,7 @@ String getKeyRole() { } @Override - @CheckForNull - public Map put(C key, Map value) { + public @Nullable Map put(C key, Map value) { throw new UnsupportedOperationException(); } } @@ -722,14 +706,12 @@ String getKeyRole() { } @Override - @CheckForNull - V getValue(int index) { + @Nullable V getValue(int index) { return at(rowIndex, index); } @Override - @CheckForNull - V setValue(int index, @CheckForNull V newValue) { + @Nullable V setValue(int index, @Nullable V newValue) { return set(rowIndex, index, newValue); } } @@ -745,7 +727,7 @@ public ImmutableSet rowKeySet() { return rowKeyToIndex.keySet(); } - @CheckForNull private transient RowMap rowMap; + @LazyInit private transient @Nullable RowMap rowMap; @Override public Map> rowMap() { @@ -775,8 +757,7 @@ String getKeyRole() { } @Override - @CheckForNull - public Map put(R key, Map value) { + public @Nullable Map put(R key, Map value) { throw new UnsupportedOperationException(); } } @@ -799,12 +780,11 @@ String getKeyRole() { Iterator<@Nullable V> valuesIterator() { return new AbstractIndexedListIterator<@Nullable V>(size()) { @Override - @CheckForNull - protected V get(int index) { + protected @Nullable V get(int index) { return getValue(index); } }; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/BaseImmutableMultimap.java b/android/guava/src/com/google/common/collect/BaseImmutableMultimap.java index 2e69c2a92e39..6ebdf14f52fa 100644 --- a/android/guava/src/com/google/common/collect/BaseImmutableMultimap.java +++ b/android/guava/src/com/google/common/collect/BaseImmutableMultimap.java @@ -22,5 +22,4 @@ * retaining additional implementation details of {@link ImmutableMultimap}. */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class BaseImmutableMultimap extends AbstractMultimap {} diff --git a/android/guava/src/com/google/common/collect/BiMap.java b/android/guava/src/com/google/common/collect/BiMap.java index 12eb4e3e1b61..9adbff493fad 100644 --- a/android/guava/src/com/google/common/collect/BiMap.java +++ b/android/guava/src/com/google/common/collect/BiMap.java @@ -20,14 +20,22 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A bimap (or "bidirectional map") is a map that preserves the uniqueness of its values as well as * that of its keys. This constraint enables bimaps to support an "inverse view", which is another * bimap containing the same entries as this bimap but with reversed keys and values. * + *

    Implementations

    + * + *
      + *
    • {@link ImmutableBiMap} + *
    • {@link HashBiMap} + *
    • {@link EnumBiMap} + *
    • {@link EnumHashBiMap} + *
    + * *

    See the Guava User Guide article on {@code BiMap}. * @@ -35,7 +43,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface BiMap extends Map { // Modification Operations @@ -48,8 +55,7 @@ public interface BiMap e */ @CanIgnoreReturnValue @Override - @CheckForNull - V put(@ParametricNullness K key, @ParametricNullness V value); + @Nullable V put(@ParametricNullness K key, @ParametricNullness V value); /** * An alternate form of {@code put} that silently removes any existing entry with the value {@code @@ -70,8 +76,7 @@ public interface BiMap e * value.) */ @CanIgnoreReturnValue - @CheckForNull - V forcePut(@ParametricNullness K key, @ParametricNullness V value); + @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value); // Bulk Operations diff --git a/android/guava/src/com/google/common/collect/BoundType.java b/android/guava/src/com/google/common/collect/BoundType.java index 00ac08c5182c..6f24a6ad62ba 100644 --- a/android/guava/src/com/google/common/collect/BoundType.java +++ b/android/guava/src/com/google/common/collect/BoundType.java @@ -24,7 +24,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public enum BoundType { /** The endpoint value is not considered part of the set ("exclusive"). */ OPEN(false), diff --git a/android/guava/src/com/google/common/collect/ByFunctionOrdering.java b/android/guava/src/com/google/common/collect/ByFunctionOrdering.java index 43ebdddf62b3..143e7be5562b 100644 --- a/android/guava/src/com/google/common/collect/ByFunctionOrdering.java +++ b/android/guava/src/com/google/common/collect/ByFunctionOrdering.java @@ -19,18 +19,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Objects; import java.io.Serializable; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An ordering that orders elements by applying an order to the result of a function on those * elements. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class ByFunctionOrdering extends Ordering implements Serializable { final Function function; @@ -47,7 +47,7 @@ public int compare(@ParametricNullness F left, @ParametricNullness F right) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -68,5 +68,5 @@ public String toString() { return ordering + ".onResultOf(" + function + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/CartesianList.java b/android/guava/src/com/google/common/collect/CartesianList.java index 4c31b12998e5..8150370afd04 100644 --- a/android/guava/src/com/google/common/collect/CartesianList.java +++ b/android/guava/src/com/google/common/collect/CartesianList.java @@ -17,12 +17,14 @@ import static com.google.common.base.Preconditions.checkElementIndex; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.math.IntMath; import java.util.AbstractList; import java.util.List; import java.util.ListIterator; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Lists#cartesianProduct(List)}. @@ -30,7 +32,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class CartesianList extends AbstractList> implements RandomAccess { private final transient ImmutableList> axes; @@ -68,7 +69,7 @@ private int getAxisIndexForProductIndex(int index, int axis) { } @Override - public int indexOf(@CheckForNull Object o) { + public int indexOf(@Nullable Object o) { if (!(o instanceof List)) { return -1; } @@ -90,7 +91,7 @@ public int indexOf(@CheckForNull Object o) { } @Override - public int lastIndexOf(@CheckForNull Object o) { + public int lastIndexOf(@Nullable Object o) { if (!(o instanceof List)) { return -1; } @@ -132,6 +133,15 @@ public E get(int axis) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @J2ktIncompatible // serialization + @Override + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } @@ -141,7 +151,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (!(object instanceof List)) { return false; } diff --git a/android/guava/src/com/google/common/collect/ClassToInstanceMap.java b/android/guava/src/com/google/common/collect/ClassToInstanceMap.java index 9bd826fd92f6..12c8ee32a346 100644 --- a/android/guava/src/com/google/common/collect/ClassToInstanceMap.java +++ b/android/guava/src/com/google/common/collect/ClassToInstanceMap.java @@ -20,7 +20,8 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A map, each entry of which maps a Java raw type to an @@ -30,43 +31,34 @@ *

    Like any other {@code Map}, this map may contain entries for primitive types, * and a primitive type and its corresponding wrapper type may map to different values. * - *

    This class's support for {@code null} requires some explanation: From release 31.0 onward, - * Guava specifies the nullness of its types through annotations. In the case of {@code - * ClassToInstanceMap}, it specifies that both the key and value types are restricted to - * non-nullable types. This specification is reasonable for keys, which must be non-null - * classes. This is in contrast to the specification for values: Null values are - * supported by the implementation {@link MutableClassToInstanceMap}, even though that - * implementation and this interface specify otherwise. Thus, if you use a nullness checker, you can - * safely suppress any warnings it produces when you write null values into a {@code - * MutableClassToInstanceMap}. Just be sure to be prepared for null values when reading from it, - * since nullness checkers will assume that vaules are non-null then, too. + *

    Implementations

    * - *

    See the Guava User Guide article on {@code - * ClassToInstanceMap}. + *

      + *
    • {@link ImmutableClassToInstanceMap} + *
    • {@link MutableClassToInstanceMap} + *
    * *

    To map a generic type to an instance of that type, use {@link * com.google.common.reflect.TypeToInstanceMap} instead. * - * @param the common supertype that all entries must share; often this is simply {@link Object} - * @author Kevin Bourrillion + *

    See the Guava User Guide article on {@code + * ClassToInstanceMap}. + * + * @param the common supertype that all values will share. When in doubt, just use {@link + * Object}, or use {@code @Nullable Object} to allow null values. * @since 2.0 */ @DoNotMock("Use ImmutableClassToInstanceMap or MutableClassToInstanceMap") @GwtCompatible -@ElementTypesAreNonnullByDefault -// If we ever support non-null projections (https://github.com/jspecify/jspecify/issues/86), we -// we might annotate this as... -// ClassToInstanceMap extends Map, B> -// ...and change its methods similarly ( or Class<@Nonnull T>). -public interface ClassToInstanceMap extends Map, B> { +public interface ClassToInstanceMap + extends Map, B> { /** * Returns the value the specified class is mapped to, or {@code null} if no entry for this class * is present. This will only return a value that was bound to this specific class, not a value * that may have been bound to a subtype. */ - @CheckForNull - T getInstance(Class type); + @Nullable T getInstance(Class type); /** * Maps the specified class to the specified value. Does not associate this value with any @@ -76,6 +68,5 @@ public interface ClassToInstanceMap extends Map, B> { * null} if there was no previous entry. */ @CanIgnoreReturnValue - @CheckForNull - T putInstance(Class type, T value); + @Nullable T putInstance(Class<@NonNull T> type, @ParametricNullness T value); } diff --git a/android/guava/src/com/google/common/collect/CollectCollectors.java b/android/guava/src/com/google/common/collect/CollectCollectors.java new file mode 100644 index 000000000000..f12c882093ce --- /dev/null +++ b/android/guava/src/com/google/common/collect/CollectCollectors.java @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toMap; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.TreeMap; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; + +/** Collectors utilities for {@code common.collect} internals. */ +@GwtCompatible +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // used only from APIs with Java 8 types in them +final class CollectCollectors { + + private static final Collector> TO_IMMUTABLE_LIST = + Collector.of( + ImmutableList::builder, + ImmutableList.Builder::add, + ImmutableList.Builder::combine, + ImmutableList.Builder::build); + + private static final Collector> TO_IMMUTABLE_SET = + Collector.of( + ImmutableSet::builder, + ImmutableSet.Builder::add, + ImmutableSet.Builder::combine, + ImmutableSet.Builder::build); + + @GwtIncompatible + private static final Collector>, ?, ImmutableRangeSet>> + TO_IMMUTABLE_RANGE_SET = + Collector.of( + ImmutableRangeSet::builder, + ImmutableRangeSet.Builder::add, + ImmutableRangeSet.Builder::combine, + ImmutableRangeSet.Builder::build); + + // Lists + + @SuppressWarnings({"rawtypes", "unchecked"}) + static Collector> toImmutableList() { + return (Collector) TO_IMMUTABLE_LIST; + } + + // Sets + + @SuppressWarnings({"rawtypes", "unchecked"}) + static Collector> toImmutableSet() { + return (Collector) TO_IMMUTABLE_SET; + } + + static Collector> toImmutableSortedSet( + Comparator comparator) { + checkNotNull(comparator); + return Collector.of( + () -> new ImmutableSortedSet.Builder(comparator), + ImmutableSortedSet.Builder::add, + ImmutableSortedSet.Builder::combine, + ImmutableSortedSet.Builder::build); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + static > Collector> toImmutableEnumSet() { + return (Collector) EnumSetAccumulator.TO_IMMUTABLE_ENUM_SET; + } + + private static > + Collector, ImmutableSet> toImmutableEnumSetGeneric() { + return Collector.of( + EnumSetAccumulator::new, + EnumSetAccumulator::add, + EnumSetAccumulator::combine, + EnumSetAccumulator::toImmutableSet, + Collector.Characteristics.UNORDERED); + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private static final class EnumSetAccumulator> { + @SuppressWarnings({"rawtypes", "unchecked"}) + static final Collector, ?, ImmutableSet>> TO_IMMUTABLE_ENUM_SET = + (Collector) toImmutableEnumSetGeneric(); + + private @Nullable EnumSet set; + + void add(E e) { + if (set == null) { + set = EnumSet.of(e); + } else { + set.add(e); + } + } + + EnumSetAccumulator combine(EnumSetAccumulator other) { + if (this.set == null) { + return other; + } else if (other.set == null) { + return this; + } else { + this.set.addAll(other.set); + return this; + } + } + + ImmutableSet toImmutableSet() { + if (set == null) { + return ImmutableSet.of(); + } + ImmutableSet ret = ImmutableEnumSet.asImmutable(set); + set = null; // subsequent manual manipulation of the accumulator mustn't affect ret + return ret; + } + } + + @GwtIncompatible + @SuppressWarnings({"rawtypes", "unchecked"}) + static > + Collector, ?, ImmutableRangeSet> toImmutableRangeSet() { + return (Collector) TO_IMMUTABLE_RANGE_SET; + } + + // Multisets + + static Collector> toImmutableMultiset( + Function elementFunction, ToIntFunction countFunction) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + return Collector.of( + LinkedHashMultiset::create, + (multiset, t) -> + multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)), + (multiset1, multiset2) -> { + multiset1.addAll(multiset2); + return multiset1; + }, + (Multiset multiset) -> ImmutableMultiset.copyFromEntries(multiset.entrySet())); + } + + static > + Collector toMultiset( + Function elementFunction, + ToIntFunction countFunction, + Supplier multisetSupplier) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + checkNotNull(multisetSupplier); + return Collector.of( + multisetSupplier, + (ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)), + (ms1, ms2) -> { + ms1.addAll(ms2); + return ms1; + }); + } + + // Maps + + static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableMap.Builder::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableMap.Builder::combine, + ImmutableMap.Builder::buildOrThrow); + } + + static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new), ImmutableMap::copyOf); + } + + static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + /* + * We will always fail if there are duplicate keys, and the keys are always sorted by + * the Comparator, so the entries can come in an arbitrary order -- so we report UNORDERED. + */ + return Collector.of( + () -> new ImmutableSortedMap.Builder(comparator), + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableSortedMap.Builder::combine, + ImmutableSortedMap.Builder::buildOrThrow, + Collector.Characteristics.UNORDERED); + } + + static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + toMap(keyFunction, valueFunction, mergeFunction, () -> new TreeMap(comparator)), + ImmutableSortedMap::copyOfSorted); + } + + static Collector> toImmutableBiMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableBiMap.Builder::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableBiMap.Builder::combine, + ImmutableBiMap.Builder::buildOrThrow, + new Collector.Characteristics[0]); + } + + static , V> + Collector> toImmutableEnumMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + () -> + new EnumMapAccumulator( + (v1, v2) -> { + throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2); + }), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap, + Collector.Characteristics.UNORDERED); + } + + static , V> + Collector> toImmutableEnumMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + // not UNORDERED because we don't know if mergeFunction is commutative + return Collector.of( + () -> new EnumMapAccumulator(mergeFunction), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap); + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private static class EnumMapAccumulator, V> { + private final BinaryOperator mergeFunction; + private @Nullable EnumMap map = null; + + EnumMapAccumulator(BinaryOperator mergeFunction) { + this.mergeFunction = mergeFunction; + } + + void put(K key, V value) { + if (map == null) { + map = new EnumMap<>(singletonMap(key, value)); + } else { + map.merge(key, value, mergeFunction); + } + } + + EnumMapAccumulator combine(EnumMapAccumulator other) { + if (this.map == null) { + return other; + } else if (other.map == null) { + return this; + } else { + other.map.forEach(this::put); + return this; + } + } + + ImmutableMap toImmutableMap() { + return (map == null) ? ImmutableMap.of() : ImmutableEnumMap.asImmutable(map); + } + } + + @GwtIncompatible + static , V> + Collector> toImmutableRangeMap( + Function> keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableRangeMap::builder, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableRangeMap.Builder::combine, + ImmutableRangeMap.Builder::build); + } + + // Multimaps + + static + Collector> toImmutableListMultimap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableListMultimap::builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableListMultimap.Builder::combine, + ImmutableListMultimap.Builder::build); + } + + static + Collector> flatteningToImmutableListMultimap( + Function keyFunction, + Function> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().arrayListValues()::build), + ImmutableListMultimap::copyOf); + } + + static + Collector> toImmutableSetMultimap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableSetMultimap::builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableSetMultimap.Builder::combine, + ImmutableSetMultimap.Builder::build); + } + + static + Collector> flatteningToImmutableSetMultimap( + Function keyFunction, + Function> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().linkedHashSetValues()::build), + ImmutableSetMultimap::copyOf); + } + + static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap> + Collector toMultimap( + Function keyFunction, + Function valueFunction, + Supplier multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)), + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } + + static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap> + Collector flatteningToMultimap( + Function keyFunction, + Function> valueFunction, + Supplier multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> { + K key = keyFunction.apply(input); + Collection valuesForKey = multimap.get(key); + valueFunction.apply(input).forEachOrdered(valuesForKey::add); + }, + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } + + private CollectCollectors() {} +} diff --git a/android/guava/src/com/google/common/collect/CollectPreconditions.java b/android/guava/src/com/google/common/collect/CollectPreconditions.java index c649b03233e3..98b30c6d5e7b 100644 --- a/android/guava/src/com/google/common/collect/CollectPreconditions.java +++ b/android/guava/src/com/google/common/collect/CollectPreconditions.java @@ -23,7 +23,6 @@ /** Precondition checks useful in collection implementations. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class CollectPreconditions { static void checkEntryNotNull(Object key, Object value) { diff --git a/android/guava/src/com/google/common/collect/CollectSpliterators.java b/android/guava/src/com/google/common/collect/CollectSpliterators.java new file mode 100644 index 000000000000..207aa2d09c96 --- /dev/null +++ b/android/guava/src/com/google/common/collect/CollectSpliterators.java @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.lang.Math.max; + +import com.google.common.annotations.GwtCompatible; +import com.google.j2objc.annotations.Weak; +import java.util.Comparator; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.Function; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.LongConsumer; +import java.util.function.Predicate; +import java.util.stream.IntStream; +import org.jspecify.annotations.Nullable; + +/** Spliterator utilities for {@code common.collect} internals. */ +@GwtCompatible +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // used only from APIs that work with Stream +final class CollectSpliterators { + private CollectSpliterators() {} + + static Spliterator indexed( + int size, int extraCharacteristics, IntFunction function) { + return indexed(size, extraCharacteristics, function, null); + } + + static Spliterator indexed( + int size, + int extraCharacteristics, + IntFunction function, + @Nullable Comparator comparator) { + if (comparator != null) { + checkArgument((extraCharacteristics & Spliterator.SORTED) != 0); + } + /* + * @IgnoreJRERequirement should be redundant with the one on Streams itself, but it's necessary + * as of Animal Sniffer 1.24. Maybe Animal Sniffer processes this nested class before it + * processes Streams and thus hasn't had a chance to see Streams's annotation? + */ + @IgnoreJRERequirement + class WithCharacteristics implements Spliterator { + private final Spliterator.OfInt delegate; + + WithCharacteristics(Spliterator.OfInt delegate) { + this.delegate = delegate; + } + + @Override + public boolean tryAdvance(Consumer action) { + return delegate.tryAdvance((IntConsumer) i -> action.accept(function.apply(i))); + } + + @Override + public void forEachRemaining(Consumer action) { + delegate.forEachRemaining((IntConsumer) i -> action.accept(function.apply(i))); + } + + @Override + public @Nullable Spliterator trySplit() { + Spliterator.OfInt split = delegate.trySplit(); + return (split == null) ? null : new WithCharacteristics(split); + } + + @Override + public long estimateSize() { + return delegate.estimateSize(); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED + | Spliterator.SIZED + | Spliterator.SUBSIZED + | extraCharacteristics; + } + + @Override + public @Nullable Comparator getComparator() { + if (hasCharacteristics(Spliterator.SORTED)) { + return comparator; + } else { + throw new IllegalStateException(); + } + } + } + return new WithCharacteristics(IntStream.range(0, size).spliterator()); + } + + /** + * Returns a {@code Spliterator} over the elements of {@code fromSpliterator} mapped by {@code + * function}. + */ + static + Spliterator map( + Spliterator fromSpliterator, + Function function) { + checkNotNull(fromSpliterator); + checkNotNull(function); + return new Spliterator() { + + @Override + public boolean tryAdvance(Consumer action) { + return fromSpliterator.tryAdvance( + fromElement -> action.accept(function.apply(fromElement))); + } + + @Override + public void forEachRemaining(Consumer action) { + fromSpliterator.forEachRemaining(fromElement -> action.accept(function.apply(fromElement))); + } + + @Override + public @Nullable Spliterator trySplit() { + Spliterator fromSplit = fromSpliterator.trySplit(); + return (fromSplit != null) ? map(fromSplit, function) : null; + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & ~(Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SORTED); + } + }; + } + + /** Returns a {@code Spliterator} filtered by the specified predicate. */ + static Spliterator filter( + Spliterator fromSpliterator, Predicate predicate) { + checkNotNull(fromSpliterator); + checkNotNull(predicate); + @IgnoreJRERequirement // see earlier comment about redundancy + class Splitr implements Spliterator, Consumer { + @Nullable T holder = null; + + @Override + public void accept(@ParametricNullness T t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + while (fromSpliterator.tryAdvance(this)) { + try { + // The cast is safe because tryAdvance puts a T into `holder`. + T next = uncheckedCastNullableTToT(holder); + if (predicate.test(next)) { + action.accept(next); + return true; + } + } finally { + holder = null; + } + } + return false; + } + + @Override + public @Nullable Spliterator trySplit() { + Spliterator fromSplit = fromSpliterator.trySplit(); + return (fromSplit == null) ? null : filter(fromSplit, predicate); + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize() / 2; + } + + @Override + public @Nullable Comparator getComparator() { + return fromSpliterator.getComparator(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & (Spliterator.DISTINCT + | Spliterator.NONNULL + | Spliterator.ORDERED + | Spliterator.SORTED); + } + } + return new Splitr(); + } + + /** + * Returns a {@code Spliterator} that iterates over the elements of the spliterators generated by + * applying {@code function} to the elements of {@code fromSpliterator}. + */ + static + Spliterator flatMap( + Spliterator fromSpliterator, + Function> function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfObject<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfInt} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfInt flatMapToInt( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfInt<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfLong} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfLong flatMapToLong( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfLong<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfDouble} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfDouble flatMapToDouble( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfDouble<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Implements the {@link Stream#flatMap} operation on spliterators. + * + * @param the element type of the input spliterator + * @param the element type of the output spliterators + * @param the type of the output spliterators + */ + @IgnoreJRERequirement // see earlier comment about redundancy + abstract static class FlatMapSpliterator< + InElementT extends @Nullable Object, + OutElementT extends @Nullable Object, + OutSpliteratorT extends Spliterator> + implements Spliterator { + /** Factory for constructing {@link FlatMapSpliterator} instances. */ + @IgnoreJRERequirement // should be redundant with the annotations on *both* enclosing classes + interface Factory> { + OutSpliteratorT newFlatMapSpliterator( + @Nullable OutSpliteratorT prefix, + Spliterator fromSplit, + Function function, + int splitCharacteristics, + long estSplitSize); + } + + @Weak @Nullable OutSpliteratorT prefix; + final Spliterator from; + final Function function; + final Factory factory; + int characteristics; + long estimatedSize; + + FlatMapSpliterator( + @Nullable OutSpliteratorT prefix, + Spliterator from, + Function function, + Factory factory, + int characteristics, + long estimatedSize) { + this.prefix = prefix; + this.from = from; + this.function = function; + this.factory = factory; + this.characteristics = characteristics; + this.estimatedSize = estimatedSize; + } + + /* + * The tryAdvance and forEachRemaining in FlatMapSpliteratorOfPrimitive are overloads of these + * methods, not overrides. They are annotated @Override because they implement methods from + * Spliterator.OfPrimitive (and override default implementations from Spliterator.OfPrimitive or + * a subtype like Spliterator.OfInt). + */ + + @Override + public /*non-final for J2KT*/ boolean tryAdvance(Consumer action) { + while (true) { + if (prefix != null && prefix.tryAdvance(action)) { + if (estimatedSize != Long.MAX_VALUE) { + estimatedSize--; + } + return true; + } else { + prefix = null; + } + if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) { + return false; + } + } + } + + @Override + public /*non-final for J2KT*/ void forEachRemaining(Consumer action) { + if (prefix != null) { + prefix.forEachRemaining(action); + prefix = null; + } + from.forEachRemaining( + fromElement -> { + Spliterator elements = function.apply(fromElement); + if (elements != null) { + elements.forEachRemaining(action); + } + }); + estimatedSize = 0; + } + + @Override + public final @Nullable OutSpliteratorT trySplit() { + Spliterator fromSplit = from.trySplit(); + if (fromSplit != null) { + int splitCharacteristics = characteristics & ~Spliterator.SIZED; + long estSplitSize = estimateSize(); + if (estSplitSize < Long.MAX_VALUE) { + estSplitSize /= 2; + this.estimatedSize -= estSplitSize; + this.characteristics = splitCharacteristics; + } + OutSpliteratorT result = + factory.newFlatMapSpliterator( + this.prefix, fromSplit, function, splitCharacteristics, estSplitSize); + this.prefix = null; + return result; + } else if (prefix != null) { + OutSpliteratorT result = prefix; + this.prefix = null; + return result; + } else { + return null; + } + } + + @Override + public final long estimateSize() { + if (prefix != null) { + estimatedSize = max(estimatedSize, prefix.estimateSize()); + } + return max(estimatedSize, 0); + } + + @Override + public final int characteristics() { + return characteristics; + } + } + + /** + * Implementation of {@link Stream#flatMap} with an object spliterator output type. + * + *

    To avoid having this type, we could use {@code FlatMapSpliterator} directly. The main + * advantages to having the type are the ability to use its constructor reference below and the + * parallelism with the primitive version. In short, it makes its caller ({@code flatMap}) + * simpler. + * + * @param the element type of the input spliterator + * @param the element type of the output spliterators + */ + @IgnoreJRERequirement // see earlier comment about redundancy + static final class FlatMapSpliteratorOfObject< + InElementT extends @Nullable Object, OutElementT extends @Nullable Object> + extends FlatMapSpliterator> { + FlatMapSpliteratorOfObject( + @Nullable Spliterator prefix, + Spliterator from, + Function> function, + int characteristics, + long estimatedSize) { + super( + prefix, from, function, FlatMapSpliteratorOfObject::new, characteristics, estimatedSize); + } + } + + /** + * Implementation of {@link Stream#flatMap} with a primitive spliterator output type. + * + * @param the element type of the input spliterator + * @param the (boxed) element type of the output spliterators + * @param the specialized consumer type for the primitive output type + * @param the primitive spliterator type associated with {@code OutElementT} + */ + @IgnoreJRERequirement // see earlier comment about redundancy + abstract static class FlatMapSpliteratorOfPrimitive< + InElementT extends @Nullable Object, + OutElementT extends @Nullable Object, + OutConsumerT, + OutSpliteratorT extends + Spliterator.OfPrimitive> + extends FlatMapSpliterator + implements Spliterator.OfPrimitive { + + FlatMapSpliteratorOfPrimitive( + @Nullable OutSpliteratorT prefix, + Spliterator from, + Function function, + Factory factory, + int characteristics, + long estimatedSize) { + super(prefix, from, function, factory, characteristics, estimatedSize); + } + + @Override + public final boolean tryAdvance(OutConsumerT action) { + while (true) { + if (prefix != null && prefix.tryAdvance(action)) { + if (estimatedSize != Long.MAX_VALUE) { + estimatedSize--; + } + return true; + } else { + prefix = null; + } + if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) { + return false; + } + } + } + + @Override + public final void forEachRemaining(OutConsumerT action) { + if (prefix != null) { + prefix.forEachRemaining(action); + prefix = null; + } + from.forEachRemaining( + fromElement -> { + OutSpliteratorT elements = function.apply(fromElement); + if (elements != null) { + elements.forEachRemaining(action); + } + }); + estimatedSize = 0; + } + } + + /** Implementation of {@link #flatMapToInt}. */ + @IgnoreJRERequirement // see earlier comment about redundancy + static final class FlatMapSpliteratorOfInt + extends FlatMapSpliteratorOfPrimitive + implements Spliterator.OfInt { + FlatMapSpliteratorOfInt( + Spliterator.@Nullable OfInt prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super(prefix, from, function, FlatMapSpliteratorOfInt::new, characteristics, estimatedSize); + } + } + + /** Implementation of {@link #flatMapToLong}. */ + @IgnoreJRERequirement // see earlier comment about redundancy + static final class FlatMapSpliteratorOfLong + extends FlatMapSpliteratorOfPrimitive + implements Spliterator.OfLong { + FlatMapSpliteratorOfLong( + Spliterator.@Nullable OfLong prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super(prefix, from, function, FlatMapSpliteratorOfLong::new, characteristics, estimatedSize); + } + } + + /** Implementation of {@link #flatMapToDouble}. */ + @IgnoreJRERequirement // see earlier comment about redundancy + static final class FlatMapSpliteratorOfDouble + extends FlatMapSpliteratorOfPrimitive< + InElementT, Double, DoubleConsumer, Spliterator.OfDouble> + implements Spliterator.OfDouble { + FlatMapSpliteratorOfDouble( + Spliterator.@Nullable OfDouble prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super( + prefix, from, function, FlatMapSpliteratorOfDouble::new, characteristics, estimatedSize); + } + } +} diff --git a/android/guava/src/com/google/common/collect/Collections2.java b/android/guava/src/com/google/common/collect/Collections2.java index 1e651b50a0f9..d4707bb54564 100644 --- a/android/guava/src/com/google/common/collect/Collections2.java +++ b/android/guava/src/com/google/common/collect/Collections2.java @@ -19,9 +19,9 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -36,16 +36,15 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides static methods for working with {@code Collection} instances. * - *

    Java 8 users: several common uses for this class are now more comprehensively addressed - * by the new {@link java.util.stream.Stream} library. Read the method documentation below for - * comparisons. These methods are not being deprecated, but we gently encourage you to migrate to - * streams. + *

    Java 8+ users: several common uses for this class are now more comprehensively + * addressed by the new {@link java.util.stream.Stream} library. Read the method documentation below + * for comparisons. These methods are not being deprecated, but we gently encourage you to migrate + * to streams. * * @author Chris Povirk * @author Mike Bostock @@ -53,7 +52,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Collections2 { private Collections2() {} @@ -92,14 +90,14 @@ private Collections2() {} return ((FilteredCollection) unfiltered).createCombined(predicate); } - return new FilteredCollection(checkNotNull(unfiltered), checkNotNull(predicate)); + return new FilteredCollection<>(checkNotNull(unfiltered), checkNotNull(predicate)); } /** * Delegates to {@link Collection#contains}. Returns {@code false} if the {@code contains} method * throws a {@code ClassCastException} or {@code NullPointerException}. */ - static boolean safeContains(Collection collection, @CheckForNull Object object) { + static boolean safeContains(Collection collection, @Nullable Object object) { checkNotNull(collection); try { return collection.contains(object); @@ -112,7 +110,7 @@ static boolean safeContains(Collection collection, @CheckForNull Object objec * Delegates to {@link Collection#remove}. Returns {@code false} if the {@code remove} method * throws a {@code ClassCastException} or {@code NullPointerException}. */ - static boolean safeRemove(Collection collection, @CheckForNull Object object) { + static boolean safeRemove(Collection collection, @Nullable Object object) { checkNotNull(collection); try { return collection.remove(object); @@ -131,8 +129,7 @@ static class FilteredCollection extends AbstractColl } FilteredCollection createCombined(Predicate newPredicate) { - return new FilteredCollection(unfiltered, Predicates.and(predicate, newPredicate)); - // . above needed to compile in JDK 5 + return new FilteredCollection<>(unfiltered, Predicates.and(predicate, newPredicate)); } @Override @@ -155,7 +152,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { if (safeContains(unfiltered, element)) { @SuppressWarnings("unchecked") // element is in unfiltered, so it must be an E E e = (E) element; @@ -180,12 +177,12 @@ public Iterator iterator() { } @Override - public boolean remove(@CheckForNull Object element) { + public boolean remove(@Nullable Object element) { return contains(element) && unfiltered.remove(element); } @Override - public boolean removeAll(final Collection collection) { + public boolean removeAll(Collection collection) { boolean changed = false; Iterator itr = unfiltered.iterator(); while (itr.hasNext()) { @@ -199,7 +196,7 @@ public boolean removeAll(final Collection collection) { } @Override - public boolean retainAll(final Collection collection) { + public boolean retainAll(Collection collection) { boolean changed = false; Iterator itr = unfiltered.iterator(); while (itr.hasNext()) { @@ -312,7 +309,7 @@ static boolean containsAllImpl(Collection self, Collection c) { } /** An implementation of {@link Collection#toString()}. */ - static String toStringImpl(final Collection collection) { + static String toStringImpl(Collection collection) { StringBuilder sb = newStringBuilderForCollection(collection.size()).append('['); boolean first = true; for (Object o : collection) { @@ -332,7 +329,7 @@ static String toStringImpl(final Collection collection) { /** Returns best-effort-sized StringBuilder based on the given collection size. */ static StringBuilder newStringBuilderForCollection(int size) { checkNonnegative(size, "size"); - return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); + return new StringBuilder((int) min(size * 8L, Ints.MAX_POWER_OF_TWO)); } /** @@ -357,7 +354,6 @@ static StringBuilder newStringBuilderForCollection(int size) { * @throws NullPointerException if the specified iterable is null or has any null elements. * @since 12.0 */ - @Beta public static > Collection> orderedPermutations( Iterable elements) { return orderedPermutations(elements, Ordering.natural()); @@ -409,7 +405,6 @@ public static > Collection> orderedPermu * the specified comparator is null. * @since 12.0 */ - @Beta public static Collection> orderedPermutations( Iterable elements, Comparator comparator) { return new OrderedPermutationCollection(elements, comparator); @@ -472,7 +467,7 @@ public Iterator> iterator() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (obj instanceof List) { List list = (List) obj; return isPermutation(inputList, list); @@ -487,7 +482,7 @@ public String toString() { } private static final class OrderedPermutationIterator extends AbstractIterator> { - @CheckForNull List nextPermutation; + @Nullable List nextPermutation; final Comparator comparator; OrderedPermutationIterator(List list, Comparator comparator) { @@ -496,8 +491,7 @@ private static final class OrderedPermutationIterator extends AbstractIterato } @Override - @CheckForNull - protected List computeNext() { + protected @Nullable List computeNext() { if (nextPermutation == null) { return endOfData(); } @@ -571,7 +565,6 @@ int findNextL(int j) { * @throws NullPointerException if the specified collection is null or has any null elements. * @since 12.0 */ - @Beta public static Collection> permutations(Collection elements) { return new PermutationCollection(ImmutableList.copyOf(elements)); } @@ -599,7 +592,7 @@ public Iterator> iterator() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (obj instanceof List) { List list = (List) obj; return isPermutation(inputList, list); @@ -620,7 +613,7 @@ private static class PermutationIterator extends AbstractIterator> { int j; PermutationIterator(List list) { - this.list = new ArrayList(list); + this.list = new ArrayList<>(list); int n = list.size(); c = new int[n]; o = new int[n]; @@ -630,8 +623,7 @@ private static class PermutationIterator extends AbstractIterator> { } @Override - @CheckForNull - protected List computeNext() { + protected @Nullable List computeNext() { if (j <= 0) { return endOfData(); } @@ -695,7 +687,8 @@ private static boolean isPermutation(List first, List second) { return true; } - private static ObjectCountHashMap counts(Collection collection) { + private static ObjectCountHashMap counts( + Collection collection) { ObjectCountHashMap map = new ObjectCountHashMap<>(); for (E e : collection) { map.put(e, map.get(e) + 1); diff --git a/android/guava/src/com/google/common/collect/CompactHashMap.java b/android/guava/src/com/google/common/collect/CompactHashMap.java index e70596dd222d..b2e01092ec16 100644 --- a/android/guava/src/com/google/common/collect/CompactHashMap.java +++ b/android/guava/src/com/google/common/collect/CompactHashMap.java @@ -21,14 +21,18 @@ import static com.google.common.collect.Hashing.smearedHash; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; import static com.google.common.collect.NullnessCasts.unsafeNull; +import static java.lang.Math.max; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.io.IOException; import java.io.InvalidObjectException; @@ -46,8 +50,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * CompactHashMap is an implementation of a Map. All optional operations (put and remove) are @@ -76,7 +79,6 @@ * @author Jon Noack */ @GwtIncompatible // not worth using in GWT for now -@ElementTypesAreNonnullByDefault class CompactHashMap extends AbstractMap implements Serializable { /* @@ -182,7 +184,7 @@ CompactHashMap createWithExpectedSize(int expectedSize) { *

  • null, if no entries have yet been added to the map * */ - @CheckForNull private transient Object table; + private transient @Nullable Object table; /** * Contains the logical entries, in the range of [0, size()). The high bits of each int are the @@ -199,19 +201,19 @@ CompactHashMap createWithExpectedSize(int expectedSize) { * *

    The pointers in [size(), entries.length) are all "null" (UNSET). */ - @VisibleForTesting @CheckForNull transient int[] entries; + @VisibleForTesting transient int @Nullable [] entries; /** * The keys of the entries in the map, in the range of [0, size()). The keys in [size(), * keys.length) are all {@code null}. */ - @VisibleForTesting @CheckForNull transient @Nullable Object[] keys; + @VisibleForTesting transient @Nullable Object @Nullable [] keys; /** * The values of the entries in the map, in the range of [0, size()). The values in [size(), * values.length) are all {@code null}. */ - @VisibleForTesting @CheckForNull transient @Nullable Object[] values; + @VisibleForTesting transient @Nullable Object @Nullable [] values; /** * Keeps track of metadata like the number of hash table bits and modifications of this data @@ -253,7 +255,6 @@ void init(int expectedSize) { } /** Returns whether arrays need to be allocated. */ - @VisibleForTesting boolean needsAllocArrays() { return table == null; } @@ -277,8 +278,7 @@ int allocArrays() { @SuppressWarnings("unchecked") @VisibleForTesting - @CheckForNull - Map delegateOrNull() { + @Nullable Map delegateOrNull() { if (table instanceof Map) { return (Map) table; } @@ -289,7 +289,6 @@ Map createHashFloodingResistantDelegate(int tableSize) { return new LinkedHashMap<>(tableSize, 1.0f); } - @VisibleForTesting @CanIgnoreReturnValue Map convertToHashFloodingResistantImplementation() { Map newDelegate = createHashFloodingResistantDelegate(hashTableMask() + 1); @@ -330,8 +329,7 @@ void accessEntry(int index) { @CanIgnoreReturnValue @Override - @CheckForNull - public V put(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { if (needsAllocArrays()) { allocArrays(); } @@ -410,8 +408,7 @@ private void resizeMeMaybe(int newSize) { int entriesSize = requireEntries().length; if (newSize > entriesSize) { // 1.5x but round up to nearest odd (this is optimal for memory consumption on Android) - int newCapacity = - Math.min(CompactHashing.MAX_SIZE, (entriesSize + Math.max(1, entriesSize >>> 1)) | 1); + int newCapacity = min(CompactHashing.MAX_SIZE, (entriesSize + max(1, entriesSize >>> 1)) | 1); if (newCapacity != entriesSize) { resizeEntries(newCapacity); } @@ -475,7 +472,7 @@ private int resizeTable(int oldMask, int newCapacity, int targetHash, int target return newMask; } - private int indexOf(@CheckForNull Object key) { + private int indexOf(@Nullable Object key) { if (needsAllocArrays()) { return -1; } @@ -499,14 +496,13 @@ private int indexOf(@CheckForNull Object key) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { Map delegate = delegateOrNull(); return (delegate != null) ? delegate.containsKey(key) : indexOf(key) != -1; } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { Map delegate = delegateOrNull(); if (delegate != null) { return delegate.get(key); @@ -522,8 +518,7 @@ public V get(@CheckForNull Object key) { @CanIgnoreReturnValue @SuppressWarnings("unchecked") // known to be a V @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { Map delegate = delegateOrNull(); if (delegate != null) { return delegate.remove(key); @@ -532,7 +527,7 @@ public V remove(@CheckForNull Object key) { return (oldValue == NOT_FOUND) ? null : (V) oldValue; } - private @Nullable Object removeHelper(@CheckForNull Object key) { + private @Nullable Object removeHelper(@Nullable Object key) { if (needsAllocArrays()) { return NOT_FOUND; } @@ -670,7 +665,7 @@ private void checkForConcurrentModification() { } } - @CheckForNull private transient Set keySetView; + @LazyInit private transient @Nullable Set keySetView; @Override public Set keySet() { @@ -689,12 +684,12 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return CompactHashMap.this.containsKey(o); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { Map delegate = delegateOrNull(); return (delegate != null) ? delegate.keySet().remove(o) @@ -726,7 +721,7 @@ K getOutput(int entry) { }; } - @CheckForNull private transient Set> entrySetView; + @LazyInit private transient @Nullable Set> entrySetView; @Override public Set> entrySet() { @@ -756,7 +751,7 @@ public Iterator> iterator() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { Map delegate = delegateOrNull(); if (delegate != null) { return delegate.entrySet().contains(o); @@ -769,7 +764,7 @@ public boolean contains(@CheckForNull Object o) { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { Map delegate = delegateOrNull(); if (delegate != null) { return delegate.entrySet().remove(o); @@ -893,7 +888,7 @@ public boolean isEmpty() { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { Map delegate = delegateOrNull(); if (delegate != null) { return delegate.containsValue(value); @@ -906,7 +901,7 @@ public boolean containsValue(@CheckForNull Object value) { return false; } - @CheckForNull private transient Collection valuesView; + @LazyInit private transient @Nullable Collection valuesView; @Override public Collection values() { @@ -997,6 +992,7 @@ public void clear() { } } + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(size()); @@ -1009,6 +1005,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @SuppressWarnings("unchecked") + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int elementCount = stream.readInt(); diff --git a/android/guava/src/com/google/common/collect/CompactHashSet.java b/android/guava/src/com/google/common/collect/CompactHashSet.java index 08c729423fe4..0c55bc8ac9cb 100644 --- a/android/guava/src/com/google/common/collect/CompactHashSet.java +++ b/android/guava/src/com/google/common/collect/CompactHashSet.java @@ -19,9 +19,12 @@ import static com.google.common.collect.CollectPreconditions.checkRemove; import static com.google.common.collect.CompactHashing.UNSET; import static com.google.common.collect.Hashing.smearedHash; +import static java.lang.Math.max; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Preconditions; @@ -41,8 +44,7 @@ import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * CompactHashSet is an implementation of a Set. All optional operations (adding and removing) are @@ -72,7 +74,6 @@ * @author Jon Noack */ @GwtIncompatible // not worth using in GWT for now -@ElementTypesAreNonnullByDefault class CompactHashSet extends AbstractSet implements Serializable { // TODO(user): cache all field accesses in local vars @@ -160,7 +161,7 @@ class CompactHashSet extends AbstractSet implemen *

  • null, if no entries have yet been added to the map * */ - @CheckForNull private transient Object table; + private transient @Nullable Object table; /** * Contains the logical entries, in the range of [0, size()). The high bits of each int are the @@ -177,13 +178,13 @@ class CompactHashSet extends AbstractSet implemen * *

    The pointers in [size(), entries.length) are all "null" (UNSET). */ - @CheckForNull private transient int[] entries; + private transient int @Nullable [] entries; /** * The elements contained in the set, in the range of [0, size()). The elements in [size(), * elements.length) are all {@code null}. */ - @VisibleForTesting @CheckForNull transient @Nullable Object[] elements; + @VisibleForTesting transient @Nullable Object @Nullable [] elements; /** * Keeps track of metadata like the number of hash table bits and modifications of this data @@ -219,7 +220,6 @@ void init(int expectedSize) { } /** Returns whether arrays need to be allocated. */ - @VisibleForTesting boolean needsAllocArrays() { return table == null; } @@ -242,8 +242,7 @@ int allocArrays() { @SuppressWarnings("unchecked") @VisibleForTesting - @CheckForNull - Set delegateOrNull() { + @Nullable Set delegateOrNull() { if (table instanceof Set) { return (Set) table; } @@ -254,7 +253,6 @@ private Set createHashFloodingResistantDelegate(int tableSize) { return new LinkedHashSet<>(tableSize, 1.0f); } - @VisibleForTesting @CanIgnoreReturnValue Set convertToHashFloodingResistantImplementation() { Set newDelegate = createHashFloodingResistantDelegate(hashTableMask() + 1); @@ -362,8 +360,7 @@ private void resizeMeMaybe(int newSize) { int entriesSize = requireEntries().length; if (newSize > entriesSize) { // 1.5x but round up to nearest odd (this is optimal for memory consumption on Android) - int newCapacity = - Math.min(CompactHashing.MAX_SIZE, (entriesSize + Math.max(1, entriesSize >>> 1)) | 1); + int newCapacity = min(CompactHashing.MAX_SIZE, (entriesSize + max(1, entriesSize >>> 1)) | 1); if (newCapacity != entriesSize) { resizeEntries(newCapacity); } @@ -417,7 +414,7 @@ private int resizeTable(int oldMask, int newCapacity, int targetHash, int target } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (needsAllocArrays()) { return false; } @@ -446,7 +443,7 @@ public boolean contains(@CheckForNull Object object) { @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { if (needsAllocArrays()) { return false; } @@ -669,6 +666,7 @@ public void clear() { } } + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(size()); @@ -678,6 +676,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @SuppressWarnings("unchecked") + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int elementCount = stream.readInt(); diff --git a/android/guava/src/com/google/common/collect/CompactHashing.java b/android/guava/src/com/google/common/collect/CompactHashing.java index a8fe90214c78..67ec6b5cffac 100644 --- a/android/guava/src/com/google/common/collect/CompactHashing.java +++ b/android/guava/src/com/google/common/collect/CompactHashing.java @@ -16,12 +16,13 @@ package com.google.common.collect; +import static java.lang.Math.max; + import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Objects; import com.google.common.primitives.Ints; import java.util.Arrays; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Helper classes and static methods for implementing compact hash-based collections. @@ -29,7 +30,6 @@ * @author Jon Noack */ @GwtIncompatible -@ElementTypesAreNonnullByDefault final class CompactHashing { private CompactHashing() {} @@ -69,7 +69,7 @@ private CompactHashing() {} */ static int tableSize(int expectedSize) { // We use entries next == 0 to indicate UNSET, so actual capacity is 1 less than requested. - return Math.max(MIN_HASH_TABLE_SIZE, Hashing.closedTableSize(expectedSize + 1, 1.0f)); + return max(MIN_HASH_TABLE_SIZE, Hashing.closedTableSize(expectedSize + 1, 1.0)); } /** Creates and returns a properly-sized array with the given number of buckets. */ @@ -157,13 +157,13 @@ static int maskCombine(int prefix, int suffix, int mask) { } static int remove( - @CheckForNull Object key, - @CheckForNull Object value, + @Nullable Object key, + @Nullable Object value, int mask, Object table, int[] entries, @Nullable Object[] keys, - @CheckForNull @Nullable Object[] values) { + @Nullable Object @Nullable [] values) { int hash = Hashing.smearedHash(key); int tableIndex = hash & mask; int next = tableGet(table, tableIndex); diff --git a/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java b/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java index fb648a7a4f1b..93638ece8980 100644 --- a/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java +++ b/android/guava/src/com/google/common/collect/CompactLinkedHashMap.java @@ -19,13 +19,13 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * CompactLinkedHashMap is an implementation of a Map with insertion or LRU iteration order, @@ -47,8 +47,8 @@ * * @author Louis Wasserman */ +@J2ktIncompatible // no support for access-order mode in LinkedHashMap delegate @GwtIncompatible // not worth using in GWT for now -@ElementTypesAreNonnullByDefault class CompactLinkedHashMap extends CompactHashMap { // TODO(lowasser): implement removeEldestEntry so this can be used as a drop-in replacement @@ -84,7 +84,7 @@ CompactLinkedHashMap createWithExpectedSize(int expectedSize) { *

    A node with "prev" pointer equal to {@code ENDPOINT} is the first node in the linked list, * and a node with "next" pointer equal to {@code ENDPOINT} is the last node. */ - @CheckForNull @VisibleForTesting transient long[] links; + @VisibleForTesting transient long @Nullable [] links; /** Pointer to the first node in the linked list, or {@code ENDPOINT} if there are no entries. */ private transient int firstEntry; @@ -123,7 +123,7 @@ int allocArrays() { @Override Map createHashFloodingResistantDelegate(int tableSize) { - return new LinkedHashMap(tableSize, 1.0f, accessOrder); + return new LinkedHashMap<>(tableSize, 1.0f, accessOrder); } @Override @@ -150,7 +150,7 @@ int getSuccessor(int entry) { } private void setSuccessor(int entry, int succ) { - long succMask = (~0L) >>> 32; + long succMask = ~0L >>> 32; setLink(entry, (link(entry) & ~succMask) | ((succ + 1) & succMask)); } diff --git a/android/guava/src/com/google/common/collect/CompactLinkedHashSet.java b/android/guava/src/com/google/common/collect/CompactLinkedHashSet.java index 21d48058ad72..5a042d48176e 100644 --- a/android/guava/src/com/google/common/collect/CompactLinkedHashSet.java +++ b/android/guava/src/com/google/common/collect/CompactLinkedHashSet.java @@ -24,8 +24,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * CompactLinkedHashSet is an implementation of a Set, which a predictable iteration order that @@ -49,7 +48,6 @@ * @author Louis Wasserman */ @GwtIncompatible // not worth using in GWT for now -@ElementTypesAreNonnullByDefault class CompactLinkedHashSet extends CompactHashSet { /** Creates an empty {@code CompactLinkedHashSet} instance. */ @@ -109,13 +107,13 @@ class CompactLinkedHashSet extends CompactHashSet * Pointer to the predecessor of an entry in insertion order. ENDPOINT indicates a node is the * first node in insertion order; all values at indices ≥ {@link #size()} are UNSET. */ - @CheckForNull private transient int[] predecessor; + private transient int @Nullable [] predecessor; /** * Pointer to the successor of an entry in insertion order. ENDPOINT indicates a node is the last * node in insertion order; all values at indices ≥ {@link #size()} are UNSET. */ - @CheckForNull private transient int[] successor; + private transient int @Nullable [] successor; /** Pointer to the first node in the linked list, or {@code ENDPOINT} if there are no entries. */ private transient int firstEntry; @@ -123,9 +121,7 @@ class CompactLinkedHashSet extends CompactHashSet /** Pointer to the last node in the linked list, or {@code ENDPOINT} if there are no entries. */ private transient int lastEntry; - CompactLinkedHashSet() { - super(); - } + CompactLinkedHashSet() {} CompactLinkedHashSet(int expectedSize) { super(expectedSize); diff --git a/android/guava/src/com/google/common/collect/ComparatorOrdering.java b/android/guava/src/com/google/common/collect/ComparatorOrdering.java index 8b3407074421..410875167955 100644 --- a/android/guava/src/com/google/common/collect/ComparatorOrdering.java +++ b/android/guava/src/com/google/common/collect/ComparatorOrdering.java @@ -19,14 +19,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Comparator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** An ordering for a pre-existing comparator. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class ComparatorOrdering extends Ordering implements Serializable { final Comparator comparator; @@ -41,7 +41,7 @@ public int compare(@ParametricNullness T a, @ParametricNullness T b) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -62,5 +62,5 @@ public String toString() { return comparator.toString(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/Comparators.java b/android/guava/src/com/google/common/collect/Comparators.java index 6a5670c7f968..8ff31df70dfb 100644 --- a/android/guava/src/com/google/common/collect/Comparators.java +++ b/android/guava/src/com/google/common/collect/Comparators.java @@ -17,16 +17,19 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * Provides static methods for working with {@link Comparator} instances. For many other helpful - * comparator utilities, see either {@code Comparator} itself (for Java 8 or later), or {@code + * comparator utilities, see either {@code Comparator} itself (for Java 8+), or {@code * com.google.common.collect.Ordering} (otherwise). * *

    Relationship to {@code Ordering}

    @@ -39,9 +42,7 @@ * @since 21.0 * @author Louis Wasserman */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Comparators { private Comparators() {} @@ -108,6 +109,108 @@ private Comparators() {} return true; } + /** + * Returns a {@code Collector} that returns the {@code k} smallest (relative to the specified + * {@code Comparator}) input elements, in ascending order, as an unmodifiable {@code List}. Ties + * are broken arbitrarily. + * + *

    For example: + * + *

    {@code
    +   * Stream.of("foo", "quux", "banana", "elephant")
    +   *     .collect(least(2, comparingInt(String::length)))
    +   * // returns {"foo", "quux"}
    +   * }
    + * + *

    This {@code Collector} uses O(k) memory and takes expected time O(n) (worst-case O(n log + * k)), as opposed to e.g. {@code Stream.sorted(comparator).limit(k)}, which currently takes O(n + * log n) time and O(n) space. + * + * @throws IllegalArgumentException if {@code k < 0} + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> least( + int k, Comparator comparator) { + checkNonnegative(k, "k"); + checkNotNull(comparator); + return Collector.of( + () -> TopKSelector.least(k, comparator), + TopKSelector::offer, + TopKSelector::combine, + TopKSelector::topK, + Collector.Characteristics.UNORDERED); + } + + /** + * Returns a {@code Collector} that returns the {@code k} greatest (relative to the specified + * {@code Comparator}) input elements, in descending order, as an unmodifiable {@code List}. Ties + * are broken arbitrarily. + * + *

    For example: + * + *

    {@code
    +   * Stream.of("foo", "quux", "banana", "elephant")
    +   *     .collect(greatest(2, comparingInt(String::length)))
    +   * // returns {"elephant", "banana"}
    +   * }
    + * + *

    This {@code Collector} uses O(k) memory and takes expected time O(n) (worst-case O(n log + * k)), as opposed to e.g. {@code Stream.sorted(comparator.reversed()).limit(k)}, which currently + * takes O(n log n) time and O(n) space. + * + * @throws IllegalArgumentException if {@code k < 0} + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> greatest( + int k, Comparator comparator) { + return least(k, comparator.reversed()); + } + + /** + * Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as less + * than all other values, and orders the rest using {@code valueComparator} on the contained + * value. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Optional. + public static Comparator> emptiesFirst(Comparator valueComparator) { + checkNotNull(valueComparator); + return Comparator., @Nullable T>comparing( + o -> orElseNull(o), Comparator.nullsFirst(valueComparator)); + } + + /** + * Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as greater + * than all other values, and orders the rest using {@code valueComparator} on the contained + * value. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Optional. + public static Comparator> emptiesLast(Comparator valueComparator) { + checkNotNull(valueComparator); + return Comparator., @Nullable T>comparing( + o -> orElseNull(o), Comparator.nullsLast(valueComparator)); + } + + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // helper for emptiesFirst+emptiesLast + /* + * If we make these calls inline inside the lambda inside emptiesFirst()/emptiesLast(), we get an + * Animal Sniffer error, despite the @IgnoreJRERequirement annotation there. For details, see + * ImmutableSortedMultiset. + */ + private static @Nullable T orElseNull(Optional optional) { + return optional.orElse(null); + } + /** * Returns the minimum of the two values. If the values compare as 0, the first is returned. * @@ -121,7 +224,6 @@ private Comparators() {} * @throws ClassCastException if the parameters are not mutually comparable. * @since 30.0 */ - @Beta public static > T min(T a, T b) { return (a.compareTo(b) <= 0) ? a : b; } @@ -141,10 +243,9 @@ public static > T min(T a, T b) { * comparator. * @since 30.0 */ - @Beta @ParametricNullness public static T min( - @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { + @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { return (comparator.compare(a, b) <= 0) ? a : b; } @@ -161,7 +262,6 @@ public static > T min(T a, T b) { * @throws ClassCastException if the parameters are not mutually comparable. * @since 30.0 */ - @Beta public static > T max(T a, T b) { return (a.compareTo(b) >= 0) ? a : b; } @@ -181,10 +281,9 @@ public static > T max(T a, T b) { * comparator. * @since 30.0 */ - @Beta @ParametricNullness public static T max( - @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { + @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { return (comparator.compare(a, b) >= 0) ? a : b; } } diff --git a/android/guava/src/com/google/common/collect/ComparisonChain.java b/android/guava/src/com/google/common/collect/ComparisonChain.java index 32aeb4d4bb78..c16afa0afb72 100644 --- a/android/guava/src/com/google/common/collect/ComparisonChain.java +++ b/android/guava/src/com/google/common/collect/ComparisonChain.java @@ -17,11 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.primitives.Booleans; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; +import com.google.errorprone.annotations.InlineMe; import java.util.Comparator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A utility for performing a chained comparison statement. For example: @@ -49,7 +47,7 @@ * the presence of expensive {@code compareTo} and {@code compare} implementations. * *

    See the Guava User Guide article on {@code + * "https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained#comparecompareto">{@code * ComparisonChain}. * * @author Mark Davis @@ -57,7 +55,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ComparisonChain { private ComparisonChain() {} @@ -82,12 +79,12 @@ public ComparisonChain compare(Comparable left, Comparable right) { @Override public ComparisonChain compare(int left, int right) { - return classify(Ints.compare(left, right)); + return classify(Integer.compare(left, right)); } @Override public ComparisonChain compare(long left, long right) { - return classify(Longs.compare(left, right)); + return classify(Long.compare(left, right)); } @Override @@ -102,12 +99,12 @@ public ComparisonChain compare(double left, double right) { @Override public ComparisonChain compareTrueFirst(boolean left, boolean right) { - return classify(Booleans.compare(right, left)); // reversed + return classify(Boolean.compare(right, left)); // reversed } @Override public ComparisonChain compareFalseFirst(boolean left, boolean right) { - return classify(Booleans.compare(left, right)); + return classify(Boolean.compare(left, right)); } ComparisonChain classify(int result) { @@ -204,13 +201,13 @@ public int result() { @ParametricNullness T left, @ParametricNullness T right, Comparator comparator); /** - * Compares two {@code int} values as specified by {@link Ints#compare}, if the result of - * this comparison chain has not already been determined. + * Compares two {@code int} values as specified by {@link Integer#compare}, if the result + * of this comparison chain has not already been determined. */ public abstract ComparisonChain compare(int left, int right); /** - * Compares two {@code long} values as specified by {@link Longs#compare}, if the result of + * Compares two {@code long} values as specified by {@link Long#compare}, if the result of * this comparison chain has not already been determined. */ public abstract ComparisonChain compare(long left, long right); @@ -234,6 +231,7 @@ public int result() { * negated or reversed, undo the negation or reversal and use {@link #compareTrueFirst}. * @since 19.0 */ + @InlineMe(replacement = "this.compareFalseFirst(left, right)") @Deprecated public final ComparisonChain compare(Boolean left, Boolean right) { return compareFalseFirst(left, right); diff --git a/android/guava/src/com/google/common/collect/CompoundOrdering.java b/android/guava/src/com/google/common/collect/CompoundOrdering.java index 42feed373193..6e922f3316bc 100644 --- a/android/guava/src/com/google/common/collect/CompoundOrdering.java +++ b/android/guava/src/com/google/common/collect/CompoundOrdering.java @@ -17,25 +17,28 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Arrays; import java.util.Comparator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** An ordering that tries several comparators in order. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class CompoundOrdering extends Ordering implements Serializable { final Comparator[] comparators; + @SuppressWarnings("unchecked") // Generic array creation CompoundOrdering(Comparator primary, Comparator secondary) { - this.comparators = (Comparator[]) new Comparator[] {primary, secondary}; + this.comparators = (Comparator[]) new Comparator[] {primary, secondary}; } + @SuppressWarnings("unchecked") // Generic array creation CompoundOrdering(Iterable> comparators) { - this.comparators = Iterables.toArray(comparators, new Comparator[0]); + this.comparators = + Iterables.toArray(comparators, (Comparator[]) new Comparator[0]); } @Override @@ -50,7 +53,7 @@ public int compare(@ParametricNullness T left, @ParametricNullness T right) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -71,5 +74,5 @@ public String toString() { return "Ordering.compound(" + Arrays.toString(comparators) + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ComputationException.java b/android/guava/src/com/google/common/collect/ComputationException.java index b05577c412cc..d5f6bcd26a82 100644 --- a/android/guava/src/com/google/common/collect/ComputationException.java +++ b/android/guava/src/com/google/common/collect/ComputationException.java @@ -17,7 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Wraps an exception that occurred during a computation. @@ -34,12 +36,11 @@ */ @Deprecated @GwtCompatible -@ElementTypesAreNonnullByDefault public class ComputationException extends RuntimeException { /** Creates a new instance with the given cause. */ - public ComputationException(@CheckForNull Throwable cause) { + public ComputationException(@Nullable Throwable cause) { super(cause); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ConcurrentHashMultiset.java b/android/guava/src/com/google/common/collect/ConcurrentHashMultiset.java index 23510e8fa64a..ea831cd57562 100644 --- a/android/guava/src/com/google/common/collect/ConcurrentHashMultiset.java +++ b/android/guava/src/com/google/common/collect/ConcurrentHashMultiset.java @@ -20,9 +20,13 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.Lists.newArrayListWithExpectedSize; +import static com.google.common.collect.Maps.safeGet; +import static java.lang.Math.max; +import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Serialization.FieldSetter; import com.google.common.math.IntMath; @@ -41,23 +45,21 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A multiset that supports concurrent modifications and that provides atomic versions of most * {@code Multiset} operations (exceptions where noted). Null elements are not supported. * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Cliff L. Biffle * @author mike nonemacher * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ConcurrentHashMultiset extends AbstractMultiset implements Serializable { /* @@ -76,7 +78,7 @@ public final class ConcurrentHashMultiset extends AbstractMultiset impleme // This constant allows the deserialization code to set a final field. This holder class // makes sure it is not initialized unless an instance is deserialized. private static class FieldSettersHolder { - static final FieldSetter COUNT_MAP_FIELD_SETTER = + static final FieldSetter> COUNT_MAP_FIELD_SETTER = Serialization.getFieldSetter(ConcurrentHashMultiset.class, "countMap"); } @@ -119,7 +121,6 @@ public static ConcurrentHashMultiset create(Iterable element * @throws IllegalArgumentException if {@code countMap} is not empty * @since 20.0 */ - @Beta public static ConcurrentHashMultiset create(ConcurrentMap countMap) { return new ConcurrentHashMultiset<>(countMap); } @@ -139,8 +140,8 @@ public static ConcurrentHashMultiset create(ConcurrentMap snapshot() { - List list = Lists.newArrayListWithExpectedSize(size()); + List list = newArrayListWithExpectedSize(size()); for (Multiset.Entry entry : entrySet()) { E element = entry.getElement(); for (int i = entry.getCount(); i > 0; i--) { @@ -211,7 +212,7 @@ public int add(E element, int occurrences) { CollectPreconditions.checkPositive(occurrences, "occurrences"); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(countMap, element); if (existingCounter == null) { existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences)); if (existingCounter == null) { @@ -270,20 +271,20 @@ public int add(E element, int occurrences) { */ @CanIgnoreReturnValue @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { if (occurrences == 0) { return count(element); } CollectPreconditions.checkPositive(occurrences, "occurrences"); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(countMap, element); if (existingCounter == null) { return 0; } while (true) { int oldValue = existingCounter.get(); if (oldValue != 0) { - int newValue = Math.max(0, oldValue - occurrences); + int newValue = max(0, oldValue - occurrences); if (existingCounter.compareAndSet(oldValue, newValue)) { if (newValue == 0) { // Just CASed to 0; remove the entry to clean up the map. If the removal fails, @@ -311,13 +312,13 @@ public int remove(@CheckForNull Object element, int occurrences) { * @throws IllegalArgumentException if {@code occurrences} is negative */ @CanIgnoreReturnValue - public boolean removeExactly(@CheckForNull Object element, int occurrences) { + public boolean removeExactly(@Nullable Object element, int occurrences) { if (occurrences == 0) { return true; } CollectPreconditions.checkPositive(occurrences, "occurrences"); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(countMap, element); if (existingCounter == null) { return false; } @@ -351,7 +352,7 @@ public int setCount(E element, int count) { checkNotNull(element); checkNonnegative(count, "count"); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(countMap, element); if (existingCounter == null) { if (count == 0) { return 0; @@ -408,7 +409,7 @@ public boolean setCount(E element, int expectedOldCount, int newCount) { checkNonnegative(expectedOldCount, "oldCount"); checkNonnegative(newCount, "newCount"); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(countMap, element); if (existingCounter == null) { if (expectedOldCount != 0) { return false; @@ -457,7 +458,7 @@ protected Set delegate() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return object != null && Collections2.safeContains(delegate, object); } @@ -467,7 +468,7 @@ public boolean containsAll(Collection collection) { } @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { return object != null && Collections2.safeRemove(delegate, object); } @@ -483,7 +484,9 @@ Iterator elementIterator() { throw new AssertionError("should never be called"); } - /** @deprecated Internal method, use {@link #entrySet()}. */ + /** + * @deprecated Internal method, use {@link #entrySet()}. + */ @Deprecated @Override public Set> createEntrySet() { @@ -510,8 +513,7 @@ Iterator> entryIterator() { countMap.entrySet().iterator(); @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { while (true) { if (!mapEntries.hasNext()) { return endOfData(); @@ -526,7 +528,7 @@ protected Entry computeNext() { }; return new ForwardingIterator>() { - @CheckForNull private Entry last; + private @Nullable Entry last; @Override protected Iterator> delegate() { @@ -582,24 +584,27 @@ public Object[] toArray() { } private List> snapshot() { - List> list = Lists.newArrayListWithExpectedSize(size()); + List> list = newArrayListWithExpectedSize(size()); // Not Iterables.addAll(list, this), because that'll forward right back here. Iterators.addAll(list, iterator()); return list; } } - /** @serialData the ConcurrentMap of elements and their counts. */ + /** + * @serialData the ConcurrentMap of elements and their counts. + */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(countMap); } + @J2ktIncompatible // serialization private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); @SuppressWarnings("unchecked") // reading data stored by writeObject ConcurrentMap deserializedCountMap = - (ConcurrentMap) stream.readObject(); + (ConcurrentMap) requireNonNull(stream.readObject()); FieldSettersHolder.COUNT_MAP_FIELD_SETTER.set(this, deserializedCountMap); } diff --git a/android/guava/src/com/google/common/collect/ConsumingQueueIterator.java b/android/guava/src/com/google/common/collect/ConsumingQueueIterator.java index 7721e1277ca8..847229e6a299 100644 --- a/android/guava/src/com/google/common/collect/ConsumingQueueIterator.java +++ b/android/guava/src/com/google/common/collect/ConsumingQueueIterator.java @@ -18,15 +18,13 @@ import com.google.common.annotations.GwtCompatible; import java.util.Queue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An Iterator implementation which draws elements from a queue, removing them from the queue as it - * iterates. + * iterates. This class is not thread safe. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class ConsumingQueueIterator extends AbstractIterator { private final Queue queue; @@ -35,8 +33,7 @@ final class ConsumingQueueIterator extends AbstractI } @Override - @CheckForNull - public T computeNext() { + protected @Nullable T computeNext() { // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. if (queue.isEmpty()) { return endOfData(); diff --git a/android/guava/src/com/google/common/collect/ContiguousSet.java b/android/guava/src/com/google/common/collect/ContiguousSet.java index b3e4889d7cf7..bb5aa11a4a0e 100644 --- a/android/guava/src/com/google/common/collect/ContiguousSet.java +++ b/android/guava/src/com/google/common/collect/ContiguousSet.java @@ -18,9 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.DoNotCall; import java.util.Collections; import java.util.NoSuchElementException; @@ -50,7 +50,6 @@ */ @GwtCompatible(emulated = true) @SuppressWarnings("rawtypes") // allow ungenerified Comparable types -@ElementTypesAreNonnullByDefault public abstract class ContiguousSet extends ImmutableSortedSet { /** * Returns a {@code ContiguousSet} containing the same values in the given domain {@linkplain @@ -103,7 +102,6 @@ public static ContiguousSet create( * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} * @since 23.0 */ - @Beta public static ContiguousSet closed(int lower, int upper) { return create(Range.closed(lower, upper), DiscreteDomain.integers()); } @@ -116,7 +114,6 @@ public static ContiguousSet closed(int lower, int upper) { * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} * @since 23.0 */ - @Beta public static ContiguousSet closed(long lower, long upper) { return create(Range.closed(lower, upper), DiscreteDomain.longs()); } @@ -129,7 +126,6 @@ public static ContiguousSet closed(long lower, long upper) { * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} * @since 23.0 */ - @Beta public static ContiguousSet closedOpen(int lower, int upper) { return create(Range.closedOpen(lower, upper), DiscreteDomain.integers()); } @@ -142,7 +138,6 @@ public static ContiguousSet closedOpen(int lower, int upper) { * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} * @since 23.0 */ - @Beta public static ContiguousSet closedOpen(long lower, long upper) { return create(Range.closedOpen(lower, upper), DiscreteDomain.longs()); } @@ -159,7 +154,9 @@ public ContiguousSet headSet(C toElement) { return headSetImpl(checkNotNull(toElement), false); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public ContiguousSet headSet(C toElement, boolean inclusive) { @@ -174,7 +171,9 @@ public ContiguousSet subSet(C fromElement, C toElement) { return subSetImpl(fromElement, true, toElement, false); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public ContiguousSet subSet( @@ -190,7 +189,9 @@ public ContiguousSet tailSet(C fromElement) { return tailSetImpl(checkNotNull(fromElement), true); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public ContiguousSet tailSet(C fromElement, boolean inclusive) { @@ -245,7 +246,7 @@ ImmutableSortedSet createDescendingSet() { return new DescendingImmutableSortedSet<>(this); } - /** Returns a short-hand representation of the contents such as {@code "[1..100]"}. */ + /** Returns a shorthand representation of the contents such as {@code "[1..100]"}. */ @Override public String toString() { return range().toString(); @@ -264,4 +265,13 @@ public String toString() { public static ImmutableSortedSet.Builder builder() { throw new UnsupportedOperationException(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @J2ktIncompatible // serialization + @Override + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/Count.java b/android/guava/src/com/google/common/collect/Count.java index 7aa555080135..fa01412d9c54 100644 --- a/android/guava/src/com/google/common/collect/Count.java +++ b/android/guava/src/com/google/common/collect/Count.java @@ -16,7 +16,7 @@ import com.google.common.annotations.GwtCompatible; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A mutable value of type {@code int}, for multisets to use in tracking counts of values. @@ -24,7 +24,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class Count implements Serializable { private int value; @@ -60,7 +59,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof Count && ((Count) obj).value == value; } diff --git a/android/guava/src/com/google/common/collect/Cut.java b/android/guava/src/com/google/common/collect/Cut.java index 21fe5ff2dd9f..4d682aee35d4 100644 --- a/android/guava/src/com/google/common/collect/Cut.java +++ b/android/guava/src/com/google/common/collect/Cut.java @@ -17,10 +17,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.primitives.Booleans; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation detail for the internal structure of {@link Range} instances. Represents a unique @@ -31,8 +32,8 @@ * * @author Kevin Bourrillion */ +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class Cut implements Comparable>, Serializable { final C endpoint; @@ -54,11 +55,9 @@ abstract class Cut implements Comparable>, Serializ abstract void describeAsUpperBound(StringBuilder sb); - @CheckForNull - abstract C leastValueAbove(DiscreteDomain domain); + abstract @Nullable C leastValueAbove(DiscreteDomain domain); - @CheckForNull - abstract C greatestValueBelow(DiscreteDomain domain); + abstract @Nullable C greatestValueBelow(DiscreteDomain domain); /* * The canonical form is a BelowValue cut whenever possible, otherwise ABOVE_ALL, or @@ -82,7 +81,7 @@ public int compareTo(Cut that) { return result; } // same value. below comes before above - return Booleans.compare(this instanceof AboveValue, that instanceof AboveValue); + return Boolean.compare(this instanceof AboveValue, that instanceof AboveValue); } C endpoint() { @@ -91,7 +90,7 @@ C endpoint() { @SuppressWarnings("unchecked") // catching CCE @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Cut) { // It might not really be a Cut, but we'll catch a CCE if it's not Cut that = (Cut) obj; @@ -118,7 +117,7 @@ static Cut belowAll() { return (Cut) BelowAll.INSTANCE; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; private static final class BelowAll extends Cut> { private static final BelowAll INSTANCE = new BelowAll(); @@ -213,7 +212,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /* @@ -304,7 +303,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static Cut belowValue(C endpoint) { @@ -339,9 +338,8 @@ Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { case OPEN: C previous = domain.previous(endpoint); return (previous == null) ? Cut.belowAll() : new AboveValue(previous); - default: - throw new AssertionError(); } + throw new AssertionError(); } @Override @@ -352,9 +350,8 @@ Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { return (previous == null) ? Cut.aboveAll() : new AboveValue(previous); case OPEN: return this; - default: - throw new AssertionError(); } + throw new AssertionError(); } @Override @@ -373,8 +370,7 @@ C leastValueAbove(DiscreteDomain domain) { } @Override - @CheckForNull - C greatestValueBelow(DiscreteDomain domain) { + @Nullable C greatestValueBelow(DiscreteDomain domain) { return domain.previous(endpoint); } @@ -388,7 +384,7 @@ public String toString() { return "\\" + endpoint + "/"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static Cut aboveValue(C endpoint) { @@ -423,9 +419,8 @@ Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { case CLOSED: C next = domain.next(endpoint); return (next == null) ? Cut.belowAll() : belowValue(next); - default: - throw new AssertionError(); } + throw new AssertionError(); } @Override @@ -436,9 +431,8 @@ Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { return (next == null) ? Cut.aboveAll() : belowValue(next); case CLOSED: return this; - default: - throw new AssertionError(); } + throw new AssertionError(); } @Override @@ -452,8 +446,7 @@ void describeAsUpperBound(StringBuilder sb) { } @Override - @CheckForNull - C leastValueAbove(DiscreteDomain domain) { + @Nullable C leastValueAbove(DiscreteDomain domain) { return domain.next(endpoint); } @@ -478,6 +471,6 @@ public String toString() { return "/" + endpoint + "\\"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/DenseImmutableTable.java b/android/guava/src/com/google/common/collect/DenseImmutableTable.java index 9de77c57f213..eef7e8bab983 100644 --- a/android/guava/src/com/google/common/collect/DenseImmutableTable.java +++ b/android/guava/src/com/google/common/collect/DenseImmutableTable.java @@ -14,20 +14,21 @@ package com.google.common.collect; +import static com.google.common.collect.Maps.immutableEntry; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap.IteratorBasedImmutableMap; import com.google.errorprone.annotations.Immutable; import com.google.j2objc.annotations.WeakOuter; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A {@code RegularImmutableTable} optimized for dense data. */ @GwtCompatible @Immutable(containerOf = {"R", "C", "V"}) -@ElementTypesAreNonnullByDefault final class DenseImmutableTable extends RegularImmutableTable { private final ImmutableMap rowKeyToIndex; private final ImmutableMap columnKeyToIndex; @@ -56,8 +57,7 @@ final class DenseImmutableTable extends RegularImmutableTable ImmutableSet rowSpace, ImmutableSet columnSpace) { @SuppressWarnings("unchecked") - @Nullable - V[][] array = (@Nullable V[][]) new Object[rowSpace.size()][columnSpace.size()]; + @Nullable V[][] array = (@Nullable V[][]) new Object[rowSpace.size()][columnSpace.size()]; this.values = array; this.rowKeyToIndex = Maps.indexMap(rowSpace); this.columnKeyToIndex = Maps.indexMap(columnSpace); @@ -105,8 +105,7 @@ K getKey(int index) { return keyToIndex().keySet().asList().get(index); } - @CheckForNull - abstract V getValue(int keyIndex); + abstract @Nullable V getValue(int keyIndex); @Override ImmutableSet createKeySet() { @@ -119,8 +118,7 @@ public int size() { } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { Integer keyIndex = keyToIndex().get(key); return (keyIndex == null) ? null : getValue(keyIndex); } @@ -132,18 +130,26 @@ UnmodifiableIterator> entryIterator() { private final int maxIndex = keyToIndex().size(); @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { for (index++; index < maxIndex; index++) { V value = getValue(index); if (value != null) { - return Maps.immutableEntry(getKey(index), value); + return immutableEntry(getKey(index), value); } } return endOfData(); } }; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @J2ktIncompatible // serialization + @Override + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } private final class Row extends ImmutableArrayMap { @@ -160,8 +166,7 @@ ImmutableMap keyToIndex() { } @Override - @CheckForNull - V getValue(int keyIndex) { + @Nullable V getValue(int keyIndex) { return values[rowIndex][keyIndex]; } @@ -169,6 +174,15 @@ V getValue(int keyIndex) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } private final class Column extends ImmutableArrayMap { @@ -185,8 +199,7 @@ ImmutableMap keyToIndex() { } @Override - @CheckForNull - V getValue(int keyIndex) { + @Nullable V getValue(int keyIndex) { return values[keyIndex][columnIndex]; } @@ -194,6 +207,15 @@ V getValue(int keyIndex) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @WeakOuter @@ -216,6 +238,15 @@ ImmutableMap getValue(int keyIndex) { boolean isPartialView() { return false; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @WeakOuter @@ -238,6 +269,15 @@ ImmutableMap getValue(int keyIndex) { boolean isPartialView() { return false; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override @@ -255,8 +295,7 @@ public ImmutableMap> rowMap() { } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); return ((rowIndex == null) || (columnIndex == null)) ? null : values[rowIndex][columnIndex]; @@ -285,7 +324,9 @@ V getValue(int index) { } @Override - SerializedForm createSerializedForm() { + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { return SerializedForm.create(this, cellRowIndices, cellColumnIndices); } } diff --git a/android/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java b/android/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java index 181731cc4896..a5fec75bce63 100644 --- a/android/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java +++ b/android/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java @@ -15,7 +15,8 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * A descending wrapper around an {@code ImmutableSortedMultiset} @@ -24,7 +25,6 @@ */ @SuppressWarnings("serial") // uses writeReplace, not default serialization @GwtIncompatible -@ElementTypesAreNonnullByDefault final class DescendingImmutableSortedMultiset extends ImmutableSortedMultiset { private final transient ImmutableSortedMultiset forward; @@ -33,19 +33,17 @@ final class DescendingImmutableSortedMultiset extends ImmutableSortedMultiset } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { return forward.count(element); } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return forward.lastEntry(); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return forward.firstEntry(); } @@ -83,4 +81,12 @@ public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType boolean isPartialView() { return forward.isPartialView(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java b/android/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java index 88c7d6b5cca0..4a13415c540b 100644 --- a/android/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java +++ b/android/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java @@ -17,7 +17,8 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Skeletal implementation of {@link ImmutableSortedSet#descendingSet()}. @@ -25,7 +26,6 @@ * @author Louis Wasserman */ @GwtIncompatible -@ElementTypesAreNonnullByDefault final class DescendingImmutableSortedSet extends ImmutableSortedSet { private final ImmutableSortedSet forward; @@ -35,7 +35,7 @@ final class DescendingImmutableSortedSet extends ImmutableSortedSet { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return forward.contains(object); } @@ -84,31 +84,27 @@ ImmutableSortedSet createDescendingSet() { } @Override - @CheckForNull - public E lower(E element) { + public @Nullable E lower(E element) { return forward.higher(element); } @Override - @CheckForNull - public E floor(E element) { + public @Nullable E floor(E element) { return forward.ceiling(element); } @Override - @CheckForNull - public E ceiling(E element) { + public @Nullable E ceiling(E element) { return forward.floor(element); } @Override - @CheckForNull - public E higher(E element) { + public @Nullable E higher(E element) { return forward.lower(element); } @Override - int indexOf(@CheckForNull Object target) { + int indexOf(@Nullable Object target) { int index = forward.indexOf(target); if (index == -1) { return index; @@ -121,4 +117,12 @@ int indexOf(@CheckForNull Object target) { boolean isPartialView() { return forward.isPartialView(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/DescendingMultiset.java b/android/guava/src/com/google/common/collect/DescendingMultiset.java index 7db0fbbd4916..146fac334494 100644 --- a/android/guava/src/com/google/common/collect/DescendingMultiset.java +++ b/android/guava/src/com/google/common/collect/DescendingMultiset.java @@ -17,13 +17,13 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.util.Comparator; import java.util.Iterator; import java.util.NavigableSet; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A skeleton implementation of a descending multiset. Only needs {@code forwardMultiset()} and @@ -32,12 +32,11 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class DescendingMultiset extends ForwardingMultiset implements SortedMultiset { abstract SortedMultiset forwardMultiset(); - @CheckForNull private transient Comparator comparator; + @LazyInit private transient @Nullable Comparator comparator; @Override public Comparator comparator() { @@ -48,7 +47,7 @@ public Comparator comparator() { return result; } - @CheckForNull private transient NavigableSet elementSet; + @LazyInit private transient @Nullable NavigableSet elementSet; @Override public NavigableSet elementSet() { @@ -60,14 +59,12 @@ public NavigableSet elementSet() { } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return forwardMultiset().pollLastEntry(); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return forwardMultiset().pollFirstEntry(); } @@ -103,20 +100,18 @@ public SortedMultiset descendingMultiset() { } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return forwardMultiset().lastEntry(); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return forwardMultiset().firstEntry(); } abstract Iterator> entryIterator(); - @CheckForNull private transient Set> entrySet; + @LazyInit private transient @Nullable Set> entrySet; @Override public Set> entrySet() { diff --git a/android/guava/src/com/google/common/collect/DiscreteDomain.java b/android/guava/src/com/google/common/collect/DiscreteDomain.java index bce70622215c..13e7be404726 100644 --- a/android/guava/src/com/google/common/collect/DiscreteDomain.java +++ b/android/guava/src/com/google/common/collect/DiscreteDomain.java @@ -20,12 +20,14 @@ import static com.google.common.collect.CollectPreconditions.checkNonnegative; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; import java.math.BigInteger; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A descriptor for a discrete {@code Comparable} domain such as all {@link Integer} @@ -37,19 +39,22 @@ * represent partial domains such as "prime integers" or "strings of length 5." * *

    See the Guava User Guide section on {@code + * "https://github.com/google/guava/wiki/RangesExplained#discrete-domains">{@code * DiscreteDomain}. * * @author Kevin Bourrillion * @since 10.0 */ +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class DiscreteDomain { /** * Returns the discrete domain for values of type {@code Integer}. * + *

    This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * * @since 14.0 (since 10.0 as {@code DiscreteDomains.integers()}) */ public static DiscreteDomain integers() { @@ -64,15 +69,13 @@ private static final class IntegerDomain extends DiscreteDomain impleme } @Override - @CheckForNull - public Integer next(Integer value) { + public @Nullable Integer next(Integer value) { int i = value; return (i == Integer.MAX_VALUE) ? null : i + 1; } @Override - @CheckForNull - public Integer previous(Integer value) { + public @Nullable Integer previous(Integer value) { int i = value; return (i == Integer.MIN_VALUE) ? null : i - 1; } @@ -107,12 +110,15 @@ public String toString() { return "DiscreteDomain.integers()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns the discrete domain for values of type {@code Long}. * + *

    This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * * @since 14.0 (since 10.0 as {@code DiscreteDomains.longs()}) */ public static DiscreteDomain longs() { @@ -127,15 +133,13 @@ private static final class LongDomain extends DiscreteDomain implements Se } @Override - @CheckForNull - public Long next(Long value) { + public @Nullable Long next(Long value) { long l = value; return (l == Long.MAX_VALUE) ? null : l + 1; } @Override - @CheckForNull - public Long previous(Long value) { + public @Nullable Long previous(Long value) { long l = value; return (l == Long.MIN_VALUE) ? null : l - 1; } @@ -181,12 +185,15 @@ public String toString() { return "DiscreteDomain.longs()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** * Returns the discrete domain for values of type {@code BigInteger}. * + *

    This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * * @since 15.0 */ public static DiscreteDomain bigIntegers() { @@ -234,7 +241,7 @@ public String toString() { return "DiscreteDomain.bigIntegers()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } final boolean supportsFastOffset; @@ -274,8 +281,7 @@ C offset(C origin, long distance) { * @return the least value greater than {@code value}, or {@code null} if {@code value} is {@code * maxValue()} */ - @CheckForNull - public abstract C next(C value); + public abstract @Nullable C next(C value); /** * Returns the unique greatest value of type {@code C} that is less than {@code value}, or {@code @@ -285,8 +291,7 @@ C offset(C origin, long distance) { * @return the greatest value less than {@code value}, or {@code null} if {@code value} is {@code * minValue()} */ - @CheckForNull - public abstract C previous(C value); + public abstract @Nullable C previous(C value); /** * Returns a signed value indicating how many nested invocations of {@link #next} (if positive) or diff --git a/android/guava/src/com/google/common/collect/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/collect/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index e1c640ff5db4..000000000000 --- a/android/guava/src/com/google/common/collect/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.collect; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/collect/EmptyContiguousSet.java b/android/guava/src/com/google/common/collect/EmptyContiguousSet.java index 50d7b126a27a..2f02a9e430bb 100644 --- a/android/guava/src/com/google/common/collect/EmptyContiguousSet.java +++ b/android/guava/src/com/google/common/collect/EmptyContiguousSet.java @@ -13,12 +13,17 @@ */ package com.google.common.collect; +import static com.google.common.collect.Iterators.emptyIterator; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An empty contiguous set. @@ -27,7 +32,6 @@ */ @GwtCompatible(emulated = true) @SuppressWarnings("rawtypes") // allow ungenerified Comparable types -@ElementTypesAreNonnullByDefault final class EmptyContiguousSet extends ContiguousSet { EmptyContiguousSet(DiscreteDomain domain) { super(domain); @@ -80,25 +84,25 @@ ContiguousSet tailSetImpl(C fromElement, boolean fromInclusive) { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return false; } @GwtIncompatible // not used by GWT emulation @Override - int indexOf(@CheckForNull Object target) { + int indexOf(@Nullable Object target) { return -1; } @Override public UnmodifiableIterator iterator() { - return Iterators.emptyIterator(); + return emptyIterator(); } @GwtIncompatible // NavigableSet @Override public UnmodifiableIterator descendingIterator() { - return Iterators.emptyIterator(); + return emptyIterator(); } @Override @@ -122,7 +126,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Set) { Set that = (Set) object; return that.isEmpty(); @@ -142,6 +146,7 @@ public int hashCode() { } @GwtIncompatible // serialization + @J2ktIncompatible private static final class SerializedForm implements Serializable { private final DiscreteDomain domain; @@ -153,18 +158,25 @@ private Object readResolve() { return new EmptyContiguousSet<>(domain); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible // serialization + @J2ktIncompatible @Override Object writeReplace() { return new SerializedForm<>(domain); } + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + @GwtIncompatible // NavigableSet @Override ImmutableSortedSet createDescendingSet() { - return ImmutableSortedSet.emptySet(Ordering.natural().reverse()); + return ImmutableSortedSet.emptySet(Ordering.natural().reverse()); } } diff --git a/android/guava/src/com/google/common/collect/EmptyImmutableListMultimap.java b/android/guava/src/com/google/common/collect/EmptyImmutableListMultimap.java index 10d030fbda05..86aa27dbfb51 100644 --- a/android/guava/src/com/google/common/collect/EmptyImmutableListMultimap.java +++ b/android/guava/src/com/google/common/collect/EmptyImmutableListMultimap.java @@ -17,6 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.util.Collection; /** * Implementation of {@link ImmutableListMultimap} with no entries. @@ -24,7 +27,6 @@ * @author Jared Levy */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault class EmptyImmutableListMultimap extends ImmutableListMultimap { static final EmptyImmutableListMultimap INSTANCE = new EmptyImmutableListMultimap(); @@ -32,9 +34,20 @@ private EmptyImmutableListMultimap() { super(ImmutableMap.>of(), 0); } + /* + * TODO(b/242884182): Figure out why this helps produce the same class file when we compile most + * of common.collect a second time with the results of the first compilation on the classpath. Or + * just back this out once we stop doing that (which we'll do after our internal GWT setup + * changes). + */ + @Override + public ImmutableMap> asMap() { + return super.asMap(); + } + private Object readResolve() { return INSTANCE; // preserve singleton property } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/EmptyImmutableSetMultimap.java b/android/guava/src/com/google/common/collect/EmptyImmutableSetMultimap.java index bd0a67aa087b..7e713d32f18f 100644 --- a/android/guava/src/com/google/common/collect/EmptyImmutableSetMultimap.java +++ b/android/guava/src/com/google/common/collect/EmptyImmutableSetMultimap.java @@ -17,6 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.util.Collection; /** * Implementation of {@link ImmutableListMultimap} with no entries. @@ -24,7 +27,6 @@ * @author Mike Ward */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault class EmptyImmutableSetMultimap extends ImmutableSetMultimap { static final EmptyImmutableSetMultimap INSTANCE = new EmptyImmutableSetMultimap(); @@ -32,9 +34,20 @@ private EmptyImmutableSetMultimap() { super(ImmutableMap.>of(), 0, null); } + /* + * TODO(b/242884182): Figure out why this helps produce the same class file when we compile most + * of common.collect a second time with the results of the first compilation on the classpath. Or + * just back this out once we stop doing that (which we'll do after our internal GWT setup + * changes). + */ + @Override + public ImmutableMap> asMap() { + return super.asMap(); + } + private Object readResolve() { return INSTANCE; // preserve singleton property } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/EnumBiMap.java b/android/guava/src/com/google/common/collect/EnumBiMap.java index 82aa052d650e..f8f430c2f642 100644 --- a/android/guava/src/com/google/common/collect/EnumBiMap.java +++ b/android/guava/src/com/google/common/collect/EnumBiMap.java @@ -18,9 +18,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Platform.getDeclaringClassOrObjectForJ2cl; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -32,16 +35,30 @@ * An {@code EnumBiMap} and its inverse are both serializable. * *

    See the Guava User Guide article on {@code BiMap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap}. * * @author Mike Bostock * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public final class EnumBiMap, V extends Enum> extends AbstractBiMap { - private transient Class keyType; - private transient Class valueType; + /* + * J2CL's EnumMap does not need the Class instance, so we can use Object.class instead. (Or we + * could use null, but that messes with our nullness checking, including under J2KT. We could + * probably work around it by changing how we annotate the J2CL EnumMap, but that's probably more + * trouble than just using Object.class.) + * + * Then we declare the getters for these fields as @GwtIncompatible so that no one can try to use + * them under J2CL—or, as an unfortunate side effect, under GWT. We do still give the fields + * themselves their proper values under GWT, since GWT's EnumMap does need the Class instance. + * + * Note that sometimes these fields *do* have correct values under J2CL: They will if the caller + * calls `create(Foo.class)`, rather than `create(map)`. That's fine; we just shouldn't rely on + * it. + */ + transient Class keyTypeOrObjectUnderJ2cl; + transient Class valueTypeOrObjectUnderJ2cl; /** * Returns a new, empty {@code EnumBiMap} using the specified key and value types. @@ -64,44 +81,48 @@ public static , V extends Enum> EnumBiMap create( * mappings */ public static , V extends Enum> EnumBiMap create(Map map) { - EnumBiMap bimap = create(inferKeyType(map), inferValueType(map)); + EnumBiMap bimap = + create(inferKeyTypeOrObjectUnderJ2cl(map), inferValueTypeOrObjectUnderJ2cl(map)); bimap.putAll(map); return bimap; } - private EnumBiMap(Class keyType, Class valueType) { - super(new EnumMap(keyType), new EnumMap(valueType)); - this.keyType = keyType; - this.valueType = valueType; + private EnumBiMap(Class keyTypeOrObjectUnderJ2cl, Class valueTypeOrObjectUnderJ2cl) { + super( + new EnumMap(keyTypeOrObjectUnderJ2cl), new EnumMap(valueTypeOrObjectUnderJ2cl)); + this.keyTypeOrObjectUnderJ2cl = keyTypeOrObjectUnderJ2cl; + this.valueTypeOrObjectUnderJ2cl = valueTypeOrObjectUnderJ2cl; } - static > Class inferKeyType(Map map) { + static > Class inferKeyTypeOrObjectUnderJ2cl(Map map) { if (map instanceof EnumBiMap) { - return ((EnumBiMap) map).keyType(); + return ((EnumBiMap) map).keyTypeOrObjectUnderJ2cl; } if (map instanceof EnumHashBiMap) { - return ((EnumHashBiMap) map).keyType(); + return ((EnumHashBiMap) map).keyTypeOrObjectUnderJ2cl; } checkArgument(!map.isEmpty()); - return map.keySet().iterator().next().getDeclaringClass(); + return getDeclaringClassOrObjectForJ2cl(map.keySet().iterator().next()); } - private static > Class inferValueType(Map map) { + private static > Class inferValueTypeOrObjectUnderJ2cl(Map map) { if (map instanceof EnumBiMap) { - return ((EnumBiMap) map).valueType; + return ((EnumBiMap) map).valueTypeOrObjectUnderJ2cl; } checkArgument(!map.isEmpty()); - return map.values().iterator().next().getDeclaringClass(); + return getDeclaringClassOrObjectForJ2cl(map.values().iterator().next()); } /** Returns the associated key type. */ + @GwtIncompatible public Class keyType() { - return keyType; + return keyTypeOrObjectUnderJ2cl; } /** Returns the associated value type. */ + @GwtIncompatible public Class valueType() { - return valueType; + return valueTypeOrObjectUnderJ2cl; } @Override @@ -121,8 +142,8 @@ V checkValue(V value) { @GwtIncompatible // java.io.ObjectOutputStream private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); - stream.writeObject(keyType); - stream.writeObject(valueType); + stream.writeObject(keyTypeOrObjectUnderJ2cl); + stream.writeObject(valueTypeOrObjectUnderJ2cl); Serialization.writeMap(this, stream); } @@ -130,12 +151,12 @@ private void writeObject(ObjectOutputStream stream) throws IOException { @GwtIncompatible // java.io.ObjectInputStream private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - keyType = (Class) stream.readObject(); - valueType = (Class) stream.readObject(); - setDelegates(new EnumMap(keyType), new EnumMap(valueType)); + keyTypeOrObjectUnderJ2cl = (Class) requireNonNull(stream.readObject()); + valueTypeOrObjectUnderJ2cl = (Class) requireNonNull(stream.readObject()); + setDelegates( + new EnumMap(keyTypeOrObjectUnderJ2cl), new EnumMap(valueTypeOrObjectUnderJ2cl)); Serialization.populateMap(this, stream); } - @GwtIncompatible // not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/EnumHashBiMap.java b/android/guava/src/com/google/common/collect/EnumHashBiMap.java index f68bc4c20038..b585d571b5ae 100644 --- a/android/guava/src/com/google/common/collect/EnumHashBiMap.java +++ b/android/guava/src/com/google/common/collect/EnumHashBiMap.java @@ -17,9 +17,11 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.io.ObjectInputStream; @@ -27,8 +29,7 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@code BiMap} backed by an {@code EnumMap} instance for keys-to-values, and a {@code HashMap} @@ -36,16 +37,16 @@ * EnumHashBiMap} and its inverse are both serializable. * *

    See the Guava User Guide article on {@code BiMap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap}. * * @author Mike Bostock * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public final class EnumHashBiMap, V extends @Nullable Object> extends AbstractBiMap { - private transient Class keyType; + transient Class keyTypeOrObjectUnderJ2cl; /** * Returns a new, empty {@code EnumHashBiMap} using the specified key type. @@ -69,16 +70,15 @@ public final class EnumHashBiMap, V extends @Nullable Object> */ public static , V extends @Nullable Object> EnumHashBiMap create( Map map) { - EnumHashBiMap bimap = create(EnumBiMap.inferKeyType(map)); + EnumHashBiMap bimap = create(EnumBiMap.inferKeyTypeOrObjectUnderJ2cl(map)); bimap.putAll(map); return bimap; } private EnumHashBiMap(Class keyType) { - super( - new EnumMap(keyType), - Maps.newHashMapWithExpectedSize(keyType.getEnumConstants().length)); - this.keyType = keyType; + super(new EnumMap(keyType), new HashMap()); + // TODO: cpovirk - Pre-size the HashMap based on the number of enum values? + this.keyTypeOrObjectUnderJ2cl = keyType; } // Overriding these 3 methods to show that values may be null (but not keys) @@ -92,8 +92,7 @@ K checkKey(K key) { @Override @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. // TODO(b/192446998): Remove this override after tools understand nullness better. - @CheckForNull - public V put(K key, @ParametricNullness V value) { + public @Nullable V put(K key, @ParametricNullness V value) { return super.put(key, value); } @@ -101,14 +100,14 @@ public V put(K key, @ParametricNullness V value) { @Override @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. // TODO(b/192446998): Remove this override after tools understand nullness better. - @CheckForNull - public V forcePut(K key, @ParametricNullness V value) { + public @Nullable V forcePut(K key, @ParametricNullness V value) { return super.forcePut(key, value); } /** Returns the associated key type. */ + @GwtIncompatible public Class keyType() { - return keyType; + return keyTypeOrObjectUnderJ2cl; } /** @@ -118,7 +117,7 @@ public Class keyType() { @GwtIncompatible // java.io.ObjectOutputStream private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); - stream.writeObject(keyType); + stream.writeObject(keyTypeOrObjectUnderJ2cl); Serialization.writeMap(this, stream); } @@ -126,12 +125,15 @@ private void writeObject(ObjectOutputStream stream) throws IOException { @GwtIncompatible // java.io.ObjectInputStream private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - keyType = (Class) stream.readObject(); - setDelegates( - new EnumMap(keyType), new HashMap(keyType.getEnumConstants().length * 3 / 2)); + keyTypeOrObjectUnderJ2cl = (Class) requireNonNull(stream.readObject()); + /* + * TODO: cpovirk - Pre-size the HashMap based on the number of enum values? (But *not* based on + * the number of entries in the map, as that makes it easy for hostile inputs to trigger lots of + * allocation—not that any program should be deserializing hostile inputs to begin with!) + */ + setDelegates(new EnumMap(keyTypeOrObjectUnderJ2cl), new HashMap()); Serialization.populateMap(this, stream); } - @GwtIncompatible // only needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/EnumMultiset.java b/android/guava/src/com/google/common/collect/EnumMultiset.java index 0fed4ad06a35..0105b7cb4007 100644 --- a/android/guava/src/com/google/common/collect/EnumMultiset.java +++ b/android/guava/src/com/google/common/collect/EnumMultiset.java @@ -18,9 +18,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; import static com.google.common.collect.CollectPreconditions.checkRemove; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -30,26 +32,26 @@ import java.util.Arrays; import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Multiset implementation specialized for enum elements, supporting all single-element operations * in O(1). * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Jared Levy * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@J2ktIncompatible +@SuppressWarnings("EnumOrdinal") // This is one of the low-level utilities where it's suitable. public final class EnumMultiset> extends AbstractMultiset implements Serializable { /** Creates an empty {@code EnumMultiset}. */ public static > EnumMultiset create(Class type) { - return new EnumMultiset(type); + return new EnumMultiset<>(type); } /** @@ -94,7 +96,7 @@ private EnumMultiset(Class type) { this.counts = new int[enumConstants.length]; } - private boolean isActuallyE(@CheckForNull Object o) { + private boolean isActuallyE(@Nullable Object o) { if (o instanceof Enum) { Enum e = (Enum) o; int index = e.ordinal(); @@ -125,7 +127,7 @@ public int size() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { // isActuallyE checks for null, but we check explicitly to help nullness checkers. if (element == null || !isActuallyE(element)) { return 0; @@ -158,7 +160,7 @@ public int add(E element, int occurrences) { // Modification Operations @CanIgnoreReturnValue @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { // isActuallyE checks for null, but we check explicitly to help nullness checkers. if (element == null || !isActuallyE(element)) { return 0; @@ -261,7 +263,7 @@ E output(int index) { Iterator> entryIterator() { return new Itr>() { @Override - Entry output(final int index) { + Entry output(int index) { return new Multisets.AbstractEntry() { @Override public E getElement() { @@ -297,13 +299,12 @@ private void writeObject(ObjectOutputStream stream) throws IOException { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); @SuppressWarnings("unchecked") // reading data stored by writeObject - Class localType = (Class) stream.readObject(); + Class localType = (Class) requireNonNull(stream.readObject()); type = localType; enumConstants = type.getEnumConstants(); counts = new int[enumConstants.length]; Serialization.populateMultiset(this, stream); } - @GwtIncompatible // Not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/EvictingQueue.java b/android/guava/src/com/google/common/collect/EvictingQueue.java index 5667dfa8c054..957d10b7eb76 100644 --- a/android/guava/src/com/google/common/collect/EvictingQueue.java +++ b/android/guava/src/com/google/common/collect/EvictingQueue.java @@ -19,8 +19,9 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; @@ -43,9 +44,7 @@ * @author Kurt Alfred Kluever * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class EvictingQueue extends ForwardingQueue implements Serializable { private final Queue delegate; @@ -127,6 +126,7 @@ public boolean addAll(Collection collection) { } @Override + @J2ktIncompatible // Incompatible return type change. Use inherited implementation public Object[] toArray() { /* * If we could, we'd declare the no-arg `Collection.toArray()` to return "Object[] but elements @@ -141,5 +141,5 @@ public Object[] toArray() { return super.toArray(); } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } diff --git a/android/guava/src/com/google/common/collect/ExplicitOrdering.java b/android/guava/src/com/google/common/collect/ExplicitOrdering.java index 383318af5bb4..412bcf1613b3 100644 --- a/android/guava/src/com/google/common/collect/ExplicitOrdering.java +++ b/android/guava/src/com/google/common/collect/ExplicitOrdering.java @@ -17,13 +17,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.List; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** An ordering that compares objects according to a given order. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class ExplicitOrdering extends Ordering implements Serializable { final ImmutableMap rankMap; @@ -49,7 +50,7 @@ private int rank(T value) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof ExplicitOrdering) { ExplicitOrdering that = (ExplicitOrdering) object; return this.rankMap.equals(that.rankMap); @@ -67,5 +68,5 @@ public String toString() { return "Ordering.explicit(" + rankMap.keySet() + ")"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/FilteredEntryMultimap.java b/android/guava/src/com/google/common/collect/FilteredEntryMultimap.java index de946a215f42..f0b0c550419e 100644 --- a/android/guava/src/com/google/common/collect/FilteredEntryMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredEntryMultimap.java @@ -20,6 +20,9 @@ import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.Maps.immutableEntry; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableSet; import com.google.common.annotations.GwtCompatible; import com.google.common.base.MoreObjects; @@ -33,8 +36,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}. @@ -43,7 +45,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault class FilteredEntryMultimap extends AbstractMultimap implements FilteredMultimap { final Multimap unfiltered; @@ -70,7 +71,7 @@ public int size() { } private boolean satisfies(@ParametricNullness K key, @ParametricNullness V value) { - return predicate.apply(Maps.immutableEntry(key, value)); + return predicate.apply(immutableEntry(key, value)); } final class ValuePredicate implements Predicate { @@ -96,12 +97,12 @@ public boolean apply(@ParametricNullness V value) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return asMap().get(key) != null; } @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { return MoreObjects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection()); } @@ -154,7 +155,8 @@ boolean removeEntriesIf(Predicate>> predicate) { Entry> entry = entryIterator.next(); K key = entry.getKey(); Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { + if (!collection.isEmpty() + && predicate.apply(Maps.>immutableEntry(key, collection))) { if (collection.size() == entry.getValue().size()) { entryIterator.remove(); } else { @@ -169,7 +171,7 @@ boolean removeEntriesIf(Predicate>> predicate) { @WeakOuter class AsMap extends ViewCachingAbstractMap> { @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @@ -179,8 +181,7 @@ public void clear() { } @Override - @CheckForNull - public Collection get(@CheckForNull Object key) { + public @Nullable Collection get(@Nullable Object key) { Collection result = unfiltered.asMap().get(key); if (result == null) { return null; @@ -192,8 +193,7 @@ public Collection get(@CheckForNull Object key) { } @Override - @CheckForNull - public Collection remove(@CheckForNull Object key) { + public @Nullable Collection remove(@Nullable Object key) { Collection collection = unfiltered.asMap().get(key); if (collection == null) { return null; @@ -212,9 +212,9 @@ public Collection remove(@CheckForNull Object key) { if (result.isEmpty()) { return null; } else if (unfiltered instanceof SetMultimap) { - return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); + return unmodifiableSet(Sets.newLinkedHashSet(result)); } else { - return Collections.unmodifiableList(result); + return unmodifiableList(result); } } @@ -237,7 +237,7 @@ public boolean retainAll(Collection c) { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { return AsMap.this.remove(o) != null; } } @@ -260,15 +260,14 @@ public Iterator>> iterator() { unfiltered.asMap().entrySet().iterator(); @Override - @CheckForNull - protected Entry> computeNext() { + protected @Nullable Entry> computeNext() { while (backingIterator.hasNext()) { Entry> entry = backingIterator.next(); K key = entry.getKey(); Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); if (!collection.isEmpty()) { - return Maps.immutableEntry(key, collection); + return immutableEntry(key, collection); } } return endOfData(); @@ -303,7 +302,12 @@ class ValuesImpl extends Maps.Values> { } @Override - public boolean remove(@CheckForNull Object o) { + /* + * For discussion of equality in Multimap value collections, see the suppression for + * UndefinedEquals in AbstractMapBasedMultimap. + */ + @SuppressWarnings("UndefinedEquals") + public boolean remove(@Nullable Object o) { if (o instanceof Collection) { Collection c = (Collection) o; Iterator>> entryIterator = @@ -352,7 +356,7 @@ class Keys extends Multimaps.Keys { } @Override - public int remove(@CheckForNull Object key, int occurrences) { + public int remove(@Nullable Object key, int occurrences) { checkNonnegative(occurrences, "occurrences"); if (occurrences == 0) { return count(key); @@ -400,7 +404,7 @@ private boolean removeEntriesIf(Predicate> predicate) return FilteredEntryMultimap.this.removeEntriesIf( (Map.Entry> entry) -> predicate.apply( - Multisets.immutableEntry(entry.getKey(), entry.getValue().size()))); + Multisets.immutableEntry(entry.getKey(), entry.getValue().size()))); } @Override diff --git a/android/guava/src/com/google/common/collect/FilteredEntrySetMultimap.java b/android/guava/src/com/google/common/collect/FilteredEntrySetMultimap.java index 20413f8c4096..c01894aad5ab 100644 --- a/android/guava/src/com/google/common/collect/FilteredEntrySetMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredEntrySetMultimap.java @@ -20,8 +20,7 @@ import com.google.common.base.Predicate; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimaps#filterEntries(SetMultimap, Predicate)}. @@ -29,7 +28,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class FilteredEntrySetMultimap extends FilteredEntryMultimap implements FilteredSetMultimap { @@ -48,7 +46,7 @@ public Set get(@ParametricNullness K key) { } @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { return (Set) super.removeAll(key); } diff --git a/android/guava/src/com/google/common/collect/FilteredKeyListMultimap.java b/android/guava/src/com/google/common/collect/FilteredKeyListMultimap.java index c82c8daf770d..d7b66a71994b 100644 --- a/android/guava/src/com/google/common/collect/FilteredKeyListMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredKeyListMultimap.java @@ -19,8 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Predicate; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimaps#filterKeys(ListMultimap, Predicate)}. @@ -28,7 +27,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class FilteredKeyListMultimap extends FilteredKeyMultimap implements ListMultimap { FilteredKeyListMultimap(ListMultimap unfiltered, Predicate keyPredicate) { @@ -46,7 +44,7 @@ public List get(@ParametricNullness K key) { } @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { return (List) super.removeAll(key); } diff --git a/android/guava/src/com/google/common/collect/FilteredKeyMultimap.java b/android/guava/src/com/google/common/collect/FilteredKeyMultimap.java index 68fad75b1445..1ca1aa0371e7 100644 --- a/android/guava/src/com/google/common/collect/FilteredKeyMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredKeyMultimap.java @@ -24,14 +24,12 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.j2objc.annotations.WeakOuter; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. @@ -39,7 +37,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault class FilteredKeyMultimap extends AbstractMultimap implements FilteredMultimap { final Multimap unfiltered; @@ -70,7 +67,7 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { if (unfiltered.containsKey(key)) { @SuppressWarnings("unchecked") // k is equal to a K, if not one itself K k = (K) key; @@ -80,7 +77,7 @@ public boolean containsKey(@CheckForNull Object key) { } @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); } @@ -134,7 +131,7 @@ public boolean addAll(Collection collection) { @Override protected Set delegate() { - return Collections.emptySet(); + return emptySet(); } } @@ -174,7 +171,7 @@ public boolean addAll(int index, Collection elements) { @Override protected List delegate() { - return Collections.emptyList(); + return emptyList(); } } @@ -197,7 +194,7 @@ protected Collection> delegate() { @Override @SuppressWarnings("unchecked") - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o instanceof Entry) { Entry entry = (Entry) o; if (unfiltered.containsKey(entry.getKey()) diff --git a/android/guava/src/com/google/common/collect/FilteredKeySetMultimap.java b/android/guava/src/com/google/common/collect/FilteredKeySetMultimap.java index e492a5c1dea9..58c473c9846b 100644 --- a/android/guava/src/com/google/common/collect/FilteredKeySetMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredKeySetMultimap.java @@ -20,8 +20,7 @@ import com.google.common.base.Predicate; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimaps#filterKeys(SetMultimap, Predicate)}. @@ -29,7 +28,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class FilteredKeySetMultimap extends FilteredKeyMultimap implements FilteredSetMultimap { @@ -48,7 +46,7 @@ public Set get(@ParametricNullness K key) { } @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { return (Set) super.removeAll(key); } @@ -74,7 +72,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { return Sets.equalsImpl(this, o); } } diff --git a/android/guava/src/com/google/common/collect/FilteredMultimap.java b/android/guava/src/com/google/common/collect/FilteredMultimap.java index 4e1fa066f80b..173302e29b5f 100644 --- a/android/guava/src/com/google/common/collect/FilteredMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredMultimap.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Predicate; import java.util.Map.Entry; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An interface for all filtered multimap types. @@ -27,7 +27,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault interface FilteredMultimap extends Multimap { Multimap unfiltered(); diff --git a/android/guava/src/com/google/common/collect/FilteredMultimapValues.java b/android/guava/src/com/google/common/collect/FilteredMultimapValues.java index ecbfab2a1d11..b39dd9a8802f 100644 --- a/android/guava/src/com/google/common/collect/FilteredMultimapValues.java +++ b/android/guava/src/com/google/common/collect/FilteredMultimapValues.java @@ -15,18 +15,20 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Maps.valuePredicateOnEntries; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; import com.google.common.base.Predicate; -import com.google.common.base.Predicates; import com.google.j2objc.annotations.Weak; import java.util.AbstractCollection; import java.util.Collection; import java.util.Iterator; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation for {@link FilteredMultimap#values()}. @@ -34,7 +36,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class FilteredMultimapValues extends AbstractCollection { @Weak private final FilteredMultimap multimap; @@ -49,7 +50,7 @@ public Iterator iterator() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return multimap.containsValue(o); } @@ -59,7 +60,7 @@ public int size() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { Predicate> entryPredicate = multimap.entryPredicate(); for (Iterator> unfilteredItr = multimap.unfiltered().entries().iterator(); unfilteredItr.hasNext(); ) { @@ -76,19 +77,14 @@ public boolean remove(@CheckForNull Object o) { public boolean removeAll(Collection c) { return Iterables.removeIf( multimap.unfiltered().entries(), - // explicit > is required to build with JDK6 - Predicates.>and( - multimap.entryPredicate(), Maps.valuePredicateOnEntries(Predicates.in(c)))); + and(multimap.entryPredicate(), valuePredicateOnEntries(in(c)))); } @Override public boolean retainAll(Collection c) { return Iterables.removeIf( multimap.unfiltered().entries(), - // explicit > is required to build with JDK6 - Predicates.>and( - multimap.entryPredicate(), - Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c))))); + and(multimap.entryPredicate(), valuePredicateOnEntries(not(in(c))))); } @Override diff --git a/android/guava/src/com/google/common/collect/FilteredSetMultimap.java b/android/guava/src/com/google/common/collect/FilteredSetMultimap.java index 8e2ff7c0c1a6..7737377ea202 100644 --- a/android/guava/src/com/google/common/collect/FilteredSetMultimap.java +++ b/android/guava/src/com/google/common/collect/FilteredSetMultimap.java @@ -17,7 +17,7 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A supertype for filtered {@link SetMultimap} implementations. @@ -25,7 +25,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault interface FilteredSetMultimap extends FilteredMultimap, SetMultimap { @Override diff --git a/android/guava/src/com/google/common/collect/FluentIterable.java b/android/guava/src/com/google/common/collect/FluentIterable.java index 72401d752ab9..72eb54afe305 100644 --- a/android/guava/src/com/google/common/collect/FluentIterable.java +++ b/android/guava/src/com/google/common/collect/FluentIterable.java @@ -16,7 +16,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; @@ -32,8 +31,9 @@ import java.util.Iterator; import java.util.List; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Stream; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * An expanded {@code Iterable} API, providing functionality similar to Java 8's powerful implements Iterable { // We store 'iterable' and use it instead of 'this' to allow Iterables to perform instanceof // checks on the _original_ iterable when FluentIterable.from is used. @@ -141,7 +140,7 @@ private Iterable getDelegate() { *

    {@code Stream} equivalent: {@code iterable.stream()} if {@code iterable} is a {@link * Collection}; {@code StreamSupport.stream(iterable.spliterator(), false)} otherwise. */ - public static FluentIterable from(final Iterable iterable) { + public static FluentIterable from(Iterable iterable) { return (iterable instanceof FluentIterable) ? (FluentIterable) iterable : new FluentIterable(iterable) { @@ -162,7 +161,6 @@ public Iterator iterator() { * * @since 20.0 (since 18.0 as an overload of {@code of}) */ - @Beta public static FluentIterable from(E[] elements) { return from(Arrays.asList(elements)); } @@ -195,7 +193,6 @@ public Iterator iterator() { * * @since 20.0 */ - @Beta public static FluentIterable concat( Iterable a, Iterable b) { return concatNoDefensiveCopy(a, b); @@ -214,7 +211,6 @@ public Iterator iterator() { * * @since 20.0 */ - @Beta public static FluentIterable concat( Iterable a, Iterable b, Iterable c) { return concatNoDefensiveCopy(a, b, c); @@ -234,7 +230,6 @@ public Iterator iterator() { * * @since 20.0 */ - @Beta public static FluentIterable concat( Iterable a, Iterable b, @@ -258,7 +253,7 @@ public Iterator iterator() { * @throws NullPointerException if any of the provided iterables is {@code null} * @since 20.0 */ - @Beta + @SafeVarargs public static FluentIterable concat( Iterable... inputs) { return concatNoDefensiveCopy(Arrays.copyOf(inputs, inputs.length)); @@ -278,21 +273,20 @@ public Iterator iterator() { * * @since 20.0 */ - @Beta public static FluentIterable concat( - final Iterable> inputs) { + Iterable> inputs) { checkNotNull(inputs); return new FluentIterable() { @Override public Iterator iterator() { - return Iterators.concat(Iterators.transform(inputs.iterator(), Iterables.toIterator())); + return Iterators.concat(Iterators.transform(inputs.iterator(), Iterable::iterator)); } }; } /** Concatenates a varargs array of iterables without making a defensive copy of the array. */ private static FluentIterable concatNoDefensiveCopy( - final Iterable... inputs) { + Iterable... inputs) { for (Iterable input : inputs) { checkNotNull(input); } @@ -318,7 +312,6 @@ public Iterator get(int i) { * * @since 20.0 */ - @Beta public static FluentIterable of() { return FluentIterable.from(Collections.emptyList()); } @@ -331,7 +324,6 @@ public Iterator get(int i) { * * @since 20.0 */ - @Beta public static FluentIterable of( @ParametricNullness E element, E... elements) { return from(Lists.asList(element, elements)); @@ -364,7 +356,7 @@ public final int size() { * *

    {@code Stream} equivalent: {@code stream.anyMatch(Predicate.isEqual(target))}. */ - public final boolean contains(@CheckForNull Object target) { + public final boolean contains(@Nullable Object target) { return Iterables.contains(getDelegate(), target); } @@ -400,7 +392,6 @@ public final FluentIterable cycle() { * * @since 18.0 */ - @Beta public final FluentIterable append(Iterable other) { return FluentIterable.concat(getDelegate(), other); } @@ -413,7 +404,6 @@ public final FluentIterable append(Iterable other) { * * @since 18.0 */ - @Beta public final FluentIterable append(E... elements) { return FluentIterable.concat(getDelegate(), Arrays.asList(elements)); } @@ -474,9 +464,9 @@ public final boolean allMatch(Predicate predicate) { * *

    {@code Stream} equivalent: {@code stream.filter(predicate).findFirst()}. */ - @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final Optional firstMatch(Predicate predicate) { - return Iterables.tryFind(getDelegate(), predicate); + public final Optional<@NonNull E> firstMatch(Predicate predicate) { + // Unsafe, but we can't do much about it now. + return Iterables.<@NonNull E>tryFind((Iterable<@NonNull E>) getDelegate(), predicate); } /** @@ -523,9 +513,9 @@ public final Optional firstMatch(Predicate predicate) { * iterator().next()} or {@link Iterables#getFirst} instead. */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final Optional first() { + public final Optional<@NonNull E> first() { Iterator iterator = getDelegate().iterator(); - return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.absent(); + return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.absent(); } /** @@ -540,7 +530,7 @@ public final Optional first() { * Iterables#getLast} instead. */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final Optional last() { + public final Optional<@NonNull E> last() { // Iterables#getLast was inlined here so we don't have to throw/catch a NSEE // TODO(kevinb): Support a concurrently modified collection? @@ -580,7 +570,7 @@ public final Optional last() { * iterable skips all of its elements. * *

    Modifications to this fluent iterable before a call to {@code iterator()} are reflected in - * the returned fluent iterable. That is, the its iterator skips the first {@code numberToSkip} + * the returned fluent iterable. That is, the iterator skips the first {@code numberToSkip} * elements that exist when the iterator is created, not when {@code skip()} is called. * *

    The returned fluent iterable's iterator supports {@code remove()} if the {@code Iterator} of @@ -630,8 +620,8 @@ public final boolean isEmpty() { * @since 14.0 (since 12.0 as {@code toImmutableList()}). */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableList toList() { - return ImmutableList.copyOf(getDelegate()); + public final ImmutableList<@NonNull E> toList() { + return ImmutableList.copyOf((Iterable<@NonNull E>) getDelegate()); } /** @@ -648,8 +638,8 @@ public final ImmutableList toList() { * @since 14.0 (since 13.0 as {@code toSortedImmutableList()}). */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableList toSortedList(Comparator comparator) { - return Ordering.from(comparator).immutableSortedCopy(getDelegate()); + public final ImmutableList<@NonNull E> toSortedList(Comparator comparator) { + return Ordering.from(comparator).immutableSortedCopy((Iterable<@NonNull E>) getDelegate()); } /** @@ -663,8 +653,8 @@ public final ImmutableList toSortedList(Comparator comparator) { * @since 14.0 (since 12.0 as {@code toImmutableSet()}). */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableSet toSet() { - return ImmutableSet.copyOf(getDelegate()); + public final ImmutableSet<@NonNull E> toSet() { + return ImmutableSet.copyOf((Iterable<@NonNull E>) getDelegate()); } /** @@ -682,8 +672,8 @@ public final ImmutableSet toSet() { * @since 14.0 (since 12.0 as {@code toImmutableSortedSet()}). */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableSortedSet toSortedSet(Comparator comparator) { - return ImmutableSortedSet.copyOf(comparator, getDelegate()); + public final ImmutableSortedSet<@NonNull E> toSortedSet(Comparator comparator) { + return ImmutableSortedSet.copyOf(comparator, (Iterable<@NonNull E>) getDelegate()); } /** @@ -696,8 +686,8 @@ public final ImmutableSortedSet toSortedSet(Comparator comparator) * @since 19.0 */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableMultiset toMultiset() { - return ImmutableMultiset.copyOf(getDelegate()); + public final ImmutableMultiset<@NonNull E> toMultiset() { + return ImmutableMultiset.copyOf((Iterable<@NonNull E>) getDelegate()); } /** @@ -718,8 +708,8 @@ public final ImmutableMultiset toMultiset() { * @since 14.0 */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableMap toMap(Function valueFunction) { - return Maps.toMap(getDelegate(), valueFunction); + public final ImmutableMap<@NonNull E, V> toMap(Function valueFunction) { + return Maps.toMap((Iterable<@NonNull E>) getDelegate(), valueFunction); } /** @@ -741,8 +731,8 @@ public final ImmutableMap toMap(Function valueFunction) * @since 14.0 */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableListMultimap index(Function keyFunction) { - return Multimaps.index(getDelegate(), keyFunction); + public final ImmutableListMultimap index(Function keyFunction) { + return Multimaps.index((Iterable<@NonNull E>) getDelegate(), keyFunction); } /** @@ -778,8 +768,8 @@ public final ImmutableListMultimap index(Function keyFun * @since 14.0 */ @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. - public final ImmutableMap uniqueIndex(Function keyFunction) { - return Maps.uniqueIndex(getDelegate(), keyFunction); + public final ImmutableMap uniqueIndex(Function keyFunction) { + return Maps.uniqueIndex((Iterable<@NonNull E>) getDelegate(), keyFunction); } /** @@ -795,16 +785,8 @@ public final ImmutableMap uniqueIndex(Function keyFuncti * copied */ @GwtIncompatible // Array.newArray(Class, int) - /* - * Both the declaration of our Class parameter and its usage in a call to Iterables.toArray - * produce a nullness error: E may be a nullable type, and our nullness checker has Class's type - * parameter bounded to non-null types. To avoid that, we'd use Class<@Nonnull E> if we could. - * (Granted, this is only one of many nullness-checking problems that arise from letting - * FluentIterable support null elements, and most of the other produce outright unsoundness.) - */ - @SuppressWarnings("nullness") - public final @Nullable E[] toArray(Class type) { - return Iterables.toArray(getDelegate(), type); + public final E[] toArray(Class<@NonNull E> type) { + return Iterables.toArray(getDelegate(), type); } /** @@ -842,7 +824,6 @@ public final > C copyInto(C collection) { * * @since 18.0 */ - @Beta public final String join(Joiner joiner) { return joiner.join(this); } diff --git a/android/guava/src/com/google/common/collect/ForwardingBlockingDeque.java b/android/guava/src/com/google/common/collect/ForwardingBlockingDeque.java index 49d4bcf6c232..4be9c3072dff 100644 --- a/android/guava/src/com/google/common/collect/ForwardingBlockingDeque.java +++ b/android/guava/src/com/google/common/collect/ForwardingBlockingDeque.java @@ -17,10 +17,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Collection; import java.util.concurrent.BlockingDeque; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. @@ -46,8 +47,8 @@ * com.google.common.util.concurrent.ForwardingBlockingDeque} instead. */ @Deprecated +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingBlockingDeque extends ForwardingDeque implements BlockingDeque { @@ -93,14 +94,12 @@ public E takeLast() throws InterruptedException { } @Override - @CheckForNull - public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { return delegate().pollFirst(timeout, unit); } @Override - @CheckForNull - public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E pollLast(long timeout, TimeUnit unit) throws InterruptedException { return delegate().pollLast(timeout, unit); } @@ -120,8 +119,7 @@ public E take() throws InterruptedException { } @Override - @CheckForNull - public E poll(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(long timeout, TimeUnit unit) throws InterruptedException { return delegate().poll(timeout, unit); } diff --git a/android/guava/src/com/google/common/collect/ForwardingCollection.java b/android/guava/src/com/google/common/collect/ForwardingCollection.java index ca1edc118303..45cb5a86c0f8 100644 --- a/android/guava/src/com/google/common/collect/ForwardingCollection.java +++ b/android/guava/src/com/google/common/collect/ForwardingCollection.java @@ -21,8 +21,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A collection which forwards all its method calls to another collection. Subclasses should @@ -47,7 +46,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingCollection extends ForwardingObject implements Collection { // TODO(lowasser): identify places where thread safety is actually lost @@ -80,7 +78,7 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return delegate().contains(object); } @@ -92,7 +90,7 @@ public boolean add(@ParametricNullness E element) { @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { return delegate().remove(object); } @@ -137,7 +135,7 @@ public void clear() { * * @since 7.0 */ - protected boolean standardContains(@CheckForNull Object object) { + protected boolean standardContains(@Nullable Object object) { return Iterators.contains(iterator(), object); } @@ -169,7 +167,7 @@ protected boolean standardAddAll(Collection collection) { * * @since 7.0 */ - protected boolean standardRemove(@CheckForNull Object object) { + protected boolean standardRemove(@Nullable Object object) { Iterator iterator = iterator(); while (iterator.hasNext()) { if (Objects.equal(iterator.next(), object)) { diff --git a/android/guava/src/com/google/common/collect/ForwardingConcurrentMap.java b/android/guava/src/com/google/common/collect/ForwardingConcurrentMap.java index b662b0774a0f..51eb005641f2 100644 --- a/android/guava/src/com/google/common/collect/ForwardingConcurrentMap.java +++ b/android/guava/src/com/google/common/collect/ForwardingConcurrentMap.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.ConcurrentMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A concurrent map which forwards all its method calls to another concurrent map. Subclasses should @@ -37,7 +37,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingConcurrentMap extends ForwardingMap implements ConcurrentMap { @@ -49,26 +48,25 @@ protected ForwardingConcurrentMap() {} @CanIgnoreReturnValue @Override - @CheckForNull - public V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(K key, V value) { return delegate().putIfAbsent(key, value); } @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { return delegate().remove(key, value); } @CanIgnoreReturnValue @Override - @CheckForNull - public V replace(K key, V value) { + public @Nullable V replace(K key, V value) { return delegate().replace(key, value); } @CanIgnoreReturnValue @Override + @SuppressWarnings("nullness") // https://github.com/jspecify/jdk/issues/118 public boolean replace(K key, V oldValue, V newValue) { return delegate().replace(key, oldValue, newValue); } diff --git a/android/guava/src/com/google/common/collect/ForwardingDeque.java b/android/guava/src/com/google/common/collect/ForwardingDeque.java index 571535cab917..33663142d773 100644 --- a/android/guava/src/com/google/common/collect/ForwardingDeque.java +++ b/android/guava/src/com/google/common/collect/ForwardingDeque.java @@ -17,11 +17,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Deque; import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A deque which forwards all its method calls to another deque. Subclasses should override one or @@ -40,8 +40,8 @@ * @author Kurt Alfred Kluever * @since 12.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingDeque extends ForwardingQueue implements Deque { @@ -91,28 +91,24 @@ public boolean offerLast(@ParametricNullness E e) { } @Override - @CheckForNull - public E peekFirst() { + public @Nullable E peekFirst() { return delegate().peekFirst(); } @Override - @CheckForNull - public E peekLast() { + public @Nullable E peekLast() { return delegate().peekLast(); } @CanIgnoreReturnValue // TODO(cpovirk): Consider removing this? @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return delegate().pollFirst(); } @CanIgnoreReturnValue // TODO(cpovirk): Consider removing this? @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return delegate().pollLast(); } @@ -144,13 +140,13 @@ public E removeLast() { @CanIgnoreReturnValue @Override - public boolean removeFirstOccurrence(@CheckForNull Object o) { + public boolean removeFirstOccurrence(@Nullable Object o) { return delegate().removeFirstOccurrence(o); } @CanIgnoreReturnValue @Override - public boolean removeLastOccurrence(@CheckForNull Object o) { + public boolean removeLastOccurrence(@Nullable Object o) { return delegate().removeLastOccurrence(o); } } diff --git a/android/guava/src/com/google/common/collect/ForwardingImmutableCollection.java b/android/guava/src/com/google/common/collect/ForwardingImmutableCollection.java index 043fe5863593..c0b9c5e54d74 100644 --- a/android/guava/src/com/google/common/collect/ForwardingImmutableCollection.java +++ b/android/guava/src/com/google/common/collect/ForwardingImmutableCollection.java @@ -24,7 +24,6 @@ * @author Hayward Chan */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault class ForwardingImmutableCollection { private ForwardingImmutableCollection() {} } diff --git a/android/guava/src/com/google/common/collect/ForwardingImmutableList.java b/android/guava/src/com/google/common/collect/ForwardingImmutableList.java index bd5480d589c4..2b9092ea4c93 100644 --- a/android/guava/src/com/google/common/collect/ForwardingImmutableList.java +++ b/android/guava/src/com/google/common/collect/ForwardingImmutableList.java @@ -24,7 +24,6 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ForwardingImmutableList { private ForwardingImmutableList() {} } diff --git a/android/guava/src/com/google/common/collect/ForwardingImmutableMap.java b/android/guava/src/com/google/common/collect/ForwardingImmutableMap.java index 22cc9ff8711c..a36715743f0a 100644 --- a/android/guava/src/com/google/common/collect/ForwardingImmutableMap.java +++ b/android/guava/src/com/google/common/collect/ForwardingImmutableMap.java @@ -24,7 +24,6 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ForwardingImmutableMap { private ForwardingImmutableMap() {} } diff --git a/android/guava/src/com/google/common/collect/ForwardingImmutableSet.java b/android/guava/src/com/google/common/collect/ForwardingImmutableSet.java index 047d5fd32216..c7d7bf6d778b 100644 --- a/android/guava/src/com/google/common/collect/ForwardingImmutableSet.java +++ b/android/guava/src/com/google/common/collect/ForwardingImmutableSet.java @@ -24,7 +24,6 @@ * @author Chris Povirk */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ForwardingImmutableSet { private ForwardingImmutableSet() {} } diff --git a/android/guava/src/com/google/common/collect/ForwardingIterator.java b/android/guava/src/com/google/common/collect/ForwardingIterator.java index 1f5a8f110eaf..47449aa6207a 100644 --- a/android/guava/src/com/google/common/collect/ForwardingIterator.java +++ b/android/guava/src/com/google/common/collect/ForwardingIterator.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An iterator which forwards all its method calls to another iterator. Subclasses should override @@ -37,7 +37,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingIterator extends ForwardingObject implements Iterator { diff --git a/android/guava/src/com/google/common/collect/ForwardingList.java b/android/guava/src/com/google/common/collect/ForwardingList.java index 4b4551e5e367..eae6d3e8a06b 100644 --- a/android/guava/src/com/google/common/collect/ForwardingList.java +++ b/android/guava/src/com/google/common/collect/ForwardingList.java @@ -16,15 +16,13 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A list which forwards all its method calls to another list. Subclasses should override one or @@ -52,7 +50,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingList extends ForwardingCollection implements List { // TODO(lowasser): identify places where thread safety is actually lost @@ -81,12 +78,12 @@ public E get(int index) { } @Override - public int indexOf(@CheckForNull Object element) { + public int indexOf(@Nullable Object element) { return delegate().indexOf(element); } @Override - public int lastIndexOf(@CheckForNull Object element) { + public int lastIndexOf(@Nullable Object element) { return delegate().lastIndexOf(element); } @@ -120,7 +117,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return object == this || delegate().equals(object); } @@ -159,7 +156,7 @@ protected boolean standardAddAll(int index, Iterable elements) { * * @since 7.0 */ - protected int standardIndexOf(@CheckForNull Object element) { + protected int standardIndexOf(@Nullable Object element) { return Lists.indexOfImpl(this, element); } @@ -170,7 +167,7 @@ protected int standardIndexOf(@CheckForNull Object element) { * * @since 7.0 */ - protected int standardLastIndexOf(@CheckForNull Object element) { + protected int standardLastIndexOf(@Nullable Object element) { return Lists.lastIndexOfImpl(this, element); } @@ -204,7 +201,6 @@ protected ListIterator standardListIterator() { * * @since 7.0 */ - @Beta protected ListIterator standardListIterator(int start) { return Lists.listIteratorImpl(this, start); } @@ -215,7 +211,6 @@ protected ListIterator standardListIterator(int start) { * * @since 7.0 */ - @Beta protected List standardSubList(int fromIndex, int toIndex) { return Lists.subListImpl(this, fromIndex, toIndex); } @@ -227,8 +222,7 @@ protected List standardSubList(int fromIndex, int toIndex) { * * @since 7.0 */ - @Beta - protected boolean standardEquals(@CheckForNull Object object) { + protected boolean standardEquals(@Nullable Object object) { return Lists.equalsImpl(this, object); } @@ -239,7 +233,6 @@ protected boolean standardEquals(@CheckForNull Object object) { * * @since 7.0 */ - @Beta protected int standardHashCode() { return Lists.hashCodeImpl(this); } diff --git a/android/guava/src/com/google/common/collect/ForwardingListIterator.java b/android/guava/src/com/google/common/collect/ForwardingListIterator.java index a2ac32b6fad9..5b2518c23c09 100644 --- a/android/guava/src/com/google/common/collect/ForwardingListIterator.java +++ b/android/guava/src/com/google/common/collect/ForwardingListIterator.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ListIterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A list iterator which forwards all its method calls to another list iterator. Subclasses should @@ -37,7 +37,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingListIterator extends ForwardingIterator implements ListIterator { diff --git a/android/guava/src/com/google/common/collect/ForwardingListMultimap.java b/android/guava/src/com/google/common/collect/ForwardingListMultimap.java index 11779c0bb826..5ba9b978d160 100644 --- a/android/guava/src/com/google/common/collect/ForwardingListMultimap.java +++ b/android/guava/src/com/google/common/collect/ForwardingListMultimap.java @@ -19,8 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A list multimap which forwards all its method calls to another list multimap. Subclasses should @@ -35,7 +34,6 @@ * @since 3.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingListMultimap extends ForwardingMultimap implements ListMultimap { @@ -52,7 +50,7 @@ public List get(@ParametricNullness K key) { @CanIgnoreReturnValue @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { return delegate().removeAll(key); } diff --git a/android/guava/src/com/google/common/collect/ForwardingMap.java b/android/guava/src/com/google/common/collect/ForwardingMap.java index 315a4fabca3d..b1d8bfc866ef 100644 --- a/android/guava/src/com/google/common/collect/ForwardingMap.java +++ b/android/guava/src/com/google/common/collect/ForwardingMap.java @@ -16,7 +16,6 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -24,8 +23,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A map which forwards all its method calls to another map. Subclasses should override one or more @@ -56,7 +54,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingMap extends ForwardingObject implements Map { // TODO(lowasser): identify places where thread safety is actually lost @@ -79,8 +76,7 @@ public boolean isEmpty() { @CanIgnoreReturnValue @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { return delegate().remove(key); } @@ -90,25 +86,23 @@ public void clear() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return delegate().containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return delegate().containsValue(value); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { return delegate().get(key); } @CanIgnoreReturnValue @Override - @CheckForNull - public V put(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { return delegate().put(key, value); } @@ -133,7 +127,7 @@ public Set> entrySet() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return object == this || delegate().equals(object); } @@ -163,9 +157,7 @@ protected void standardPutAll(Map map) { * * @since 7.0 */ - @Beta - @CheckForNull - protected V standardRemove(@CheckForNull Object key) { + protected @Nullable V standardRemove(@Nullable Object key) { Iterator> entryIterator = entrySet().iterator(); while (entryIterator.hasNext()) { Entry entry = entryIterator.next(); @@ -198,7 +190,6 @@ protected void standardClear() { * * @since 10.0 */ - @Beta protected class StandardKeySet extends Maps.KeySet { /** Constructor for use by subclasses. */ public StandardKeySet() { @@ -213,8 +204,7 @@ public StandardKeySet() { * * @since 7.0 */ - @Beta - protected boolean standardContainsKey(@CheckForNull Object key) { + protected boolean standardContainsKey(@Nullable Object key) { return Maps.containsKeyImpl(this, key); } @@ -227,7 +217,6 @@ protected boolean standardContainsKey(@CheckForNull Object key) { * * @since 10.0 */ - @Beta protected class StandardValues extends Maps.Values { /** Constructor for use by subclasses. */ public StandardValues() { @@ -242,7 +231,7 @@ public StandardValues() { * * @since 7.0 */ - protected boolean standardContainsValue(@CheckForNull Object value) { + protected boolean standardContainsValue(@Nullable Object value) { return Maps.containsValueImpl(this, value); } @@ -255,10 +244,9 @@ protected boolean standardContainsValue(@CheckForNull Object value) { * * @since 10.0 */ - @Beta protected abstract class StandardEntrySet extends Maps.EntrySet { /** Constructor for use by subclasses. */ - public StandardEntrySet() {} + protected StandardEntrySet() {} @Override Map map() { @@ -284,7 +272,7 @@ protected boolean standardIsEmpty() { * * @since 7.0 */ - protected boolean standardEquals(@CheckForNull Object object) { + protected boolean standardEquals(@Nullable Object object) { return Maps.equalsImpl(this, object); } diff --git a/android/guava/src/com/google/common/collect/ForwardingMapEntry.java b/android/guava/src/com/google/common/collect/ForwardingMapEntry.java index 6816ccbbefce..001af42de011 100644 --- a/android/guava/src/com/google/common/collect/ForwardingMapEntry.java +++ b/android/guava/src/com/google/common/collect/ForwardingMapEntry.java @@ -16,13 +16,12 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A map entry which forwards all its method calls to another map entry. Subclasses should override @@ -48,7 +47,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingMapEntry extends ForwardingObject implements Map.Entry { // TODO(lowasser): identify places where thread safety is actually lost @@ -73,12 +71,13 @@ public V getValue() { @Override @ParametricNullness + @CanIgnoreReturnValue public V setValue(@ParametricNullness V value) { return delegate().setValue(value); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return delegate().equals(object); } @@ -94,7 +93,7 @@ public int hashCode() { * * @since 7.0 */ - protected boolean standardEquals(@CheckForNull Object object) { + protected boolean standardEquals(@Nullable Object object) { if (object instanceof Entry) { Entry that = (Entry) object; return Objects.equal(this.getKey(), that.getKey()) @@ -123,7 +122,6 @@ protected int standardHashCode() { * * @since 7.0 */ - @Beta protected String standardToString() { return getKey() + "=" + getValue(); } diff --git a/android/guava/src/com/google/common/collect/ForwardingMultimap.java b/android/guava/src/com/google/common/collect/ForwardingMultimap.java index a3db0618ed80..817d91c032f8 100644 --- a/android/guava/src/com/google/common/collect/ForwardingMultimap.java +++ b/android/guava/src/com/google/common/collect/ForwardingMultimap.java @@ -22,8 +22,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A multimap which forwards all its method calls to another multimap. Subclasses should override @@ -38,7 +37,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingMultimap extends ForwardingObject implements Multimap { @@ -59,17 +57,17 @@ public void clear() { } @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { return delegate().containsEntry(key, value); } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return delegate().containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return delegate().containsValue(value); } @@ -118,13 +116,13 @@ public boolean putAll(Multimap multimap) { @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { return delegate().remove(key, value); } @CanIgnoreReturnValue @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { return delegate().removeAll(key); } @@ -145,7 +143,9 @@ public Collection values() { } @Override - public boolean equals(@CheckForNull Object object) { + // A forwarding implementation can't do any better than the underlying object. + @SuppressWarnings("UndefinedEquals") + public boolean equals(@Nullable Object object) { return object == this || delegate().equals(object); } diff --git a/android/guava/src/com/google/common/collect/ForwardingMultiset.java b/android/guava/src/com/google/common/collect/ForwardingMultiset.java index 857b9aec7bbe..5c59b5ab4302 100644 --- a/android/guava/src/com/google/common/collect/ForwardingMultiset.java +++ b/android/guava/src/com/google/common/collect/ForwardingMultiset.java @@ -16,15 +16,13 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A multiset which forwards all its method calls to another multiset. Subclasses should override @@ -49,7 +47,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingMultiset extends ForwardingCollection implements Multiset { @@ -60,7 +57,7 @@ protected ForwardingMultiset() {} protected abstract Multiset delegate(); @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { return delegate().count(element); } @@ -72,7 +69,7 @@ public int add(@ParametricNullness E element, int occurrences) { @CanIgnoreReturnValue @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { return delegate().remove(element, occurrences); } @@ -87,7 +84,7 @@ public Set> entrySet() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return object == this || delegate().equals(object); } @@ -115,7 +112,7 @@ public boolean setCount(@ParametricNullness E element, int oldCount, int newCoun * @since 7.0 */ @Override - protected boolean standardContains(@CheckForNull Object object) { + protected boolean standardContains(@Nullable Object object) { return count(object) > 0; } @@ -138,8 +135,7 @@ protected void standardClear() { * * @since 7.0 */ - @Beta - protected int standardCount(@CheckForNull Object object) { + protected int standardCount(@Nullable Object object) { for (Entry entry : this.entrySet()) { if (Objects.equal(entry.getElement(), object)) { return entry.getCount(); @@ -167,7 +163,6 @@ protected boolean standardAdd(@ParametricNullness E element) { * * @since 7.0 */ - @Beta @Override protected boolean standardAddAll(Collection elementsToAdd) { return Multisets.addAllImpl(this, elementsToAdd); @@ -181,7 +176,7 @@ protected boolean standardAddAll(Collection elementsToAdd) { * @since 7.0 */ @Override - protected boolean standardRemove(@CheckForNull Object element) { + protected boolean standardRemove(@Nullable Object element) { return remove(element, 1) > 0; } @@ -243,7 +238,6 @@ protected boolean standardSetCount(@ParametricNullness E element, int oldCount, * * @since 10.0 */ - @Beta protected class StandardElementSet extends Multisets.ElementSet { /** Constructor for use by subclasses. */ public StandardElementSet() {} @@ -288,7 +282,7 @@ protected int standardSize() { * * @since 7.0 */ - protected boolean standardEquals(@CheckForNull Object object) { + protected boolean standardEquals(@Nullable Object object) { return Multisets.equalsImpl(this, object); } diff --git a/android/guava/src/com/google/common/collect/ForwardingNavigableMap.java b/android/guava/src/com/google/common/collect/ForwardingNavigableMap.java index e0e1c39e8ef3..35bfda60fc01 100644 --- a/android/guava/src/com/google/common/collect/ForwardingNavigableMap.java +++ b/android/guava/src/com/google/common/collect/ForwardingNavigableMap.java @@ -18,15 +18,13 @@ import static com.google.common.collect.Maps.keyOrNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import java.util.Iterator; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.SortedMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A navigable map which forwards all its method calls to another navigable map. Subclasses should @@ -55,7 +53,6 @@ * @since 12.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingNavigableMap extends ForwardingSortedMap implements NavigableMap { @@ -66,8 +63,7 @@ protected ForwardingNavigableMap() {} protected abstract NavigableMap delegate(); @Override - @CheckForNull - public Entry lowerEntry(@ParametricNullness K key) { + public @Nullable Entry lowerEntry(@ParametricNullness K key) { return delegate().lowerEntry(key); } @@ -76,14 +72,12 @@ public Entry lowerEntry(@ParametricNullness K key) { * #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override {@code * lowerEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardLowerEntry(@ParametricNullness K key) { + protected @Nullable Entry standardLowerEntry(@ParametricNullness K key) { return headMap(key, false).lastEntry(); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return delegate().lowerKey(key); } @@ -92,14 +86,12 @@ public K lowerKey(@ParametricNullness K key) { * {@link #lowerEntry}, you may wish to override {@code lowerKey} to forward to this * implementation. */ - @CheckForNull - protected K standardLowerKey(@ParametricNullness K key) { + protected @Nullable K standardLowerKey(@ParametricNullness K key) { return keyOrNull(lowerEntry(key)); } @Override - @CheckForNull - public Entry floorEntry(@ParametricNullness K key) { + public @Nullable Entry floorEntry(@ParametricNullness K key) { return delegate().floorEntry(key); } @@ -108,14 +100,12 @@ public Entry floorEntry(@ParametricNullness K key) { * #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override {@code * floorEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardFloorEntry(@ParametricNullness K key) { + protected @Nullable Entry standardFloorEntry(@ParametricNullness K key) { return headMap(key, true).lastEntry(); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return delegate().floorKey(key); } @@ -124,14 +114,12 @@ public K floorKey(@ParametricNullness K key) { * {@code floorEntry}, you may wish to override {@code floorKey} to forward to this * implementation. */ - @CheckForNull - protected K standardFloorKey(@ParametricNullness K key) { + protected @Nullable K standardFloorKey(@ParametricNullness K key) { return keyOrNull(floorEntry(key)); } @Override - @CheckForNull - public Entry ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry ceilingEntry(@ParametricNullness K key) { return delegate().ceilingEntry(key); } @@ -140,14 +128,12 @@ public Entry ceilingEntry(@ParametricNullness K key) { * #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override {@code * ceilingEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardCeilingEntry(@ParametricNullness K key) { + protected @Nullable Entry standardCeilingEntry(@ParametricNullness K key) { return tailMap(key, true).firstEntry(); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return delegate().ceilingKey(key); } @@ -156,14 +142,12 @@ public K ceilingKey(@ParametricNullness K key) { * {@code ceilingEntry}, you may wish to override {@code ceilingKey} to forward to this * implementation. */ - @CheckForNull - protected K standardCeilingKey(@ParametricNullness K key) { + protected @Nullable K standardCeilingKey(@ParametricNullness K key) { return keyOrNull(ceilingEntry(key)); } @Override - @CheckForNull - public Entry higherEntry(@ParametricNullness K key) { + public @Nullable Entry higherEntry(@ParametricNullness K key) { return delegate().higherEntry(key); } @@ -172,14 +156,12 @@ public Entry higherEntry(@ParametricNullness K key) { * #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override {@code * higherEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardHigherEntry(@ParametricNullness K key) { + protected @Nullable Entry standardHigherEntry(@ParametricNullness K key) { return tailMap(key, false).firstEntry(); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return delegate().higherKey(key); } @@ -188,14 +170,12 @@ public K higherKey(@ParametricNullness K key) { * {@code higherEntry}, you may wish to override {@code higherKey} to forward to this * implementation. */ - @CheckForNull - protected K standardHigherKey(@ParametricNullness K key) { + protected @Nullable K standardHigherKey(@ParametricNullness K key) { return keyOrNull(higherEntry(key)); } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return delegate().firstEntry(); } @@ -204,9 +184,8 @@ public Entry firstEntry() { * #entrySet}. If you override {@code entrySet}, you may wish to override {@code firstEntry} to * forward to this implementation. */ - @CheckForNull - protected Entry standardFirstEntry() { - return Iterables.getFirst(entrySet(), null); + protected @Nullable Entry standardFirstEntry() { + return Iterables.<@Nullable Entry>getFirst(entrySet(), null); } /** @@ -224,8 +203,7 @@ protected K standardFirstKey() { } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return delegate().lastEntry(); } @@ -234,9 +212,8 @@ public Entry lastEntry() { * #entrySet} of {@link #descendingMap}. If you override {@code descendingMap}, you may wish to * override {@code lastEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardLastEntry() { - return Iterables.getFirst(descendingMap().entrySet(), null); + protected @Nullable Entry standardLastEntry() { + return Iterables.<@Nullable Entry>getFirst(descendingMap().entrySet(), null); } /** @@ -253,8 +230,7 @@ protected K standardLastKey() { } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return delegate().pollFirstEntry(); } @@ -263,14 +239,12 @@ public Entry pollFirstEntry() { * entrySet}. If you override {@code entrySet}, you may wish to override {@code pollFirstEntry} to * forward to this implementation. */ - @CheckForNull - protected Entry standardPollFirstEntry() { + protected @Nullable Entry standardPollFirstEntry() { return Iterators.pollNext(entrySet().iterator()); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return delegate().pollLastEntry(); } @@ -279,8 +253,7 @@ public Entry pollLastEntry() { * entrySet} of {@code descendingMap}. If you override {@code descendingMap}, you may wish to * override {@code pollFirstEntry} to forward to this implementation. */ - @CheckForNull - protected Entry standardPollLastEntry() { + protected @Nullable Entry standardPollLastEntry() { return Iterators.pollNext(descendingMap().entrySet().iterator()); } @@ -300,7 +273,6 @@ public NavigableMap descendingMap() { * * @since 12.0 */ - @Beta protected class StandardDescendingMap extends Maps.DescendingMap { /** Constructor for use by subclasses. */ public StandardDescendingMap() {} @@ -313,8 +285,8 @@ NavigableMap forward() { @Override protected Iterator> entryIterator() { return new Iterator>() { - @CheckForNull private Entry toRemove = null; - @CheckForNull private Entry nextOrNull = forward().lastEntry(); + private @Nullable Entry toRemove = null; + private @Nullable Entry nextOrNull = forward().lastEntry(); @Override public boolean hasNext() { @@ -322,7 +294,7 @@ public boolean hasNext() { } @Override - public java.util.Map.Entry next() { + public Entry next() { if (nextOrNull == null) { throw new NoSuchElementException(); } @@ -359,7 +331,6 @@ public NavigableSet navigableKeySet() { * * @since 12.0 */ - @Beta protected class StandardNavigableKeySet extends Maps.NavigableKeySet { /** Constructor for use by subclasses. */ public StandardNavigableKeySet() { @@ -379,7 +350,6 @@ public NavigableSet descendingKeySet() { * descendingMap}, you may wish to override {@code descendingKeySet} to forward to this * implementation. */ - @Beta protected NavigableSet standardDescendingKeySet() { return descendingMap().navigableKeySet(); } diff --git a/android/guava/src/com/google/common/collect/ForwardingNavigableSet.java b/android/guava/src/com/google/common/collect/ForwardingNavigableSet.java index 6822aa87d4fc..8cf8286325ce 100644 --- a/android/guava/src/com/google/common/collect/ForwardingNavigableSet.java +++ b/android/guava/src/com/google/common/collect/ForwardingNavigableSet.java @@ -16,13 +16,11 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import java.util.Iterator; import java.util.NavigableSet; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A navigable set which forwards all its method calls to another navigable set. Subclasses should @@ -51,7 +49,6 @@ * @since 12.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingNavigableSet extends ForwardingSortedSet implements NavigableSet { @@ -62,8 +59,7 @@ protected ForwardingNavigableSet() {} protected abstract NavigableSet delegate(); @Override - @CheckForNull - public E lower(@ParametricNullness E e) { + public @Nullable E lower(@ParametricNullness E e) { return delegate().lower(e); } @@ -72,14 +68,12 @@ public E lower(@ParametricNullness E e) { * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may * wish to override {@link #lower} to forward to this implementation. */ - @CheckForNull - protected E standardLower(@ParametricNullness E e) { + protected @Nullable E standardLower(@ParametricNullness E e) { return Iterators.getNext(headSet(e, false).descendingIterator(), null); } @Override - @CheckForNull - public E floor(@ParametricNullness E e) { + public @Nullable E floor(@ParametricNullness E e) { return delegate().floor(e); } @@ -88,14 +82,12 @@ public E floor(@ParametricNullness E e) { * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may * wish to override {@link #floor} to forward to this implementation. */ - @CheckForNull - protected E standardFloor(@ParametricNullness E e) { + protected @Nullable E standardFloor(@ParametricNullness E e) { return Iterators.getNext(headSet(e, true).descendingIterator(), null); } @Override - @CheckForNull - public E ceiling(@ParametricNullness E e) { + public @Nullable E ceiling(@ParametricNullness E e) { return delegate().ceiling(e); } @@ -104,14 +96,12 @@ public E ceiling(@ParametricNullness E e) { * #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may wish to * override {@link #ceiling} to forward to this implementation. */ - @CheckForNull - protected E standardCeiling(@ParametricNullness E e) { + protected @Nullable E standardCeiling(@ParametricNullness E e) { return Iterators.getNext(tailSet(e, true).iterator(), null); } @Override - @CheckForNull - public E higher(@ParametricNullness E e) { + public @Nullable E higher(@ParametricNullness E e) { return delegate().higher(e); } @@ -120,14 +110,12 @@ public E higher(@ParametricNullness E e) { * #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may wish to * override {@link #higher} to forward to this implementation. */ - @CheckForNull - protected E standardHigher(@ParametricNullness E e) { + protected @Nullable E standardHigher(@ParametricNullness E e) { return Iterators.getNext(tailSet(e, false).iterator(), null); } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return delegate().pollFirst(); } @@ -136,14 +124,12 @@ public E pollFirst() { * override {@link #iterator} you may wish to override {@link #pollFirst} to forward to this * implementation. */ - @CheckForNull - protected E standardPollFirst() { + protected @Nullable E standardPollFirst() { return Iterators.pollNext(iterator()); } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return delegate().pollLast(); } @@ -152,8 +138,7 @@ public E pollLast() { * If you override {@link #descendingIterator} you may wish to override {@link #pollLast} to * forward to this implementation. */ - @CheckForNull - protected E standardPollLast() { + protected @Nullable E standardPollLast() { return Iterators.pollNext(descendingIterator()); } @@ -181,7 +166,6 @@ public NavigableSet descendingSet() { * * @since 12.0 */ - @Beta protected class StandardDescendingSet extends Sets.DescendingSet { /** Constructor for use by subclasses. */ public StandardDescendingSet() { @@ -208,7 +192,6 @@ public NavigableSet subSet( * {@code headSet} and {@code tailSet} methods. In many cases, you may wish to override {@link * #subSet(Object, boolean, Object, boolean)} to forward to this implementation. */ - @Beta protected NavigableSet standardSubSet( @ParametricNullness E fromElement, boolean fromInclusive, diff --git a/android/guava/src/com/google/common/collect/ForwardingObject.java b/android/guava/src/com/google/common/collect/ForwardingObject.java index 64af9082d224..712b14fa1343 100644 --- a/android/guava/src/com/google/common/collect/ForwardingObject.java +++ b/android/guava/src/com/google/common/collect/ForwardingObject.java @@ -44,7 +44,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingObject { /** Constructor for use by subclasses. */ diff --git a/android/guava/src/com/google/common/collect/ForwardingQueue.java b/android/guava/src/com/google/common/collect/ForwardingQueue.java index 43c2eaafb56d..8fbe467a07eb 100644 --- a/android/guava/src/com/google/common/collect/ForwardingQueue.java +++ b/android/guava/src/com/google/common/collect/ForwardingQueue.java @@ -20,8 +20,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.NoSuchElementException; import java.util.Queue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A queue which forwards all its method calls to another queue. Subclasses should override one or @@ -46,7 +45,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingQueue extends ForwardingCollection implements Queue { @@ -64,8 +62,7 @@ public boolean offer(@ParametricNullness E o) { @CanIgnoreReturnValue // TODO(cpovirk): Consider removing this? @Override - @CheckForNull - public E poll() { + public @Nullable E poll() { return delegate().poll(); } @@ -77,8 +74,7 @@ public E remove() { } @Override - @CheckForNull - public E peek() { + public @Nullable E peek() { return delegate().peek(); } @@ -108,8 +104,7 @@ protected boolean standardOffer(@ParametricNullness E e) { * * @since 7.0 */ - @CheckForNull - protected E standardPeek() { + protected @Nullable E standardPeek() { try { return element(); } catch (NoSuchElementException caught) { @@ -123,8 +118,7 @@ protected E standardPeek() { * * @since 7.0 */ - @CheckForNull - protected E standardPoll() { + protected @Nullable E standardPoll() { try { return remove(); } catch (NoSuchElementException caught) { diff --git a/android/guava/src/com/google/common/collect/ForwardingSet.java b/android/guava/src/com/google/common/collect/ForwardingSet.java index bc27272b0267..6a2444402102 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSet.java +++ b/android/guava/src/com/google/common/collect/ForwardingSet.java @@ -21,8 +21,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A set which forwards all its method calls to another set. Subclasses should override one or more @@ -47,7 +46,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingSet extends ForwardingCollection implements Set { // TODO(lowasser): identify places where thread safety is actually lost @@ -59,7 +57,7 @@ protected ForwardingSet() {} protected abstract Set delegate(); @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return object == this || delegate().equals(object); } @@ -87,7 +85,7 @@ protected boolean standardRemoveAll(Collection collection) { * * @since 7.0 */ - protected boolean standardEquals(@CheckForNull Object object) { + protected boolean standardEquals(@Nullable Object object) { return Sets.equalsImpl(this, object); } diff --git a/android/guava/src/com/google/common/collect/ForwardingSetMultimap.java b/android/guava/src/com/google/common/collect/ForwardingSetMultimap.java index 5077c6803cf9..84876397917d 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSetMultimap.java +++ b/android/guava/src/com/google/common/collect/ForwardingSetMultimap.java @@ -20,8 +20,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A set multimap which forwards all its method calls to another set multimap. Subclasses should @@ -36,9 +35,10 @@ * @since 3.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingSetMultimap extends ForwardingMultimap implements SetMultimap { + /** Constructor for use by subclasses. */ + public ForwardingSetMultimap() {} @Override protected abstract SetMultimap delegate(); @@ -55,7 +55,7 @@ public Set get(@ParametricNullness K key) { @CanIgnoreReturnValue @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { return delegate().removeAll(key); } diff --git a/android/guava/src/com/google/common/collect/ForwardingSortedMap.java b/android/guava/src/com/google/common/collect/ForwardingSortedMap.java index e0882abc7aa9..2dcfba314183 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSortedMap.java +++ b/android/guava/src/com/google/common/collect/ForwardingSortedMap.java @@ -18,13 +18,11 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.NoSuchElementException; import java.util.SortedMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A sorted map which forwards all its method calls to another sorted map. Subclasses should @@ -52,7 +50,11 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault +/* + * We provide and encourage use of ForwardingNavigableSet over this class, but we still provide this + * one to preserve compatibility. + */ +@SuppressWarnings("JdkObsolete") public abstract class ForwardingSortedMap extends ForwardingMap implements SortedMap { // TODO(lowasser): identify places where thread safety is actually lost @@ -64,8 +66,7 @@ protected ForwardingSortedMap() {} protected abstract SortedMap delegate(); @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return delegate().comparator(); } @@ -103,7 +104,6 @@ public SortedMap tailMap(@ParametricNullness K fromKey) { * * @since 15.0 */ - @Beta protected class StandardKeySet extends Maps.SortedKeySet { /** Constructor for use by subclasses. */ public StandardKeySet() { @@ -114,7 +114,7 @@ public StandardKeySet() { // unsafe, but worst case is a CCE or NPE is thrown, which callers will be expecting @SuppressWarnings({"unchecked", "nullness"}) static int unsafeCompare( - @CheckForNull Comparator comparator, @CheckForNull Object o1, @CheckForNull Object o2) { + @Nullable Comparator comparator, @Nullable Object o1, @Nullable Object o2) { if (comparator == null) { return ((Comparable<@Nullable Object>) o1).compareTo(o2); } else { @@ -130,8 +130,7 @@ static int unsafeCompare( * @since 7.0 */ @Override - @Beta - protected boolean standardContainsKey(@CheckForNull Object key) { + protected boolean standardContainsKey(@Nullable Object key) { try { // any CCE or NPE will be caught @SuppressWarnings({"unchecked", "nullness"}) @@ -150,7 +149,6 @@ protected boolean standardContainsKey(@CheckForNull Object key) { * * @since 7.0 */ - @Beta protected SortedMap standardSubMap(K fromKey, K toKey) { checkArgument(unsafeCompare(comparator(), fromKey, toKey) <= 0, "fromKey must be <= toKey"); return tailMap(fromKey).headMap(toKey); diff --git a/android/guava/src/com/google/common/collect/ForwardingSortedMultiset.java b/android/guava/src/com/google/common/collect/ForwardingSortedMultiset.java index 4626d3193a58..18d3c61d1554 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSortedMultiset.java +++ b/android/guava/src/com/google/common/collect/ForwardingSortedMultiset.java @@ -14,13 +14,11 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; import java.util.NavigableSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A sorted multiset which forwards all its method calls to another sorted multiset. Subclasses @@ -44,9 +42,7 @@ * @author Louis Wasserman * @since 15.0 */ -@Beta @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class ForwardingSortedMultiset extends ForwardingMultiset implements SortedMultiset { /** Constructor for use by subclasses. */ @@ -113,8 +109,7 @@ SortedMultiset forwardMultiset() { } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return delegate().firstEntry(); } @@ -124,8 +119,7 @@ public Entry firstEntry() { *

    If you override {@link #entrySet()}, you may wish to override {@link #firstEntry()} to * forward to this implementation. */ - @CheckForNull - protected Entry standardFirstEntry() { + protected @Nullable Entry standardFirstEntry() { Iterator> entryIterator = entrySet().iterator(); if (!entryIterator.hasNext()) { return null; @@ -135,8 +129,7 @@ protected Entry standardFirstEntry() { } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return delegate().lastEntry(); } @@ -147,8 +140,7 @@ public Entry lastEntry() { *

    If you override {@link #descendingMultiset} or {@link #entrySet()}, you may wish to override * {@link #firstEntry()} to forward to this implementation. */ - @CheckForNull - protected Entry standardLastEntry() { + protected @Nullable Entry standardLastEntry() { Iterator> entryIterator = descendingMultiset().entrySet().iterator(); if (!entryIterator.hasNext()) { return null; @@ -158,8 +150,7 @@ protected Entry standardLastEntry() { } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return delegate().pollFirstEntry(); } @@ -169,8 +160,7 @@ public Entry pollFirstEntry() { *

    If you override {@link #entrySet()}, you may wish to override {@link #pollFirstEntry()} to * forward to this implementation. */ - @CheckForNull - protected Entry standardPollFirstEntry() { + protected @Nullable Entry standardPollFirstEntry() { Iterator> entryIterator = entrySet().iterator(); if (!entryIterator.hasNext()) { return null; @@ -182,8 +172,7 @@ protected Entry standardPollFirstEntry() { } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return delegate().pollLastEntry(); } @@ -194,8 +183,7 @@ public Entry pollLastEntry() { *

    If you override {@link #descendingMultiset()} or {@link #entrySet()}, you may wish to * override {@link #pollLastEntry()} to forward to this implementation. */ - @CheckForNull - protected Entry standardPollLastEntry() { + protected @Nullable Entry standardPollLastEntry() { Iterator> entryIterator = descendingMultiset().entrySet().iterator(); if (!entryIterator.hasNext()) { return null; diff --git a/android/guava/src/com/google/common/collect/ForwardingSortedSet.java b/android/guava/src/com/google/common/collect/ForwardingSortedSet.java index 32625afd161f..42d96d89c3fb 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSortedSet.java +++ b/android/guava/src/com/google/common/collect/ForwardingSortedSet.java @@ -18,14 +18,12 @@ import static com.google.common.collect.ForwardingSortedMap.unsafeCompare; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A sorted set which forwards all its method calls to another sorted set. Subclasses should @@ -55,7 +53,11 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault +/* + * We provide and encourage use of ForwardingNavigableSet over this class, but we still provide this + * one to preserve compatibility. + */ +@SuppressWarnings("JdkObsolete") public abstract class ForwardingSortedSet extends ForwardingSet implements SortedSet { @@ -66,8 +68,7 @@ protected ForwardingSortedSet() {} protected abstract SortedSet delegate(); @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return delegate().comparator(); } @@ -106,8 +107,7 @@ public SortedSet tailSet(@ParametricNullness E fromElement) { * @since 7.0 */ @Override - @Beta - protected boolean standardContains(@CheckForNull Object object) { + protected boolean standardContains(@Nullable Object object) { try { // any ClassCastExceptions and NullPointerExceptions are caught @SuppressWarnings({"unchecked", "nullness"}) @@ -127,8 +127,7 @@ protected boolean standardContains(@CheckForNull Object object) { * @since 7.0 */ @Override - @Beta - protected boolean standardRemove(@CheckForNull Object object) { + protected boolean standardRemove(@Nullable Object object) { try { // any ClassCastExceptions and NullPointerExceptions are caught @SuppressWarnings({"unchecked", "nullness"}) @@ -154,7 +153,6 @@ protected boolean standardRemove(@CheckForNull Object object) { * * @since 7.0 */ - @Beta protected SortedSet standardSubSet( @ParametricNullness E fromElement, @ParametricNullness E toElement) { return tailSet(fromElement).headSet(toElement); diff --git a/android/guava/src/com/google/common/collect/ForwardingSortedSetMultimap.java b/android/guava/src/com/google/common/collect/ForwardingSortedSetMultimap.java index b91a68b3445a..ff405066f856 100644 --- a/android/guava/src/com/google/common/collect/ForwardingSortedSetMultimap.java +++ b/android/guava/src/com/google/common/collect/ForwardingSortedSetMultimap.java @@ -19,8 +19,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A sorted set multimap which forwards all its method calls to another sorted set multimap. @@ -35,7 +34,6 @@ * @since 3.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingSortedSetMultimap< K extends @Nullable Object, V extends @Nullable Object> extends ForwardingSetMultimap implements SortedSetMultimap { @@ -52,7 +50,7 @@ public SortedSet get(@ParametricNullness K key) { } @Override - public SortedSet removeAll(@CheckForNull Object key) { + public SortedSet removeAll(@Nullable Object key) { return delegate().removeAll(key); } @@ -62,8 +60,7 @@ public SortedSet replaceValues(@ParametricNullness K key, Iterable valueComparator() { + public @Nullable Comparator valueComparator() { return delegate().valueComparator(); } } diff --git a/android/guava/src/com/google/common/collect/ForwardingTable.java b/android/guava/src/com/google/common/collect/ForwardingTable.java index 4fcb858b725f..51f5861b051e 100644 --- a/android/guava/src/com/google/common/collect/ForwardingTable.java +++ b/android/guava/src/com/google/common/collect/ForwardingTable.java @@ -21,8 +21,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A table which forwards all its method calls to another table. Subclasses should override one or @@ -33,7 +32,6 @@ * @since 7.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingTable< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends ForwardingObject implements Table { @@ -69,28 +67,27 @@ public Map> columnMap() { } @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return delegate().contains(rowKey, columnKey); } @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { return delegate().containsColumn(columnKey); } @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { return delegate().containsRow(rowKey); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return delegate().containsValue(value); } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { return delegate().get(rowKey, columnKey); } @@ -101,8 +98,7 @@ public boolean isEmpty() { @CanIgnoreReturnValue @Override - @CheckForNull - public V put( + public @Nullable V put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { return delegate().put(rowKey, columnKey, value); } @@ -114,8 +110,7 @@ public void putAll(Table table) { @CanIgnoreReturnValue @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { return delegate().remove(rowKey, columnKey); } @@ -145,7 +140,7 @@ public Collection values() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return (obj == this) || delegate().equals(obj); } diff --git a/android/guava/src/com/google/common/collect/GeneralRange.java b/android/guava/src/com/google/common/collect/GeneralRange.java index cdf68f52031e..69bd0695563f 100644 --- a/android/guava/src/com/google/common/collect/GeneralRange.java +++ b/android/guava/src/com/google/common/collect/GeneralRange.java @@ -22,10 +22,10 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; import java.util.Comparator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A generalized interval on any ordering, for internal use. Supports {@code null}. Unlike {@link @@ -37,9 +37,9 @@ * @author Louis Wasserman */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class GeneralRange implements Serializable { /** Converts a Range to a GeneralRange. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 static GeneralRange from(Range range) { T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null; BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : OPEN; @@ -94,19 +94,19 @@ static GeneralRange from(Range range) { private final Comparator comparator; private final boolean hasLowerBound; - @CheckForNull private final T lowerEndpoint; + private final @Nullable T lowerEndpoint; private final BoundType lowerBoundType; private final boolean hasUpperBound; - @CheckForNull private final T upperEndpoint; + private final @Nullable T upperEndpoint; private final BoundType upperBoundType; private GeneralRange( Comparator comparator, boolean hasLowerBound, - @CheckForNull T lowerEndpoint, + @Nullable T lowerEndpoint, BoundType lowerBoundType, boolean hasUpperBound, - @CheckForNull T upperEndpoint, + @Nullable T upperEndpoint, BoundType upperBoundType) { this.comparator = checkNotNull(comparator); this.hasLowerBound = hasLowerBound; @@ -122,12 +122,14 @@ private GeneralRange( * whenever they pass `true` for the matching `has*Bound` parameter. */ if (hasLowerBound) { - comparator.compare( - uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(lowerEndpoint)); + int unused = + comparator.compare( + uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(lowerEndpoint)); } if (hasUpperBound) { - comparator.compare( - uncheckedCastNullableTToT(upperEndpoint), uncheckedCastNullableTToT(upperEndpoint)); + int unused = + comparator.compare( + uncheckedCastNullableTToT(upperEndpoint), uncheckedCastNullableTToT(upperEndpoint)); } if (hasLowerBound && hasUpperBound) { @@ -138,7 +140,7 @@ private GeneralRange( checkArgument( cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint, upperEndpoint); if (cmp == 0) { - checkArgument(lowerBoundType != OPEN | upperBoundType != OPEN); + checkArgument(lowerBoundType != OPEN || upperBoundType != OPEN); } } } @@ -237,7 +239,7 @@ GeneralRange intersect(GeneralRange other) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof GeneralRange) { GeneralRange r = (GeneralRange) obj; return comparator.equals(r.comparator) @@ -261,7 +263,7 @@ public int hashCode() { getUpperBoundType()); } - @CheckForNull private transient GeneralRange reverse; + @LazyInit private transient @Nullable GeneralRange reverse; /** Returns the same range relative to the reversed comparator. */ GeneralRange reverse() { @@ -269,7 +271,7 @@ GeneralRange reverse() { if (result == null) { result = new GeneralRange<>( - Ordering.from(comparator).reverse(), + reverseComparator(comparator), hasUpperBound, getUpperEndpoint(), getUpperBoundType(), @@ -282,6 +284,12 @@ GeneralRange reverse() { return result; } + // This method helps J2KT's type inference. + private static Comparator reverseComparator( + Comparator comparator) { + return Ordering.from(comparator).reverse(); + } + @Override public String toString() { return comparator @@ -293,8 +301,7 @@ public String toString() { + (upperBoundType == CLOSED ? ']' : ')'); } - @CheckForNull - T getLowerEndpoint() { + @Nullable T getLowerEndpoint() { return lowerEndpoint; } @@ -302,8 +309,7 @@ BoundType getLowerBoundType() { return lowerBoundType; } - @CheckForNull - T getUpperEndpoint() { + @Nullable T getUpperEndpoint() { return upperEndpoint; } diff --git a/android/guava/src/com/google/common/collect/GwtTransient.java b/android/guava/src/com/google/common/collect/GwtTransient.java index ce5ea48848c9..9c09c53c946f 100644 --- a/android/guava/src/com/google/common/collect/GwtTransient.java +++ b/android/guava/src/com/google/common/collect/GwtTransient.java @@ -33,5 +33,4 @@ @GwtCompatible @Retention(RUNTIME) @Target(FIELD) -@ElementTypesAreNonnullByDefault @interface GwtTransient {} diff --git a/android/guava/src/com/google/common/collect/HashBasedTable.java b/android/guava/src/com/google/common/collect/HashBasedTable.java index c8ba50fd15a9..db3f7d660af1 100644 --- a/android/guava/src/com/google/common/collect/HashBasedTable.java +++ b/android/guava/src/com/google/common/collect/HashBasedTable.java @@ -19,6 +19,8 @@ import static com.google.common.collect.CollectPreconditions.checkNonnegative; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; import java.io.Serializable; import java.util.LinkedHashMap; @@ -41,13 +43,12 @@ * concurrently and one of the threads modifies the table, it must be synchronized externally. * *

    See the Guava User Guide article on {@code Table}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#table">{@code Table}. * * @author Jared Levy * @since 7.0 */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault public class HashBasedTable extends StandardTable { private static class Factory implements Supplier>, Serializable { final int expectedSize; @@ -61,7 +62,7 @@ public Map get() { return Maps.newLinkedHashMapWithExpectedSize(expectedSize); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** Creates an empty {@code HashBasedTable}. */ @@ -102,5 +103,5 @@ public static HashBasedTable create( super(backingMap, factory); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/HashBiMap.java b/android/guava/src/com/google/common/collect/HashBiMap.java index 010f73cb3d61..ad9138c8eca4 100644 --- a/android/guava/src/com/google/common/collect/HashBiMap.java +++ b/android/guava/src/com/google/common/collect/HashBiMap.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.LazyInit; @@ -36,8 +37,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A @@ -46,14 +46,13 @@ *

    This implementation guarantees insertion-based iteration order of its keys. * *

    See the Guava User Guide article on {@code BiMap} . + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">{@code BiMap} . * * @author Louis Wasserman * @author Mike Bostock * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class HashBiMap extends AbstractMap implements BiMap, Serializable { @@ -89,25 +88,34 @@ public final class HashBiMap keySet; + @LazyInit private transient Set keySet; @Override public Set keySet() { @@ -770,12 +770,12 @@ K forEntry(int entry) { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return HashBiMap.this.containsKey(o); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { int oHash = Hashing.smearedHash(o); int entry = findEntryByKey(o, oHash); if (entry != ABSENT) { @@ -787,7 +787,7 @@ public boolean remove(@CheckForNull Object o) { } } - private transient Set valueSet; + @LazyInit private transient Set valueSet; @Override public Set values() { @@ -808,12 +808,12 @@ V forEntry(int entry) { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return HashBiMap.this.containsValue(o); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { int oHash = Hashing.smearedHash(o); int entry = findEntryByValue(o, oHash); if (entry != ABSENT) { @@ -825,7 +825,7 @@ public boolean remove(@CheckForNull Object o) { } } - private transient Set> entrySet; + @LazyInit private transient Set> entrySet; @Override public Set> entrySet() { @@ -839,7 +839,7 @@ final class EntrySet extends View> { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Entry) { Entry e = (Entry) o; Object k = e.getKey(); @@ -852,7 +852,7 @@ public boolean contains(@CheckForNull Object o) { @Override @CanIgnoreReturnValue - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o instanceof Entry) { Entry e = (Entry) o; Object k = e.getKey(); @@ -942,7 +942,7 @@ public V setValue(@ParametricNullness V value) { } } - @LazyInit @RetainedWith @CheckForNull private transient BiMap inverse; + @LazyInit @RetainedWith private transient @Nullable BiMap inverse; @Override public BiMap inverse() { @@ -964,32 +964,29 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return forward.containsValue(key); } @Override - @CheckForNull - public K get(@CheckForNull Object key) { + public @Nullable K get(@Nullable Object key) { return forward.getInverse(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return forward.containsKey(value); } @Override @CanIgnoreReturnValue - @CheckForNull - public K put(@ParametricNullness V value, @ParametricNullness K key) { + public @Nullable K put(@ParametricNullness V value, @ParametricNullness K key) { return forward.putInverse(value, key, false); } @Override @CanIgnoreReturnValue - @CheckForNull - public K forcePut(@ParametricNullness V value, @ParametricNullness K key) { + public @Nullable K forcePut(@ParametricNullness V value, @ParametricNullness K key) { return forward.putInverse(value, key, true); } @@ -1000,8 +997,7 @@ public BiMap inverse() { @Override @CanIgnoreReturnValue - @CheckForNull - public K remove(@CheckForNull Object value) { + public @Nullable K remove(@Nullable Object value) { return forward.removeInverse(value); } @@ -1042,7 +1038,7 @@ static class InverseEntrySet e = (Entry) o; Object v = e.getKey(); @@ -1054,7 +1050,7 @@ public boolean contains(@CheckForNull Object o) { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o instanceof Entry) { Entry e = (Entry) o; Object v = e.getKey(); @@ -1134,16 +1130,20 @@ public K setValue(@ParametricNullness K key) { * @serialData the number of entries, first key, first value, second key, second value, and so on. */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Serialization.writeMap(this, stream); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int size = Serialization.readCount(stream); init(16); // resist hostile attempts to allocate gratuitous heap Serialization.populateMap(this, stream, size); } + + // TODO(cpovirk): Should we have a serialVersionUID here? } diff --git a/android/guava/src/com/google/common/collect/HashMultimap.java b/android/guava/src/com/google/common/collect/HashMultimap.java index 9e4c1c2233f1..719c0b40f66e 100644 --- a/android/guava/src/com/google/common/collect/HashMultimap.java +++ b/android/guava/src/com/google/common/collect/HashMultimap.java @@ -18,6 +18,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import java.io.IOException; @@ -26,7 +27,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link Multimap} using hash tables. @@ -49,7 +50,6 @@ * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public final class HashMultimap extends HashMultimapGwtSerializationDependencies { private static final int DEFAULT_VALUES_PER_KEY = 2; @@ -59,8 +59,9 @@ public final class HashMultimapThis method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys().hashSetValues().build()}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys().hashSetValues().build()}, which provides more control over the + * underlying data structure. */ public static HashMultimap create() { @@ -71,8 +72,9 @@ HashMultimap create() { * Constructs an empty {@code HashMultimap} with enough capacity to hold the specified numbers of * keys and values without rehashing. * - *

    This method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys(expectedKeys).hashSetValues(expectedValuesPerKey).build()}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys(expectedKeys).hashSetValues(expectedValuesPerKey).build()}, which + * provides more control over the underlying data structure. * * @param expectedKeys the expected number of distinct keys * @param expectedValuesPerKey the expected average number of values per key @@ -89,8 +91,9 @@ HashMultimap create() { * key-value mapping appears multiple times in the input multimap, it only appears once in the * constructed multimap. * - *

    This method will soon be deprecated in favor of {@code - * MultimapBuilder.hashKeys().hashSetValues().build(multimap)}. + *

    You may also consider the equivalent {@code + * MultimapBuilder.hashKeys().hashSetValues().build(multimap)}, which provides more control over + * the underlying data structure. * * @param multimap the multimap whose contents are copied to this multimap */ @@ -131,12 +134,14 @@ Set createCollection() { * key, number of values for that key, and the key's values */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Serialization.writeMultimap(this, stream); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; @@ -146,6 +151,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo Serialization.populateMultimap(this, stream, distinctKeys); } - @GwtIncompatible // Not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/HashMultimapGwtSerializationDependencies.java b/android/guava/src/com/google/common/collect/HashMultimapGwtSerializationDependencies.java index 0922c3839080..66ec054ec575 100644 --- a/android/guava/src/com/google/common/collect/HashMultimapGwtSerializationDependencies.java +++ b/android/guava/src/com/google/common/collect/HashMultimapGwtSerializationDependencies.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.Nullable; /** * A dummy superclass to support GWT serialization of the element types of a {@link HashMultimap}. @@ -30,7 +31,9 @@ *

    TODO(cpovirk): Consider applying this subclass approach to our other types. */ @GwtCompatible(emulated = true) -abstract class HashMultimapGwtSerializationDependencies extends AbstractSetMultimap { +abstract class HashMultimapGwtSerializationDependencies< + K extends @Nullable Object, V extends @Nullable Object> + extends AbstractSetMultimap { HashMultimapGwtSerializationDependencies(Map> map) { super(map); } diff --git a/android/guava/src/com/google/common/collect/HashMultiset.java b/android/guava/src/com/google/common/collect/HashMultiset.java index 0748e68be4d9..b6a28a1aae06 100644 --- a/android/guava/src/com/google/common/collect/HashMultiset.java +++ b/android/guava/src/com/google/common/collect/HashMultiset.java @@ -18,7 +18,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import org.checkerframework.checker.nullness.qual.Nullable; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Multiset implementation that uses hashing for key and entry access. @@ -28,7 +29,6 @@ * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public final class HashMultiset extends AbstractMapBasedMultiset { /** Creates a new, empty {@code HashMultiset} using the default initial capacity. */ @@ -44,7 +44,7 @@ public final class HashMultiset extends AbstractMapB * @throws IllegalArgumentException if {@code distinctElements} is negative */ public static HashMultiset create(int distinctElements) { - return new HashMultiset(distinctElements); + return new HashMultiset<>(distinctElements); } /** @@ -70,6 +70,5 @@ ObjectCountHashMap newBackingMap(int distinctElements) { return new ObjectCountHashMap<>(distinctElements); } - @GwtIncompatible // Not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/Hashing.java b/android/guava/src/com/google/common/collect/Hashing.java index 81ef67ebbc76..1ea3af1c91d8 100644 --- a/android/guava/src/com/google/common/collect/Hashing.java +++ b/android/guava/src/com/google/common/collect/Hashing.java @@ -16,9 +16,11 @@ package com.google.common.collect; +import static java.lang.Math.max; + import com.google.common.annotations.GwtCompatible; import com.google.common.primitives.Ints; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static methods for implementing hash-based collections. @@ -28,7 +30,6 @@ * @author Austin Appleby */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class Hashing { private Hashing() {} @@ -51,7 +52,7 @@ static int smear(int hashCode) { return (int) (C2 * Integer.rotateLeft((int) (hashCode * C1), 15)); } - static int smearedHash(@CheckForNull Object o) { + static int smearedHash(@Nullable Object o) { return smear((o == null) ? 0 : o.hashCode()); } @@ -60,7 +61,7 @@ static int smearedHash(@CheckForNull Object o) { static int closedTableSize(int expectedEntries, double loadFactor) { // Get the recommended table size. // Round down to the nearest power of 2. - expectedEntries = Math.max(expectedEntries, 2); + expectedEntries = max(expectedEntries, 2); int tableSize = Integer.highestOneBit(expectedEntries); // Check to make sure that we will not exceed the maximum load factor. if (expectedEntries > (int) (loadFactor * tableSize)) { diff --git a/android/guava/src/com/google/common/collect/IgnoreJRERequirement.java b/android/guava/src/com/google/common/collect/IgnoreJRERequirement.java new file mode 100644 index 000000000000..a5e2448cdd32 --- /dev/null +++ b/android/guava/src/com/google/common/collect/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/collect/ImmutableAsList.java b/android/guava/src/com/google/common/collect/ImmutableAsList.java index c39747931796..b94e7f5d26cf 100644 --- a/android/guava/src/com/google/common/collect/ImmutableAsList.java +++ b/android/guava/src/com/google/common/collect/ImmutableAsList.java @@ -18,10 +18,11 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks to the @@ -32,12 +33,11 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") -@ElementTypesAreNonnullByDefault abstract class ImmutableAsList extends ImmutableList { abstract ImmutableCollection delegateCollection(); @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // The collection's contains() is at least as fast as ImmutableList's // and is often faster. return delegateCollection().contains(target); @@ -60,6 +60,7 @@ boolean isPartialView() { /** Serialized form that leads to the same performance as the original list. */ @GwtIncompatible // serialization + @J2ktIncompatible static class SerializedForm implements Serializable { final ImmutableCollection collection; @@ -71,15 +72,17 @@ Object readResolve() { return collection.asList(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible // serialization + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Use SerializedForm"); } @GwtIncompatible // serialization + @J2ktIncompatible @Override Object writeReplace() { return new SerializedForm(delegateCollection()); diff --git a/android/guava/src/com/google/common/collect/ImmutableBiMap.java b/android/guava/src/com/google/common/collect/ImmutableBiMap.java index bbae03613dc1..79e4f16c8901 100644 --- a/android/guava/src/com/google/common/collect/ImmutableBiMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableBiMap.java @@ -19,15 +19,22 @@ import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Map; -import javax.annotation.CheckForNull; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; /** * A {@link BiMap} whose contents will never change, with many other important properties detailed @@ -37,9 +44,29 @@ * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public abstract class ImmutableBiMap extends ImmutableMap implements BiMap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableBiMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * Entries appear in the result {@code ImmutableBiMap} in encounter order. + * + *

    If the mapped keys or values contain duplicates (according to {@link + * Object#equals(Object)}), an {@code IllegalArgumentException} is thrown when the collection + * operation is performed. (This differs from the {@code Collector} returned by {@link + * Collectors#toMap(Function, Function)}, which throws an {@code IllegalStateException}.) + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableBiMap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableBiMap(keyFunction, valueFunction); + } + /** * Returns the empty bimap. * @@ -126,6 +153,7 @@ public static ImmutableBiMap of( return new RegularImmutableBiMap( new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6}, 6); } + /** * Returns an immutable map containing the given entries, in order. * @@ -144,6 +172,7 @@ public static ImmutableBiMap of( return new RegularImmutableBiMap( new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7}, 7); } + /** * Returns an immutable map containing the given entries, in order. * @@ -178,6 +207,7 @@ public static ImmutableBiMap of( return new RegularImmutableBiMap( new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8}, 8); } + /** * Returns an immutable map containing the given entries, in order. * @@ -215,6 +245,7 @@ public static ImmutableBiMap of( return new RegularImmutableBiMap( new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9}, 9); } + /** * Returns an immutable map containing the given entries, in order. * @@ -294,7 +325,6 @@ public static Builder builder() { * * @since 23.1 */ - @Beta public static Builder builderWithExpectedSize(int expectedSize) { checkNonnegative(expectedSize, "expectedSize"); return new Builder<>(expectedSize); @@ -386,7 +416,6 @@ public Builder putAll(Map map) { * @since 19.0 */ @CanIgnoreReturnValue - @Beta @Override public Builder putAll(Iterable> entries) { super.putAll(entries); @@ -404,7 +433,6 @@ public Builder putAll(Iterable> * @since 19.0 */ @CanIgnoreReturnValue - @Beta @Override public Builder orderEntriesByValue(Comparator valueComparator) { super.orderEntriesByValue(valueComparator); @@ -448,10 +476,30 @@ public ImmutableBiMap buildOrThrow() { if (size == 0) { return of(); } - sortEntries(); + if (valueComparator != null) { + if (entriesUsed) { + alternatingKeysAndValues = Arrays.copyOf(alternatingKeysAndValues, 2 * size); + } + sortEntries(alternatingKeysAndValues, size, valueComparator); + } entriesUsed = true; return new RegularImmutableBiMap(alternatingKeysAndValues, size); } + + /** + * Throws {@link UnsupportedOperationException}. This method is inherited from {@link + * ImmutableMap.Builder}, but it does not make sense for bimaps. + * + * @throws UnsupportedOperationException always + * @deprecated This method does not make sense for bimaps and should not be called. + * @since 31.1 + */ + @DoNotCall + @Deprecated + @Override + public ImmutableBiMap buildKeepingLast() { + throw new UnsupportedOperationException("Not supported for bimaps"); + } } /** @@ -492,7 +540,6 @@ public static ImmutableBiMap copyOf(Map m * @throws NullPointerException if any key, value, or entry is null * @since 19.0 */ - @Beta public static ImmutableBiMap copyOf( Iterable> entries) { int estimatedSize = @@ -536,8 +583,7 @@ final ImmutableSet createValues() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final V forcePut(K key, V value) { + public final @Nullable V forcePut(K key, V value) { throw new UnsupportedOperationException(); } @@ -549,6 +595,7 @@ public final V forcePut(K key, V value) { *

    Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the * bimap and its inverse in sync during serialization, the way AbstractBiMap does. */ + @J2ktIncompatible // serialization private static class SerializedForm extends ImmutableMap.SerializedForm { SerializedForm(ImmutableBiMap bimap) { super(bimap); @@ -559,11 +606,59 @@ Builder makeBuilder(int size) { return new Builder<>(size); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm<>(this); } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** + * Not supported. Use {@link #toImmutableBiMap} instead. This method exists only to hide {@link + * ImmutableMap#toImmutableMap(Function, Function)} from consumers of {@code ImmutableBiMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableBiMap#toImmutableBiMap(Function, Function)}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @Deprecated + @DoNotCall("Use toImmutableBiMap") + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. This method does not make sense for {@code BiMap}. This method exists only to + * hide {@link ImmutableMap#toImmutableMap(Function, Function, BinaryOperator)} from consumers of + * {@code ImmutableBiMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Merging values does not make sense for a {@code BiMap}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @Deprecated + @DoNotCall("Use toImmutableBiMap") + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java b/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java index 438fa493a55b..f93a47969eff 100644 --- a/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java @@ -25,7 +25,8 @@ import com.google.errorprone.annotations.Immutable; import java.io.Serializable; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A {@link ClassToInstanceMap} whose contents will never change, with many other important @@ -36,8 +37,9 @@ */ @Immutable(containerOf = "B") @GwtIncompatible -@ElementTypesAreNonnullByDefault -public final class ImmutableClassToInstanceMap extends ForwardingMap, B> +// TODO(b/278589132): Remove the redundant "@NonNull" on B once it's no longer required by J2KT. +public final class ImmutableClassToInstanceMap + extends ForwardingMap, B> implements ClassToInstanceMap, Serializable { private static final ImmutableClassToInstanceMap EMPTY = @@ -91,6 +93,9 @@ public static Builder builder() { * @since 2.0 */ public static final class Builder { + /** Creates a new builder. */ + public Builder() {} + private final ImmutableMap.Builder, B> mapBuilder = ImmutableMap.builder(); /** @@ -120,7 +125,7 @@ public Builder putAll(Map, ? exten return this; } - private static T cast(Class type, B value) { + private static T cast(Class type, Object value) { return Primitives.wrap(type).cast(value); } @@ -131,7 +136,7 @@ private static T cast(Class type, B value) { * @throws IllegalArgumentException if duplicate keys were added */ public ImmutableClassToInstanceMap build() { - ImmutableMap, B> map = mapBuilder.build(); + ImmutableMap, B> map = mapBuilder.buildOrThrow(); if (map.isEmpty()) { return of(); } else { @@ -154,8 +159,10 @@ public ImmutableClassToInstanceMap build() { public static ImmutableClassToInstanceMap copyOf( Map, ? extends S> map) { if (map instanceof ImmutableClassToInstanceMap) { + @SuppressWarnings("rawtypes") // JDT-based J2KT Java frontend does not permit the direct cast + Map rawMap = map; @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) - ImmutableClassToInstanceMap cast = (ImmutableClassToInstanceMap) map; + ImmutableClassToInstanceMap cast = (ImmutableClassToInstanceMap) rawMap; return cast; } return new Builder().putAll(map).build(); @@ -174,8 +181,7 @@ protected Map, B> delegate() { @Override @SuppressWarnings("unchecked") // value could not get in if not a T - @CheckForNull - public T getInstance(Class type) { + public @Nullable T getInstance(Class type) { return (T) delegate.get(checkNotNull(type)); } @@ -189,8 +195,7 @@ public T getInstance(Class type) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public T putInstance(Class type, T value) { + public @Nullable T putInstance(Class type, T value) { throw new UnsupportedOperationException(); } diff --git a/android/guava/src/com/google/common/collect/ImmutableCollection.java b/android/guava/src/com/google/common/collect/ImmutableCollection.java index b5ac2a14cb41..84a676048a84 100644 --- a/android/guava/src/com/google/common/collect/ImmutableCollection.java +++ b/android/guava/src/com/google/common/collect/ImmutableCollection.java @@ -21,9 +21,13 @@ import static com.google.common.collect.ObjectArrays.checkElementsNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotMock; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.AbstractCollection; import java.util.Arrays; @@ -32,8 +36,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Spliterator; +import java.util.Spliterators; +import org.jspecify.annotations.Nullable; /** * A {@link Collection} whose contents will never change, and which offers a few additional @@ -156,17 +161,26 @@ *

    See also

    * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @since 2.0 */ @DoNotMock("Use ImmutableList.of or another implementation") @GwtCompatible(emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault // TODO(kevinb): I think we should push everything down to "BaseImmutableCollection" or something, // just to do everything we can to emphasize the "practically an interface" nature of this class. public abstract class ImmutableCollection extends AbstractCollection implements Serializable { + /* + * We expect SIZED (and SUBSIZED, if applicable) to be added by the spliterator factory methods. + * These are properties of the collection as a whole; SIZED and SUBSIZED are more properties of + * the spliterator implementation. + */ + @SuppressWarnings("Java7ApiChecker") + // @IgnoreJRERequirement is not necessary because this compiles down to a constant. + // (which is fortunate because Animal Sniffer doesn't look for @IgnoreJRERequirement on fields) + static final int SPLITERATOR_CHARACTERISTICS = + Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED; ImmutableCollection() {} @@ -174,9 +188,18 @@ public abstract class ImmutableCollection extends AbstractCollection imple @Override public abstract UnmodifiableIterator iterator(); + @Override + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // used only from APIs with Java 8 types in them + // (not used within guava-android as of this writing, but we include it in the jar as a test) + public Spliterator spliterator() { + return Spliterators.spliterator(this, SPLITERATOR_CHARACTERISTICS); + } + private static final Object[] EMPTY_ARRAY = {}; @Override + @J2ktIncompatible // Incompatible return type change. Use inherited (unoptimized) implementation public final Object[] toArray() { return toArray(EMPTY_ARRAY); } @@ -214,9 +237,7 @@ public final Object[] toArray() { } /** If this collection is backed by an array of its elements in insertion order, returns it. */ - @CheckForNull - @Nullable - Object[] internalArray() { + @Nullable Object @Nullable [] internalArray() { return null; } @@ -237,7 +258,7 @@ int internalArrayEnd() { } @Override - public abstract boolean contains(@CheckForNull Object object); + public abstract boolean contains(@Nullable Object object); /** * Guaranteed to throw an exception and leave the collection unmodified. @@ -263,7 +284,7 @@ public final boolean add(E e) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final boolean remove(@CheckForNull Object object) { + public final boolean remove(@Nullable Object object) { throw new UnsupportedOperationException(); } @@ -333,7 +354,7 @@ public final void clear() { * @since 2.0 */ public ImmutableList asList() { - return isEmpty() ? ImmutableList.of() : ImmutableList.asImmutableList(toArray()); + return isEmpty() ? ImmutableList.of() : ImmutableList.asImmutableList(toArray()); } /** @@ -356,11 +377,18 @@ int copyIntoArray(@Nullable Object[] dst, int offset) { return offset; } + @J2ktIncompatible // serialization + @GwtIncompatible // serialization Object writeReplace() { // We serialize by default to ImmutableList, the simplest thing that works. return new ImmutableList.SerializedForm(toArray()); } + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + /** * Abstract base class for builders of {@link ImmutableCollection} types. * @@ -372,7 +400,9 @@ public abstract static class Builder { static int expandedCapacity(int oldCapacity, int minCapacity) { if (minCapacity < 0) { - throw new AssertionError("cannot store more than MAX_VALUE elements"); + throw new IllegalArgumentException("cannot store more than Integer.MAX_VALUE elements"); + } else if (minCapacity <= oldCapacity) { + return oldCapacity; } // careful of overflow! int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; @@ -481,13 +511,12 @@ abstract static class ArrayBasedBuilder extends ImmutableCollection.Builder contents.length || forceCopy) { + this.contents = Arrays.copyOf(this.contents, newCapacity); forceCopy = false; } } @@ -496,7 +525,7 @@ private void getReadyToExpandTo(int minCapacity) { @Override public ArrayBasedBuilder add(E element) { checkNotNull(element); - getReadyToExpandTo(size + 1); + ensureRoomFor(1); contents[size++] = element; return this; } @@ -510,7 +539,7 @@ public Builder add(E... elements) { final void addAll(@Nullable Object[] elements, int n) { checkElementsNotNull(elements, n); - getReadyToExpandTo(size + n); + ensureRoomFor(n); /* * The following call is not statically checked, since arraycopy accepts plain Object for its * parameters. If it were statically checked, the checker would still be OK with it, since @@ -528,7 +557,7 @@ final void addAll(@Nullable Object[] elements, int n) { public Builder addAll(Iterable elements) { if (elements instanceof Collection) { Collection collection = (Collection) elements; - getReadyToExpandTo(size + collection.size()); + ensureRoomFor(collection.size()); if (collection instanceof ImmutableCollection) { ImmutableCollection immutableCollection = (ImmutableCollection) collection; size = immutableCollection.copyIntoArray(contents, size); @@ -539,4 +568,6 @@ public Builder addAll(Iterable elements) { return this; } } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableEntry.java b/android/guava/src/com/google/common/collect/ImmutableEntry.java index edc25f490fd5..1cc8ac35077d 100644 --- a/android/guava/src/com/google/common/collect/ImmutableEntry.java +++ b/android/guava/src/com/google/common/collect/ImmutableEntry.java @@ -17,12 +17,17 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; -/** @see com.google.common.collect.Maps#immutableEntry(Object, Object) */ +/** + * An immutable {@code Map.Entry}, used both by {@link + * com.google.common.collect.Maps#immutableEntry(Object, Object)} and by other parts of {@code + * common.collect} as a superclass. + */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault class ImmutableEntry extends AbstractMapEntry implements Serializable { @ParametricNullness final K key; @@ -51,5 +56,5 @@ public final V setValue(@ParametricNullness V value) { throw new UnsupportedOperationException(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ImmutableEnumMap.java b/android/guava/src/com/google/common/collect/ImmutableEnumMap.java index 66680f0e909e..769af2d8cd03 100644 --- a/android/guava/src/com/google/common/collect/ImmutableEnumMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableEnumMap.java @@ -17,12 +17,17 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.getOnlyElement; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMap.IteratorBasedImmutableMap; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.EnumMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableMap} backed by a non-empty {@link java.util.EnumMap}. @@ -31,14 +36,13 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault final class ImmutableEnumMap, V> extends IteratorBasedImmutableMap { static , V> ImmutableMap asImmutable(EnumMap map) { switch (map.size()) { case 0: return ImmutableMap.of(); case 1: - Entry entry = Iterables.getOnlyElement(map.entrySet()); + Entry entry = getOnlyElement(map.entrySet()); return ImmutableMap.of(entry.getKey(), entry.getValue()); default: return new ImmutableEnumMap<>(map); @@ -63,18 +67,17 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return delegate.containsKey(key); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { return delegate.get(key); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -96,13 +99,20 @@ boolean isPartialView() { // All callers of the constructor are restricted to >. @Override + @J2ktIncompatible // serialization Object writeReplace() { return new EnumSerializedForm<>(delegate); } + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EnumSerializedForm"); + } + /* * This class is used to serialize ImmutableEnumMap instances. */ + @J2ktIncompatible // serialization private static class EnumSerializedForm, V> implements Serializable { final EnumMap delegate; @@ -114,6 +124,6 @@ Object readResolve() { return new ImmutableEnumMap<>(delegate); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/ImmutableEnumSet.java b/android/guava/src/com/google/common/collect/ImmutableEnumSet.java index 90787e5d6349..d8c37fc3efcb 100644 --- a/android/guava/src/com/google/common/collect/ImmutableEnumSet.java +++ b/android/guava/src/com/google/common/collect/ImmutableEnumSet.java @@ -16,12 +16,18 @@ package com.google.common.collect; +import static com.google.common.collect.Iterables.getOnlyElement; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.concurrent.LazyInit; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Collection; import java.util.EnumSet; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableSet} backed by a non-empty {@link java.util.EnumSet}. @@ -30,17 +36,15 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault final class ImmutableEnumSet> extends ImmutableSet { - @SuppressWarnings("rawtypes") // necessary to compile against Java 8 - static ImmutableSet asImmutable(EnumSet set) { + static > ImmutableSet asImmutable(EnumSet set) { switch (set.size()) { case 0: return ImmutableSet.of(); case 1: - return ImmutableSet.of(Iterables.getOnlyElement(set)); + return ImmutableSet.of(getOnlyElement(set)); default: - return new ImmutableEnumSet(set); + return new ImmutableEnumSet<>(set); } } @@ -74,7 +78,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return delegate.contains(object); } @@ -92,7 +96,7 @@ public boolean isEmpty() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -122,13 +126,20 @@ public String toString() { // All callers of the constructor are restricted to >. @Override + @J2ktIncompatible // serialization Object writeReplace() { return new EnumSerializedForm(delegate); } + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + /* * This class is used to serialize ImmutableEnumSet instances. */ + @J2ktIncompatible // serialization private static class EnumSerializedForm> implements Serializable { final EnumSet delegate; @@ -141,6 +152,6 @@ Object readResolve() { return new ImmutableEnumSet(delegate.clone()); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/ImmutableList.java b/android/guava/src/com/google/common/collect/ImmutableList.java index 6ebd7335a5e9..b4a58dc38691 100644 --- a/android/guava/src/com/google/common/collect/ImmutableList.java +++ b/android/guava/src/com/google/common/collect/ImmutableList.java @@ -25,8 +25,9 @@ import static com.google.common.collect.ObjectArrays.checkElementsNotNull; import static com.google.common.collect.RegularImmutableList.EMPTY; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.InlineMe; @@ -40,15 +41,15 @@ import java.util.Iterator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link List} whose contents will never change, with many other important properties detailed at * {@link ImmutableCollection}. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @see ImmutableMap * @see ImmutableSet @@ -57,9 +58,21 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault public abstract class ImmutableList extends ImmutableCollection implements List, RandomAccess { + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableList}, in encounter order. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableList() { + return CollectCollectors.toImmutableList(); + } + /** * Returns the empty immutable list. This list behaves and performs comparably to {@link * Collections#emptyList}, and is preferable mainly for consistency and maintainability of your @@ -78,10 +91,10 @@ public static ImmutableList of() { * comparably to {@link Collections#singletonList}, but will not accept a null element. It is * preferable mainly for consistency and maintainability of your code. * - * @throws NullPointerException if {@code element} is null + * @throws NullPointerException if the element is null */ - public static ImmutableList of(E element) { - return construct(element); + public static ImmutableList of(E e1) { + return construct(e1); } /** @@ -232,8 +245,8 @@ public static ImmutableList copyOf(Iterable elements) { * *

    Note that if {@code list} is a {@code List}, then {@code ImmutableList.copyOf(list)} * returns an {@code ImmutableList} containing each of the strings in {@code list}, while - * ImmutableList.of(list)} returns an {@code ImmutableList>} containing one element - * (the given list itself). + * {@code ImmutableList.of(list)} returns an {@code ImmutableList>} containing one + * element (the given list itself). * *

    This method is safe to use even when {@code elements} is a synchronized or concurrent * collection that is currently being modified by another thread. @@ -288,7 +301,7 @@ public static ImmutableList copyOf(E[] elements) { * ImmutableSortedSet.copyOf(elements)}; if you want a {@code List} you can use its {@code * asList()} view. * - *

    Java 8 users: If you want to convert a {@link java.util.stream.Stream} to a sorted + *

    Java 8+ users: If you want to convert a {@link java.util.stream.Stream} to a sorted * {@code ImmutableList}, use {@code stream.sorted().collect(toImmutableList())}. * * @throws NullPointerException if any element in the input is null @@ -311,7 +324,7 @@ public static > ImmutableList sortedCopyOf( * ImmutableSortedSet.copyOf(comparator, elements)}; if you want a {@code List} you can use its * {@code asList()} view. * - *

    Java 8 users: If you want to convert a {@link java.util.stream.Stream} to a sorted + *

    Java 8+ users: If you want to convert a {@link java.util.stream.Stream} to a sorted * {@code ImmutableList}, use {@code stream.sorted(comparator).collect(toImmutableList())}. * * @throws NullPointerException if any element in the input is null @@ -375,6 +388,8 @@ public UnmodifiableListIterator listIterator(int index) { } /** A singleton implementation of iterator() for the empty ImmutableList. */ + // TODO(b/345814817): Move this to RegularImmutableList? + @SuppressWarnings("ClassInitializationDeadlock") private static final UnmodifiableListIterator EMPTY_ITR = new Itr(RegularImmutableList.EMPTY, 0); @@ -393,17 +408,17 @@ protected E get(int index) { } @Override - public int indexOf(@CheckForNull Object object) { + public int indexOf(@Nullable Object object) { return (object == null) ? -1 : Lists.indexOfImpl(this, object); } @Override - public int lastIndexOf(@CheckForNull Object object) { + public int lastIndexOf(@Nullable Object object) { return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return indexOf(object) >= 0; } @@ -413,6 +428,12 @@ public boolean contains(@CheckForNull Object object) { * Returns an immutable list of the elements between the specified {@code fromIndex}, inclusive, * and {@code toIndex}, exclusive. (If {@code fromIndex} and {@code toIndex} are equal, the empty * immutable list is returned.) + * + *

    Note: in almost all circumstances, the returned {@link ImmutableList} retains a + * strong reference to {@code this}, which may prevent the original list from being garbage + * collected. If you want the original list to be eligible for garbage collection, you should + * create and use a copy of the sub list (e.g., {@code + * ImmutableList.copyOf(originalList.subList(...))}). */ @Override public ImmutableList subList(int fromIndex, int toIndex) { @@ -450,9 +471,7 @@ public int size() { } @Override - @CheckForNull - @Nullable - Object[] internalArray() { + @Nullable Object @Nullable [] internalArray() { return ImmutableList.this.internalArray(); } @@ -482,6 +501,15 @@ public ImmutableList subList(int fromIndex, int toIndex) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } /** @@ -594,18 +622,18 @@ public ImmutableList reverse() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return forwardList.contains(object); } @Override - public int indexOf(@CheckForNull Object object) { + public int indexOf(@Nullable Object object) { int index = forwardList.lastIndexOf(object); return (index >= 0) ? reverseIndex(index) : -1; } @Override - public int lastIndexOf(@CheckForNull Object object) { + public int lastIndexOf(@Nullable Object object) { int index = forwardList.indexOf(object); return (index >= 0) ? reverseIndex(index) : -1; } @@ -631,10 +659,19 @@ public int size() { boolean isPartialView() { return forwardList.isPartialView(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return Lists.equalsImpl(this, obj); } @@ -655,6 +692,7 @@ public int hashCode() { * Serializes ImmutableLists as their logical contents. This ensures that * implementation types do not leak into the serialized representation. */ + @J2ktIncompatible // serialization static class SerializedForm implements Serializable { final Object[] elements; @@ -666,14 +704,17 @@ Object readResolve() { return copyOf(elements); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } + @J2ktIncompatible // serialization private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Use SerializedForm"); } @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization Object writeReplace() { return new SerializedForm(toArray()); } @@ -683,7 +724,7 @@ Object writeReplace() { * Builder} constructor. */ public static Builder builder() { - return new Builder(); + return new Builder<>(); } /** @@ -698,10 +739,9 @@ public static Builder builder() { * * @since 23.1 */ - @Beta public static Builder builderWithExpectedSize(int expectedSize) { checkNonnegative(expectedSize, "expectedSize"); - return new ImmutableList.Builder(expectedSize); + return new ImmutableList.Builder<>(expectedSize); } /** @@ -807,5 +847,22 @@ public ImmutableList build() { forceCopy = true; return asImmutableList(contents, size); } + + /** + * Returns a newly-created {@code ImmutableList} based on the contents of the {@code Builder}, + * sorted according to the specified comparator. + */ + @SuppressWarnings("unchecked") + ImmutableList buildSorted(Comparator comparator) { + // Currently only used by ImmutableListMultimap.Builder.orderValuesBy. + // In particular, this implies that the comparator can never get "removed," so this can't + // invalidate future builds. + + forceCopy = true; + Arrays.sort((E[]) contents, 0, size, comparator); + return asImmutableList(contents, size); + } } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xcafebabe; } diff --git a/android/guava/src/com/google/common/collect/ImmutableListMultimap.java b/android/guava/src/com/google/common/collect/ImmutableListMultimap.java index cc0a910051e0..dd17698e4069 100644 --- a/android/guava/src/com/google/common/collect/ImmutableListMultimap.java +++ b/android/guava/src/com/google/common/collect/ImmutableListMultimap.java @@ -16,9 +16,12 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.concurrent.LazyInit; @@ -31,23 +34,100 @@ import java.util.Comparator; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; /** * A {@link ListMultimap} whose contents will never change, with many other important properties * detailed at {@link ImmutableCollection}. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jared Levy * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public class ImmutableListMultimap extends ImmutableMultimap implements ListMultimap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableListMultimap} + * whose keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

    For streams with defined encounter order (as defined in the Ordering section of the {@link + * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. + * + *

    Example: + * + *

    {@code
    +   * static final Multimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(toImmutableListMultimap(str -> str.charAt(0), str -> str.substring(1)));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final Multimap FIRST_LETTER_MULTIMAP =
    +   *     new ImmutableListMultimap.Builder()
    +   *         .put('b', "anana")
    +   *         .putAll('a', "pple", "sparagus")
    +   *         .putAll('c', "arrot", "herry")
    +   *         .build();
    +   * }
    + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableListMultimap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction); + } + + /** + * Returns a {@code Collector} accumulating entries into an {@code ImmutableListMultimap}. Each + * input element is mapped to a key and a stream of values, each of which are put into the + * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the + * streams of values. + * + *

    Example: + * + *

    {@code
    +   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(
    +   *             flatteningToImmutableListMultimap(
    +   *                  str -> str.charAt(0),
    +   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
    +   *     ImmutableListMultimap.builder()
    +   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
    +   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
    +   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
    +   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
    +   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
    +   *         .build();
    +   * }
    +   * }
    + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> flatteningToImmutableListMultimap( + Function keyFunction, + Function> valuesFunction) { + return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction); + } /** * Returns the empty multimap. @@ -117,6 +197,19 @@ public static Builder builder() { return new Builder<>(); } + /** + * Returns a new builder with a hint for how many distinct keys are expected to be added. The + * generated builder is equivalent to that returned by {@link #builder}, but may perform better if + * {@code expectedKeys} is a good estimate. + * + * @throws IllegalArgumentException if {@code expectedKeys} is negative + * @since 33.3.0 + */ + public static Builder builderWithExpectedKeys(int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new Builder<>(expectedKeys); + } + /** * A builder for creating immutable {@code ListMultimap} instances, especially {@code public * static final} multimaps ("constant multimaps"). Example: @@ -143,6 +236,23 @@ public static final class Builder extends ImmutableMultimap.Builder */ public Builder() {} + /** Creates a new builder with a hint for the number of distinct keys. */ + Builder(int expectedKeys) { + super(expectedKeys); + } + + /** + * {@inheritDoc} + * + * @since 33.3.0 + */ + @CanIgnoreReturnValue + @Override + public Builder expectedValuesPerKey(int expectedValuesPerKey) { + super.expectedValuesPerKey(expectedValuesPerKey); + return this; + } + @CanIgnoreReturnValue @Override public Builder put(K key, V value) { @@ -168,7 +278,6 @@ public Builder put(Entry entry) { * @since 19.0 */ @CanIgnoreReturnValue - @Beta @Override public Builder putAll(Iterable> entries) { super.putAll(entries); @@ -196,6 +305,13 @@ public Builder putAll(Multimap multimap) { return this; } + @CanIgnoreReturnValue + @Override + Builder combine(ImmutableMultimap.Builder other) { + super.combine(other); + return this; + } + /** * {@inheritDoc} * @@ -220,13 +336,6 @@ public Builder orderValuesBy(Comparator valueComparator) { return this; } - @CanIgnoreReturnValue - @Override - Builder combine(ImmutableMultimap.Builder other) { - super.combine(other); - return this; - } - /** Returns a newly-created immutable list multimap. */ @Override public ImmutableListMultimap build() { @@ -271,7 +380,6 @@ public static ImmutableListMultimap copyOf( * @throws NullPointerException if any key, value, or entry is null * @since 19.0 */ - @Beta public static ImmutableListMultimap copyOf( Iterable> entries) { return new Builder().putAll(entries).build(); @@ -301,7 +409,30 @@ static ImmutableListMultimap fromMapEntries( } } - return new ImmutableListMultimap<>(builder.build(), size); + return new ImmutableListMultimap<>(builder.buildOrThrow(), size); + } + + /** Creates an ImmutableListMultimap from an asMap.entrySet. */ + static ImmutableListMultimap fromMapBuilderEntries( + Collection>> mapEntries, + @Nullable Comparator valueComparator) { + if (mapEntries.isEmpty()) { + return of(); + } + ImmutableMap.Builder> builder = + new ImmutableMap.Builder<>(mapEntries.size()); + int size = 0; + + for (Entry> entry : mapEntries) { + K key = entry.getKey(); + ImmutableList.Builder values = (ImmutableList.Builder) entry.getValue(); + ImmutableList list = + (valueComparator == null) ? values.build() : values.buildSorted(valueComparator); + builder.put(key, list); + size += list.size(); + } + + return new ImmutableListMultimap<>(builder.buildOrThrow(), size); } ImmutableListMultimap(ImmutableMap> map, int size) { @@ -322,7 +453,7 @@ public ImmutableList get(K key) { return (list == null) ? ImmutableList.of() : list; } - @LazyInit @RetainedWith @CheckForNull private transient ImmutableListMultimap inverse; + @LazyInit @RetainedWith private transient @Nullable ImmutableListMultimap inverse; /** * {@inheritDoc} @@ -359,7 +490,7 @@ private ImmutableListMultimap invert() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final ImmutableList removeAll(@CheckForNull Object key) { + public final ImmutableList removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -382,12 +513,14 @@ public final ImmutableList replaceValues(K key, Iterable values) * values for that key, and the key's values */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); Serialization.writeMultimap(this, stream); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int keyCount = stream.readInt(); @@ -398,7 +531,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo int tmpSize = 0; for (int i = 0; i < keyCount; i++) { - Object key = stream.readObject(); + Object key = requireNonNull(stream.readObject()); int valueCount = stream.readInt(); if (valueCount <= 0) { throw new InvalidObjectException("Invalid value count " + valueCount); @@ -406,7 +539,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo ImmutableList.Builder valuesBuilder = ImmutableList.builder(); for (int j = 0; j < valueCount; j++) { - valuesBuilder.add(stream.readObject()); + valuesBuilder.add(requireNonNull(stream.readObject())); } builder.put(key, valuesBuilder.build()); tmpSize += valueCount; @@ -414,7 +547,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo ImmutableMap> tmpMap; try { - tmpMap = builder.build(); + tmpMap = builder.buildOrThrow(); } catch (IllegalArgumentException e) { throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); } @@ -423,6 +556,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); } - @GwtIncompatible // Not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ImmutableMap.java b/android/guava/src/com/google/common/collect/ImmutableMap.java index fd87e0b0d23f..21617df3b3b8 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableMap.java @@ -20,35 +20,45 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static java.lang.System.arraycopy; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotMock; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import com.google.j2objc.annotations.WeakOuter; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.AbstractMap; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.SortedMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; /** * A {@link Map} whose contents will never change, with many other important properties detailed at * {@link ImmutableCollection}. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jesse Wilson * @author Kevin Bourrillion @@ -57,9 +67,52 @@ @DoNotMock("Use ImmutableMap.of or another implementation") @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault public abstract class ImmutableMap implements Map, Serializable { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * Entries appear in the result {@code ImmutableMap} in encounter order. + * + *

    If the mapped keys contain duplicates (according to {@link Object#equals(Object)}, an {@code + * IllegalArgumentException} is thrown when the collection operation is performed. (This differs + * from the {@code Collector} returned by {@link Collectors#toMap(Function, Function)}, which + * throws an {@code IllegalStateException}.) + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableMap(keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * + *

    If the mapped keys contain duplicates (according to {@link Object#equals(Object)}), the + * values are merged using the specified merging function. If the merging function returns {@code + * null}, then the collector removes the value that has been computed for the key thus far (though + * future occurrences of the key would reinsert it). + * + *

    Entries will appear in the encounter order of the first occurrence of the key. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableMap(keyFunction, valueFunction, mergeFunction); + } + /** * Returns the empty map. This map behaves and performs comparably to {@link * Collections#emptyMap}, and is preferable mainly for consistency and maintainability of your @@ -150,6 +203,7 @@ public static ImmutableMap of( return RegularImmutableMap.create( 6, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6}); } + /** * Returns an immutable map containing the given entries, in order. * @@ -168,6 +222,7 @@ public static ImmutableMap of( return RegularImmutableMap.create( 7, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7}); } + /** * Returns an immutable map containing the given entries, in order. * @@ -202,6 +257,7 @@ public static ImmutableMap of( return RegularImmutableMap.create( 8, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8}); } + /** * Returns an immutable map containing the given entries, in order. * @@ -239,6 +295,7 @@ public static ImmutableMap of( return RegularImmutableMap.create( 9, new Object[] {k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9}); } + /** * Returns an immutable map containing the given entries, in order. * @@ -330,14 +387,13 @@ public static Builder builder() { * * @since 23.1 */ - @Beta public static Builder builderWithExpectedSize(int expectedSize) { checkNonnegative(expectedSize, "expectedSize"); return new Builder<>(expectedSize); } static void checkNoConflict( - boolean safe, String conflictDescription, Entry entry1, Entry entry2) { + boolean safe, String conflictDescription, Object entry1, Object entry2) { if (!safe) { throw conflictException(conflictDescription, entry1, entry2); } @@ -380,11 +436,17 @@ static IllegalArgumentException conflictException( */ @DoNotMock public static class Builder { - @CheckForNull Comparator valueComparator; + @Nullable Comparator valueComparator; @Nullable Object[] alternatingKeysAndValues; int size; boolean entriesUsed; + /** + * If non-null, a duplicate key we found in a previous buildKeepingLast() or buildOrThrow() + * call. A later buildOrThrow() can simply report this duplicate immediately. + */ + @Nullable DuplicateKey duplicateKey; + /** * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableMap#builder}. @@ -412,8 +474,9 @@ private void ensureCapacity(int minCapacity) { } /** - * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed, - * and will cause {@link #build} to fail. + * Associates {@code key} with {@code value} in the built map. If the same key is put more than + * once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last + * value put for that key. */ @CanIgnoreReturnValue public Builder put(K key, V value) { @@ -426,8 +489,9 @@ public Builder put(K key, V value) { } /** - * Adds the given {@code entry} to the map, making it immutable if necessary. Duplicate keys are - * not allowed, and will cause {@link #build} to fail. + * Adds the given {@code entry} to the map, making it immutable if necessary. If the same key is + * put more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will + * keep the last value put for that key. * * @since 11.0 */ @@ -437,8 +501,9 @@ public Builder put(Entry entry) { } /** - * Associates all of the given map's keys and values in the built map. Duplicate keys are not - * allowed, and will cause {@link #build} to fail. + * Associates all of the given map's keys and values in the built map. If the same key is put + * more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep + * the last value put for that key. * * @throws NullPointerException if any key or value in {@code map} is null */ @@ -448,14 +513,14 @@ public Builder putAll(Map map) { } /** - * Adds all of the given entries to the built map. Duplicate keys are not allowed, and will - * cause {@link #build} to fail. + * Adds all of the given entries to the built map. If the same key is put more than once, {@link + * #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last value put for + * that key. * * @throws NullPointerException if any key, value, or entry is null * @since 19.0 */ @CanIgnoreReturnValue - @Beta public Builder putAll(Iterable> entries) { if (entries instanceof Collection) { ensureCapacity(size + ((Collection) entries).size()); @@ -477,7 +542,6 @@ public Builder putAll(Iterable> * @since 19.0 */ @CanIgnoreReturnValue - @Beta public Builder orderEntriesByValue(Comparator valueComparator) { checkState(this.valueComparator == null, "valueComparator was already set"); this.valueComparator = checkNotNull(valueComparator, "valueComparator"); @@ -488,7 +552,7 @@ public Builder orderEntriesByValue(Comparator valueComparator) Builder combine(Builder other) { checkNotNull(other); ensureCapacity(this.size + other.size); - System.arraycopy( + arraycopy( other.alternatingKeysAndValues, 0, this.alternatingKeysAndValues, @@ -498,10 +562,46 @@ Builder combine(Builder other) { return this; } - /* - * TODO(kevinb): Should build() and the ImmutableBiMap & ImmutableSortedMap - * versions throw an IllegalStateException instead? - */ + private ImmutableMap build(boolean throwIfDuplicateKeys) { + if (throwIfDuplicateKeys && duplicateKey != null) { + throw duplicateKey.exception(); + } + /* + * If entries is full, then this implementation may end up using the entries array + * directly and writing over the entry objects with non-terminal entries, but this is + * safe; if this Builder is used further, it will grow the entries array (so it can't + * affect the original array), and future build() calls will always copy any entry + * objects that cannot be safely reused. + */ + // localAlternatingKeysAndValues is an alias for the alternatingKeysAndValues field, except if + // we end up removing duplicates in a copy of the array. + @Nullable Object[] localAlternatingKeysAndValues; + int localSize = size; + if (valueComparator == null) { + localAlternatingKeysAndValues = alternatingKeysAndValues; + } else { + if (entriesUsed) { + alternatingKeysAndValues = Arrays.copyOf(alternatingKeysAndValues, 2 * size); + } + localAlternatingKeysAndValues = alternatingKeysAndValues; + if (!throwIfDuplicateKeys) { + // We want to retain only the last-put value for any given key, before sorting. + // This could be improved, but orderEntriesByValue is rather rarely used anyway. + localAlternatingKeysAndValues = lastEntryForEachKey(localAlternatingKeysAndValues, size); + if (localAlternatingKeysAndValues.length < alternatingKeysAndValues.length) { + localSize = localAlternatingKeysAndValues.length >>> 1; + } + } + sortEntries(localAlternatingKeysAndValues, localSize, valueComparator); + } + entriesUsed = true; + ImmutableMap map = + RegularImmutableMap.create(localSize, localAlternatingKeysAndValues, this); + if (throwIfDuplicateKeys && duplicateKey != null) { + throw duplicateKey.exception(); + } + return map; + } /** * Returns a newly-created immutable map. The iteration order of the returned map is the order @@ -527,40 +627,92 @@ public ImmutableMap build() { * @throws IllegalArgumentException if duplicate keys were added * @since 31.0 */ - @SuppressWarnings("unchecked") public ImmutableMap buildOrThrow() { - /* - * If entries is full, then this implementation may end up using the entries array - * directly and writing over the entry objects with non-terminal entries, but this is - * safe; if this Builder is used further, it will grow the entries array (so it can't - * affect the original array), and future build() calls will always copy any entry - * objects that cannot be safely reused. - */ - sortEntries(); - entriesUsed = true; - return RegularImmutableMap.create(size, alternatingKeysAndValues); + return build(true); } - void sortEntries() { - if (valueComparator != null) { - if (entriesUsed) { - alternatingKeysAndValues = Arrays.copyOf(alternatingKeysAndValues, 2 * size); - } - Entry[] entries = new Entry[size]; - for (int i = 0; i < size; i++) { - // requireNonNull is safe because the first `2*size` elements have been filled in. - entries[i] = - new AbstractMap.SimpleImmutableEntry( - (K) requireNonNull(alternatingKeysAndValues[2 * i]), - (V) requireNonNull(alternatingKeysAndValues[2 * i + 1])); + /** + * Returns a newly-created immutable map, using the last value for any key that was added more + * than once. The iteration order of the returned map is the order in which entries were + * inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case + * entries are sorted by value. If a key was added more than once, it appears in iteration order + * based on the first time it was added, again unless {@link #orderEntriesByValue} was called. + * + *

    In the current implementation, all values associated with a given key are stored in the + * {@code Builder} object, even though only one of them will be used in the built map. If there + * can be many repeated keys, it may be more space-efficient to use a {@link + * java.util.LinkedHashMap LinkedHashMap} and {@link ImmutableMap#copyOf(Map)} rather than + * {@code ImmutableMap.Builder}. + * + * @since 31.1 + */ + public ImmutableMap buildKeepingLast() { + return build(false); + } + + static void sortEntries( + @Nullable Object[] alternatingKeysAndValues, + int size, + Comparator valueComparator) { + @SuppressWarnings({"rawtypes", "unchecked"}) + Entry[] entries = new Entry[size]; + for (int i = 0; i < size; i++) { + // requireNonNull is safe because the first `2*size` elements have been filled in. + Object key = requireNonNull(alternatingKeysAndValues[2 * i]); + @SuppressWarnings("unchecked") + V value = (V) requireNonNull(alternatingKeysAndValues[2 * i + 1]); + entries[i] = new AbstractMap.SimpleImmutableEntry(key, value); + } + Arrays.sort( + entries, 0, size, Ordering.from(valueComparator).onResultOf(Maps.valueFunction())); + for (int i = 0; i < size; i++) { + alternatingKeysAndValues[2 * i] = entries[i].getKey(); + alternatingKeysAndValues[2 * i + 1] = entries[i].getValue(); + } + } + + private @Nullable Object[] lastEntryForEachKey( + @Nullable Object[] localAlternatingKeysAndValues, int size) { + Set seenKeys = new HashSet<>(); + BitSet dups = new BitSet(); // slots that are overridden by a later duplicate key + for (int i = size - 1; i >= 0; i--) { + Object key = requireNonNull(localAlternatingKeysAndValues[2 * i]); + if (!seenKeys.add(key)) { + dups.set(i); } - Arrays.sort( - entries, 0, size, Ordering.from(valueComparator).onResultOf(Maps.valueFunction())); - for (int i = 0; i < size; i++) { - alternatingKeysAndValues[2 * i] = entries[i].getKey(); - alternatingKeysAndValues[2 * i + 1] = entries[i].getValue(); + } + if (dups.isEmpty()) { + return localAlternatingKeysAndValues; + } + Object[] newAlternatingKeysAndValues = new Object[(size - dups.cardinality()) * 2]; + for (int inI = 0, outI = 0; inI < size * 2; ) { + if (dups.get(inI >>> 1)) { + inI += 2; + } else { + newAlternatingKeysAndValues[outI++] = + requireNonNull(localAlternatingKeysAndValues[inI++]); + newAlternatingKeysAndValues[outI++] = + requireNonNull(localAlternatingKeysAndValues[inI++]); } } + return newAlternatingKeysAndValues; + } + + static final class DuplicateKey { + private final Object key; + private final Object value1; + private final Object value2; + + DuplicateKey(Object key, Object value1, Object value2) { + this.key = key; + this.value1 = value1; + this.value2 = value2; + } + + IllegalArgumentException exception() { + return new IllegalArgumentException( + "Multiple entries with same key: " + key + "=" + value1 + " and " + key + "=" + value2); + } } } @@ -595,7 +747,6 @@ public static ImmutableMap copyOf(Map map * @throws IllegalArgumentException if two entries have the same key * @since 19.0 */ - @Beta public static ImmutableMap copyOf( Iterable> entries) { int initialCapacity = @@ -629,6 +780,15 @@ ImmutableMap map() { public UnmodifiableIterator> iterator() { return entryIterator(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } return new EntrySetImpl(); } @@ -637,6 +797,15 @@ public UnmodifiableIterator> iterator() { ImmutableCollection createValues() { return new ImmutableMapValues<>(this); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } ImmutableMap() {} @@ -651,8 +820,7 @@ ImmutableCollection createValues() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final V put(K k, V v) { + public final @Nullable V put(K k, V v) { throw new UnsupportedOperationException(); } @@ -665,8 +833,7 @@ public final V put(K k, V v) { @CanIgnoreReturnValue @Deprecated @Override - @CheckForNull - public final V remove(@CheckForNull Object o) { + public final @Nullable V remove(@Nullable Object o) { throw new UnsupportedOperationException(); } @@ -702,19 +869,18 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return values().contains(value); } // Overriding to mark it Nullable @Override - @CheckForNull - public abstract V get(@CheckForNull Object key); + public abstract @Nullable V get(@Nullable Object key); /** * {@inheritDoc} @@ -725,11 +891,36 @@ public boolean containsValue(@CheckForNull Object value) { * * @since 23.5 (but since 21.0 in the JRE flavor). - * Note that API Level 24 users can call this method with any version of Guava. + * Note, however, that Java 8+ users can call this method with any version and flavor of + * Guava. */ - // @Override under Java 8 / API Level 24 - @CheckForNull - public final V getOrDefault(@CheckForNull Object key, @CheckForNull V defaultValue) { + @Override + public final @Nullable V getOrDefault(@Nullable Object key, @Nullable V defaultValue) { + /* + * Even though it's weird to pass a defaultValue that is null, some callers do so. Those who + * pass a literal "null" should probably just use `get`, but I would expect other callers to + * pass an expression that *might* be null. This could happen with: + * + * - a `getFooOrDefault(@Nullable Foo defaultValue)` method that returns + * `map.getOrDefault(FOO_KEY, defaultValue)` + * + * - a call that consults a chain of maps, as in `mapA.getOrDefault(key, mapB.getOrDefault(key, + * ...))` + * + * So it makes sense for the parameter (and thus the return type) to be @Nullable. + * + * Two other points: + * + * 1. We'll want to use something like @PolyNull once we can make that work for the various + * platforms we target. + * + * 2. Kotlin's Map type has a getOrDefault method that accepts and returns a "plain V," in + * contrast to the "V?" type that we're using. As a result, Kotlin sees a conflict between the + * nullness annotations in ImmutableMap and those in its own Map type. In response, it considers + * the parameter and return type both to be platform types. As a result, Kotlin permits calls + * that can lead to NullPointerException. That's unfortunate. But hopefully most Kotlin callers + * use `get(key) ?: defaultValue` instead of this method, anyway. + */ V result = get(key); // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. if (result != null) { @@ -739,7 +930,7 @@ public final V getOrDefault(@CheckForNull Object key, @CheckForNull V defaultVal } } - @LazyInit @RetainedWith @CheckForNull private transient ImmutableSet> entrySet; + @LazyInit @RetainedWith private transient @Nullable ImmutableSet> entrySet; /** * Returns an immutable set of the mappings in this map. The iteration order is specified by the @@ -753,7 +944,7 @@ public ImmutableSet> entrySet() { abstract ImmutableSet> createEntrySet(); - @LazyInit @RetainedWith @CheckForNull private transient ImmutableSet keySet; + @LazyInit @RetainedWith private transient @Nullable ImmutableSet keySet; /** * Returns an immutable set of the keys in this map, in the same order that they appear in {@link @@ -773,7 +964,7 @@ public ImmutableSet keySet() { abstract ImmutableSet createKeySet(); UnmodifiableIterator keyIterator() { - final UnmodifiableIterator> entryIterator = entrySet().iterator(); + UnmodifiableIterator> entryIterator = entrySet().iterator(); return new UnmodifiableIterator() { @Override public boolean hasNext() { @@ -787,7 +978,7 @@ public K next() { }; } - @LazyInit @RetainedWith @CheckForNull private transient ImmutableCollection values; + @LazyInit @RetainedWith private transient @Nullable ImmutableCollection values; /** * Returns an immutable collection of the values in this map, in the same order that they appear @@ -807,7 +998,7 @@ public ImmutableCollection values() { abstract ImmutableCollection createValues(); // cached so that this.multimapView().inverse() only computes inverse once - @LazyInit @CheckForNull private transient ImmutableSetMultimap multimapView; + @LazyInit private transient @Nullable ImmutableSetMultimap multimapView; /** * Returns a multimap view of the map. @@ -840,13 +1031,12 @@ ImmutableSet createKeySet() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return ImmutableMap.this.containsKey(key); } @Override - @CheckForNull - public ImmutableSet get(@CheckForNull Object key) { + public @Nullable ImmutableSet get(@Nullable Object key) { V outerValue = ImmutableMap.this.get(key); return (outerValue == null) ? null : ImmutableSet.of(outerValue); } @@ -869,7 +1059,7 @@ boolean isHashCodeFast() { @Override UnmodifiableIterator>> entryIterator() { - final Iterator> backingIterator = ImmutableMap.this.entrySet().iterator(); + Iterator> backingIterator = ImmutableMap.this.entrySet().iterator(); return new UnmodifiableIterator>>() { @Override public boolean hasNext() { @@ -878,7 +1068,7 @@ public boolean hasNext() { @Override public Entry> next() { - final Entry backingEntry = backingIterator.next(); + Entry backingEntry = backingIterator.next(); return new AbstractMapEntry>() { @Override public K getKey() { @@ -893,10 +1083,19 @@ public ImmutableSet getValue() { } }; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return Maps.equalsImpl(this, object); } @@ -921,6 +1120,7 @@ public String toString() { * reconstructed using public factory methods. This ensures that the implementation types remain * as implementation details. */ + @J2ktIncompatible // serialization static class SerializedForm implements Serializable { // This object retains references to collections returned by keySet() and value(). This saves // bytes when the both the map and its keySet or value collection are written to the same @@ -969,7 +1169,7 @@ final Object readResolve() { builder.put(keyIter.next(), valueIter.next()); } - return builder.build(); + return builder.buildOrThrow(); } @SuppressWarnings("unchecked") @@ -982,7 +1182,7 @@ final Object legacyReadResolve() { for (int i = 0; i < keys.length; i++) { builder.put(keys[i], values[i]); } - return builder.build(); + return builder.buildOrThrow(); } /** @@ -992,7 +1192,7 @@ Builder makeBuilder(int size) { return new Builder<>(size); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -1000,7 +1200,15 @@ Builder makeBuilder(int size) { * method. Publicly-accessible subclasses must override this method and should return a subclass * of SerializedForm whose readResolve() method returns objects of the subclass type. */ + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm<>(this); } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableMapEntrySet.java b/android/guava/src/com/google/common/collect/ImmutableMapEntrySet.java index 7ae6422e2dfb..3136a414b821 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMapEntrySet.java +++ b/android/guava/src/com/google/common/collect/ImmutableMapEntrySet.java @@ -18,10 +18,12 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * {@code entrySet()} implementation for {@link ImmutableMap}. @@ -30,7 +32,6 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ImmutableMapEntrySet extends ImmutableSet> { static final class RegularEntrySet extends ImmutableMapEntrySet { private final transient ImmutableMap map; @@ -65,6 +66,15 @@ public UnmodifiableIterator> iterator() { ImmutableList> createAsList() { return entries; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } ImmutableMapEntrySet() {} @@ -77,7 +87,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object instanceof Entry) { Entry entry = (Entry) object; V value = map().get(entry.getKey()); @@ -103,12 +113,20 @@ public int hashCode() { } @GwtIncompatible // serialization + @J2ktIncompatible @Override Object writeReplace() { return new EntrySetSerializedForm<>(map()); } @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EntrySetSerializedForm"); + } + + @GwtIncompatible // serialization + @J2ktIncompatible private static class EntrySetSerializedForm implements Serializable { final ImmutableMap map; @@ -120,6 +138,6 @@ Object readResolve() { return map.entrySet(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/ImmutableMapKeySet.java b/android/guava/src/com/google/common/collect/ImmutableMapKeySet.java index 9bd1d43386b8..fc22cbb8ac5f 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMapKeySet.java +++ b/android/guava/src/com/google/common/collect/ImmutableMapKeySet.java @@ -18,8 +18,9 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * {@code keySet()} implementation for {@link ImmutableMap}. @@ -28,7 +29,6 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class ImmutableMapKeySet extends IndexedImmutableSet { private final ImmutableMap map; @@ -47,7 +47,7 @@ public UnmodifiableIterator iterator() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return map.containsKey(object); } @@ -61,13 +61,15 @@ boolean isPartialView() { return true; } - @GwtIncompatible // serialization @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization Object writeReplace() { return new KeySetSerializedForm(map); } @GwtIncompatible // serialization + @J2ktIncompatible private static class KeySetSerializedForm implements Serializable { final ImmutableMap map; @@ -79,6 +81,6 @@ Object readResolve() { return map.keySet(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/ImmutableMapValues.java b/android/guava/src/com/google/common/collect/ImmutableMapValues.java index 8b7284d1e33c..c85b1a1b93e1 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMapValues.java +++ b/android/guava/src/com/google/common/collect/ImmutableMapValues.java @@ -18,9 +18,10 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Map.Entry; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * {@code values()} implementation for {@link ImmutableMap}. @@ -29,7 +30,6 @@ * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class ImmutableMapValues extends ImmutableCollection { private final ImmutableMap map; @@ -60,7 +60,7 @@ public V next() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return object != null && Iterators.contains(iterator(), object); } @@ -71,7 +71,7 @@ boolean isPartialView() { @Override public ImmutableList asList() { - final ImmutableList> entryList = map.entrySet().asList(); + ImmutableList> entryList = map.entrySet().asList(); return new ImmutableList() { @Override public V get(int index) { @@ -87,6 +87,15 @@ boolean isPartialView() { public int size() { return entryList.size(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } @@ -97,6 +106,7 @@ Object writeReplace() { } @GwtIncompatible // serialization + @J2ktIncompatible private static class SerializedForm implements Serializable { final ImmutableMap map; @@ -108,6 +118,6 @@ Object readResolve() { return map.values(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/collect/ImmutableMultimap.java b/android/guava/src/com/google/common/collect/ImmutableMultimap.java index 04a6c4a412ac..b50053445439 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMultimap.java +++ b/android/guava/src/com/google/common/collect/ImmutableMultimap.java @@ -18,28 +18,31 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.Iterators.emptyIterator; import static com.google.common.collect.Maps.immutableEntry; +import static java.lang.Math.max; +import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotMock; import com.google.j2objc.annotations.Weak; import com.google.j2objc.annotations.WeakOuter; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link Multimap} whose contents will never change, with many other important properties @@ -62,13 +65,12 @@ * immediately after the last entry having that key. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jared Levy * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class ImmutableMultimap extends BaseImmutableMultimap implements Serializable { @@ -126,6 +128,19 @@ public static Builder builder() { return new Builder<>(); } + /** + * Returns a new builder with a hint for how many distinct keys are expected to be added. The + * generated builder is equivalent to that returned by {@link #builder}, but may perform better if + * {@code expectedKeys} is a good estimate. + * + * @throws IllegalArgumentException if {@code expectedKeys} is negative + * @since 33.3.0 + */ + public static Builder builderWithExpectedKeys(int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new Builder<>(expectedKeys); + } + /** * A builder for creating immutable multimap instances, especially {@code public static final} * multimaps ("constant multimaps"). Example: @@ -147,31 +162,86 @@ public static Builder builder() { */ @DoNotMock public static class Builder { - final Map> builderMap; - @CheckForNull Comparator keyComparator; - @CheckForNull Comparator valueComparator; + @Nullable Map> builderMap; + @Nullable Comparator keyComparator; + @Nullable Comparator valueComparator; + int expectedValuesPerKey = ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY; /** * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableMultimap#builder}. */ - public Builder() { - this.builderMap = Platform.preservesInsertionOrderOnPutsMap(); + public Builder() {} + + /** Creates a new builder with a hint for the number of distinct keys. */ + Builder(int expectedKeys) { + if (expectedKeys > 0) { + builderMap = Platform.preservesInsertionOrderOnPutsMapWithExpectedSize(expectedKeys); + } + // otherwise, leave it null to be constructed lazily + } + + Map> ensureBuilderMapNonNull() { + Map> result = builderMap; + if (result == null) { + result = Platform.preservesInsertionOrderOnPutsMap(); + builderMap = result; + } + return result; + } + + ImmutableCollection.Builder newValueCollectionBuilderWithExpectedSize(int expectedSize) { + return ImmutableList.builderWithExpectedSize(expectedSize); + } + + /** + * Provides a hint for how many values will be associated with each key newly added to the + * builder after this call. This does not change semantics, but may improve performance if + * {@code expectedValuesPerKey} is a good estimate. + * + *

    This may be called more than once; each newly added key will use the most recent call to + * {@link #expectedValuesPerKey} as its hint. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey} is negative + * @since 33.3.0 + */ + @CanIgnoreReturnValue + public Builder expectedValuesPerKey(int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + + // Always presize to at least 1, since we only bother creating a value collection if there's + // at least one element. + this.expectedValuesPerKey = max(expectedValuesPerKey, 1); + + return this; } - Collection newMutableValueCollection() { - return new ArrayList<>(); + /** + * By default, if we are handed a value collection bigger than expectedValuesPerKey, presize to + * accept that many elements. + * + *

    This gets overridden in ImmutableSetMultimap.Builder to only trust the size of {@code + * values} if it is a Set and therefore probably already deduplicated. + */ + int expectedValueCollectionSize(int defaultExpectedValues, Iterable values) { + if (values instanceof Collection) { + Collection collection = (Collection) values; + return max(defaultExpectedValues, collection.size()); + } else { + return defaultExpectedValues; + } } /** Adds a key-value mapping to the built multimap. */ @CanIgnoreReturnValue public Builder put(K key, V value) { checkEntryNotNull(key, value); - Collection valueCollection = builderMap.get(key); - if (valueCollection == null) { - builderMap.put(key, valueCollection = newMutableValueCollection()); + ImmutableCollection.Builder valuesBuilder = ensureBuilderMapNonNull().get(key); + if (valuesBuilder == null) { + valuesBuilder = newValueCollectionBuilderWithExpectedSize(expectedValuesPerKey); + ensureBuilderMapNonNull().put(key, valuesBuilder); } - valueCollection.add(value); + valuesBuilder.add(value); return this; } @@ -191,7 +261,6 @@ public Builder put(Entry entry) { * @since 19.0 */ @CanIgnoreReturnValue - @Beta public Builder putAll(Iterable> entries) { for (Entry entry : entries) { put(entry); @@ -210,25 +279,22 @@ public Builder putAll(K key, Iterable values) { if (key == null) { throw new NullPointerException("null key in entry: null=" + Iterables.toString(values)); } - Collection valueCollection = builderMap.get(key); - if (valueCollection != null) { - for (V value : values) { - checkEntryNotNull(key, value); - valueCollection.add(value); - } - return this; - } Iterator valuesItr = values.iterator(); if (!valuesItr.hasNext()) { return this; } - valueCollection = newMutableValueCollection(); + ImmutableCollection.Builder valuesBuilder = ensureBuilderMapNonNull().get(key); + if (valuesBuilder == null) { + valuesBuilder = + newValueCollectionBuilderWithExpectedSize( + expectedValueCollectionSize(expectedValuesPerKey, values)); + ensureBuilderMapNonNull().put(key, valuesBuilder); + } while (valuesItr.hasNext()) { V value = valuesItr.next(); checkEntryNotNull(key, value); - valueCollection.add(value); + valuesBuilder.add(value); } - builderMap.put(key, valueCollection); return this; } @@ -240,7 +306,7 @@ public Builder putAll(K key, Iterable values) { */ @CanIgnoreReturnValue public Builder putAll(K key, V... values) { - return putAll(key, Arrays.asList(values)); + return putAll(key, asList(values)); } /** @@ -284,19 +350,24 @@ public Builder orderValuesBy(Comparator valueComparator) { @CanIgnoreReturnValue Builder combine(Builder other) { - for (Map.Entry> entry : other.builderMap.entrySet()) { - putAll(entry.getKey(), entry.getValue()); + if (other.builderMap != null) { + for (Map.Entry> entry : other.builderMap.entrySet()) { + putAll(entry.getKey(), entry.getValue().build()); + } } return this; } /** Returns a newly-created immutable multimap. */ public ImmutableMultimap build() { - Collection>> mapEntries = builderMap.entrySet(); + if (builderMap == null) { + return ImmutableListMultimap.of(); + } + Collection>> mapEntries = builderMap.entrySet(); if (keyComparator != null) { mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); } - return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator); + return ImmutableListMultimap.fromMapBuilderEntries(mapEntries, valueComparator); } } @@ -329,7 +400,6 @@ public static ImmutableMultimap copyOf(Multimap ImmutableMultimap copyOf( Iterable> entries) { return ImmutableListMultimap.copyOf(entries); @@ -342,10 +412,11 @@ public static ImmutableMultimap copyOf( // holder class makes sure they are not initialized unless an instance is // deserialized. @GwtIncompatible // java serialization is not supported + @J2ktIncompatible static class FieldSettersHolder { - static final Serialization.FieldSetter MAP_FIELD_SETTER = + static final Serialization.FieldSetter> MAP_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "map"); - static final Serialization.FieldSetter SIZE_FIELD_SETTER = + static final Serialization.FieldSetter> SIZE_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "size"); } @@ -369,7 +440,7 @@ static class FieldSettersHolder { // DoNotCall wants this to be final, but we want to override it to return more specific types. // Inheritance is closed, and all subtypes are @DoNotCall, so this is safe to suppress. @SuppressWarnings("DoNotCall") - public ImmutableCollection removeAll(@CheckForNull Object key) { + public ImmutableCollection removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -471,7 +542,7 @@ public final boolean putAll(Multimap multimap) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public final boolean remove(@Nullable Object key, @Nullable Object value) { throw new UnsupportedOperationException(); } @@ -488,12 +559,12 @@ boolean isPartialView() { // accessors @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return value != null && super.containsValue(value); } @@ -567,7 +638,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object instanceof Entry) { Entry entry = (Entry) object; return multimap.containsEntry(entry.getKey(), entry.getValue()); @@ -575,7 +646,16 @@ public boolean contains(@CheckForNull Object object) { return false; } - private static final long serialVersionUID = 0; + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @Override @@ -583,8 +663,8 @@ UnmodifiableIterator> entryIterator() { return new UnmodifiableIterator>() { final Iterator>> asMapItr = map.entrySet().iterator(); - @CheckForNull K currentKey = null; - Iterator valueItr = Iterators.emptyIterator(); + @Nullable K currentKey = null; + Iterator valueItr = emptyIterator(); @Override public boolean hasNext() { @@ -626,12 +706,12 @@ ImmutableMultiset createKeys() { @WeakOuter class Keys extends ImmutableMultiset { @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return containsKey(object); } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { Collection values = map.get(element); return (values == null) ? 0 : values.size(); } @@ -658,13 +738,21 @@ boolean isPartialView() { } @GwtIncompatible + @J2ktIncompatible @Override Object writeReplace() { return new KeysSerializedForm(ImmutableMultimap.this); } + + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use KeysSerializedForm"); + } } @GwtIncompatible + @J2ktIncompatible private static final class KeysSerializedForm implements Serializable { final ImmutableMultimap multimap; @@ -695,7 +783,7 @@ ImmutableCollection createValues() { UnmodifiableIterator valueIterator() { return new UnmodifiableIterator() { Iterator> valueCollectionItr = map.values().iterator(); - Iterator valueItr = Iterators.emptyIterator(); + Iterator valueItr = emptyIterator(); @Override public boolean hasNext() { @@ -720,7 +808,7 @@ private static final class Values extends ImmutableCollection { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return multimap.containsValue(object); } @@ -748,8 +836,17 @@ boolean isPartialView() { return true; } - private static final long serialVersionUID = 0; + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ImmutableMultiset.java b/android/guava/src/com/google/common/collect/ImmutableMultiset.java index d7febe2c60fe..016fc80ff9be 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMultiset.java +++ b/android/guava/src/com/google/common/collect/ImmutableMultiset.java @@ -21,16 +21,22 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link Multiset} whose contents will never change, with many other important properties @@ -41,7 +47,7 @@ * element when the multiset was created. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jared Levy * @author Louis Wasserman @@ -49,9 +55,42 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault public abstract class ImmutableMultiset extends ImmutableMultisetGwtSerializationDependencies implements Multiset { + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableMultiset}. Elements iterate in order by the first appearance of that element in + * encounter order. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableMultiset() { + return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose + * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to + * the result of applying {@code countFunction} to the inputs. + * + *

    If the mapped elements contain duplicates (according to {@link Object#equals}), the first + * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of + * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. + * + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMultiset( + Function elementFunction, + ToIntFunction countFunction) { + return CollectCollectors.toImmutableMultiset(elementFunction, countFunction); + } + /** * Returns the empty immutable multiset. * @@ -65,11 +104,11 @@ public static ImmutableMultiset of() { /** * Returns an immutable multiset containing a single element. * - * @throws NullPointerException if {@code element} is null + * @throws NullPointerException if the element is null * @since 6.0 (source-compatible since 2.0) */ - public static ImmutableMultiset of(E element) { - return copyFromElements(element); + public static ImmutableMultiset of(E e1) { + return copyFromElements(e1); } /** @@ -184,10 +223,10 @@ static ImmutableMultiset copyFromEntries( @Override public UnmodifiableIterator iterator() { - final Iterator> entryIterator = entrySet().iterator(); + Iterator> entryIterator = entrySet().iterator(); return new UnmodifiableIterator() { int remaining; - @CheckForNull E element; + @Nullable E element; @Override public boolean hasNext() { @@ -211,7 +250,7 @@ public E next() { }; } - @LazyInit @CheckForNull private transient ImmutableList asList; + @LazyInit private transient @Nullable ImmutableList asList; @Override public ImmutableList asList() { @@ -220,7 +259,7 @@ public ImmutableList asList() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return count(object) > 0; } @@ -248,7 +287,7 @@ public final int add(E element, int occurrences) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final int remove(@CheckForNull Object element, int occurrences) { + public final int remove(@Nullable Object element, int occurrences) { throw new UnsupportedOperationException(); } @@ -282,7 +321,7 @@ public final boolean setCount(E element, int oldCount, int newCount) { @GwtIncompatible // not present in emulated superclass @Override - int copyIntoArray(Object[] dst, int offset) { + int copyIntoArray(@Nullable Object[] dst, int offset) { for (Multiset.Entry entry : entrySet()) { Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); offset += entry.getCount(); @@ -291,7 +330,7 @@ int copyIntoArray(Object[] dst, int offset) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return Multisets.equalsImpl(this, object); } @@ -305,11 +344,13 @@ public String toString() { return entrySet().toString(); } - /** @since 21.0 (present with return type {@code Set} since 2.0) */ + /** + * @since 21.0 (present with return type {@code Set} since 2.0) + */ @Override public abstract ImmutableSet elementSet(); - @LazyInit @CheckForNull private transient ImmutableSet> entrySet; + @LazyInit private transient @Nullable ImmutableSet> entrySet; @Override public ImmutableSet> entrySet() { @@ -341,7 +382,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Entry) { Entry entry = (Entry) o; if (entry.getCount() <= 0) { @@ -359,15 +400,23 @@ public int hashCode() { } @GwtIncompatible + @J2ktIncompatible @Override Object writeReplace() { return new EntrySetSerializedForm(ImmutableMultiset.this); } - private static final long serialVersionUID = 0; + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EntrySetSerializedForm"); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible + @J2ktIncompatible static class EntrySetSerializedForm implements Serializable { final ImmutableMultiset multiset; @@ -381,15 +430,22 @@ Object readResolve() { } @GwtIncompatible + @J2ktIncompatible @Override abstract Object writeReplace(); + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + /** * Returns a new builder. The generated builder is equivalent to the builder created by the {@link * Builder} constructor. */ public static Builder builder() { - return new Builder(); + return new Builder<>(); } /** @@ -417,13 +473,14 @@ public static class Builder extends ImmutableCollection.Builder { * subclass overrides all the methods that access it here. Thus, all the methods here can safely * assume that this field is non-null. */ - @CheckForNull ObjectCountHashMap contents; + @Nullable ObjectCountHashMap contents; /** * If build() has been called on the current contents multiset, we need to copy it on any future * modifications, or we'll modify the already-built ImmutableMultiset. */ boolean buildInvoked = false; + /** * In the event of a setCount(elem, 0) call, we may need to remove elements, which destroys the * insertion order property of ObjectCountHashMap. In that event, we need to convert to a @@ -546,7 +603,7 @@ public Builder setCount(E element, int count) { public Builder addAll(Iterable elements) { requireNonNull(contents); // see the comment on the field if (elements instanceof Multiset) { - Multiset multiset = Multisets.cast(elements); + Multiset multiset = (Multiset) elements; ObjectCountHashMap backingMap = tryGetMap(multiset); if (backingMap != null) { contents.ensureCapacity(Math.max(contents.size(), backingMap.size())); @@ -585,8 +642,7 @@ public Builder addAll(Iterator elements) { * efficient to iterate over it by index rather than an entry iterator, which will need to * allocate an object for each entry, so we check for that. */ - @CheckForNull - static ObjectCountHashMap tryGetMap(Iterable multiset) { + static @Nullable ObjectCountHashMap tryGetMap(Iterable multiset) { if (multiset instanceof RegularImmutableMultiset) { return ((RegularImmutableMultiset) multiset).contents; } else if (multiset instanceof AbstractMapBasedMultiset) { @@ -617,4 +673,6 @@ public ImmutableMultiset build() { return new RegularImmutableMultiset(contents); } } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java b/android/guava/src/com/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java index 2469a188a2a6..a8b1899d280c 100644 --- a/android/guava/src/com/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java +++ b/android/guava/src/com/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java @@ -37,5 +37,4 @@ * and make types non-final. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ImmutableMultisetGwtSerializationDependencies extends ImmutableCollection {} diff --git a/android/guava/src/com/google/common/collect/ImmutableRangeMap.java b/android/guava/src/com/google/common/collect/ImmutableRangeMap.java index 8a477961989d..6f17a53b7074 100644 --- a/android/guava/src/com/google/common/collect/ImmutableRangeMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableRangeMap.java @@ -17,21 +17,26 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Maps.immutableEntry; +import static java.util.Collections.sort; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.SortedLists.KeyAbsentBehavior; import com.google.common.collect.SortedLists.KeyPresentBehavior; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotMock; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import java.util.function.Function; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link RangeMap} whose contents will never change, with many other important properties @@ -40,14 +45,27 @@ * @author Louis Wasserman * @since 14.0 */ -@Beta @GwtIncompatible // NavigableMap -@ElementTypesAreNonnullByDefault public class ImmutableRangeMap, V> implements RangeMap, Serializable { private static final ImmutableRangeMap, Object> EMPTY = new ImmutableRangeMap<>(ImmutableList.>>of(), ImmutableList.of()); + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableRangeMap}. As in {@link Builder}, overlapping ranges are not permitted. + * + * @since 33.2.0 (available since 23.1 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static , V> + Collector> toImmutableRangeMap( + Function> keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableRangeMap(keyFunction, valueFunction); + } + /** * Returns an empty immutable range map. * @@ -71,7 +89,7 @@ public static , V> ImmutableRangeMap copyOf( } Map, ? extends V> map = rangeMap.asMapOfRanges(); ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder<>(map.size()); - ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder<>(map.size()); for (Entry, ? extends V> entry : map.entrySet()) { rangesBuilder.add(entry.getKey()); valuesBuilder.add(entry.getValue()); @@ -107,7 +125,7 @@ public Builder put(Range range, V value) { checkNotNull(range); checkNotNull(value); checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); - entries.add(Maps.immutableEntry(range, value)); + entries.add(immutableEntry(range, value)); return this; } @@ -133,9 +151,9 @@ Builder combine(Builder builder) { * @throws IllegalArgumentException if any two ranges inserted into this builder overlap */ public ImmutableRangeMap build() { - Collections.sort(entries, Range.rangeLexOrdering().onKeys()); + sort(entries, Range.rangeLexOrdering().onKeys()); ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder<>(entries.size()); - ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(entries.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder<>(entries.size()); for (int i = 0; i < entries.size(); i++) { Range range = entries.get(i).getKey(); if (i > 0) { @@ -161,12 +179,11 @@ public ImmutableRangeMap build() { } @Override - @CheckForNull - public V get(K key) { + public @Nullable V get(K key) { int index = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); @@ -179,12 +196,11 @@ public V get(K key) { } @Override - @CheckForNull - public Entry, V> getEntry(K key) { + public @Nullable Entry, V> getEntry(K key) { int index = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); @@ -192,7 +208,7 @@ public Entry, V> getEntry(K key) { return null; } else { Range range = ranges.get(index); - return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; + return range.contains(key) ? immutableEntry(range, values.get(index)) : null; } } @@ -241,7 +257,7 @@ public final void putCoalescing(Range range, V value) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final void putAll(RangeMap rangeMap) { + public final void putAll(RangeMap rangeMap) { throw new UnsupportedOperationException(); } @@ -292,7 +308,7 @@ public ImmutableMap, V> asDescendingMapOfRanges() { } @Override - public ImmutableRangeMap subRangeMap(final Range range) { + public ImmutableRangeMap subRangeMap(Range range) { if (checkNotNull(range).isEmpty()) { return ImmutableRangeMap.of(); } else if (ranges.isEmpty() || range.encloses(span())) { @@ -301,22 +317,22 @@ public ImmutableRangeMap subRangeMap(final Range range) { int lowerIndex = SortedLists.binarySearch( ranges, - Range.upperBoundFn(), + Range::upperBound, range.lowerBound, KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); int upperIndex = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, range.upperBound, KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); if (lowerIndex >= upperIndex) { return ImmutableRangeMap.of(); } - final int off = lowerIndex; - final int len = upperIndex - lowerIndex; + int off = lowerIndex; + int len = upperIndex - lowerIndex; ImmutableList> subRanges = new ImmutableList>() { @Override @@ -338,8 +354,16 @@ public Range get(int index) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; - final ImmutableRangeMap outer = this; + ImmutableRangeMap outer = this; return new ImmutableRangeMap(subRanges, values.subList(lowerIndex, upperIndex)) { @Override public ImmutableRangeMap subRangeMap(Range subRange) { @@ -349,6 +373,14 @@ public ImmutableRangeMap subRangeMap(Range subRange) { return ImmutableRangeMap.of(); } } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } @@ -358,7 +390,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof RangeMap) { RangeMap rangeMap = (RangeMap) o; return asMapOfRanges().equals(rangeMap.asMapOfRanges()); @@ -399,12 +431,17 @@ Object createRangeMap() { return builder.build(); } - private static final long serialVersionUID = 0; + @J2ktIncompatible private static final long serialVersionUID = 0; } Object writeReplace() { return new SerializedForm<>(asMapOfRanges()); } - private static final long serialVersionUID = 0; + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ImmutableRangeSet.java b/android/guava/src/com/google/common/collect/ImmutableRangeSet.java index a0eb5b5efb08..afee8f858ef1 100644 --- a/android/guava/src/com/google/common/collect/ImmutableRangeSet.java +++ b/android/guava/src/com/google/common/collect/ImmutableRangeSet.java @@ -17,26 +17,32 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Iterators.emptyIterator; import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; +import static java.util.Collections.sort; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.SortedLists.KeyAbsentBehavior; import com.google.common.collect.SortedLists.KeyPresentBehavior; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.concurrent.LazyInit; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link RangeSet} whose contents will never change, with many other important properties @@ -45,9 +51,8 @@ * @author Louis Wasserman * @since 14.0 */ -@Beta +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ImmutableRangeSet extends AbstractRangeSet implements Serializable { @@ -57,6 +62,20 @@ public final class ImmutableRangeSet extends AbstractRange private static final ImmutableRangeSet> ALL = new ImmutableRangeSet<>(ImmutableList.of(Range.>all())); + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableRangeSet}. As in {@link Builder}, overlapping ranges are not permitted and adjacent + * ranges will be merged. + * + * @since 33.2.0 (available since 23.1 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static > + Collector, ?, ImmutableRangeSet> toImmutableRangeSet() { + return CollectCollectors.toImmutableRangeSet(); + } + /** * Returns an empty immutable range set. * @@ -78,7 +97,7 @@ public static ImmutableRangeSet of(Range range) { } else if (range.equals(Range.all())) { return all(); } else { - return new ImmutableRangeSet(ImmutableList.of(range)); + return new ImmutableRangeSet<>(ImmutableList.of(range)); } } @@ -103,7 +122,7 @@ public static ImmutableRangeSet copyOf(RangeSet ran return immutableRangeSet; } } - return new ImmutableRangeSet(ImmutableList.copyOf(rangeSet.asRanges())); + return new ImmutableRangeSet<>(ImmutableList.copyOf(rangeSet.asRanges())); } /** @@ -146,7 +165,7 @@ public boolean intersects(Range otherRange) { int ceilingIndex = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, otherRange.lowerBound, Ordering.natural(), ANY_PRESENT, @@ -166,7 +185,7 @@ public boolean encloses(Range otherRange) { int index = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, otherRange.lowerBound, Ordering.natural(), ANY_PRESENT, @@ -175,12 +194,11 @@ public boolean encloses(Range otherRange) { } @Override - @CheckForNull - public Range rangeContaining(C value) { + public @Nullable Range rangeContaining(C value) { int index = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, Cut.belowValue(value), Ordering.natural(), ANY_PRESENT, @@ -299,7 +317,7 @@ public ImmutableSet> asDescendingSetOfRanges() { return new RegularImmutableSortedSet<>(ranges.reverse(), Range.rangeLexOrdering().reverse()); } - @LazyInit @CheckForNull private transient ImmutableRangeSet complement; + @LazyInit private transient @Nullable ImmutableRangeSet complement; private final class ComplementRanges extends ImmutableList> { // True if the "positive" range set is empty or bounded below. @@ -354,6 +372,14 @@ public Range get(int index) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override @@ -367,7 +393,7 @@ public ImmutableRangeSet complement() { return complement = of(); } else { ImmutableList> complementRanges = new ComplementRanges(); - result = complement = new ImmutableRangeSet(complementRanges, this); + result = complement = new ImmutableRangeSet<>(complementRanges, this); } return result; } @@ -417,19 +443,19 @@ public ImmutableRangeSet difference(RangeSet other) { * Returns a list containing the nonempty intersections of {@code range} with the ranges in this * range set. */ - private ImmutableList> intersectRanges(final Range range) { + private ImmutableList> intersectRanges(Range range) { if (ranges.isEmpty() || range.isEmpty()) { return ImmutableList.of(); } else if (range.encloses(span())) { return ranges; } - final int fromIndex; + int fromIndex; if (range.hasLowerBound()) { fromIndex = SortedLists.binarySearch( ranges, - Range.upperBoundFn(), + Range::upperBound, range.lowerBound, KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); @@ -442,14 +468,14 @@ private ImmutableList> intersectRanges(final Range range) { toIndex = SortedLists.binarySearch( ranges, - Range.lowerBoundFn(), + Range::lowerBound, range.upperBound, KeyPresentBehavior.FIRST_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); } else { toIndex = ranges.size(); } - final int length = toIndex - fromIndex; + int length = toIndex - fromIndex; if (length == 0) { return ImmutableList.of(); } else { @@ -473,6 +499,15 @@ public Range get(int index) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } } @@ -485,7 +520,7 @@ public ImmutableRangeSet subRangeSet(Range range) { if (range.encloses(span)) { return this; } else if (range.isConnected(span)) { - return new ImmutableRangeSet(intersectRanges(range)); + return new ImmutableRangeSet<>(intersectRanges(range)); } } return of(); @@ -504,7 +539,7 @@ public ImmutableRangeSet subRangeSet(Range range) { * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link * Collections#frequency}) can cause major performance problems. * - *

    The returned set's {@link Object#toString} method returns a short-hand form of the set's + *

    The returned set's {@link Object#toString} method returns a shorthand form of the set's * contents, such as {@code "[1..100]}"}. * * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if @@ -541,7 +576,7 @@ private final class AsSet extends ImmutableSortedSet { this.domain = domain; } - @CheckForNull private transient Integer size; + @LazyInit private transient @Nullable Integer size; @Override public int size() { @@ -564,11 +599,10 @@ public int size() { public UnmodifiableIterator iterator() { return new AbstractIterator() { final Iterator> rangeItr = ranges.iterator(); - Iterator elemItr = Iterators.emptyIterator(); + Iterator elemItr = emptyIterator(); @Override - @CheckForNull - protected C computeNext() { + protected @Nullable C computeNext() { while (!elemItr.hasNext()) { if (rangeItr.hasNext()) { elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator(); @@ -586,11 +620,10 @@ protected C computeNext() { public UnmodifiableIterator descendingIterator() { return new AbstractIterator() { final Iterator> rangeItr = ranges.reverse().iterator(); - Iterator elemItr = Iterators.emptyIterator(); + Iterator elemItr = emptyIterator(); @Override - @CheckForNull - protected C computeNext() { + protected @Nullable C computeNext() { while (!elemItr.hasNext()) { if (rangeItr.hasNext()) { elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator(); @@ -630,7 +663,7 @@ ImmutableSortedSet tailSetImpl(C fromElement, boolean inclusive) { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o == null) { return false; } @@ -644,7 +677,7 @@ public boolean contains(@CheckForNull Object o) { } @Override - int indexOf(@CheckForNull Object target) { + int indexOf(@Nullable Object target) { if (contains(target)) { @SuppressWarnings("unchecked") // if it's contained, it's definitely a C C c = (C) requireNonNull(target); @@ -663,7 +696,7 @@ int indexOf(@CheckForNull Object target) { @Override ImmutableSortedSet createDescendingSet() { - return new DescendingImmutableSortedSet(this); + return new DescendingImmutableSortedSet<>(this); } @Override @@ -677,9 +710,15 @@ public String toString() { } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new AsSetSerializedForm(ranges, domain); } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } } private static class AsSetSerializedForm implements Serializable { @@ -708,7 +747,7 @@ boolean isPartialView() { /** Returns a new builder for an immutable range set. */ public static > Builder builder() { - return new Builder(); + return new Builder<>(); } /** @@ -777,7 +816,7 @@ Builder combine(Builder builder) { public ImmutableRangeSet build() { ImmutableList.Builder> mergedRangesBuilder = new ImmutableList.Builder<>(ranges.size()); - Collections.sort(ranges, Range.rangeLexOrdering()); + sort(ranges, Range.rangeLexOrdering()); PeekingIterator> peekingItr = Iterators.peekingIterator(ranges.iterator()); while (peekingItr.hasNext()) { Range range = peekingItr.next(); @@ -799,11 +838,10 @@ public ImmutableRangeSet build() { ImmutableList> mergedRanges = mergedRangesBuilder.build(); if (mergedRanges.isEmpty()) { return of(); - } else if (mergedRanges.size() == 1 - && Iterables.getOnlyElement(mergedRanges).equals(Range.all())) { + } else if (mergedRanges.size() == 1 && getOnlyElement(mergedRanges).equals(Range.all())) { return all(); } else { - return new ImmutableRangeSet(mergedRanges); + return new ImmutableRangeSet<>(mergedRanges); } } } @@ -826,7 +864,13 @@ Object readResolve() { } } + @J2ktIncompatible // java.io.ObjectInputStream Object writeReplace() { return new SerializedForm(ranges); } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } } diff --git a/android/guava/src/com/google/common/collect/ImmutableSet.java b/android/guava/src/com/google/common/collect/ImmutableSet.java index 1ff54e205baf..51d434fca57c 100644 --- a/android/guava/src/com/google/common/collect/ImmutableSet.java +++ b/android/guava/src/com/google/common/collect/ImmutableSet.java @@ -20,15 +20,19 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; import static com.google.common.collect.ObjectArrays.checkElementNotNull; +import static java.lang.Math.max; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; @@ -36,8 +40,8 @@ import java.util.Iterator; import java.util.Set; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link Set} whose contents will never change, with many other important properties detailed at @@ -47,8 +51,22 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault public abstract class ImmutableSet extends ImmutableCollection implements Set { + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableSet}. Elements appear in the resulting set in the encounter order of the stream; if + * the stream contains duplicates (according to {@link Object#equals(Object)}), only the first + * duplicate in encounter order will appear in the result. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableSet() { + return CollectCollectors.toImmutableSet(); + } + /** * Returns the empty immutable set. Preferred over {@link Collections#emptySet} for code * consistency, and because the return type conveys the immutability guarantee. @@ -61,12 +79,12 @@ public static ImmutableSet of() { } /** - * Returns an immutable set containing {@code element}. Preferred over {@link + * Returns an immutable set containing the given element. Preferred over {@link * Collections#singleton} for code consistency, {@code null} rejection, and because the return * type conveys the immutability guarantee. */ - public static ImmutableSet of(E element) { - return new SingletonImmutableSet(element); + public static ImmutableSet of(E e1) { + return new SingletonImmutableSet<>(e1); } /** @@ -188,8 +206,7 @@ private static ImmutableSet construct(int n, @Nullable Object... elements // Resize the table when the array includes too many duplicates. return construct(uniques, elements); } else { - @Nullable - Object[] uniqueElements = + @Nullable Object[] uniqueElements = shouldTrim(uniques, elements.length) ? Arrays.copyOf(elements, uniques) : elements; return new RegularImmutableSet(uniqueElements, hashCode, table, mask, uniques); } @@ -215,7 +232,7 @@ private static boolean shouldTrim(int actualUnique, int expectedUnique) { */ @VisibleForTesting static int chooseTableSize(int setSize) { - setSize = Math.max(setSize, 2); + setSize = max(setSize, 2); // Correct the size for open addressing to match desired load factor. if (setSize < CUTOFF) { // Round up to the next highest power of 2. @@ -243,6 +260,11 @@ static int chooseTableSize(int setSize) { * @throws NullPointerException if any of {@code elements} is null * @since 7.0 (source-compatible since 2.0) */ + // This the best we could do to get copyOfEnumSet to compile in the mainline. + // The suppression also covers the cast to E[], discussed below. + // In the backport, we don't have those cases and thus don't need this suppression. + // We keep it to minimize diffs. + @SuppressWarnings("unchecked") public static ImmutableSet copyOf(Collection elements) { /* * TODO(lowasser): consider checking for ImmutableAsList here @@ -323,7 +345,7 @@ boolean isHashCodeFast() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -346,7 +368,7 @@ public int hashCode() { @Override public abstract UnmodifiableIterator iterator(); - @LazyInit @RetainedWith @CheckForNull private transient ImmutableList asList; + @LazyInit @RetainedWith private transient @Nullable ImmutableList asList; @Override public ImmutableList asList() { @@ -365,6 +387,7 @@ ImmutableList createAsList() { * static factories. This is necessary to ensure that the existence of a * particular implementation type is an implementation detail. */ + @J2ktIncompatible // serialization private static class SerializedForm implements Serializable { final Object[] elements; @@ -376,20 +399,26 @@ Object readResolve() { return copyOf(elements); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm(toArray()); } + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + /** * Returns a new builder. The generated builder is equivalent to the builder created by the {@link * Builder} constructor. */ public static Builder builder() { - return new Builder(); + return new Builder<>(); } /** @@ -404,10 +433,9 @@ public static Builder builder() { * * @since 23.1 */ - @Beta public static Builder builderWithExpectedSize(int expectedSize) { checkNonnegative(expectedSize, "expectedSize"); - return new Builder(expectedSize); + return new Builder<>(expectedSize, true); } /** @@ -429,7 +457,7 @@ public static Builder builderWithExpectedSize(int expectedSize) { * @since 2.0 */ public static class Builder extends ImmutableCollection.ArrayBasedBuilder { - @VisibleForTesting @CheckForNull @Nullable Object[] hashTable; + @VisibleForTesting @Nullable Object @Nullable [] hashTable; private int hashCode; /** @@ -440,9 +468,11 @@ public Builder() { super(DEFAULT_INITIAL_CAPACITY); } - Builder(int capacity) { + Builder(int capacity, boolean makeHashTable) { super(capacity); - this.hashTable = new @Nullable Object[chooseTableSize(capacity)]; + if (makeHashTable) { + this.hashTable = new @Nullable Object[chooseTableSize(capacity)]; + } } /** @@ -579,8 +609,7 @@ public ImmutableSet build() { default: ImmutableSet result; if (hashTable != null && chooseTableSize(size) == hashTable.length) { - @Nullable - Object[] uniqueElements = + @Nullable Object[] uniqueElements = shouldTrim(size, contents.length) ? Arrays.copyOf(contents, size) : contents; result = new RegularImmutableSet( @@ -597,4 +626,6 @@ public ImmutableSet build() { } } } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableSetMultimap.java b/android/guava/src/com/google/common/collect/ImmutableSetMultimap.java index 18cb2efa37ea..8d6c51dc9aea 100644 --- a/android/guava/src/com/google/common/collect/ImmutableSetMultimap.java +++ b/android/guava/src/com/google/common/collect/ImmutableSetMultimap.java @@ -17,10 +17,14 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static java.lang.Math.max; +import static java.util.Arrays.asList; +import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; @@ -31,12 +35,15 @@ import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; /** * A {@link SetMultimap} whose contents will never change, with many other important properties @@ -47,15 +54,99 @@ * Undefined behavior and bugs will result. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Mike Ward * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public class ImmutableSetMultimap extends ImmutableMultimap implements SetMultimap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap} + * whose keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

    For streams with defined encounter order (as defined in the Ordering section of the {@link + * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. + * + *

    Example: + * + *

    {@code
    +   * static final Multimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(toImmutableSetMultimap(str -> str.charAt(0), str -> str.substring(1)));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final Multimap FIRST_LETTER_MULTIMAP =
    +   *     new ImmutableSetMultimap.Builder()
    +   *         .put('b', "anana")
    +   *         .putAll('a', "pple", "sparagus")
    +   *         .putAll('c', "arrot", "herry")
    +   *         .build();
    +   * }
    + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableSetMultimap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableSetMultimap(keyFunction, valueFunction); + } + + /** + * Returns a {@code Collector} accumulating entries into an {@code ImmutableSetMultimap}. Each + * input element is mapped to a key and a stream of values, each of which are put into the + * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the + * streams of values. + * + *

    Example: + * + *

    {@code
    +   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(
    +   *             flatteningToImmutableSetMultimap(
    +   *                  str -> str.charAt(0),
    +   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
    +   *     ImmutableSetMultimap.builder()
    +   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
    +   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
    +   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
    +   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
    +   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
    +   *         .build();
    +   *
    +   * // after deduplication, the resulting multimap is equivalent to
    +   *
    +   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
    +   *     ImmutableSetMultimap.builder()
    +   *         .putAll('b', Arrays.asList('a', 'n'))
    +   *         .putAll('a', Arrays.asList('p', 'l', 'e', 's', 'a', 'r', 'g', 'u'))
    +   *         .putAll('c', Arrays.asList('a', 'r', 'o', 't', 'h', 'e', 'y'))
    +   *         .build();
    +   * }
    +   * }
    + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> flatteningToImmutableSetMultimap( + Function keyFunction, + Function> valuesFunction) { + return CollectCollectors.flatteningToImmutableSetMultimap(keyFunction, valuesFunction); + } /** * Returns the empty multimap. @@ -134,6 +225,19 @@ public static Builder builder() { return new Builder<>(); } + /** + * Returns a new builder with a hint for how many distinct keys are expected to be added. The + * generated builder is equivalent to that returned by {@link #builder}, but may perform better if + * {@code expectedKeys} is a good estimate. + * + * @throws IllegalArgumentException if {@code expectedKeys} is negative + * @since 33.3.0 + */ + public static Builder builderWithExpectedKeys(int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new Builder<>(expectedKeys); + } + /** * A builder for creating immutable {@code SetMultimap} instances, especially {@code public static * final} multimaps ("constant multimaps"). Example: @@ -158,13 +262,43 @@ public static final class Builder extends ImmutableMultimap.Builder * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableSetMultimap#builder}. */ - public Builder() { - super(); + public Builder() {} + + Builder(int expectedKeys) { + super(expectedKeys); } @Override - Collection newMutableValueCollection() { - return Platform.preservesInsertionOrderOnAddsSet(); + ImmutableCollection.Builder newValueCollectionBuilderWithExpectedSize(int expectedSize) { + return (valueComparator == null) + ? ImmutableSet.builderWithExpectedSize(expectedSize) + : new ImmutableSortedSet.Builder(valueComparator, expectedSize); + } + + @Override + int expectedValueCollectionSize(int defaultExpectedValues, Iterable values) { + // Only trust the size of `values` if it is a Set and therefore probably already deduplicated. + if (values instanceof Set) { + Set collection = (Set) values; + return max(defaultExpectedValues, collection.size()); + } else { + return defaultExpectedValues; + } + } + + /** + * {@inheritDoc} + * + *

    Note that {@code expectedValuesPerKey} is taken to mean the expected number of + * distinct values per key. + * + * @since 33.3.0 + */ + @CanIgnoreReturnValue + @Override + public Builder expectedValuesPerKey(int expectedValuesPerKey) { + super.expectedValuesPerKey(expectedValuesPerKey); + return this; } /** Adds a key-value mapping to the built multimap if it is not already present. */ @@ -193,7 +327,6 @@ public Builder put(Entry entry) { * @since 19.0 */ @CanIgnoreReturnValue - @Beta @Override public Builder putAll(Iterable> entries) { super.putAll(entries); @@ -210,7 +343,7 @@ public Builder putAll(K key, Iterable values) { @CanIgnoreReturnValue @Override public Builder putAll(K key, V... values) { - return putAll(key, Arrays.asList(values)); + return putAll(key, asList(values)); } @CanIgnoreReturnValue @@ -263,11 +396,14 @@ public Builder orderValuesBy(Comparator valueComparator) { /** Returns a newly-created immutable set multimap. */ @Override public ImmutableSetMultimap build() { - Collection>> mapEntries = builderMap.entrySet(); + if (builderMap == null) { + return ImmutableSetMultimap.of(); + } + Collection>> mapEntries = builderMap.entrySet(); if (keyComparator != null) { mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); } - return fromMapEntries(mapEntries, valueComparator); + return fromMapBuilderEntries(mapEntries, valueComparator); } } @@ -290,7 +426,7 @@ public static ImmutableSetMultimap copyOf( private static ImmutableSetMultimap copyOf( Multimap multimap, - @CheckForNull Comparator valueComparator) { + @Nullable Comparator valueComparator) { checkNotNull(multimap); // eager for GWT if (multimap.isEmpty() && valueComparator == null) { return of(); @@ -316,7 +452,6 @@ private static ImmutableSetMultimap copyOf( * @throws NullPointerException if any key, value, or entry is null * @since 19.0 */ - @Beta public static ImmutableSetMultimap copyOf( Iterable> entries) { return new Builder().putAll(entries).build(); @@ -325,7 +460,7 @@ public static ImmutableSetMultimap copyOf( /** Creates an ImmutableSetMultimap from an asMap.entrySet. */ static ImmutableSetMultimap fromMapEntries( Collection>> mapEntries, - @CheckForNull Comparator valueComparator) { + @Nullable Comparator valueComparator) { if (mapEntries.isEmpty()) { return of(); } @@ -343,7 +478,33 @@ static ImmutableSetMultimap fromMapEntries( } } - return new ImmutableSetMultimap<>(builder.build(), size, valueComparator); + return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator); + } + + /** Creates an ImmutableSetMultimap from a map to builders. */ + static ImmutableSetMultimap fromMapBuilderEntries( + Collection>> mapEntries, + @Nullable Comparator valueComparator) { + if (mapEntries.isEmpty()) { + return of(); + } + ImmutableMap.Builder> builder = + new ImmutableMap.Builder<>(mapEntries.size()); + int size = 0; + + for (Entry> entry : mapEntries) { + K key = entry.getKey(); + ImmutableSet.Builder values = (ImmutableSet.Builder) entry.getValue(); + // If orderValuesBy got called at the very end, we may need to do the ImmutableSet to + // ImmutableSortedSet copy for each of these. + ImmutableSet set = valueSet(valueComparator, values.build()); + if (!set.isEmpty()) { + builder.put(key, set); + size += set.size(); + } + } + + return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator); } /** @@ -355,7 +516,7 @@ static ImmutableSetMultimap fromMapEntries( ImmutableSetMultimap( ImmutableMap> map, int size, - @CheckForNull Comparator valueComparator) { + @Nullable Comparator valueComparator) { super(map, size); this.emptySet = emptySet(valueComparator); } @@ -374,7 +535,7 @@ public ImmutableSet get(K key) { return MoreObjects.firstNonNull(set, emptySet); } - @LazyInit @RetainedWith @CheckForNull private transient ImmutableSetMultimap inverse; + @LazyInit @RetainedWith private transient @Nullable ImmutableSetMultimap inverse; /** * {@inheritDoc} @@ -409,7 +570,7 @@ private ImmutableSetMultimap invert() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final ImmutableSet removeAll(@CheckForNull Object key) { + public final ImmutableSet removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -427,7 +588,7 @@ public final ImmutableSet replaceValues(K key, Iterable values) throw new UnsupportedOperationException(); } - @LazyInit @RetainedWith @CheckForNull private transient ImmutableSet> entries; + @LazyInit @RetainedWith private transient @Nullable ImmutableSet> entries; /** * Returns an immutable collection of all key-value pairs in the multimap. Its iterator traverses @@ -447,7 +608,7 @@ private static final class EntrySet extends ImmutableSet> { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object instanceof Entry) { Entry entry = (Entry) object; return multimap.containsEntry(entry.getKey(), entry.getValue()); @@ -469,23 +630,32 @@ public UnmodifiableIterator> iterator() { boolean isPartialView() { return false; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } private static ImmutableSet valueSet( - @CheckForNull Comparator valueComparator, Collection values) { + @Nullable Comparator valueComparator, Collection values) { return (valueComparator == null) ? ImmutableSet.copyOf(values) : ImmutableSortedSet.copyOf(valueComparator, values); } - private static ImmutableSet emptySet(@CheckForNull Comparator valueComparator) { + private static ImmutableSet emptySet(@Nullable Comparator valueComparator) { return (valueComparator == null) ? ImmutableSet.of() : ImmutableSortedSet.emptySet(valueComparator); } private static ImmutableSet.Builder valuesBuilder( - @CheckForNull Comparator valueComparator) { + @Nullable Comparator valueComparator) { return (valueComparator == null) ? new ImmutableSet.Builder() : new ImmutableSortedSet.Builder(valueComparator); @@ -496,26 +666,29 @@ private static ImmutableSet.Builder valuesBuilder( * values for that key, and the key's values */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(valueComparator()); Serialization.writeMultimap(this, stream); } - @CheckForNull - Comparator valueComparator() { + @Nullable Comparator valueComparator() { return emptySet instanceof ImmutableSortedSet ? ((ImmutableSortedSet) emptySet).comparator() : null; } @GwtIncompatible // java serialization + @J2ktIncompatible private static final class SetFieldSettersHolder { - static final Serialization.FieldSetter EMPTY_SET_FIELD_SETTER = - Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); + static final Serialization.FieldSetter> + EMPTY_SET_FIELD_SETTER = + Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible // Serialization type safety is at the caller's mercy. @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { @@ -529,7 +702,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo int tmpSize = 0; for (int i = 0; i < keyCount; i++) { - Object key = stream.readObject(); + Object key = requireNonNull(stream.readObject()); int valueCount = stream.readInt(); if (valueCount <= 0) { throw new InvalidObjectException("Invalid value count " + valueCount); @@ -537,7 +710,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo ImmutableSet.Builder valuesBuilder = valuesBuilder(valueComparator); for (int j = 0; j < valueCount; j++) { - valuesBuilder.add(stream.readObject()); + valuesBuilder.add(requireNonNull(stream.readObject())); } ImmutableSet valueSet = valuesBuilder.build(); if (valueSet.size() != valueCount) { @@ -549,7 +722,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo ImmutableMap> tmpMap; try { - tmpMap = builder.build(); + tmpMap = builder.buildOrThrow(); } catch (IllegalArgumentException e) { throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); } @@ -559,6 +732,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo SetFieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); } - @GwtIncompatible // not needed in emulated source. - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedMap.java b/android/guava/src/com/google/common/collect/ImmutableSortedMap.java index ac5f0bcefb9f..4387b76b93a1 100644 --- a/android/guava/src/com/google/common/collect/ImmutableSortedMap.java +++ b/android/guava/src/com/google/common/collect/ImmutableSortedMap.java @@ -20,12 +20,16 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; import static com.google.common.collect.Maps.keyOrNull; +import static java.util.Arrays.sort; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.util.AbstractMap; import java.util.Arrays; import java.util.Comparator; @@ -33,8 +37,11 @@ import java.util.NavigableMap; import java.util.SortedMap; import java.util.TreeMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; /** * A {@link NavigableMap} whose contents will never change, with many other important properties @@ -47,24 +54,67 @@ * not correctly obey its specification. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jared Levy * @author Louis Wasserman * @since 2.0 (implements {@code NavigableMap} since 12.0) */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault -public final class ImmutableSortedMap extends ImmutableSortedMapFauxverideShim +public final class ImmutableSortedMap extends ImmutableMap implements NavigableMap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSortedMap} whose + * keys and values are the result of applying the provided mapping functions to the input + * elements. The generated map is sorted by the specified comparator. + * + *

    If the mapped keys contain duplicates (according to the specified comparator), an {@code + * IllegalArgumentException} is thrown when the collection operation is performed. (This differs + * from the {@code Collector} returned by {@link Collectors#toMap(Function, Function)}, which + * throws an {@code IllegalStateException}.) + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableSortedMap(comparator, keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSortedMap} whose + * keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

    If the mapped keys contain duplicates (according to the comparator), the values are merged + * using the specified merging function. Entries will appear in the encounter order of the first + * occurrence of the key. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableSortedMap( + comparator, keyFunction, valueFunction, mergeFunction); + } /* * TODO(kevinb): Confirm that ImmutableSortedMap is faster to construct and * uses less memory than TreeMap; then say so in the class Javadoc. */ - private static final Comparator NATURAL_ORDER = Ordering.natural(); + private static final Comparator NATURAL_ORDER = Ordering.natural(); - private static final ImmutableSortedMap NATURAL_EMPTY_MAP = + private static final ImmutableSortedMap, Object> NATURAL_EMPTY_MAP = new ImmutableSortedMap<>( ImmutableSortedSet.emptySet(Ordering.natural()), ImmutableList.of()); @@ -107,7 +157,6 @@ private static ImmutableSortedMap of(Comparator comparat * * @throws IllegalArgumentException if the two keys are equal according to their natural ordering */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2) { return fromEntries(entryOf(k1, v1), entryOf(k2, v2)); @@ -119,7 +168,6 @@ public static , V> ImmutableSortedMap of( * * @throws IllegalArgumentException if any two keys are equal according to their natural ordering */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2, K k3, V v3) { return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); @@ -131,7 +179,6 @@ public static , V> ImmutableSortedMap of( * * @throws IllegalArgumentException if any two keys are equal according to their natural ordering */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); @@ -143,7 +190,6 @@ public static , V> ImmutableSortedMap of( * * @throws IllegalArgumentException if any two keys are equal according to their natural ordering */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { return fromEntries( @@ -157,7 +203,6 @@ public static , V> ImmutableSortedMap of( * @throws IllegalArgumentException if any two keys are equal according to their natural ordering * @since 31.0 */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { return fromEntries( @@ -176,7 +221,6 @@ public static , V> ImmutableSortedMap of( * @throws IllegalArgumentException if any two keys are equal according to their natural ordering * @since 31.0 */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { return fromEntries( @@ -196,7 +240,6 @@ public static , V> ImmutableSortedMap of( * @throws IllegalArgumentException if any two keys are equal according to their natural ordering * @since 31.0 */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, @@ -232,7 +275,6 @@ public static , V> ImmutableSortedMap of( * @throws IllegalArgumentException if any two keys are equal according to their natural ordering * @since 31.0 */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, @@ -252,7 +294,11 @@ public static , V> ImmutableSortedMap of( V v8, K k9, V v9) { - return fromEntries( + /* + * This explicit type parameter works around what seems to be a javac bug in certain + * configurations: b/339186525#comment6 + */ + return ImmutableSortedMap.fromEntries( entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), @@ -271,7 +317,6 @@ public static , V> ImmutableSortedMap of( * @throws IllegalArgumentException if any two keys are equal according to their natural ordering * @since 31.0 */ - @SuppressWarnings("unchecked") public static , V> ImmutableSortedMap of( K k1, V v1, @@ -356,7 +401,6 @@ public static ImmutableSortedMap copyOf( * @throws IllegalArgumentException if any two keys are equal according to the comparator * @since 19.0 */ - @Beta public static ImmutableSortedMap copyOf( Iterable> entries) { // Hack around K not being a subtype of Comparable. @@ -374,7 +418,6 @@ public static ImmutableSortedMap copyOf( * @throws IllegalArgumentException if any two keys are equal according to the comparator * @since 19.0 */ - @Beta public static ImmutableSortedMap copyOf( Iterable> entries, Comparator comparator) { @@ -455,7 +498,7 @@ private static ImmutableSortedMap fromEntries( } private static ImmutableSortedMap fromEntries( - final Comparator comparator, + Comparator comparator, boolean sameComparator, @Nullable Entry[] entryArray, int size) { @@ -484,18 +527,15 @@ private static ImmutableSortedMap fromEntries( // Need to sort and check for nulls and dupes. // Inline the Comparator implementation rather than transforming with a Function // to save code size. - Arrays.sort( + sort( entryArray, 0, size, - new Comparator<@Nullable Entry>() { - @Override - public int compare(@CheckForNull Entry e1, @CheckForNull Entry e2) { - // requireNonNull is safe because the first `size` elements have been filled in. - requireNonNull(e1); - requireNonNull(e2); - return comparator.compare(e1.getKey(), e2.getKey()); - } + (e1, e2) -> { + // requireNonNull is safe because the first `size` elements have been filled in. + requireNonNull(e1); + requireNonNull(e2); + return comparator.compare(e1.getKey(), e2.getKey()); }); // requireNonNull is safe because the first `size` elements have been filled in. Entry firstEntry = requireNonNull(entryArray[0]); @@ -547,7 +587,7 @@ public static Builder orderedBy(Comparator comparator) { * their natural ordering. */ public static , V> Builder reverseOrder() { - return new Builder<>(Ordering.natural().reverse()); + return new Builder<>(Ordering.natural().reverse()); } /** @@ -580,7 +620,6 @@ public static class Builder extends ImmutableMap.Builder { * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableSortedMap#orderedBy}. */ - @SuppressWarnings("unchecked") public Builder(Comparator comparator) { this(comparator, ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY); } @@ -652,7 +691,6 @@ public Builder putAll(Map map) { * @since 19.0 */ @CanIgnoreReturnValue - @Beta @Override public Builder putAll(Iterable> entries) { super.putAll(entries); @@ -666,7 +704,6 @@ public Builder putAll(Iterable> * @deprecated Unsupported by ImmutableSortedMap.Builder. */ @CanIgnoreReturnValue - @Beta @Override @Deprecated @DoNotCall("Always throws UnsupportedOperationException") @@ -674,7 +711,12 @@ public final Builder orderEntriesByValue(Comparator valueCompar throw new UnsupportedOperationException("Not available on ImmutableSortedMap.Builder"); } - @CanIgnoreReturnValue + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Anyway, the purpose of this method is to implement a BinaryOperator combiner for a Collector, + * so its return value will get used naturally. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") Builder combine(ImmutableSortedMap.Builder other) { ensureCapacity(size + other.size); System.arraycopy(other.keys, 0, this.keys, this.size, other.size); @@ -707,16 +749,22 @@ public ImmutableSortedMap build() { * @since 31.0 */ @Override + @SuppressWarnings("unchecked") // see inline comments public ImmutableSortedMap buildOrThrow() { switch (size) { case 0: return emptyMap(comparator); case 1: + // We're careful to put only K and V instances in. // requireNonNull is safe because the first `size` elements have been filled in. - return of(comparator, (K) requireNonNull(keys[0]), (V) requireNonNull(values[0])); + ImmutableSortedMap result = + of(comparator, (K) requireNonNull(keys[0]), (V) requireNonNull(values[0])); + return result; default: Object[] sortedKeys = Arrays.copyOf(keys, size); - Arrays.sort((K[]) sortedKeys, comparator); + // We're careful to put only K instances in. + K[] sortedKs = (K[]) sortedKeys; + Arrays.sort(sortedKs, comparator); Object[] sortedValues = new Object[size]; // We might, somehow, be able to reorder values in-place. But it doesn't seem like @@ -724,6 +772,7 @@ public ImmutableSortedMap buildOrThrow() { // one array of size n, we might as well allocate two -- to say nothing of the allocation // done in Arrays.sort. for (int i = 0; i < size; i++) { + // We're careful to put only K instances in. if (i > 0 && comparator.compare((K) sortedKeys[i - 1], (K) sortedKeys[i]) == 0) { throw new IllegalArgumentException( "keys required to be distinct but compared as equal: " @@ -732,6 +781,7 @@ public ImmutableSortedMap buildOrThrow() { + sortedKeys[i]); } // requireNonNull is safe because the first `size` elements have been filled in. + // We're careful to put only K instances in. int index = Arrays.binarySearch((K[]) sortedKeys, (K) requireNonNull(keys[i]), comparator); sortedValues[index] = requireNonNull(values[i]); @@ -742,11 +792,29 @@ public ImmutableSortedMap buildOrThrow() { ImmutableList.asImmutableList(sortedValues)); } } + + /** + * Throws UnsupportedOperationException. A future version may support this operation. Then the + * value for any given key will be the one that was last supplied in a {@code put} operation for + * that key. + * + * @throws UnsupportedOperationException always + * @since 31.1 + * @deprecated This method is not currently implemented, and may never be. + */ + @DoNotCall + @Deprecated + @Override + public final ImmutableSortedMap buildKeepingLast() { + // TODO(emcmanus): implement + throw new UnsupportedOperationException( + "ImmutableSortedMap.Builder does not yet implement buildKeepingLast()"); + } } private final transient RegularImmutableSortedSet keySet; private final transient ImmutableList valueList; - @CheckForNull private transient ImmutableSortedMap descendingMap; + private transient @Nullable ImmutableSortedMap descendingMap; ImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList) { this(keySet, valueList, null); @@ -755,7 +823,7 @@ public ImmutableSortedMap buildOrThrow() { ImmutableSortedMap( RegularImmutableSortedSet keySet, ImmutableList valueList, - @CheckForNull ImmutableSortedMap descendingMap) { + @Nullable ImmutableSortedMap descendingMap) { this.keySet = keySet; this.valueList = valueList; this.descendingMap = descendingMap; @@ -767,8 +835,7 @@ public int size() { } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { int index = keySet.indexOf(key); return (index == -1) ? null : valueList.get(index); } @@ -810,6 +877,15 @@ boolean isPartialView() { public int size() { return ImmutableSortedMap.this.size(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } @@ -817,6 +893,15 @@ public int size() { ImmutableMap map() { return ImmutableSortedMap.this; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } return isEmpty() ? ImmutableSet.>of() : new EntrySet(); } @@ -979,62 +1064,52 @@ public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { } @Override - @CheckForNull - public Entry lowerEntry(K key) { + public @Nullable Entry lowerEntry(K key) { return headMap(key, false).lastEntry(); } @Override - @CheckForNull - public K lowerKey(K key) { + public @Nullable K lowerKey(K key) { return keyOrNull(lowerEntry(key)); } @Override - @CheckForNull - public Entry floorEntry(K key) { + public @Nullable Entry floorEntry(K key) { return headMap(key, true).lastEntry(); } @Override - @CheckForNull - public K floorKey(K key) { + public @Nullable K floorKey(K key) { return keyOrNull(floorEntry(key)); } @Override - @CheckForNull - public Entry ceilingEntry(K key) { + public @Nullable Entry ceilingEntry(K key) { return tailMap(key, true).firstEntry(); } @Override - @CheckForNull - public K ceilingKey(K key) { + public @Nullable K ceilingKey(K key) { return keyOrNull(ceilingEntry(key)); } @Override - @CheckForNull - public Entry higherEntry(K key) { + public @Nullable Entry higherEntry(K key) { return tailMap(key, false).firstEntry(); } @Override - @CheckForNull - public K higherKey(K key) { + public @Nullable K higherKey(K key) { return keyOrNull(higherEntry(key)); } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return isEmpty() ? null : entrySet().asList().get(0); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return isEmpty() ? null : entrySet().asList().get(size() - 1); } @@ -1048,8 +1123,7 @@ public Entry lastEntry() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final Entry pollFirstEntry() { + public final @Nullable Entry pollFirstEntry() { throw new UnsupportedOperationException(); } @@ -1063,23 +1137,24 @@ public final Entry pollFirstEntry() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final Entry pollLastEntry() { + public final @Nullable Entry pollLastEntry() { throw new UnsupportedOperationException(); } @Override public ImmutableSortedMap descendingMap() { - // TODO(kevinb): the descendingMap is never actually cached at all. Either it should be or the - // code below simplified. + // TODO(kevinb): The descendingMap is never actually cached at all. Either: + // + // - Cache it, and annotate the field with @LazyInit. + // - Simplify the code below, and consider eliminating the field (b/287198172), which is also + // set by one of the constructors. ImmutableSortedMap result = descendingMap; if (result == null) { if (isEmpty()) { - return result = emptyMap(Ordering.from(comparator()).reverse()); + return emptyMap(Ordering.from(comparator()).reverse()); } else { - return result = - new ImmutableSortedMap<>( - (RegularImmutableSortedSet) keySet.descendingSet(), valueList.reverse(), this); + return new ImmutableSortedMap<>( + (RegularImmutableSortedSet) keySet.descendingSet(), valueList.reverse(), this); } } return result; @@ -1100,6 +1175,7 @@ public ImmutableSortedSet descendingKeySet() { * are reconstructed using public factory methods. This ensures that the implementation types * remain as implementation details. */ + @J2ktIncompatible // serialization private static class SerializedForm extends ImmutableMap.SerializedForm { private final Comparator comparator; @@ -1113,15 +1189,314 @@ Builder makeBuilder(int size) { return new Builder<>(comparator); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm<>(this); } + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + // This class is never actually serialized directly, but we have to make the // warning go away (and suppressing would suppress for all nested classes too) - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; + + /** + * Not supported. Use {@link #toImmutableSortedMap}, which offers better type-safety, instead. + * This method exists only to hide {@link ImmutableMap#toImmutableMap} from consumers of {@code + * ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#toImmutableSortedMap}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @DoNotCall("Use toImmutableSortedMap") + @Deprecated + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link #toImmutableSortedMap}, which offers better type-safety, instead. + * This method exists only to hide {@link ImmutableMap#toImmutableMap} from consumers of {@code + * ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#toImmutableSortedMap}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @DoNotCall("Use toImmutableSortedMap") + @Deprecated + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link #naturalOrder}, which offers better type-safety, instead. This method + * exists only to hide {@link ImmutableMap#builder} from consumers of {@code ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#naturalOrder}, which offers better type-safety. + */ + @DoNotCall("Use naturalOrder") + @Deprecated + public static ImmutableSortedMap.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported for ImmutableSortedMap. + * + * @throws UnsupportedOperationException always + * @deprecated Not supported for ImmutableSortedMap. + */ + @DoNotCall("Use naturalOrder (which does not accept an expected size)") + @Deprecated + public static ImmutableSortedMap.Builder builderWithExpectedSize(int expectedSize) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain a non-{@code Comparable} + * key. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this dummy + * version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a key of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object)}. + */ + @DoNotCall("Pass a key of type Comparable") + @Deprecated + public static ImmutableSortedMap of(K k1, V v1) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls to will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @DoNotCall("Pass keys of type Comparable") + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. + * + * @deprecated Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. + */ + @DoNotCall("ImmutableSortedMap.ofEntries not currently available; use ImmutableSortedMap.copyOf") + @Deprecated + @SafeVarargs + public static ImmutableSortedMap ofEntries( + Entry... entries) { + throw new UnsupportedOperationException(); + } } diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedMapFauxverideShim.java b/android/guava/src/com/google/common/collect/ImmutableSortedMapFauxverideShim.java deleted file mode 100644 index 9b40f556f466..000000000000 --- a/android/guava/src/com/google/common/collect/ImmutableSortedMapFauxverideShim.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (C) 2009 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.collect; - -import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.DoNotCall; - -/** - * "Overrides" the {@link ImmutableMap} static methods that lack {@link ImmutableSortedMap} - * equivalents with deprecated, exception-throwing versions. See {@link - * ImmutableSortedSetFauxverideShim} for details. - * - * @author Chris Povirk - */ -@GwtIncompatible -@ElementTypesAreNonnullByDefault -abstract class ImmutableSortedMapFauxverideShim extends ImmutableMap { - /** - * Not supported. Use {@link ImmutableSortedMap#naturalOrder}, which offers better type-safety, - * instead. This method exists only to hide {@link ImmutableMap#builder} from consumers of {@code - * ImmutableSortedMap}. - * - * @throws UnsupportedOperationException always - * @deprecated Use {@link ImmutableSortedMap#naturalOrder}, which offers better type-safety. - */ - @DoNotCall("Use naturalOrder") - @Deprecated - public static ImmutableSortedMap.Builder builder() { - throw new UnsupportedOperationException(); - } - - /** - * Not supported for ImmutableSortedMap. - * - * @throws UnsupportedOperationException always - * @deprecated Not supported for ImmutableSortedMap. - */ - @DoNotCall("Use naturalOrder (which does not accept an expected size)") - @Deprecated - public static ImmutableSortedMap.Builder builderWithExpectedSize(int expectedSize) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain a non-{@code Comparable} - * key. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this dummy - * version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass a key of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object)}. - */ - @DoNotCall("Pass a key of type Comparable") - @Deprecated - public static ImmutableSortedMap of(K k1, V v1) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of(K k1, V v1, K k2, V v2) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls to will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a map that may contain non-{@code Comparable} - * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass keys of type {@code Comparable} to use {@link - * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, - * Comparable, Object, Comparable, Object)}. - */ - @DoNotCall("Pass keys of type Comparable") - @Deprecated - public static ImmutableSortedMap of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9, - K k10, - V v10) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. - * - * @deprecated Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. - */ - @DoNotCall("ImmutableSortedMap.ofEntries not currently available; use ImmutableSortedMap.copyOf") - @Deprecated - public static ImmutableSortedMap ofEntries( - Entry... entries) { - throw new UnsupportedOperationException(); - } - - // No copyOf() fauxveride; see ImmutableSortedSetFauxverideShim. -} diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedMultiset.java b/android/guava/src/com/google/common/collect/ImmutableSortedMultiset.java index 5a7529e6c82a..9650fd5cc2f6 100644 --- a/android/guava/src/com/google/common/collect/ImmutableSortedMultiset.java +++ b/android/guava/src/com/google/common/collect/ImmutableSortedMultiset.java @@ -18,11 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.math.IntMath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.concurrent.LazyInit; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; @@ -30,7 +33,10 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link SortedMultiset} whose contents will never change, with many other important properties @@ -43,17 +49,81 @@ * collection will not correctly obey its specification. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Louis Wasserman * @since 12.0 */ @GwtIncompatible // hasn't been tested yet -@ElementTypesAreNonnullByDefault -public abstract class ImmutableSortedMultiset extends ImmutableSortedMultisetFauxverideShim +public abstract class ImmutableSortedMultiset extends ImmutableMultiset implements SortedMultiset { // TODO(lowasser): GWT compatibility + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableMultiset}. Elements are sorted by the specified comparator. + * + *

    Warning: {@code comparator} should be consistent with {@code equals} as + * explained in the {@link Comparator} documentation. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableSortedMultiset( + Comparator comparator) { + return toImmutableSortedMultiset(comparator, Function.identity(), e -> 1); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableSortedMultiset} + * whose elements are the result of applying {@code elementFunction} to the inputs, with counts + * equal to the result of applying {@code countFunction} to the inputs. + * + *

    If the mapped elements contain duplicates (according to {@code comparator}), the first + * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of + * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. + * + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableSortedMultiset( + Comparator comparator, + Function elementFunction, + ToIntFunction countFunction) { + checkNotNull(comparator); + checkNotNull(elementFunction); + checkNotNull(countFunction); + return Collector.of( + () -> TreeMultiset.create(comparator), + (multiset, t) -> mapAndAdd(t, multiset, elementFunction, countFunction), + (multiset1, multiset2) -> { + multiset1.addAll(multiset2); + return multiset1; + }, + (Multiset multiset) -> copyOfSortedEntries(comparator, multiset.entrySet())); + } + + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // helper for toImmutableSortedMultiset + /* + * If we make these calls inline inside toImmutableSortedMultiset, we get an Animal Sniffer error, + * despite the @IgnoreJRERequirement annotation there. My assumption is that, because javac + * generates a synthetic method for the body of the lambda, the actual method calls that Animal + * Sniffer is flagging don't appear inside toImmutableSortedMultiset but rather inside that + * synthetic method. By moving those calls to a named method, we're able to apply + * @IgnoreJRERequirement somewhere that it will help. + */ + private static void mapAndAdd( + T t, + Multiset multiset, + Function elementFunction, + ToIntFunction countFunction) { + multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)); + } + /** * Returns the empty immutable sorted multiset. * @@ -65,11 +135,11 @@ public static ImmutableSortedMultiset of() { } /** Returns an immutable sorted multiset containing a single element. */ - public static > ImmutableSortedMultiset of(E element) { + public static > ImmutableSortedMultiset of(E e1) { RegularImmutableSortedSet elementSet = - (RegularImmutableSortedSet) ImmutableSortedSet.of(element); + (RegularImmutableSortedSet) ImmutableSortedSet.of(e1); long[] cumulativeCounts = {0, 1}; - return new RegularImmutableSortedMultiset(elementSet, cumulativeCounts, 0, 1); + return new RegularImmutableSortedMultiset<>(elementSet, cumulativeCounts, 0, 1); } /** @@ -164,7 +234,7 @@ public static ImmutableSortedMultiset copyOf(Iterable elemen // Hack around E not being a subtype of Comparable. // Unsafe, see ImmutableSortedMultisetFauxverideShim. @SuppressWarnings("unchecked") - Ordering naturalOrder = (Ordering) Ordering.natural(); + Ordering naturalOrder = (Ordering) Ordering.>natural(); return copyOf(naturalOrder, elements); } @@ -182,7 +252,7 @@ public static ImmutableSortedMultiset copyOf(Iterator elemen // Hack around E not being a subtype of Comparable. // Unsafe, see ImmutableSortedMultisetFauxverideShim. @SuppressWarnings("unchecked") - Ordering naturalOrder = (Ordering) Ordering.natural(); + Ordering naturalOrder = (Ordering) Ordering.>natural(); return copyOf(naturalOrder, elements); } @@ -208,7 +278,6 @@ public static ImmutableSortedMultiset copyOf( * * @throws NullPointerException if {@code comparator} or any of {@code elements} is null */ - @SuppressWarnings("unchecked") public static ImmutableSortedMultiset copyOf( Comparator comparator, Iterable elements) { if (elements instanceof ImmutableSortedMultiset) { @@ -249,7 +318,7 @@ private static ImmutableSortedMultiset copyOfSortedEntries( if (entries.isEmpty()) { return emptyMultiset(comparator); } - ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder<>(entries.size()); long[] cumulativeCounts = new long[entries.size() + 1]; int i = 0; for (Entry entry : entries) { @@ -257,7 +326,7 @@ private static ImmutableSortedMultiset copyOfSortedEntries( cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount(); i++; } - return new RegularImmutableSortedMultiset( + return new RegularImmutableSortedMultiset<>( new RegularImmutableSortedSet(elementsBuilder.build(), comparator), cumulativeCounts, 0, @@ -269,7 +338,7 @@ static ImmutableSortedMultiset emptyMultiset(Comparator compar if (Ordering.natural().equals(comparator)) { return (ImmutableSortedMultiset) RegularImmutableSortedMultiset.NATURAL_EMPTY_MULTISET; } else { - return new RegularImmutableSortedMultiset(comparator); + return new RegularImmutableSortedMultiset<>(comparator); } } @@ -283,7 +352,7 @@ public final Comparator comparator() { @Override public abstract ImmutableSortedSet elementSet(); - @LazyInit @CheckForNull transient ImmutableSortedMultiset descendingMultiset; + @LazyInit transient @Nullable ImmutableSortedMultiset descendingMultiset; @Override public ImmutableSortedMultiset descendingMultiset() { @@ -309,8 +378,7 @@ public ImmutableSortedMultiset descendingMultiset() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final Entry pollFirstEntry() { + public final @Nullable Entry pollFirstEntry() { throw new UnsupportedOperationException(); } @@ -326,8 +394,7 @@ public final Entry pollFirstEntry() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final Entry pollLastEntry() { + public final @Nullable Entry pollLastEntry() { throw new UnsupportedOperationException(); } @@ -357,7 +424,7 @@ public ImmutableSortedMultiset subMultiset( * @throws NullPointerException if {@code comparator} is null */ public static Builder orderedBy(Comparator comparator) { - return new Builder(comparator); + return new Builder<>(comparator); } /** @@ -365,11 +432,11 @@ public static Builder orderedBy(Comparator comparator) { * reverse of their natural ordering. * *

    Note: the type parameter {@code E} extends {@code Comparable} rather than {@code - * Comparable} as a workaround for javac bug 6468354. + * Comparable} in order to accommodate users of obsolete javac versions affected by JDK-6468354. */ public static > Builder reverseOrder() { - return new Builder(Ordering.natural().reverse()); + return new Builder<>(Ordering.natural().reverse()); } /** @@ -379,11 +446,11 @@ public static > Builder reverseOrder() { * that implement {@link Comparable}. * *

    Note: the type parameter {@code E} extends {@code Comparable} rather than {@code - * Comparable} as a workaround for javac bug 6468354. + * Comparable} in order to accommodate users of obsolete javac versions affected by JDK-6468354. */ public static > Builder naturalOrder() { - return new Builder(Ordering.natural()); + return new Builder<>(Ordering.natural()); } /** @@ -638,6 +705,7 @@ public ImmutableSortedMultiset build() { } } + @J2ktIncompatible // serialization private static final class SerializedForm implements Serializable { final Comparator comparator; final E[] elements; @@ -668,7 +736,175 @@ Object readResolve() { } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm(this); } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** + * Not supported. Use {@link #toImmutableSortedMultiset} instead. This method exists only to hide + * {@link ImmutableMultiset#toImmutableMultiset} from consumers of {@code + * ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @DoNotCall("Use toImmutableSortedMultiset.") + @Deprecated + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableMultiset() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link #toImmutableSortedMultiset} instead. This method exists only to hide + * {@link ImmutableMultiset#toImmutableMultiset} from consumers of {@code + * ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset}. + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @DoNotCall("Use toImmutableSortedMultiset.") + @Deprecated + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableMultiset( + Function elementFunction, + ToIntFunction countFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link #naturalOrder}, which offers better type-safety, instead. This method + * exists only to hide {@link ImmutableMultiset#builder} from consumers of {@code + * ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#naturalOrder}, which offers better type-safety. + */ + @DoNotCall("Use naturalOrder.") + @Deprecated + public static ImmutableSortedMultiset.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable)}. + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of(E e1) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable)}. + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable)}. + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable)}. + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable)} . + * + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable, + * Comparable, Comparable...)} . + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + public static ImmutableSortedMultiset of( + E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain non-{@code + * Comparable} elements. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#copyOf(Comparable[])}. + */ + @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") + @Deprecated + // The usage of "Z" here works around bugs in Javadoc (JDK-8318093) and JDiff. + public static ImmutableSortedMultiset copyOf(Z[] elements) { + throw new UnsupportedOperationException(); + } + + @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java b/android/guava/src/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java deleted file mode 100644 index c8a7ed782fc4..000000000000 --- a/android/guava/src/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2011 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.collect; - -import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.DoNotCall; - -/** - * "Overrides" the {@link ImmutableMultiset} static methods that lack {@link - * ImmutableSortedMultiset} equivalents with deprecated, exception-throwing versions. This prevents - * accidents like the following: - * - *

    {@code
    - * List objects = ...;
    - * // Sort them:
    - * Set sorted = ImmutableSortedMultiset.copyOf(objects);
    - * // BAD CODE! The returned multiset is actually an unsorted ImmutableMultiset!
    - * }
    - *
    - * 

    While we could put the overrides in {@link ImmutableSortedMultiset} itself, it seems clearer - * to separate these "do not call" methods from those intended for normal use. - * - * @author Louis Wasserman - */ -@GwtIncompatible -@ElementTypesAreNonnullByDefault -abstract class ImmutableSortedMultisetFauxverideShim extends ImmutableMultiset { - /** - * Not supported. Use {@link ImmutableSortedMultiset#naturalOrder}, which offers better - * type-safety, instead. This method exists only to hide {@link ImmutableMultiset#builder} from - * consumers of {@code ImmutableSortedMultiset}. - * - * @throws UnsupportedOperationException always - * @deprecated Use {@link ImmutableSortedMultiset#naturalOrder}, which offers better type-safety. - */ - @DoNotCall("Use naturalOrder.") - @Deprecated - public static ImmutableSortedMultiset.Builder builder() { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass a parameter of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable)}. - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of(E element) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable, Comparable)}. - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of(E e1, E e2) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable)}. - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of(E e1, E e2, E e3) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable)}. - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable)} . - * - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain a non-{@code - * Comparable} element. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable, - * Comparable, Comparable...)} . - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset of( - E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a multiset that may contain non-{@code - * Comparable} elements. Proper calls will resolve to the version in {@code - * ImmutableSortedMultiset}, not this dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass parameters of type {@code Comparable} to use {@link - * ImmutableSortedMultiset#copyOf(Comparable[])}. - */ - @DoNotCall("Elements must be Comparable. (Or, pass a Comparator to orderedBy or copyOf.)") - @Deprecated - public static ImmutableSortedMultiset copyOf(E[] elements) { - throw new UnsupportedOperationException(); - } - - /* - * We would like to include an unsupported " copyOf(Iterable)" here, providing only the - * properly typed "> copyOf(Iterable)" in ImmutableSortedMultiset (and - * likewise for the Iterator equivalent). However, due to a change in Sun's interpretation of the - * JLS (as described at http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler - * available as of this writing rejects our attempts. To maintain compatibility with that version - * and with any other compilers that interpret the JLS similarly, there is no definition of - * copyOf() here, and the definition in ImmutableSortedMultiset matches that in - * ImmutableMultiset. - * - * The result is that ImmutableSortedMultiset.copyOf() may be called on non-Comparable elements. - * We have not discovered a better solution. In retrospect, the static factory methods should - * have gone in a separate class so that ImmutableSortedMultiset wouldn't "inherit" - * too-permissive factory methods from ImmutableMultiset. - */ -} diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedSet.java b/android/guava/src/com/google/common/collect/ImmutableSortedSet.java index fabf599f16c1..489d3e6b41c0 100644 --- a/android/guava/src/com/google/common/collect/ImmutableSortedSet.java +++ b/android/guava/src/com/google/common/collect/ImmutableSortedSet.java @@ -19,9 +19,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ObjectArrays.checkElementsNotNull; +import static java.lang.System.arraycopy; +import static java.util.Arrays.sort; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.concurrent.LazyInit; @@ -35,8 +38,8 @@ import java.util.Iterator; import java.util.NavigableSet; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link NavigableSet} whose contents will never change, with many other important properties @@ -49,7 +52,7 @@ * collection will not correctly obey its specification. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Jared Levy * @author Louis Wasserman @@ -58,14 +61,32 @@ // TODO(benyu): benchmark and optimize all creation paths, which are a mess now @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization -@ElementTypesAreNonnullByDefault -public abstract class ImmutableSortedSet extends ImmutableSortedSetFauxverideShim +public abstract class ImmutableSortedSet extends ImmutableSet implements NavigableSet, SortedIterable { + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableSortedSet}, ordered by the specified comparator. + * + *

    If the elements contain duplicates (according to the comparator), only the first duplicate + * in encounter order will appear in the result. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableSortedSet( + Comparator comparator) { + return CollectCollectors.toImmutableSortedSet(comparator); + } + static RegularImmutableSortedSet emptySet(Comparator comparator) { if (Ordering.natural().equals(comparator)) { - return (RegularImmutableSortedSet) RegularImmutableSortedSet.NATURAL_EMPTY_SET; + @SuppressWarnings("unchecked") // The natural-ordered empty set supports all types. + RegularImmutableSortedSet result = + (RegularImmutableSortedSet) RegularImmutableSortedSet.NATURAL_EMPTY_SET; + return result; } else { - return new RegularImmutableSortedSet(ImmutableList.of(), comparator); + return new RegularImmutableSortedSet<>(ImmutableList.of(), comparator); } } @@ -74,13 +95,14 @@ static RegularImmutableSortedSet emptySet(Comparator comparato * *

    Performance note: the instance returned is a singleton. */ + @SuppressWarnings("unchecked") // The natural-ordered empty set supports all types. public static ImmutableSortedSet of() { return (ImmutableSortedSet) RegularImmutableSortedSet.NATURAL_EMPTY_SET; } /** Returns an immutable sorted set containing a single element. */ - public static > ImmutableSortedSet of(E element) { - return new RegularImmutableSortedSet(ImmutableList.of(element), Ordering.natural()); + public static > ImmutableSortedSet of(E e1) { + return new RegularImmutableSortedSet<>(ImmutableList.of(e1), Ordering.natural()); } /** @@ -90,7 +112,6 @@ public static > ImmutableSortedSet of(E eleme * * @throws NullPointerException if any element is null */ - @SuppressWarnings("unchecked") public static > ImmutableSortedSet of(E e1, E e2) { return construct(Ordering.natural(), 2, e1, e2); } @@ -102,7 +123,6 @@ public static > ImmutableSortedSet of(E e1, E * * @throws NullPointerException if any element is null */ - @SuppressWarnings("unchecked") public static > ImmutableSortedSet of(E e1, E e2, E e3) { return construct(Ordering.natural(), 3, e1, e2, e3); } @@ -114,7 +134,6 @@ public static > ImmutableSortedSet of(E e1, E * * @throws NullPointerException if any element is null */ - @SuppressWarnings("unchecked") public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4) { return construct(Ordering.natural(), 4, e1, e2, e3, e4); } @@ -126,7 +145,6 @@ public static > ImmutableSortedSet of(E e1, E * * @throws NullPointerException if any element is null */ - @SuppressWarnings("unchecked") public static > ImmutableSortedSet of( E e1, E e2, E e3, E e4, E e5) { return construct(Ordering.natural(), 5, e1, e2, e3, e4, e5); @@ -143,14 +161,14 @@ public static > ImmutableSortedSet of( @SuppressWarnings("unchecked") public static > ImmutableSortedSet of( E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { - Comparable[] contents = new Comparable[6 + remaining.length]; + Comparable[] contents = new Comparable[6 + remaining.length]; contents[0] = e1; contents[1] = e2; contents[2] = e3; contents[3] = e4; contents[4] = e5; contents[5] = e6; - System.arraycopy(remaining, 0, contents, 6, remaining.length); + arraycopy(remaining, 0, contents, 6, remaining.length); return construct(Ordering.natural(), contents.length, (E[]) contents); } @@ -193,7 +211,7 @@ public static ImmutableSortedSet copyOf(Iterable elements) { // Hack around E not being a subtype of Comparable. // Unsafe, see ImmutableSortedSetFauxverideShim. @SuppressWarnings("unchecked") - Ordering naturalOrder = (Ordering) Ordering.natural(); + Ordering naturalOrder = (Ordering) Ordering.>natural(); return copyOf(naturalOrder, elements); } @@ -225,7 +243,7 @@ public static ImmutableSortedSet copyOf(Collection elements) // Hack around E not being a subtype of Comparable. // Unsafe, see ImmutableSortedSetFauxverideShim. @SuppressWarnings("unchecked") - Ordering naturalOrder = (Ordering) Ordering.natural(); + Ordering naturalOrder = (Ordering) Ordering.>natural(); return copyOf(naturalOrder, elements); } @@ -244,7 +262,7 @@ public static ImmutableSortedSet copyOf(Iterator elements) { // Hack around E not being a subtype of Comparable. // Unsafe, see ImmutableSortedSetFauxverideShim. @SuppressWarnings("unchecked") - Ordering naturalOrder = (Ordering) Ordering.natural(); + Ordering naturalOrder = (Ordering) Ordering.>natural(); return copyOf(naturalOrder, elements); } @@ -328,7 +346,7 @@ public static ImmutableSortedSet copyOfSorted(SortedSet sortedSet) { if (list.isEmpty()) { return emptySet(comparator); } else { - return new RegularImmutableSortedSet(list, comparator); + return new RegularImmutableSortedSet<>(list, comparator); } } @@ -349,7 +367,7 @@ static ImmutableSortedSet construct( return emptySet(comparator); } checkElementsNotNull(contents, n); - Arrays.sort(contents, 0, n, comparator); + sort(contents, 0, n, comparator); int uniques = 1; for (int i = 1; i < n; i++) { E cur = contents[i]; @@ -377,7 +395,7 @@ static ImmutableSortedSet construct( * @throws NullPointerException if {@code comparator} is null */ public static Builder orderedBy(Comparator comparator) { - return new Builder(comparator); + return new Builder<>(comparator); } /** @@ -385,7 +403,7 @@ public static Builder orderedBy(Comparator comparator) { * of their natural ordering. */ public static > Builder reverseOrder() { - return new Builder(Collections.reverseOrder()); + return new Builder<>(Collections.reverseOrder()); } /** @@ -395,7 +413,7 @@ public static > Builder reverseOrder() { * implement {@link Comparable}. */ public static > Builder naturalOrder() { - return new Builder(Ordering.natural()); + return new Builder<>(Ordering.natural()); } /** @@ -422,10 +440,22 @@ public static final class Builder extends ImmutableSet.Builder { * Creates a new builder. The returned builder is equivalent to the builder generated by {@link * ImmutableSortedSet#orderedBy}. */ + /* + * TODO(cpovirk): use Object[] instead of E[] in the mainline? (The backport is different and + * doesn't need this suppression, but we keep it to minimize diffs.) Generally be more clear + * about when we have an Object[] vs. a Comparable[] or other array type in internalArray? If we + * used Object[], we might be able to optimize toArray() to use clone() sometimes. (See + * cl/592273615 and cl/592273683.) + */ public Builder(Comparator comparator) { this.comparator = checkNotNull(comparator); } + Builder(Comparator comparator, int expectedKeys) { + super(expectedKeys, false); + this.comparator = checkNotNull(comparator); + } + /** * Adds {@code element} to the {@code ImmutableSortedSet}. If the {@code ImmutableSortedSet} * already contains {@code element}, then {@code add} has no effect. (only the previously added @@ -509,11 +539,11 @@ public ImmutableSortedSet build() { } } - int unsafeCompare(Object a, @CheckForNull Object b) { + int unsafeCompare(Object a, @Nullable Object b) { return unsafeCompare(comparator, a, b); } - static int unsafeCompare(Comparator comparator, Object a, @CheckForNull Object b) { + static int unsafeCompare(Comparator comparator, Object a, @Nullable Object b) { // Pretend the comparator can compare anything. If it turns out it can't // compare a and b, we should get a CCE or NPE on the subsequent line. Only methods // that are spec'd to throw CCE and NPE should call this. @@ -556,7 +586,9 @@ public ImmutableSortedSet headSet(E toElement) { return headSet(toElement, false); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override public ImmutableSortedSet headSet(E toElement, boolean inclusive) { return headSetImpl(checkNotNull(toElement), inclusive); @@ -579,7 +611,9 @@ public ImmutableSortedSet subSet(E fromElement, E toElement) { return subSet(fromElement, true, toElement, false); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public ImmutableSortedSet subSet( @@ -605,7 +639,9 @@ public ImmutableSortedSet tailSet(E fromElement) { return tailSet(fromElement, true); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override public ImmutableSortedSet tailSet(E fromElement, boolean inclusive) { return tailSetImpl(checkNotNull(fromElement), inclusive); @@ -622,34 +658,38 @@ abstract ImmutableSortedSet subSetImpl( abstract ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive); - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override - @CheckForNull - public E lower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); + public @Nullable E lower(E e) { + return Iterators.<@Nullable E>getNext(headSet(e, false).descendingIterator(), null); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override - @CheckForNull - public E floor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); + public @Nullable E floor(E e) { + return Iterators.<@Nullable E>getNext(headSet(e, true).descendingIterator(), null); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @Override - @CheckForNull - public E ceiling(E e) { - return Iterables.getFirst(tailSet(e, true), null); + public @Nullable E ceiling(E e) { + return Iterables.<@Nullable E>getFirst(tailSet(e, true), null); } - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override - @CheckForNull - public E higher(E e) { - return Iterables.getFirst(tailSet(e, false), null); + public @Nullable E higher(E e) { + return Iterables.<@Nullable E>getFirst(tailSet(e, false), null); } @Override @@ -674,8 +714,7 @@ public E last() { @GwtIncompatible // NavigableSet @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final E pollFirst() { + public final @Nullable E pollFirst() { throw new UnsupportedOperationException(); } @@ -691,17 +730,17 @@ public final E pollFirst() { @GwtIncompatible // NavigableSet @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final E pollLast() { + public final @Nullable E pollLast() { throw new UnsupportedOperationException(); } @GwtIncompatible // NavigableSet @LazyInit - @CheckForNull - transient ImmutableSortedSet descendingSet; + transient @Nullable ImmutableSortedSet descendingSet; - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public ImmutableSortedSet descendingSet() { @@ -720,13 +759,15 @@ public ImmutableSortedSet descendingSet() { @GwtIncompatible // NavigableSet abstract ImmutableSortedSet createDescendingSet(); - /** @since 12.0 */ + /** + * @since 12.0 + */ @GwtIncompatible // NavigableSet @Override public abstract UnmodifiableIterator descendingIterator(); /** Returns the position of an element within the set, or -1 if not present. */ - abstract int indexOf(@CheckForNull Object target); + abstract int indexOf(@Nullable Object target); /* * This class is used to serialize all ImmutableSortedSet instances, @@ -734,6 +775,7 @@ public ImmutableSortedSet descendingSet() { * only. This is necessary to ensure that the existence of a particular * implementation type is an implementation detail. */ + @J2ktIncompatible // serialization private static class SerializedForm implements Serializable { final Comparator comparator; final Object[] elements; @@ -748,15 +790,168 @@ Object readResolve() { return new Builder(comparator).add((E[]) elements).build(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } + @J2ktIncompatible // serialization private void readObject(ObjectInputStream unused) throws InvalidObjectException { throw new InvalidObjectException("Use SerializedForm"); } @Override + @J2ktIncompatible // serialization Object writeReplace() { return new SerializedForm(comparator, toArray()); } + + /** + * Not supported. Use {@link #toImmutableSortedSet} instead. This method exists only to hide + * {@link ImmutableSet#toImmutableSet} from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedSet#toImmutableSortedSet}. + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @DoNotCall("Use toImmutableSortedSet") + @Deprecated + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toImmutableSet() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link #naturalOrder}, which offers better type-safety, instead. This method + * exists only to hide {@link ImmutableSet#builder} from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedSet#naturalOrder}, which offers better type-safety. + */ + @DoNotCall("Use naturalOrder") + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. This method exists only to hide {@link ImmutableSet#builderWithExpectedSize} + * from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Not supported by ImmutableSortedSet. + */ + @DoNotCall("Use naturalOrder (which does not accept an expected size)") + @Deprecated + public static ImmutableSortedSet.Builder builderWithExpectedSize(int expectedSize) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable)}. + */ + @DoNotCall("Pass a parameter of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable)}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable)}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable)}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of( Comparable, Comparable, Comparable, Comparable, Comparable)}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable, Comparable, + * Comparable, Comparable...)}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain non-{@code Comparable} + * elements. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#copyOf(Comparable[])}. + */ + @DoNotCall("Pass parameters of type Comparable") + @Deprecated + // The usage of "Z" here works around bugs in Javadoc (JDK-8318093) and JDiff. + public static ImmutableSortedSet copyOf(Z[] elements) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/ImmutableSortedSetFauxverideShim.java b/android/guava/src/com/google/common/collect/ImmutableSortedSetFauxverideShim.java deleted file mode 100644 index ca19d79db194..000000000000 --- a/android/guava/src/com/google/common/collect/ImmutableSortedSetFauxverideShim.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2009 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.collect; - -import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.DoNotCall; - -/** - * "Overrides" the {@link ImmutableSet} static methods that lack {@link ImmutableSortedSet} - * equivalents with deprecated, exception-throwing versions. This prevents accidents like the - * following: - * - *

    {@code
    - * List objects = ...;
    - * // Sort them:
    - * Set sorted = ImmutableSortedSet.copyOf(objects);
    - * // BAD CODE! The returned set is actually an unsorted ImmutableSet!
    - * }
    - *
    - * 

    While we could put the overrides in {@link ImmutableSortedSet} itself, it seems clearer to - * separate these "do not call" methods from those intended for normal use. - * - * @author Chris Povirk - */ -@GwtIncompatible -@ElementTypesAreNonnullByDefault -abstract class ImmutableSortedSetFauxverideShim extends ImmutableSet { - /** - * Not supported. Use {@link ImmutableSortedSet#naturalOrder}, which offers better type-safety, - * instead. This method exists only to hide {@link ImmutableSet#builder} from consumers of {@code - * ImmutableSortedSet}. - * - * @throws UnsupportedOperationException always - * @deprecated Use {@link ImmutableSortedSet#naturalOrder}, which offers better type-safety. - */ - @DoNotCall("Use naturalOrder") - @Deprecated - public static ImmutableSortedSet.Builder builder() { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. This method exists only to hide {@link ImmutableSet#builderWithExpectedSize} - * from consumers of {@code ImmutableSortedSet}. - * - * @throws UnsupportedOperationException always - * @deprecated Not supported by ImmutableSortedSet. - */ - @DoNotCall("Use naturalOrder (which does not accept an expected size)") - @Deprecated - public static ImmutableSortedSet.Builder builderWithExpectedSize(int expectedSize) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass a parameter of type {@code Comparable} to use {@link - * ImmutableSortedSet#of(Comparable)}. - */ - @DoNotCall("Pass a parameter of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E element) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#of(Comparable, Comparable)}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E e1, E e2) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#of(Comparable, Comparable, Comparable)}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E e1, E e2, E e3) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable)}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E e1, E e2, E e3, E e4) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#of( Comparable, Comparable, Comparable, Comparable, Comparable)}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} - * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass the parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable, Comparable, - * Comparable, Comparable...)}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { - throw new UnsupportedOperationException(); - } - - /** - * Not supported. You are attempting to create a set that may contain non-{@code Comparable} - * elements. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this - * dummy version. - * - * @throws UnsupportedOperationException always - * @deprecated Pass parameters of type {@code Comparable} to use {@link - * ImmutableSortedSet#copyOf(Comparable[])}. - */ - @DoNotCall("Pass parameters of type Comparable") - @Deprecated - public static ImmutableSortedSet copyOf(E[] elements) { - throw new UnsupportedOperationException(); - } - - /* - * We would like to include an unsupported " copyOf(Iterable)" here, - * providing only the properly typed - * "> copyOf(Iterable)" in ImmutableSortedSet (and - * likewise for the Iterator equivalent). However, due to a change in Sun's - * interpretation of the JLS (as described at - * http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler - * available as of this writing rejects our attempts. To maintain - * compatibility with that version and with any other compilers that interpret - * the JLS similarly, there is no definition of copyOf() here, and the - * definition in ImmutableSortedSet matches that in ImmutableSet. - * - * The result is that ImmutableSortedSet.copyOf() may be called on - * non-Comparable elements. We have not discovered a better solution. In - * retrospect, the static factory methods should have gone in a separate class - * so that ImmutableSortedSet wouldn't "inherit" too-permissive factory - * methods from ImmutableSet. - */ -} diff --git a/android/guava/src/com/google/common/collect/ImmutableTable.java b/android/guava/src/com/google/common/collect/ImmutableTable.java index 9dcb50c85c0d..afb16df7bede 100644 --- a/android/guava/src/com/google/common/collect/ImmutableTable.java +++ b/android/guava/src/com/google/common/collect/ImmutableTable.java @@ -17,34 +17,85 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Tables.immutableCell; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.DoNotMock; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; -import javax.annotation.CheckForNull; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A {@link Table} whose contents will never change, with many other important properties detailed * at {@link ImmutableCollection}. * *

    See the Guava User Guide article on immutable collections. + * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections. * * @author Gregory Kick * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ImmutableTable extends AbstractTable implements Serializable { + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each + * input element is mapped to one cell in the returned table, with the rows, columns, and values + * generated by applying the specified functions. + * + *

    The returned {@code Collector} will throw a {@code NullPointerException} at collection time + * if the row, column, or value functions return null on any input. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction) { + return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each + * input element is mapped to one cell in the returned table, with the rows, columns, and values + * generated by applying the specified functions. If multiple inputs are mapped to the same row + * and column pair, they will be combined with the specified merging function in encounter order. + * + *

    The returned {@code Collector} will throw a {@code NullPointerException} at collection time + * if the row, column, value, or merging functions return null on any input. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return TableCollectors.toImmutableTable( + rowFunction, columnFunction, valueFunction, mergeFunction); + } + /** * Returns an empty immutable table. * @@ -90,7 +141,7 @@ static ImmutableTable copyOf( for (Cell cell : cells) { builder.put(cell); } - return builder.build(); + return builder.buildOrThrow(); } /** @@ -106,7 +157,7 @@ public static Builder builder() { * new entry with those values. */ static Cell cellOf(R rowKey, C columnKey, V value) { - return Tables.immutableCell( + return immutableCell( checkNotNull(rowKey, "rowKey"), checkNotNull(columnKey, "columnKey"), checkNotNull(value, "value")); @@ -141,8 +192,8 @@ static Cell cellOf(R rowKey, C columnKey, V value) { @DoNotMock public static final class Builder { private final List> cells = Lists.newArrayList(); - @CheckForNull private Comparator rowComparator; - @CheckForNull private Comparator columnComparator; + private @Nullable Comparator rowComparator; + private @Nullable Comparator columnComparator; /** * Creates a new builder. The returned builder is equivalent to the builder generated by {@link @@ -239,7 +290,7 @@ public ImmutableTable buildOrThrow() { case 0: return of(); case 1: - return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells)); + return new SingletonImmutableTable<>(getOnlyElement(cells)); default: return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); } @@ -327,12 +378,12 @@ public ImmutableSet rowKeySet() { public abstract ImmutableMap> rowMap(); @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return get(rowKey, columnKey) != null; } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return values().contains(value); } @@ -359,8 +410,7 @@ public final void clear() { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final V put(R rowKey, C columnKey, V value) { + public final @Nullable V put(R rowKey, C columnKey, V value) { throw new UnsupportedOperationException(); } @@ -387,14 +437,10 @@ public final void putAll(Table table) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public final V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public final @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { throw new UnsupportedOperationException(); } - /** Creates the common serialized form for this table. */ - abstract SerializedForm createSerializedForm(); - /** * Serialized type for all ImmutableTable instances. It captures the logical contents and * preserves iteration order of all views. @@ -447,10 +493,18 @@ Object readResolve() { cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys)); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - final Object writeReplace() { - return createSerializedForm(); + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + abstract Object writeReplace(); + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/collect/IndexedImmutableSet.java b/android/guava/src/com/google/common/collect/IndexedImmutableSet.java index 25aae94e8b96..2ab6519c1517 100644 --- a/android/guava/src/com/google/common/collect/IndexedImmutableSet.java +++ b/android/guava/src/com/google/common/collect/IndexedImmutableSet.java @@ -18,10 +18,10 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import org.checkerframework.checker.nullness.qual.Nullable; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class IndexedImmutableSet extends ImmutableSet { abstract E get(int index); @@ -53,6 +53,24 @@ boolean isPartialView() { public int size() { return IndexedImmutableSet.this.size(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/Interner.java b/android/guava/src/com/google/common/collect/Interner.java index bfc2035f101b..e8a9002d5cd5 100644 --- a/android/guava/src/com/google/common/collect/Interner.java +++ b/android/guava/src/com/google/common/collect/Interner.java @@ -17,7 +17,7 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.DoNotMock; /** @@ -32,8 +32,8 @@ * @since 3.0 */ @DoNotMock("Use Interners.new*Interner") +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface Interner { /** * Chooses and returns the representative instance for any of a collection of instances that are @@ -47,6 +47,5 @@ public interface Interner { * * @throws NullPointerException if {@code sample} is null */ - @CanIgnoreReturnValue // TODO(cpovirk): Consider removing this? E intern(E sample); } diff --git a/android/guava/src/com/google/common/collect/Interners.java b/android/guava/src/com/google/common/collect/Interners.java index 10ae2746332b..1eec72eac961 100644 --- a/android/guava/src/com/google/common/collect/Interners.java +++ b/android/guava/src/com/google/common/collect/Interners.java @@ -17,12 +17,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; import com.google.common.base.Function; import com.google.common.collect.MapMaker.Dummy; import com.google.common.collect.MapMakerInternalMap.InternalEntry; -import javax.annotation.CheckForNull; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import org.jspecify.annotations.Nullable; /** * Contains static methods pertaining to instances of {@link Interner}. @@ -30,8 +32,8 @@ * @author Kevin Bourrillion * @since 3.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Interners { private Interners() {} @@ -51,6 +53,7 @@ private InternerBuilder() {} * * @see Interners#newStrongInterner() */ + @CanIgnoreReturnValue public InternerBuilder strong() { this.strong = true; return this; @@ -61,6 +64,7 @@ public InternerBuilder strong() { * * @see Interners#newWeakInterner() */ + @CanIgnoreReturnValue @GwtIncompatible("java.lang.ref.WeakReference") public InternerBuilder weak() { this.strong = false; @@ -72,6 +76,7 @@ public InternerBuilder weak() { * * @see MapMaker#concurrencyLevel(int) */ + @CanIgnoreReturnValue public InternerBuilder concurrencyLevel(int concurrencyLevel) { this.mapMaker.concurrencyLevel(concurrencyLevel); return this; @@ -180,7 +185,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof InternerFunction) { InternerFunction that = (InternerFunction) other; return interner.equals(that.interner); diff --git a/android/guava/src/com/google/common/collect/Iterables.java b/android/guava/src/com/google/common/collect/Iterables.java index 8e847e5f6243..10dff7ad268b 100644 --- a/android/guava/src/com/google/common/collect/Iterables.java +++ b/android/guava/src/com/google/common/collect/Iterables.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkRemove; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; @@ -28,6 +27,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; @@ -36,17 +36,18 @@ import java.util.Queue; import java.util.RandomAccess; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Stream; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * An assortment of mainly legacy static utility methods that operate on or return objects of type * {@code Iterable}. Except as noted, each method has a corresponding {@link Iterator}-based method * in the {@link Iterators} class. * - *

    Java 8 users: several common uses for this class are now more comprehensively addressed - * by the new {@link java.util.stream.Stream} library. Read the method documentation below for - * comparisons. This class is not being deprecated, but we gently encourage you to migrate to + *

    Java 8+ users: several common uses for this class are now more comprehensively + * addressed by the new {@link java.util.stream.Stream} library. Read the method documentation below + * for comparisons. This class is not being deprecated, but we gently encourage you to migrate to * streams. * *

    Performance notes: Unless otherwise noted, all of the iterables produced in this class @@ -54,7 +55,7 @@ * absolutely necessary. * *

    See the Guava User Guide article on {@code + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#iterables">{@code * Iterables}. * * @author Kevin Bourrillion @@ -62,13 +63,12 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Iterables { private Iterables() {} /** Returns an unmodifiable view of {@code iterable}. */ public static Iterable unmodifiableIterable( - final Iterable iterable) { + Iterable iterable) { checkNotNull(iterable); if (iterable instanceof UnmodifiableIterable || iterable instanceof ImmutableCollection) { @SuppressWarnings("unchecked") // Since it's unmodifiable, the covariant cast is safe @@ -84,6 +84,9 @@ private Iterables() {} * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(iterable)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static Iterable unmodifiableIterable(ImmutableCollection iterable) { return checkNotNull(iterable); @@ -124,7 +127,7 @@ public static int size(Iterable iterable) { */ // instead of because of Kotlin b/189937072, discussed in Joiner. public static boolean contains( - Iterable iterable, @CheckForNull Object element) { + Iterable iterable, @Nullable Object element) { if (iterable instanceof Collection) { Collection collection = (Collection) iterable; return Collections2.safeContains(collection, element); @@ -173,6 +176,9 @@ public static boolean retainAll(Iterable removeFrom, Collection elementsTo * The behavior of this method is not specified if {@code predicate} is dependent on {@code * removeFrom}. * + *

    Java 8+ users: if {@code removeFrom} is a {@link Collection}, use {@code + * removeFrom.removeIf(predicate)} instead. + * * @param removeFrom the iterable to (potentially) remove elements from * @param predicate a predicate that determines whether an element should be removed * @return {@code true} if any elements were removed from the iterable @@ -244,8 +250,7 @@ public static boolean retainAll(Iterable removeFrom, Collection elementsTo } /** Removes and returns the first matching element, or returns {@code null} if there is none. */ - @CheckForNull - static T removeFirstMatching( + static @Nullable T removeFirstMatching( Iterable removeFrom, Predicate predicate) { checkNotNull(predicate); Iterator iterator = removeFrom.iterator(); @@ -290,7 +295,7 @@ public static String toString(Iterable iterable) { /** * Returns the single element contained in {@code iterable}. * - *

    Java 8 users: the {@code Stream} equivalent to this method is {@code + *

    Java 8+ users: the {@code Stream} equivalent to this method is {@code * stream.collect(MoreCollectors.onlyElement())}. * * @throws NoSuchElementException if the iterable is empty @@ -305,7 +310,7 @@ public static String toString(Iterable iterable) { * Returns the single element contained in {@code iterable}, or {@code defaultValue} if the * iterable is empty. * - *

    Java 8 users: the {@code Stream} equivalent to this method is {@code + *

    Java 8+ users: the {@code Stream} equivalent to this method is {@code * stream.collect(MoreCollectors.toOptional()).orElse(defaultValue)}. * * @throws IllegalArgumentException if the iterator contains multiple elements @@ -324,12 +329,8 @@ public static String toString(Iterable iterable) { * @return a newly-allocated array into which all the elements of the iterable have been copied */ @GwtIncompatible // Array.newInstance(Class, int) - /* - * If we could express Class<@Nonnull T>, we could generalize the type parameter to , and then we could accept an Iterable and return a plain T[] - * instead of a @Nullable T[]. - */ - public static @Nullable T[] toArray(Iterable iterable, Class type) { + public static T[] toArray( + Iterable iterable, Class<@NonNull T> type) { return toArray(iterable, ObjectArrays.newArray(type, 0)); } @@ -379,14 +380,14 @@ public static String toString(Iterable iterable) { * Returns the number of elements in the specified iterable that equal the specified object. This * implementation avoids a full iteration when the iterable is a {@link Multiset} or {@link Set}. * - *

    Java 8 users: In most cases, the {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: In most cases, the {@code Stream} equivalent of this method is {@code * stream.filter(element::equals).count()}. If {@code element} might be null, use {@code * stream.filter(Predicate.isEqual(element)).count()} instead. * * @see java.util.Collections#frequency(Collection, Object) Collections.frequency(Collection, * Object) */ - public static int frequency(Iterable iterable, @CheckForNull Object element) { + public static int frequency(Iterable iterable, @Nullable Object element) { if ((iterable instanceof Multiset)) { return ((Multiset) iterable).count(element); } else if ((iterable instanceof Set)) { @@ -410,10 +411,10 @@ public static int frequency(Iterable iterable, @CheckForNull Object element) *

    To cycle over the iterable {@code n} times, use the following: {@code * Iterables.concat(Collections.nCopies(n, iterable))} * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code * Stream.generate(() -> iterable).flatMap(Streams::stream)}. */ - public static Iterable cycle(final Iterable iterable) { + public static Iterable cycle(Iterable iterable) { checkNotNull(iterable); return new FluentIterable() { @Override @@ -444,8 +445,8 @@ public String toString() { *

    To cycle over the elements {@code n} times, use the following: {@code * Iterables.concat(Collections.nCopies(n, Arrays.asList(elements)))} * - *

    Java 8 users: If passing a single element {@code e}, the {@code Stream} equivalent of - * this method is {@code Stream.generate(() -> e)}. Otherwise, put the elements in a collection + *

    Java 8+ users: If passing a single element {@code e}, the {@code Stream} equivalent + * of this method is {@code Stream.generate(() -> e)}. Otherwise, put the elements in a collection * and use {@code Stream.generate(() -> collection).flatMap(Collection::stream)}. */ @SafeVarargs @@ -461,8 +462,8 @@ public String toString() { *

    The returned iterable's iterator supports {@code remove()} when the corresponding input * iterator supports it. * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code Stream.concat(a, - * b)}. + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code + * Stream.concat(a, b)}. */ public static Iterable concat( Iterable a, Iterable b) { @@ -477,7 +478,7 @@ public String toString() { *

    The returned iterable's iterator supports {@code remove()} when the corresponding input * iterator supports it. * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code * Streams.concat(a, b, c)}. */ public static Iterable concat( @@ -494,7 +495,7 @@ public String toString() { *

    The returned iterable's iterator supports {@code remove()} when the corresponding input * iterator supports it. * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code * Streams.concat(a, b, c, d)}. */ public static Iterable concat( @@ -513,7 +514,7 @@ public String toString() { *

    The returned iterable's iterator supports {@code remove()} when the corresponding input * iterator supports it. * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code * Streams.concat(...)}. * * @throws NullPointerException if any of the provided iterables is null @@ -532,7 +533,7 @@ public String toString() { * iterator supports it. The methods of the returned iterable may throw {@code * NullPointerException} if any of the input iterators is null. * - *

    Java 8 users: The {@code Stream} equivalent of this method is {@code + *

    Java 8+ users: The {@code Stream} equivalent of this method is {@code * streamOfStreams.flatMap(s -> s)}. */ public static Iterable concat( @@ -563,7 +564,7 @@ public String toString() { * @throws IllegalArgumentException if {@code size} is nonpositive */ public static Iterable> partition( - final Iterable iterable, final int size) { + Iterable iterable, int size) { checkNotNull(iterable); checkArgument(size > 0); return new FluentIterable>() { @@ -590,7 +591,7 @@ public Iterator> iterator() { * @throws IllegalArgumentException if {@code size} is nonpositive */ public static Iterable> paddedPartition( - final Iterable iterable, final int size) { + Iterable iterable, int size) { checkNotNull(iterable); checkArgument(size > 0); return new FluentIterable>() { @@ -608,7 +609,7 @@ public Iterator> iterator() { *

    {@code Stream} equivalent: {@link Stream#filter}. */ public static Iterable filter( - final Iterable unfiltered, final Predicate retainIfTrue) { + Iterable unfiltered, Predicate retainIfTrue) { checkNotNull(unfiltered); checkNotNull(retainIfTrue); return new FluentIterable() { @@ -635,7 +636,7 @@ public Iterator iterator() { */ @SuppressWarnings("unchecked") @GwtIncompatible // Class.isInstance - public static Iterable filter(final Iterable unfiltered, final Class desiredType) { + public static Iterable filter(Iterable unfiltered, Class desiredType) { checkNotNull(unfiltered); checkNotNull(desiredType); return (Iterable) filter(unfiltered, Predicates.instanceOf(desiredType)); @@ -702,12 +703,9 @@ public static Iterable filter(final Iterable unfiltered, final Class T find( - Iterable iterable, - Predicate predicate, - @CheckForNull T defaultValue) { - return Iterators.find(iterable.iterator(), predicate, defaultValue); + public static @Nullable T find( + Iterable iterable, Predicate predicate, @Nullable T defaultValue) { + return Iterators.find(iterable.iterator(), predicate, defaultValue); } /** @@ -754,7 +752,7 @@ public static Optional tryFind(Iterable iterable, Predicate *

    {@code Stream} equivalent: {@link Stream#map} */ public static Iterable transform( - final Iterable fromIterable, final Function function) { + Iterable fromIterable, Function function) { checkNotNull(fromIterable); checkNotNull(function); return new FluentIterable() { @@ -805,7 +803,7 @@ public Iterator iterator() { checkNotNull(iterable); Iterators.checkNonnegative(position); if (iterable instanceof List) { - List list = Lists.cast(iterable); + List list = (List) iterable; return (position < list.size()) ? list.get(position) : defaultValue; } else { Iterator iterator = iterable.iterator(); @@ -879,7 +877,7 @@ public Iterator iterator() { if (c.isEmpty()) { return defaultValue; } else if (iterable instanceof List) { - return getLastInNonemptyList(Lists.cast(iterable)); + return getLastInNonemptyList((List) iterable); } } @@ -911,7 +909,7 @@ public Iterator iterator() { * @since 3.0 */ public static Iterable skip( - final Iterable iterable, final int numberToSkip) { + Iterable iterable, int numberToSkip) { checkNotNull(iterable); checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); @@ -919,11 +917,11 @@ public Iterator iterator() { @Override public Iterator iterator() { if (iterable instanceof List) { - final List list = (List) iterable; + List list = (List) iterable; int toSkip = Math.min(list.size(), numberToSkip); return list.subList(toSkip, list.size()).iterator(); } - final Iterator iterator = iterable.iterator(); + Iterator iterator = iterable.iterator(); Iterators.advance(iterator, numberToSkip); @@ -972,7 +970,7 @@ public void remove() { * @since 3.0 */ public static Iterable limit( - final Iterable iterable, final int limitSize) { + Iterable iterable, int limitSize) { checkNotNull(iterable); checkArgument(limitSize >= 0, "limit is negative"); return new FluentIterable() { @@ -987,10 +985,13 @@ public Iterator iterator() { * Returns a view of the supplied iterable that wraps each generated {@link Iterator} through * {@link Iterators#consumingIterator(Iterator)}. * - *

    Note: If {@code iterable} is a {@link Queue}, the returned iterable will get entries from - * {@link Queue#remove()} since {@link Queue}'s iteration order is undefined. Calling {@link - * Iterator#hasNext()} on a generated iterator from the returned iterable may cause an item to be - * immediately dequeued for return on a subsequent call to {@link Iterator#next()}. + *

    Note: If {@code iterable} is a {@link Queue}, the returned iterable will instead use {@link + * Queue#isEmpty} and {@link Queue#remove()}, since {@link Queue}'s iteration order is undefined. + * Calling {@link Iterator#hasNext()} on a generated iterator from the returned iterable may cause + * an item to be immediately dequeued for return on a subsequent call to {@link Iterator#next()}. + * + *

    Whether the input {@code iterable} is a {@link Queue} or not, the returned {@code Iterable} + * is not thread-safe. * * @param iterable the iterable to wrap * @return a view of the supplied iterable that wraps each generated iterator through {@link @@ -999,8 +1000,7 @@ public Iterator iterator() { * @see Iterators#consumingIterator(Iterator) * @since 2.0 */ - public static Iterable consumingIterable( - final Iterable iterable) { + public static Iterable consumingIterable(Iterable iterable) { checkNotNull(iterable); return new FluentIterable() { @@ -1050,10 +1050,8 @@ public static boolean isEmpty(Iterable iterable) { * * @since 11.0 */ - @Beta public static Iterable mergeSorted( - final Iterable> iterables, - final Comparator comparator) { + Iterable> iterables, Comparator comparator) { checkNotNull(iterables, "iterables"); checkNotNull(comparator, "comparator"); Iterable iterable = @@ -1061,21 +1059,9 @@ public static boolean isEmpty(Iterable iterable) { @Override public Iterator iterator() { return Iterators.mergeSorted( - Iterables.transform(iterables, Iterables.toIterator()), comparator); + Iterables.transform(iterables, Iterable::iterator), comparator); } }; return new UnmodifiableIterable<>(iterable); } - - // TODO(user): Is this the best place for this? Move to fluent functions? - // Useful as a public method? - static - Function, Iterator> toIterator() { - return new Function, Iterator>() { - @Override - public Iterator apply(Iterable iterable) { - return iterable.iterator(); - } - }; - } } diff --git a/android/guava/src/com/google/common/collect/Iterators.java b/android/guava/src/com/google/common/collect/Iterators.java index 054857d9c139..94f84aea89c4 100644 --- a/android/guava/src/com/google/common/collect/Iterators.java +++ b/android/guava/src/com/google/common/collect/Iterators.java @@ -22,9 +22,10 @@ import static com.google.common.base.Predicates.instanceOf; import static com.google.common.collect.CollectPreconditions.checkRemove; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; @@ -34,6 +35,7 @@ import com.google.common.base.Predicate; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collection; @@ -43,12 +45,11 @@ import java.util.Enumeration; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * This class contains static utility methods that operate on or return objects of type {@link @@ -60,7 +61,7 @@ * necessary. * *

    See the Guava User Guide section on {@code + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#iterables">{@code * Iterators}. * * @author Kevin Bourrillion @@ -68,7 +69,6 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Iterators { private Iterators() {} @@ -154,6 +154,9 @@ public T next() { * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(iterator)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static UnmodifiableIterator unmodifiableIterator( UnmodifiableIterator iterator) { @@ -174,7 +177,7 @@ public static int size(Iterator iterator) { } /** Returns {@code true} if {@code iterator} contains {@code element}. */ - public static boolean contains(Iterator iterator, @CheckForNull Object element) { + public static boolean contains(Iterator iterator, @Nullable Object element) { if (element == null) { while (iterator.hasNext()) { if (iterator.next() == null) { @@ -345,10 +348,10 @@ public static String toString(Iterator iterator) { * @return a newly-allocated array into which all the elements of the iterator have been copied */ @GwtIncompatible // Array.newInstance(Class, int) - // For discussion of this signature, see the corresponding overload of *Iterables*.toArray. - public static @Nullable T[] toArray(Iterator iterator, Class type) { - List<@Nullable T> list = Lists.newArrayList(iterator); - return Iterables.toArray(list, type); + public static T[] toArray( + Iterator iterator, Class<@NonNull T> type) { + List list = Lists.newArrayList(iterator); + return Iterables.toArray(list, type); } /** @@ -375,7 +378,7 @@ public static String toString(Iterator iterator) { * * @see Collections#frequency */ - public static int frequency(Iterator iterator, @CheckForNull Object element) { + public static int frequency(Iterator iterator, @Nullable Object element) { int count = 0; while (contains(iterator, element)) { // Since it lives in the same class, we know contains gets to the element and then stops, @@ -550,6 +553,7 @@ public I next() { * * @throws NullPointerException if any of the provided iterators is null */ + @SafeVarargs public static Iterator concat(Iterator... inputs) { return concatNoDefensiveCopy(Arrays.copyOf(inputs, inputs.length)); } @@ -635,8 +639,7 @@ public boolean hasNext() { throw new NoSuchElementException(); } @SuppressWarnings("unchecked") // we only put Ts in it - @Nullable - T[] array = (@Nullable T[]) new Object[size]; + @Nullable T[] array = (@Nullable T[]) new Object[size]; int count = 0; for (; count < size && iterator.hasNext(); count++) { array[count] = iterator.next(); @@ -645,7 +648,7 @@ public boolean hasNext() { array[i] = null; // for GWT } - List<@Nullable T> list = Collections.unmodifiableList(Arrays.asList(array)); + List<@Nullable T> list = unmodifiableList(asList(array)); // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. if (pad || count == size) { return list; @@ -666,8 +669,7 @@ public boolean hasNext() { checkNotNull(retainIfTrue); return new AbstractIterator() { @Override - @CheckForNull - protected T computeNext() { + protected @Nullable T computeNext() { while (unfiltered.hasNext()) { T element = unfiltered.next(); if (retainIfTrue.apply(element)) { @@ -746,11 +748,8 @@ public static UnmodifiableIterator filter(Iterator unfiltered, Class T find( - Iterator iterator, - Predicate predicate, - @CheckForNull T defaultValue) { + public static @Nullable T find( + Iterator iterator, Predicate predicate, @Nullable T defaultValue) { checkNotNull(iterator); checkNotNull(predicate); while (iterator.hasNext()) { @@ -988,7 +987,8 @@ public void remove() { * {@code iterator} as it is returned. * *

    The provided iterator must support {@link Iterator#remove()} or else the returned iterator - * will fail on the first call to {@code next}. + * will fail on the first call to {@code next}. The returned {@link Iterator} is also not + * thread-safe. * * @param iterator the iterator to remove and return elements from * @return an iterator that removes and returns elements from the supplied iterator @@ -1021,8 +1021,7 @@ public String toString() { * Deletes and returns the next value from the iterator, or returns {@code null} if there is no * such value. */ - @CheckForNull - static T pollNext(Iterator iterator) { + static @Nullable T pollNext(Iterator iterator) { if (iterator.hasNext()) { T result = iterator.next(); iterator.remove(); @@ -1055,47 +1054,40 @@ static void clear(Iterator iterator) { */ @SafeVarargs public static UnmodifiableIterator forArray(T... array) { - return forArray(array, 0, array.length, 0); + return forArrayWithPosition(array, 0); } /** - * Returns a list iterator containing the elements in the specified range of {@code array} in - * order, starting at the specified index. + * Returns a list iterator containing the elements in the specified {@code array} in order, + * starting at the specified {@code position}. * *

    The {@code Iterable} equivalent of this method is {@code - * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + * Arrays.asList(array).listIterator(position)}. */ - static UnmodifiableListIterator forArray( - T[] array, int offset, int length, int index) { - checkArgument(length >= 0); - int end = offset + length; - - // Technically we should give a slightly more descriptive error on overflow - Preconditions.checkPositionIndexes(offset, end, array.length); - Preconditions.checkPositionIndex(index, length); - if (length == 0) { + static UnmodifiableListIterator forArrayWithPosition( + T[] array, int position) { + if (array.length == 0) { + Preconditions.checkPositionIndex(position, array.length); // otherwise checked in ArrayItr return emptyListIterator(); } - return new ArrayItr<>(array, offset, length, index); + return new ArrayItr<>(array, position); } private static final class ArrayItr extends AbstractIndexedListIterator { - static final UnmodifiableListIterator EMPTY = new ArrayItr<>(new Object[0], 0, 0, 0); + static final UnmodifiableListIterator EMPTY = new ArrayItr<>(new Object[0], 0); private final T[] array; - private final int offset; - ArrayItr(T[] array, int offset, int length, int index) { - super(length, index); + ArrayItr(T[] array, int position) { + super(array.length, position); this.array = array; - this.offset = offset; } @Override @ParametricNullness protected T get(int index) { - return array[offset + index]; + return array[index]; } } @@ -1106,24 +1098,32 @@ protected T get(int index) { */ public static UnmodifiableIterator singletonIterator( @ParametricNullness T value) { - return new UnmodifiableIterator() { - boolean done; + return new SingletonIterator<>(value); + } - @Override - public boolean hasNext() { - return !done; - } + private static final class SingletonIterator + extends UnmodifiableIterator { + private final T value; + private boolean done; - @Override - @ParametricNullness - public T next() { - if (done) { - throw new NoSuchElementException(); - } - done = true; - return value; + SingletonIterator(T value) { + this.value = value; + } + + @Override + public boolean hasNext() { + return !done; + } + + @Override + @ParametricNullness + public T next() { + if (done) { + throw new NoSuchElementException(); } - }; + done = true; + return value; + } } /** @@ -1159,6 +1159,8 @@ public T next() { *

    The {@code Iterable} equivalent of this method is either {@link Collections#enumeration} (if * you have a {@link Collection}), or {@code Iterators.asEnumeration(collection.iterator())}. */ + // This is an adapter for cases in which users do need an Enumeration for whatever reason. + @SuppressWarnings("JdkObsolete") public static Enumeration asEnumeration(Iterator iterator) { checkNotNull(iterator); return new Enumeration() { @@ -1180,7 +1182,7 @@ private static class PeekingImpl implements PeekingI private final Iterator iterator; private boolean hasPeeked; - @CheckForNull private E peekedElement; + private @Nullable E peekedElement; public PeekingImpl(Iterator iterator) { this.iterator = checkNotNull(iterator); @@ -1276,6 +1278,9 @@ public E peek() { * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(iterator)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static PeekingIterator peekingIterator( PeekingIterator iterator) { @@ -1294,7 +1299,6 @@ public E peek() { * * @since 11.0 */ - @Beta public static UnmodifiableIterator mergeSorted( Iterable> iterators, Comparator comparator) { checkNotNull(iterators, "iterators"); @@ -1351,7 +1355,7 @@ public T next() { private static class ConcatenatedIterator implements Iterator { /* The last iterator to return an element. Calls to remove() go to this iterator. */ - @CheckForNull private Iterator toRemove; + private @Nullable Iterator toRemove; /* The iterator currently returning elements. */ private Iterator iterator; @@ -1363,10 +1367,10 @@ private static class ConcatenatedIterator implements * operation O(1). */ - @CheckForNull private Iterator> topMetaIterator; + private @Nullable Iterator> topMetaIterator; // Only becomes nonnull if we encounter nested concatenations. - @CheckForNull private Deque>> metaIterators; + private @Nullable Deque>> metaIterators; ConcatenatedIterator(Iterator> metaIterator) { iterator = emptyIterator(); @@ -1374,8 +1378,7 @@ private static class ConcatenatedIterator implements } // Returns a nonempty meta-iterator or, if all meta-iterators are empty, null. - @CheckForNull - private Iterator> getTopMetaIterator() { + private @Nullable Iterator> getTopMetaIterator() { while (topMetaIterator == null || !topMetaIterator.hasNext()) { if (metaIterators != null && !metaIterators.isEmpty()) { topMetaIterator = metaIterators.removeFirst(); @@ -1444,9 +1447,4 @@ public void remove() { toRemove = null; } } - - /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ - static ListIterator cast(Iterator iterator) { - return (ListIterator) iterator; - } } diff --git a/android/guava/src/com/google/common/collect/LexicographicalOrdering.java b/android/guava/src/com/google/common/collect/LexicographicalOrdering.java index 91fc7bf7241c..c899759b15b9 100644 --- a/android/guava/src/com/google/common/collect/LexicographicalOrdering.java +++ b/android/guava/src/com/google/common/collect/LexicographicalOrdering.java @@ -17,15 +17,15 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** An ordering which sorts iterables by comparing corresponding elements pairwise. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class LexicographicalOrdering extends Ordering> implements Serializable { final Comparator elementOrder; @@ -54,7 +54,7 @@ public int compare(Iterable leftIterable, Iterable rightIterable) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -75,5 +75,5 @@ public String toString() { return elementOrder + ".lexicographical()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/LinkedHashMultimap.java b/android/guava/src/com/google/common/collect/LinkedHashMultimap.java index b0f1302f6c9b..bd539a5191ac 100644 --- a/android/guava/src/com/google/common/collect/LinkedHashMultimap.java +++ b/android/guava/src/com/google/common/collect/LinkedHashMultimap.java @@ -22,6 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -37,8 +38,7 @@ import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Multimap} that does not allow duplicate key-value entries and that @@ -72,15 +72,13 @@ * result. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @author Louis Wasserman * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public final class LinkedHashMultimap extends LinkedHashMultimapGwtSerializationDependencies { @@ -163,14 +161,14 @@ static final class ValueEntry implements ValueSetLink { final int smearedValueHash; - @CheckForNull ValueEntry nextInValueBucket; + @Nullable ValueEntry nextInValueBucket; /* * The *InValueSet and *InMultimap fields below are null after construction, but we almost * always call succeedsIn*() to initialize them immediately thereafter. * * The exception is the *InValueSet fields of multimapHeaderEntry, which are never set. (That - * works out fine as long as we continue to be careful not to try delete them or iterate past - * them.) + * works out fine as long as we continue to be careful not to try to delete them or iterate + * past them.) * * We could consider "lying" and omitting @CheckNotNull from all these fields. Normally, I'm not * a fan of that: What if we someday implement (presumably to be enabled during tests only) @@ -184,22 +182,22 @@ static final class ValueEntry predecessorInValueSet; - @CheckForNull ValueSetLink successorInValueSet; + private @Nullable ValueSetLink predecessorInValueSet; + private @Nullable ValueSetLink successorInValueSet; - @CheckForNull ValueEntry predecessorInMultimap; - @CheckForNull ValueEntry successorInMultimap; + private @Nullable ValueEntry predecessorInMultimap; + private @Nullable ValueEntry successorInMultimap; ValueEntry( @ParametricNullness K key, @ParametricNullness V value, int smearedValueHash, - @CheckForNull ValueEntry nextInValueBucket) { + @Nullable ValueEntry nextInValueBucket) { super(key, value); this.smearedValueHash = smearedValueHash; this.nextInValueBucket = nextInValueBucket; @@ -210,7 +208,7 @@ static final class ValueEntry(null, null, 0, null); } - boolean matchesValue(@CheckForNull Object v, int smearedVHash) { + boolean matchesValue(@Nullable Object v, int smearedVHash) { return smearedValueHash == smearedVHash && Objects.equal(getValue(), v); } @@ -375,8 +373,7 @@ final class ValueSet extends Sets.ImprovedAbstractSet implements ValueSetLink int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR); @SuppressWarnings({"rawtypes", "unchecked"}) - @Nullable - ValueEntry[] hashTable = new @Nullable ValueEntry[tableSize]; + @Nullable ValueEntry[] hashTable = new @Nullable ValueEntry[tableSize]; this.hashTable = hashTable; } @@ -408,7 +405,7 @@ public void setSuccessorInValueSet(ValueSetLink entry) { public Iterator iterator() { return new Iterator() { ValueSetLink nextEntry = firstEntry; - @CheckForNull ValueEntry toRemove; + @Nullable ValueEntry toRemove; int expectedModCount = modCount; private void checkForComodification() { @@ -453,7 +450,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { int smearedHash = Hashing.smearedHash(o); for (ValueEntry entry = hashTable[smearedHash & mask()]; entry != null; @@ -491,7 +488,8 @@ public boolean add(@ParametricNullness V value) { private void rehashIfNecessary() { if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { @SuppressWarnings("unchecked") - ValueEntry[] hashTable = new ValueEntry[this.hashTable.length * 2]; + ValueEntry[] hashTable = + (ValueEntry[]) new ValueEntry[this.hashTable.length * 2]; this.hashTable = hashTable; int mask = hashTable.length - 1; for (ValueSetLink entry = firstEntry; @@ -507,7 +505,7 @@ private void rehashIfNecessary() { @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { int smearedHash = Hashing.smearedHash(o); int bucket = smearedHash & mask(); ValueEntry prev = null; @@ -550,7 +548,7 @@ public void clear() { Iterator> entryIterator() { return new Iterator>() { ValueEntry nextEntry = multimapHeaderEntry.getSuccessorInMultimap(); - @CheckForNull ValueEntry toRemove; + @Nullable ValueEntry toRemove; @Override public boolean hasNext() { @@ -593,6 +591,7 @@ public void clear() { * and the entries in order */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(keySet().size()); @@ -607,6 +606,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); multimapHeaderEntry = ValueEntry.newHeader(); @@ -634,6 +634,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo setMap(map); } - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } diff --git a/android/guava/src/com/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java b/android/guava/src/com/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java index bb4a2e490e45..8c91683d19c0 100644 --- a/android/guava/src/com/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java +++ b/android/guava/src/com/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java @@ -19,6 +19,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Map; +import org.jspecify.annotations.Nullable; /** * A dummy superclass to support GWT serialization of the element types of a {@link @@ -30,7 +31,8 @@ *

    TODO(cpovirk): Consider applying this subclass approach to our other types. */ @GwtCompatible(emulated = true) -abstract class LinkedHashMultimapGwtSerializationDependencies +abstract class LinkedHashMultimapGwtSerializationDependencies< + K extends @Nullable Object, V extends @Nullable Object> extends AbstractSetMultimap { LinkedHashMultimapGwtSerializationDependencies(Map> map) { super(map); diff --git a/android/guava/src/com/google/common/collect/LinkedHashMultiset.java b/android/guava/src/com/google/common/collect/LinkedHashMultiset.java index 9630786800c1..78043e3b0661 100644 --- a/android/guava/src/com/google/common/collect/LinkedHashMultiset.java +++ b/android/guava/src/com/google/common/collect/LinkedHashMultiset.java @@ -17,7 +17,7 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@code Multiset} implementation with predictable iteration order. Its iterator orders elements @@ -27,15 +27,13 @@ * element will appear at the end of the iteration. * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Kevin Bourrillion * @author Jared Levy * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public final class LinkedHashMultiset extends AbstractMapBasedMultiset { @@ -52,7 +50,7 @@ public final class LinkedHashMultiset * @throws IllegalArgumentException if {@code distinctElements} is negative */ public static LinkedHashMultiset create(int distinctElements) { - return new LinkedHashMultiset(distinctElements); + return new LinkedHashMultiset<>(distinctElements); } /** @@ -77,4 +75,6 @@ public final class LinkedHashMultiset ObjectCountHashMap newBackingMap(int distinctElements) { return new ObjectCountLinkedHashMap<>(distinctElements); } + + // TODO(cpovirk): Should we have a serialVersionUID here? } diff --git a/android/guava/src/com/google/common/collect/LinkedListMultimap.java b/android/guava/src/com/google/common/collect/LinkedListMultimap.java index 31874b2d3891..5e16af2e18d0 100644 --- a/android/guava/src/com/google/common/collect/LinkedListMultimap.java +++ b/android/guava/src/com/google/common/collect/LinkedListMultimap.java @@ -23,6 +23,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.j2objc.annotations.WeakOuter; import java.io.IOException; @@ -39,8 +40,7 @@ import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An implementation of {@code ListMultimap} that supports deterministic iteration order for both @@ -88,14 +88,13 @@ * with a call to {@link Multimaps#synchronizedListMultimap}. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Mike Bostock * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault +@SuppressWarnings("WrongCommentType") // false positive public class LinkedListMultimap extends AbstractMultimap implements ListMultimap, Serializable { /* @@ -105,14 +104,14 @@ public class LinkedListMultimap + static final class Node extends AbstractMapEntry { @ParametricNullness final K key; @ParametricNullness V value; - @CheckForNull Node next; // the next node (with any key) - @CheckForNull Node previous; // the previous node (with any key) - @CheckForNull Node nextSibling; // the next node with the same key - @CheckForNull Node previousSibling; // the previous node with the same key + @Nullable Node next; // the next node (with any key) + @Nullable Node previous; // the previous node (with any key) + @Nullable Node nextSibling; // the next node with the same key + @Nullable Node previousSibling; // the previous node with the same key Node(@ParametricNullness K key, @ParametricNullness V value) { this.key = key; @@ -154,8 +153,8 @@ private static class KeyList head; // the head for all keys - @CheckForNull private transient Node tail; // the tail for all keys + private transient @Nullable Node head; // the head for all keys + private transient @Nullable Node tail; // the tail for all keys private transient Map> keyToKeyList; private transient int size; @@ -212,13 +211,11 @@ private LinkedListMultimap(Multimap multimap) { /** * Adds a new node for the specified key-value pair before the specified {@code nextSibling} * element, or at the end of the list if {@code nextSibling} is null. Note: if {@code nextSibling} - * is specified, it MUST be for an node for the same {@code key}! + * is specified, it MUST be for a node for the same {@code key}! */ @CanIgnoreReturnValue private Node addNode( - @ParametricNullness K key, - @ParametricNullness V value, - @CheckForNull Node nextSibling) { + @ParametricNullness K key, @ParametricNullness V value, @Nullable Node nextSibling) { Node node = new Node<>(key, value); if (head == null) { // empty list head = tail = node; @@ -323,9 +320,9 @@ private void removeAllNodes(@ParametricNullness K key) { /** An {@code Iterator} over all nodes. */ private class NodeIterator implements ListIterator> { int nextIndex; - @CheckForNull Node next; - @CheckForNull Node current; - @CheckForNull Node previous; + @Nullable Node next; + @Nullable Node current; + @Nullable Node previous; int expectedModCount = modCount; NodeIterator(int index) { @@ -434,8 +431,8 @@ void setValue(@ParametricNullness V value) { /** An {@code Iterator} over distinct keys in key head order. */ private class DistinctKeyIterator implements Iterator { final Set seenKeys = Sets.newHashSetWithExpectedSize(keySet().size()); - @CheckForNull Node next = head; - @CheckForNull Node current; + @Nullable Node next = head; + @Nullable Node current; int expectedModCount = modCount; private void checkForConcurrentModification() { @@ -479,9 +476,9 @@ public void remove() { private class ValueForKeyIterator implements ListIterator { @ParametricNullness final K key; int nextIndex; - @CheckForNull Node next; - @CheckForNull Node current; - @CheckForNull Node previous; + @Nullable Node next; + @Nullable Node current; + @Nullable Node previous; /** Constructs a new iterator over all values for the specified key. */ ValueForKeyIterator(@ParametricNullness K key) { @@ -604,12 +601,12 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return keyToKeyList.containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return values().contains(value); } @@ -711,7 +708,7 @@ public void clear() { *

    The returned list is not serializable and does not have random access. */ @Override - public List get(@ParametricNullness final K key) { + public List get(@ParametricNullness K key) { return new AbstractSequentialList() { @Override public int size() { @@ -741,12 +738,12 @@ public Iterator iterator() { } @Override - public boolean contains(@CheckForNull Object key) { // for performance + public boolean contains(@Nullable Object key) { // for performance return containsKey(key); } @Override - public boolean remove(@CheckForNull Object o) { // for performance + public boolean remove(@Nullable Object o) { // for performance return !LinkedListMultimap.this.removeAll(o).isEmpty(); } } @@ -782,7 +779,7 @@ public int size() { @Override public ListIterator listIterator(int index) { - final NodeIterator nodeItr = new NodeIterator(index); + NodeIterator nodeItr = new NodeIterator(index); return new TransformedListIterator, V>(nodeItr) { @Override @ParametricNullness @@ -854,6 +851,7 @@ Map> createAsMap() { * from the entries() ordering */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(size()); @@ -864,6 +862,7 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); keyToKeyList = CompactLinkedHashMap.create(); @@ -877,6 +876,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo } } - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ListMultimap.java b/android/guava/src/com/google/common/collect/ListMultimap.java index 16860684099f..a530833267f4 100644 --- a/android/guava/src/com/google/common/collect/ListMultimap.java +++ b/android/guava/src/com/google/common/collect/ListMultimap.java @@ -21,8 +21,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@code Multimap} that can hold duplicate key-value pairs and that maintains the insertion @@ -34,14 +33,12 @@ * {@link #asMap} has {@code List} values. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface ListMultimap extends Multimap { /** @@ -63,7 +60,7 @@ public interface ListMultimap removeAll(@CheckForNull Object key); + List removeAll(@Nullable Object key); /** * {@inheritDoc} @@ -96,5 +93,5 @@ public interface ListMultimapSee the Guava User Guide article on {@code Lists}. + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#lists">{@code Lists}. * * @author Kevin Bourrillion * @author Mike Bostock @@ -64,7 +65,6 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Lists { private Lists() {} @@ -75,11 +75,14 @@ private Lists() {} * *

    Note: if mutability is not required, use {@link ImmutableList#of()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code ArrayList} {@linkplain ArrayList#ArrayList() constructor} - * directly, taking advantage of the new "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code ArrayList} {@linkplain ArrayList#ArrayList() constructor} directly, taking + * advantage of "diamond" + * syntax. */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayList() { return new ArrayList<>(); } @@ -100,6 +103,7 @@ private Lists() {} */ @SafeVarargs @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayList(E... elements) { checkNotNull(elements); // for GWT // Avoid integer overflow when a large array is passed in @@ -117,12 +121,14 @@ private Lists() {} * ImmutableList#copyOf(Iterable)} instead. (Or, change {@code elements} to be a {@link * FluentIterable} and call {@code elements.toList()}.) * - *

    Note for Java 7 and later: if {@code elements} is a {@link Collection}, you don't - * need this method. Use the {@code ArrayList} {@linkplain ArrayList#ArrayList(Collection) - * constructor} directly, taking advantage of the new "diamond" + *

    Note: if {@code elements} is a {@link Collection}, you don't need this method. Use + * the {@code ArrayList} {@linkplain ArrayList#ArrayList(Collection) constructor} directly, taking + * advantage of "diamond" * syntax. */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayList( Iterable elements) { checkNotNull(elements); // for GWT @@ -140,6 +146,7 @@ private Lists() {} * ImmutableList#copyOf(Iterator)} instead. */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayList( Iterator elements) { ArrayList list = newArrayList(); @@ -159,11 +166,12 @@ static int computeArrayListCapacity(int arraySize) { * Creates an {@code ArrayList} instance backed by an array with the specified initial size; * simply delegates to {@link ArrayList#ArrayList(int)}. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use {@code new }{@link ArrayList#ArrayList(int) ArrayList}{@code <>(int)} - * directly, taking advantage of the new "diamond" syntax. - * (Unlike here, there is no risk of overload ambiguity, since the {@code ArrayList} constructors - * very wisely did not accept varargs.) + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use {@code new }{@link ArrayList#ArrayList(int) ArrayList}{@code <>(int)} directly, taking + * advantage of "diamond" + * syntax. (Unlike here, there is no risk of overload ambiguity, since the {@code ArrayList} + * constructors very wisely did not accept varargs.) * * @param initialArraySize the exact size of the initial backing array for the returned array list * ({@code ArrayList} documentation calls this value the "capacity") @@ -172,6 +180,7 @@ static int computeArrayListCapacity(int arraySize) { * @throws IllegalArgumentException if {@code initialArraySize} is negative */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayListWithCapacity( int initialArraySize) { checkNonnegative(initialArraySize, "initialArraySize"); // for GWT. @@ -192,6 +201,7 @@ static int computeArrayListCapacity(int arraySize) { * @throws IllegalArgumentException if {@code estimatedSize} is negative */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static ArrayList newArrayListWithExpectedSize( int estimatedSize) { return new ArrayList<>(computeArrayListCapacity(estimatedSize)); @@ -209,12 +219,17 @@ static int computeArrayListCapacity(int arraySize) { * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have * spent a lot of time benchmarking your specific needs, use one of those instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code LinkedList} {@linkplain LinkedList#LinkedList() - * constructor} directly, taking advantage of the new "diamond" + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedList} {@linkplain LinkedList#LinkedList() constructor} directly, taking + * advantage of "diamond" * syntax. */ @GwtCompatible(serializable = true) + @SuppressWarnings({ + "NonApiType", // acts as a direct substitute for a constructor call + "JdkObsolete", // We recommend against this method but need to keep it for compatibility. + }) public static LinkedList newLinkedList() { return new LinkedList<>(); } @@ -231,12 +246,14 @@ static int computeArrayListCapacity(int arraySize) { * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have * spent a lot of time benchmarking your specific needs, use one of those instead. * - *

    Note for Java 7 and later: if {@code elements} is a {@link Collection}, you don't - * need this method. Use the {@code LinkedList} {@linkplain LinkedList#LinkedList(Collection) - * constructor} directly, taking advantage of the new "diamond" + *

    Note: if {@code elements} is a {@link Collection}, you don't need this method. Use + * the {@code LinkedList} {@linkplain LinkedList#LinkedList(Collection) constructor} directly, + * taking advantage of "diamond" * syntax. */ @GwtCompatible(serializable = true) + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedList newLinkedList( Iterable elements) { LinkedList list = newLinkedList(); @@ -253,6 +270,7 @@ static int computeArrayListCapacity(int arraySize) { * @return a new, empty {@code CopyOnWriteArrayList} * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArrayList public static CopyOnWriteArrayList newCopyOnWriteArrayList() { return new CopyOnWriteArrayList<>(); @@ -265,6 +283,7 @@ static int computeArrayListCapacity(int arraySize) { * @return a new {@code CopyOnWriteArrayList} containing those elements * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArrayList public static CopyOnWriteArrayList newCopyOnWriteArrayList( Iterable elements) { @@ -316,7 +335,9 @@ static int computeArrayListCapacity(int arraySize) { return new TwoPlusArrayList<>(first, second, rest); } - /** @see Lists#asList(Object, Object[]) */ + /** + * @see Lists#asList(Object, Object[]) + */ private static class OnePlusArrayList extends AbstractList implements Serializable, RandomAccess { @ParametricNullness final E first; @@ -340,10 +361,12 @@ public E get(int index) { return (index == 0) ? first : rest[index - 1]; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - /** @see Lists#asList(Object, Object, Object[]) */ + /** + * @see Lists#asList(Object, Object, Object[]) + */ private static class TwoPlusArrayList extends AbstractList implements Serializable, RandomAccess { @ParametricNullness final E first; @@ -376,7 +399,7 @@ public E get(int index) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -524,7 +547,7 @@ public static List> cartesianProduct(List... lists) { * serialize the copy. Other methods similar to this do not implement serialization at all for * this reason. * - *

    Java 8 users: many use cases for this method are better addressed by {@link + *

    Java 8+ users: many use cases for this method are better addressed by {@link * java.util.stream.Stream#map}. This method is not being deprecated, but we gently encourage you * to migrate to streams. */ @@ -556,8 +579,8 @@ private static class TransformingSequentialList< * can be overkill. That's why we forward this call directly to the backing list. */ @Override - public void clear() { - fromList.clear(); + protected void removeRange(int fromIndex, int toIndex) { + fromList.subList(fromIndex, toIndex).clear(); } @Override @@ -566,7 +589,12 @@ public int size() { } @Override - public ListIterator listIterator(final int index) { + public boolean isEmpty() { + return fromList.isEmpty(); + } + + @Override + public ListIterator listIterator(int index) { return new TransformedListIterator(fromList.listIterator(index)) { @Override @ParametricNullness @@ -576,7 +604,7 @@ T transform(@ParametricNullness F from) { }; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -597,9 +625,13 @@ private static class TransformingRandomAccessList< this.function = checkNotNull(function); } + /** + * The default implementation inherited is based on iteration and removal of each element which + * can be overkill. That's why we forward this call directly to the backing list. + */ @Override - public void clear() { - fromList.clear(); + protected void removeRange(int fromIndex, int toIndex) { + fromList.subList(fromIndex, toIndex).clear(); } @Override @@ -638,7 +670,7 @@ public int size() { return fromList.size(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -677,7 +709,7 @@ private static class Partition extends AbstractList< public List get(int index) { checkElementIndex(index, size()); int start = index * size; - int end = Math.min(start + size, list.size()); + int end = min(start + size, list.size()); return list.subList(start, end); } @@ -717,7 +749,6 @@ public static ImmutableList charactersOf(String string) { * @return an {@code List} view of the character sequence * @since 7.0 */ - @Beta public static List charactersOf(CharSequence sequence) { return new CharSequenceAsList(checkNotNull(sequence)); } @@ -732,12 +763,12 @@ private static final class StringAsImmutableList extends ImmutableList { @@ -885,7 +925,7 @@ public Iterator iterator() { @Override public ListIterator listIterator(int index) { int start = reversePosition(index); - final ListIterator forwardIterator = forwardList.listIterator(start); + ListIterator forwardIterator = forwardList.listIterator(start); return new ListIterator() { boolean canRemoveOrSet; @@ -974,7 +1014,7 @@ static int hashCodeImpl(List list) { } /** An implementation of {@link List#equals(Object)}. */ - static boolean equalsImpl(List thisList, @CheckForNull Object other) { + static boolean equalsImpl(List thisList, @Nullable Object other) { if (other == checkNotNull(thisList)) { return true; } @@ -995,7 +1035,7 @@ static boolean equalsImpl(List thisList, @CheckForNull Object other) { } return true; } else { - return Iterators.elementsEqual(thisList.iterator(), otherList.iterator()); + return elementsEqual(thisList.iterator(), otherList.iterator()); } } @@ -1012,7 +1052,7 @@ static boolean equalsImpl(List thisList, @CheckForNull Object other) { } /** An implementation of {@link List#indexOf(Object)}. */ - static int indexOfImpl(List list, @CheckForNull Object element) { + static int indexOfImpl(List list, @Nullable Object element) { if (list instanceof RandomAccess) { return indexOfRandomAccess(list, element); } else { @@ -1026,7 +1066,7 @@ static int indexOfImpl(List list, @CheckForNull Object element) { } } - private static int indexOfRandomAccess(List list, @CheckForNull Object element) { + private static int indexOfRandomAccess(List list, @Nullable Object element) { int size = list.size(); if (element == null) { for (int i = 0; i < size; i++) { @@ -1045,7 +1085,7 @@ private static int indexOfRandomAccess(List list, @CheckForNull Object elemen } /** An implementation of {@link List#lastIndexOf(Object)}. */ - static int lastIndexOfImpl(List list, @CheckForNull Object element) { + static int lastIndexOfImpl(List list, @Nullable Object element) { if (list instanceof RandomAccess) { return lastIndexOfRandomAccess(list, element); } else { @@ -1059,7 +1099,7 @@ static int lastIndexOfImpl(List list, @CheckForNull Object element) { } } - private static int lastIndexOfRandomAccess(List list, @CheckForNull Object element) { + private static int lastIndexOfRandomAccess(List list, @Nullable Object element) { if (element == null) { for (int i = list.size() - 1; i >= 0; i--) { if (list.get(i) == null) { @@ -1083,7 +1123,7 @@ private static int lastIndexOfRandomAccess(List list, @CheckForNull Object el /** An implementation of {@link List#subList(int, int)}. */ static List subListImpl( - final List list, int fromIndex, int toIndex) { + List list, int fromIndex, int toIndex) { List wrapper; if (list instanceof RandomAccess) { wrapper = @@ -1093,7 +1133,7 @@ public ListIterator listIterator(int index) { return backingList.listIterator(index); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; }; } else { wrapper = @@ -1103,7 +1143,7 @@ public ListIterator listIterator(int index) { return backingList.listIterator(index); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; }; } return wrapper.subList(fromIndex, toIndex); @@ -1145,7 +1185,7 @@ public E set(int index, @ParametricNullness E element) { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return backingList.contains(o); } @@ -1161,9 +1201,4 @@ private static class RandomAccessListWrapper super(backingList); } } - - /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ - static List cast(Iterable iterable) { - return (List) iterable; - } } diff --git a/android/guava/src/com/google/common/collect/MapDifference.java b/android/guava/src/com/google/common/collect/MapDifference.java index 5000e4b527c1..b8044d070683 100644 --- a/android/guava/src/com/google/common/collect/MapDifference.java +++ b/android/guava/src/com/google/common/collect/MapDifference.java @@ -19,8 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.DoNotMock; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An object representing the differences between two maps. @@ -30,7 +29,6 @@ */ @DoNotMock("Use Maps.difference") @GwtCompatible -@ElementTypesAreNonnullByDefault public interface MapDifference { /** * Returns {@code true} if there are no differences between the two maps; that is, if the maps are @@ -69,7 +67,7 @@ public interface MapDifference { * {@link #rightValue()} values are also equal. */ @Override - boolean equals(@CheckForNull Object other); + boolean equals(@Nullable Object other); /** * The hash code equals the value {@code Arrays.asList(leftValue(), rightValue()).hashCode()}. diff --git a/android/guava/src/com/google/common/collect/MapMaker.java b/android/guava/src/com/google/common/collect/MapMaker.java index a2612c1bd286..ee0410bb7a12 100644 --- a/android/guava/src/com/google/common/collect/MapMaker.java +++ b/android/guava/src/com/google/common/collect/MapMaker.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Equivalence; import com.google.common.base.MoreObjects; @@ -30,7 +31,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A builder of {@link ConcurrentMap} instances that can have keys or values automatically wrapped @@ -85,8 +86,8 @@ * @author Kevin Bourrillion * @since 2.0 */ +@J2ktIncompatible @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class MapMaker { private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final int DEFAULT_CONCURRENCY_LEVEL = 4; @@ -99,10 +100,10 @@ public final class MapMaker { int initialCapacity = UNSET_INT; int concurrencyLevel = UNSET_INT; - @CheckForNull Strength keyStrength; - @CheckForNull Strength valueStrength; + @Nullable Strength keyStrength; + @Nullable Strength valueStrength; - @CheckForNull Equivalence keyEquivalence; + @Nullable Equivalence keyEquivalence; /** * Constructs a new {@code MapMaker} instance with default settings, including strong keys, strong @@ -206,6 +207,7 @@ public MapMaker weakKeys() { return setKeyStrength(Strength.WEAK); } + @CanIgnoreReturnValue MapMaker setKeyStrength(Strength strength) { checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); keyStrength = checkNotNull(strength); @@ -252,6 +254,7 @@ enum Dummy { VALUE } + @CanIgnoreReturnValue MapMaker setValueStrength(Strength strength) { checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); valueStrength = checkNotNull(strength); diff --git a/android/guava/src/com/google/common/collect/MapMakerInternalMap.java b/android/guava/src/com/google/common/collect/MapMakerInternalMap.java index bdef10ebc9e4..b2d14725794c 100644 --- a/android/guava/src/com/google/common/collect/MapMakerInternalMap.java +++ b/android/guava/src/com/google/common/collect/MapMakerInternalMap.java @@ -16,17 +16,21 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkRemove; +import static java.lang.Math.min; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; import com.google.common.collect.MapMaker.Dummy; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.GuardedBy; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.Weak; import com.google.j2objc.annotations.WeakOuter; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -36,7 +40,6 @@ import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; -import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -47,7 +50,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.locks.ReentrantLock; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; /** * The concurrent hash map implementation built by {@link MapMaker}. @@ -64,12 +68,13 @@ * @author Doug Lea ({@code ConcurrentHashMap}) */ // TODO(kak): Consider removing @CanIgnoreReturnValue from this class. +@J2ktIncompatible @GwtIncompatible @SuppressWarnings({ "GuardedBy", // TODO(b/35466881): Fix or suppress. "nullness", // too much trouble for the payoff }) -// TODO(cpovirk): Annotate for nullness. +@NullUnmarked // TODO(cpovirk): Annotate for nullness. class MapMakerInternalMap< K, V, @@ -130,8 +135,6 @@ class MapMakerInternalMap< // TODO(fry): empirically optimize this static final int DRAIN_MAX = 16; - static final long CLEANUP_EXECUTOR_DELAY_SECS = 60; - // Fields /** @@ -162,12 +165,12 @@ class MapMakerInternalMap< * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. */ private MapMakerInternalMap(MapMaker builder, InternalEntryHelper entryHelper) { - concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + concurrencyLevel = min(builder.getConcurrencyLevel(), MAX_SEGMENTS); keyEquivalence = builder.getKeyEquivalence(); this.entryHelper = entryHelper; - int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); + int initialCapacity = min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); // Find power-of-two sizes best matching arguments. Constraints: // (segmentCount > concurrencyLevel) @@ -193,7 +196,7 @@ private MapMakerInternalMap(MapMaker builder, InternalEntryHelper en } for (int i = 0; i < this.segments.length; ++i) { - this.segments[i] = createSegment(segmentSize, MapMaker.UNSET_INT); + this.segments[i] = createSegment(segmentSize); } } @@ -290,18 +293,18 @@ interface InternalEntryHelper< Strength valueStrength(); /** Returns a freshly created segment, typed at the {@code S} type. */ - S newSegment(MapMakerInternalMap map, int initialCapacity, int maxSegmentSize); + S newSegment(MapMakerInternalMap map, int initialCapacity); /** * Returns a freshly created entry, typed at the {@code E} type, for the given {@code segment}. */ - E newEntry(S segment, K key, int hash, @CheckForNull E next); + E newEntry(S segment, K key, int hash, @Nullable E next); /** * Returns a freshly created entry, typed at the {@code E} type, for the given {@code segment}, * that is a copy of the given {@code entry}. */ - E copy(S segment, E entry, @CheckForNull E newNext); + E copy(S segment, E entry, @Nullable E newNext); /** * Sets the value of the given {@code entry} in the given {@code segment} to be the given {@code @@ -343,27 +346,25 @@ abstract static class AbstractStrongKeyEntry { final K key; final int hash; - @CheckForNull final E next; - AbstractStrongKeyEntry(K key, int hash, @CheckForNull E next) { + AbstractStrongKeyEntry(K key, int hash) { this.key = key; this.hash = hash; - this.next = next; } @Override - public K getKey() { - return this.key; + public final K getKey() { + return key; } @Override - public int getHash() { + public final int getHash() { return hash; } @Override - public E getNext() { - return next; + public @Nullable E getNext() { + return null; } } @@ -375,12 +376,6 @@ interface StrongValueEntry> interface WeakValueEntry> extends InternalEntry { /** Gets the weak value reference held by entry. */ WeakValueReference getValueReference(); - - /** - * Clears the weak value reference held by the entry. Should be used when the entry's value is - * overwritten. - */ - void clearValue(); } @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value @@ -390,30 +385,33 @@ WeakValueReference unsetWeakValueReference() { } /** Concrete implementation of {@link InternalEntry} for strong keys and strong values. */ - static final class StrongKeyStrongValueEntry + static class StrongKeyStrongValueEntry extends AbstractStrongKeyEntry> implements StrongValueEntry> { - @CheckForNull private volatile V value = null; + private volatile @Nullable V value = null; - StrongKeyStrongValueEntry(K key, int hash, @CheckForNull StrongKeyStrongValueEntry next) { - super(key, hash, next); + private StrongKeyStrongValueEntry(K key, int hash) { + super(key, hash); } @Override - @CheckForNull - public V getValue() { + public final @Nullable V getValue() { return value; } - void setValue(V value) { - this.value = value; - } + private static final class LinkedStrongKeyStrongValueEntry + extends StrongKeyStrongValueEntry { + private final StrongKeyStrongValueEntry next; - StrongKeyStrongValueEntry copy(StrongKeyStrongValueEntry newNext) { - StrongKeyStrongValueEntry newEntry = - new StrongKeyStrongValueEntry<>(this.key, this.hash, newNext); - newEntry.value = this.value; - return newEntry; + LinkedStrongKeyStrongValueEntry(K key, int hash, StrongKeyStrongValueEntry next) { + super(key, hash); + this.next = next; + } + + @Override + public StrongKeyStrongValueEntry getNext() { + return next; + } } /** Concrete implementation of {@link InternalEntryHelper} for strong keys and strong values. */ @@ -442,17 +440,19 @@ public StrongKeyStrongValueSegment newSegment( MapMakerInternalMap< K, V, StrongKeyStrongValueEntry, StrongKeyStrongValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new StrongKeyStrongValueSegment<>(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new StrongKeyStrongValueSegment<>(map, initialCapacity); } @Override public StrongKeyStrongValueEntry copy( StrongKeyStrongValueSegment segment, StrongKeyStrongValueEntry entry, - @CheckForNull StrongKeyStrongValueEntry newNext) { - return entry.copy(newNext); + @Nullable StrongKeyStrongValueEntry newNext) { + StrongKeyStrongValueEntry newEntry = + newEntry(segment, entry.key, entry.hash, newNext); + newEntry.value = entry.value; + return newEntry; } @Override @@ -460,7 +460,7 @@ public void setValue( StrongKeyStrongValueSegment segment, StrongKeyStrongValueEntry entry, V value) { - entry.setValue(value); + entry.value = value; } @Override @@ -468,49 +468,48 @@ public StrongKeyStrongValueEntry newEntry( StrongKeyStrongValueSegment segment, K key, int hash, - @CheckForNull StrongKeyStrongValueEntry next) { - return new StrongKeyStrongValueEntry<>(key, hash, next); + @Nullable StrongKeyStrongValueEntry next) { + return next == null + ? new StrongKeyStrongValueEntry<>(key, hash) + : new LinkedStrongKeyStrongValueEntry<>(key, hash, next); } } } /** Concrete implementation of {@link InternalEntry} for strong keys and weak values. */ - static final class StrongKeyWeakValueEntry + static class StrongKeyWeakValueEntry extends AbstractStrongKeyEntry> implements WeakValueEntry> { private volatile WeakValueReference> valueReference = unsetWeakValueReference(); - StrongKeyWeakValueEntry(K key, int hash, @CheckForNull StrongKeyWeakValueEntry next) { - super(key, hash, next); + private StrongKeyWeakValueEntry(K key, int hash) { + super(key, hash); } @Override - public V getValue() { + public final @Nullable V getValue() { return valueReference.get(); } @Override - public void clearValue() { - valueReference.clear(); + public final WeakValueReference> getValueReference() { + return valueReference; } - void setValue(V value, ReferenceQueue queueForValues) { - WeakValueReference> previous = this.valueReference; - this.valueReference = new WeakValueReferenceImpl<>(queueForValues, value, this); - previous.clear(); - } + private static final class LinkedStrongKeyWeakValueEntry + extends StrongKeyWeakValueEntry { + private final StrongKeyWeakValueEntry next; - StrongKeyWeakValueEntry copy( - ReferenceQueue queueForValues, StrongKeyWeakValueEntry newNext) { - StrongKeyWeakValueEntry newEntry = new StrongKeyWeakValueEntry<>(key, hash, newNext); - newEntry.valueReference = valueReference.copyFor(queueForValues, newEntry); - return newEntry; - } + LinkedStrongKeyWeakValueEntry(K key, int hash, StrongKeyWeakValueEntry next) { + super(key, hash); + this.next = next; + } - @Override - public WeakValueReference> getValueReference() { - return valueReference; + @Override + public StrongKeyWeakValueEntry getNext() { + return next; + } } /** Concrete implementation of {@link InternalEntryHelper} for strong keys and weak values. */ @@ -538,26 +537,29 @@ public Strength valueStrength() { public StrongKeyWeakValueSegment newSegment( MapMakerInternalMap, StrongKeyWeakValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new StrongKeyWeakValueSegment<>(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new StrongKeyWeakValueSegment<>(map, initialCapacity); } @Override - public StrongKeyWeakValueEntry copy( + public @Nullable StrongKeyWeakValueEntry copy( StrongKeyWeakValueSegment segment, StrongKeyWeakValueEntry entry, - @CheckForNull StrongKeyWeakValueEntry newNext) { + @Nullable StrongKeyWeakValueEntry newNext) { if (Segment.isCollected(entry)) { return null; } - return entry.copy(segment.queueForValues, newNext); + StrongKeyWeakValueEntry newEntry = newEntry(segment, entry.key, entry.hash, newNext); + newEntry.valueReference = entry.valueReference.copyFor(segment.queueForValues, newEntry); + return newEntry; } @Override public void setValue( StrongKeyWeakValueSegment segment, StrongKeyWeakValueEntry entry, V value) { - entry.setValue(value, segment.queueForValues); + WeakValueReference> previous = entry.valueReference; + entry.valueReference = new WeakValueReferenceImpl<>(segment.queueForValues, value, entry); + previous.clear(); } @Override @@ -565,29 +567,41 @@ public StrongKeyWeakValueEntry newEntry( StrongKeyWeakValueSegment segment, K key, int hash, - @CheckForNull StrongKeyWeakValueEntry next) { - return new StrongKeyWeakValueEntry<>(key, hash, next); + @Nullable StrongKeyWeakValueEntry next) { + return next == null + ? new StrongKeyWeakValueEntry<>(key, hash) + : new LinkedStrongKeyWeakValueEntry<>(key, hash, next); } } } /** Concrete implementation of {@link InternalEntry} for strong keys and {@link Dummy} values. */ - static final class StrongKeyDummyValueEntry + static class StrongKeyDummyValueEntry extends AbstractStrongKeyEntry> implements StrongValueEntry> { - StrongKeyDummyValueEntry(K key, int hash, @CheckForNull StrongKeyDummyValueEntry next) { - super(key, hash, next); + + private StrongKeyDummyValueEntry(K key, int hash) { + super(key, hash); } @Override - public Dummy getValue() { + public final Dummy getValue() { return Dummy.VALUE; } - void setValue(Dummy value) {} + private static final class LinkedStrongKeyDummyValueEntry + extends StrongKeyDummyValueEntry { + private final StrongKeyDummyValueEntry next; + + LinkedStrongKeyDummyValueEntry(K key, int hash, StrongKeyDummyValueEntry next) { + super(key, hash); + this.next = next; + } - StrongKeyDummyValueEntry copy(StrongKeyDummyValueEntry newNext) { - return new StrongKeyDummyValueEntry(this.key, this.hash, newNext); + @Override + public StrongKeyDummyValueEntry getNext() { + return next; + } } /** @@ -618,17 +632,16 @@ public Strength valueStrength() { public StrongKeyDummyValueSegment newSegment( MapMakerInternalMap, StrongKeyDummyValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new StrongKeyDummyValueSegment(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new StrongKeyDummyValueSegment<>(map, initialCapacity); } @Override public StrongKeyDummyValueEntry copy( StrongKeyDummyValueSegment segment, StrongKeyDummyValueEntry entry, - @CheckForNull StrongKeyDummyValueEntry newNext) { - return entry.copy(newNext); + @Nullable StrongKeyDummyValueEntry newNext) { + return newEntry(segment, entry.key, entry.hash, newNext); } @Override @@ -640,8 +653,10 @@ public StrongKeyDummyValueEntry newEntry( StrongKeyDummyValueSegment segment, K key, int hash, - @CheckForNull StrongKeyDummyValueEntry next) { - return new StrongKeyDummyValueEntry(key, hash, next); + @Nullable StrongKeyDummyValueEntry next) { + return next == null + ? new StrongKeyDummyValueEntry(key, hash) + : new LinkedStrongKeyDummyValueEntry<>(key, hash, next); } } } @@ -650,49 +665,55 @@ public StrongKeyDummyValueEntry newEntry( abstract static class AbstractWeakKeyEntry> extends WeakReference implements InternalEntry { final int hash; - @CheckForNull final E next; - AbstractWeakKeyEntry(ReferenceQueue queue, K key, int hash, @CheckForNull E next) { + AbstractWeakKeyEntry(ReferenceQueue queue, K key, int hash) { super(key, queue); this.hash = hash; - this.next = next; } @Override - public K getKey() { + public final K getKey() { return get(); } @Override - public int getHash() { + public final int getHash() { return hash; } @Override - public E getNext() { - return next; + public @Nullable E getNext() { + return null; } } /** Concrete implementation of {@link InternalEntry} for weak keys and {@link Dummy} values. */ - static final class WeakKeyDummyValueEntry + static class WeakKeyDummyValueEntry extends AbstractWeakKeyEntry> implements StrongValueEntry> { - WeakKeyDummyValueEntry( - ReferenceQueue queue, K key, int hash, @CheckForNull WeakKeyDummyValueEntry next) { - super(queue, key, hash, next); + + private WeakKeyDummyValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); } @Override - public Dummy getValue() { + public final Dummy getValue() { return Dummy.VALUE; } - void setValue(Dummy value) {} + private static final class LinkedWeakKeyDummyValueEntry extends WeakKeyDummyValueEntry { + private final WeakKeyDummyValueEntry next; + + private LinkedWeakKeyDummyValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyDummyValueEntry next) { + super(queue, key, hash); + this.next = next; + } - WeakKeyDummyValueEntry copy( - ReferenceQueue queueForKeys, WeakKeyDummyValueEntry newNext) { - return new WeakKeyDummyValueEntry(queueForKeys, getKey(), this.hash, newNext); + @Override + public WeakKeyDummyValueEntry getNext() { + return next; + } } /** @@ -722,21 +743,21 @@ public Strength valueStrength() { @Override public WeakKeyDummyValueSegment newSegment( MapMakerInternalMap, WeakKeyDummyValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new WeakKeyDummyValueSegment(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new WeakKeyDummyValueSegment<>(map, initialCapacity); } @Override - public WeakKeyDummyValueEntry copy( + public @Nullable WeakKeyDummyValueEntry copy( WeakKeyDummyValueSegment segment, WeakKeyDummyValueEntry entry, - @CheckForNull WeakKeyDummyValueEntry newNext) { - if (entry.getKey() == null) { + @Nullable WeakKeyDummyValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { // key collected return null; } - return entry.copy(segment.queueForKeys, newNext); + return newEntry(segment, key, entry.hash, newNext); } @Override @@ -748,42 +769,43 @@ public WeakKeyDummyValueEntry newEntry( WeakKeyDummyValueSegment segment, K key, int hash, - @CheckForNull WeakKeyDummyValueEntry next) { - return new WeakKeyDummyValueEntry(segment.queueForKeys, key, hash, next); + @Nullable WeakKeyDummyValueEntry next) { + return next == null + ? new WeakKeyDummyValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyDummyValueEntry<>(segment.queueForKeys, key, hash, next); } } } /** Concrete implementation of {@link InternalEntry} for weak keys and strong values. */ - static final class WeakKeyStrongValueEntry + static class WeakKeyStrongValueEntry extends AbstractWeakKeyEntry> implements StrongValueEntry> { - @CheckForNull private volatile V value = null; + private volatile @Nullable V value = null; - WeakKeyStrongValueEntry( - ReferenceQueue queue, - K key, - int hash, - @CheckForNull WeakKeyStrongValueEntry next) { - super(queue, key, hash, next); + private WeakKeyStrongValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); } @Override - @CheckForNull - public V getValue() { + public final @Nullable V getValue() { return value; } - void setValue(V value) { - this.value = value; - } + private static final class LinkedWeakKeyStrongValueEntry + extends WeakKeyStrongValueEntry { + private final WeakKeyStrongValueEntry next; + + private LinkedWeakKeyStrongValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyStrongValueEntry next) { + super(queue, key, hash); + this.next = next; + } - WeakKeyStrongValueEntry copy( - ReferenceQueue queueForKeys, WeakKeyStrongValueEntry newNext) { - WeakKeyStrongValueEntry newEntry = - new WeakKeyStrongValueEntry<>(queueForKeys, getKey(), this.hash, newNext); - newEntry.setValue(value); - return newEntry; + @Override + public WeakKeyStrongValueEntry getNext() { + return next; + } } /** Concrete implementation of {@link InternalEntryHelper} for weak keys and strong values. */ @@ -811,27 +833,29 @@ public Strength valueStrength() { public WeakKeyStrongValueSegment newSegment( MapMakerInternalMap, WeakKeyStrongValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new WeakKeyStrongValueSegment<>(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new WeakKeyStrongValueSegment<>(map, initialCapacity); } @Override - public WeakKeyStrongValueEntry copy( + public @Nullable WeakKeyStrongValueEntry copy( WeakKeyStrongValueSegment segment, WeakKeyStrongValueEntry entry, - @CheckForNull WeakKeyStrongValueEntry newNext) { - if (entry.getKey() == null) { + @Nullable WeakKeyStrongValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { // key collected return null; } - return entry.copy(segment.queueForKeys, newNext); + WeakKeyStrongValueEntry newEntry = newEntry(segment, key, entry.hash, newNext); + newEntry.value = entry.value; + return newEntry; } @Override public void setValue( WeakKeyStrongValueSegment segment, WeakKeyStrongValueEntry entry, V value) { - entry.setValue(value); + entry.value = value; } @Override @@ -839,53 +863,49 @@ public WeakKeyStrongValueEntry newEntry( WeakKeyStrongValueSegment segment, K key, int hash, - @CheckForNull WeakKeyStrongValueEntry next) { - return new WeakKeyStrongValueEntry<>(segment.queueForKeys, key, hash, next); + @Nullable WeakKeyStrongValueEntry next) { + return next == null + ? new WeakKeyStrongValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyStrongValueEntry<>(segment.queueForKeys, key, hash, next); } } } /** Concrete implementation of {@link InternalEntry} for weak keys and weak values. */ - static final class WeakKeyWeakValueEntry + static class WeakKeyWeakValueEntry extends AbstractWeakKeyEntry> implements WeakValueEntry> { private volatile WeakValueReference> valueReference = unsetWeakValueReference(); - WeakKeyWeakValueEntry( - ReferenceQueue queue, K key, int hash, @CheckForNull WeakKeyWeakValueEntry next) { - super(queue, key, hash, next); + WeakKeyWeakValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); } @Override - public V getValue() { + public final V getValue() { return valueReference.get(); } - WeakKeyWeakValueEntry copy( - ReferenceQueue queueForKeys, - ReferenceQueue queueForValues, - WeakKeyWeakValueEntry newNext) { - WeakKeyWeakValueEntry newEntry = - new WeakKeyWeakValueEntry<>(queueForKeys, getKey(), this.hash, newNext); - newEntry.valueReference = valueReference.copyFor(queueForValues, newEntry); - return newEntry; - } - @Override - public void clearValue() { - valueReference.clear(); + public final WeakValueReference> getValueReference() { + return valueReference; } - void setValue(V value, ReferenceQueue queueForValues) { - WeakValueReference> previous = this.valueReference; - this.valueReference = new WeakValueReferenceImpl<>(queueForValues, value, this); - previous.clear(); - } + private static final class LinkedWeakKeyWeakValueEntry + extends WeakKeyWeakValueEntry { + private final WeakKeyWeakValueEntry next; - @Override - public WeakValueReference> getValueReference() { - return valueReference; + LinkedWeakKeyWeakValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyWeakValueEntry next) { + super(queue, key, hash); + this.next = next; + } + + @Override + public WeakKeyWeakValueEntry getNext() { + return next; + } } /** Concrete implementation of {@link InternalEntryHelper} for weak keys and weak values. */ @@ -912,30 +932,34 @@ public Strength valueStrength() { @Override public WeakKeyWeakValueSegment newSegment( MapMakerInternalMap, WeakKeyWeakValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - return new WeakKeyWeakValueSegment<>(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + return new WeakKeyWeakValueSegment<>(map, initialCapacity); } @Override - public WeakKeyWeakValueEntry copy( + public @Nullable WeakKeyWeakValueEntry copy( WeakKeyWeakValueSegment segment, WeakKeyWeakValueEntry entry, - @CheckForNull WeakKeyWeakValueEntry newNext) { - if (entry.getKey() == null) { + @Nullable WeakKeyWeakValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { // key collected return null; } if (Segment.isCollected(entry)) { return null; } - return entry.copy(segment.queueForKeys, segment.queueForValues, newNext); + WeakKeyWeakValueEntry newEntry = newEntry(segment, key, entry.hash, newNext); + newEntry.valueReference = entry.valueReference.copyFor(segment.queueForValues, newEntry); + return newEntry; } @Override public void setValue( WeakKeyWeakValueSegment segment, WeakKeyWeakValueEntry entry, V value) { - entry.setValue(value, segment.queueForValues); + WeakValueReference> previous = entry.valueReference; + entry.valueReference = new WeakValueReferenceImpl<>(segment.queueForValues, value, entry); + previous.clear(); } @Override @@ -943,8 +967,10 @@ public WeakKeyWeakValueEntry newEntry( WeakKeyWeakValueSegment segment, K key, int hash, - @CheckForNull WeakKeyWeakValueEntry next) { - return new WeakKeyWeakValueEntry<>(segment.queueForKeys, key, hash, next); + @Nullable WeakKeyWeakValueEntry next) { + return next == null + ? new WeakKeyWeakValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyWeakValueEntry<>(segment.queueForKeys, key, hash, next); } } } @@ -955,8 +981,7 @@ interface WeakValueReference> { * Returns the current value being referenced, or {@code null} if there is none (e.g. because * either it got collected, or {@link #clear} was called, or it wasn't set in the first place). */ - @CheckForNull - V get(); + @Nullable V get(); /** Returns the entry which contains this {@link WeakValueReference}. */ E getEntry(); @@ -966,7 +991,7 @@ interface WeakValueReference> { /** * Returns a freshly created {@link WeakValueReference} for the given {@code entry} (and on the - * given {@code queue} with the same value as this {@link WeakValueReference}. + * given {@code queue}) with the same value as this {@link WeakValueReference}. */ WeakValueReference copyFor(ReferenceQueue queue, E entry); } @@ -1003,13 +1028,13 @@ public Object getValue() { } /** - * A singleton {@link WeakValueReference} used to denote an unset value in a entry with weak + * A singleton {@link WeakValueReference} used to denote an unset value in an entry with weak * values. */ static final WeakValueReference UNSET_WEAK_VALUE_REFERENCE = new WeakValueReference() { @Override - public DummyInternalEntry getEntry() { + public @Nullable DummyInternalEntry getEntry() { return null; } @@ -1017,7 +1042,7 @@ public DummyInternalEntry getEntry() { public void clear() {} @Override - public Object get() { + public @Nullable Object get() { return null; } @@ -1115,15 +1140,15 @@ Segment segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } - Segment createSegment(int initialCapacity, int maxSegmentSize) { - return entryHelper.newSegment(this, initialCapacity, maxSegmentSize); + Segment createSegment(int initialCapacity) { + return entryHelper.newSegment(this, initialCapacity); } /** * Gets the value from an entry. Returns {@code null} if the entry is invalid, partially-collected * or computing. */ - V getLiveValue(E entry) { + @Nullable V getLiveValue(E entry) { if (entry.getKey() == null) { return null; } @@ -1132,7 +1157,7 @@ V getLiveValue(E entry) { @SuppressWarnings("unchecked") final Segment[] newSegmentArray(int ssize) { - return new Segment[ssize]; + return (Segment[]) new Segment[ssize]; } // Inner Classes @@ -1197,10 +1222,7 @@ abstract static class Segment< int threshold; /** The per-segment table. */ - @CheckForNull volatile AtomicReferenceArray table; - - /** The maximum size of this map. MapMaker.UNSET_INT if there is no maximum. */ - final int maxSegmentSize; + volatile @Nullable AtomicReferenceArray table; /** * A counter of the number of reads since the last write, used to drain queues on a small @@ -1208,9 +1230,8 @@ abstract static class Segment< */ final AtomicInteger readCount = new AtomicInteger(); - Segment(MapMakerInternalMap map, int initialCapacity, int maxSegmentSize) { + Segment(MapMakerInternalMap map, int initialCapacity) { this.map = map; - this.maxSegmentSize = maxSegmentSize; initTable(newEntryArray(initialCapacity)); } @@ -1235,20 +1256,16 @@ void setValue(E entry, V value) { } /** Returns a copy of the given {@code entry}. */ - E copyEntry(E original, E newNext) { + @Nullable E copyEntry(E original, E newNext) { return this.map.entryHelper.copy(self(), original, newNext); } AtomicReferenceArray newEntryArray(int size) { - return new AtomicReferenceArray(size); + return new AtomicReferenceArray<>(size); } void initTable(AtomicReferenceArray newTable) { this.threshold = newTable.length() * 3 / 4; // 0.75 - if (this.threshold == maxSegmentSize) { - // prevent spurious expansion before eviction - this.threshold++; - } this.table = newTable; } @@ -1259,7 +1276,7 @@ void initTable(AtomicReferenceArray newTable) { * implementation type. * *

    This method is provided as a convenience for tests. Otherwise they'd need to be - * knowledgable about all the implementation details of our type system trickery. + * knowledgeable about all the implementation details of our type system trickery. */ abstract E castForTesting(InternalEntry entry); @@ -1305,7 +1322,7 @@ void setTableEntryForTesting(int i, InternalEntry entry) { } /** Unsafely returns a copy of the given entry. */ - E copyForTesting(InternalEntry entry, @CheckForNull InternalEntry newNext) { + E copyForTesting(InternalEntry entry, @Nullable InternalEntry newNext) { return this.map.entryHelper.copy(self(), castForTesting(entry), castForTesting(newNext)); } @@ -1315,7 +1332,7 @@ void setValueForTesting(InternalEntry entry, V value) { } /** Unsafely returns a fresh entry. */ - E newEntryForTesting(K key, int hash, @CheckForNull InternalEntry next) { + E newEntryForTesting(K key, int hash, @Nullable InternalEntry next) { return this.map.entryHelper.newEntry(self(), key, hash, castForTesting(next)); } @@ -1326,15 +1343,15 @@ boolean removeTableEntryForTesting(InternalEntry entry) { } /** Unsafely removes the given entry from the given chain in this segment's hash table. */ - E removeFromChainForTesting(InternalEntry first, InternalEntry entry) { + @Nullable E removeFromChainForTesting( + InternalEntry first, InternalEntry entry) { return removeFromChain(castForTesting(first), castForTesting(entry)); } /** * Unsafely returns the value of the given entry if it's still live, or {@code null} otherwise. */ - @CheckForNull - V getLiveValueForTesting(InternalEntry entry) { + @Nullable V getLiveValueForTesting(InternalEntry entry) { return getLiveValue(castForTesting(entry)); } @@ -1384,7 +1401,7 @@ void clearReferenceQueue(ReferenceQueue referenceQueue) { } /** Returns first entry of bin for given hash. */ - E getFirst(int hash) { + @Nullable E getFirst(int hash) { // read this volatile field only once AtomicReferenceArray table = this.table; return table.get(hash & (table.length() - 1)); @@ -1392,7 +1409,7 @@ E getFirst(int hash) { // Specialized implementations of map methods - E getEntry(Object key, int hash) { + @Nullable E getEntry(Object key, int hash) { if (count != 0) { // read-volatile for (E e = getFirst(hash); e != null; e = e.getNext()) { if (e.getHash() != hash) { @@ -1414,11 +1431,11 @@ E getEntry(Object key, int hash) { return null; } - E getLiveEntry(Object key, int hash) { + @Nullable E getLiveEntry(Object key, int hash) { return getEntry(key, hash); } - V get(Object key, int hash) { + @Nullable V get(Object key, int hash) { try { E e = getLiveEntry(key, hash); if (e == null) { @@ -1477,7 +1494,7 @@ boolean containsValue(Object value) { } } - V put(K key, int hash, V value, boolean onlyIfAbsent) { + @Nullable V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { preWriteCleanup(); @@ -1650,7 +1667,7 @@ boolean replace(K key, int hash, V oldValue, V newValue) { } } - V replace(K key, int hash, V newValue) { + @Nullable V replace(K key, int hash, V newValue) { lock(); try { preWriteCleanup(); @@ -1692,7 +1709,7 @@ V replace(K key, int hash, V newValue) { } @CanIgnoreReturnValue - V remove(Object key, int hash) { + @Nullable V remove(Object key, int hash) { lock(); try { preWriteCleanup(); @@ -1805,7 +1822,7 @@ void clear() { * @return the new first entry for the table */ @GuardedBy("this") - E removeFromChain(E first, E entry) { + @Nullable E removeFromChain(E first, E entry) { int newCount = count; E newFirst = entry.getNext(); for (E e = first; e != entry; e = e.getNext()) { @@ -1948,8 +1965,7 @@ static > boolean isCollected(E entry) { * Gets the value from an entry. Returns {@code null} if the entry is invalid or * partially-collected. */ - @CheckForNull - V getLiveValue(E entry) { + @Nullable V getLiveValue(E entry) { if (entry.getKey() == null) { tryDrainReferenceQueues(); return null; @@ -2006,9 +2022,8 @@ static final class StrongKeyStrongValueSegment MapMakerInternalMap< K, V, StrongKeyStrongValueEntry, StrongKeyStrongValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2018,7 +2033,8 @@ StrongKeyStrongValueSegment self() { @SuppressWarnings("unchecked") @Override - public StrongKeyStrongValueEntry castForTesting(InternalEntry entry) { + public @Nullable StrongKeyStrongValueEntry castForTesting( + @Nullable InternalEntry entry) { return (StrongKeyStrongValueEntry) entry; } } @@ -2026,14 +2042,13 @@ public StrongKeyStrongValueEntry castForTesting(InternalEntry ent /** Concrete implementation of {@link Segment} for strong keys and weak values. */ static final class StrongKeyWeakValueSegment extends Segment, StrongKeyWeakValueSegment> { - private final ReferenceQueue queueForValues = new ReferenceQueue(); + private final ReferenceQueue queueForValues = new ReferenceQueue<>(); StrongKeyWeakValueSegment( MapMakerInternalMap, StrongKeyWeakValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2048,7 +2063,8 @@ ReferenceQueue getValueReferenceQueueForTesting() { @SuppressWarnings("unchecked") @Override - public StrongKeyWeakValueEntry castForTesting(InternalEntry entry) { + public @Nullable StrongKeyWeakValueEntry castForTesting( + @Nullable InternalEntry entry) { return (StrongKeyWeakValueEntry) entry; } @@ -2094,9 +2110,8 @@ static final class StrongKeyDummyValueSegment StrongKeyDummyValueSegment( MapMakerInternalMap, StrongKeyDummyValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2114,14 +2129,13 @@ public StrongKeyDummyValueEntry castForTesting(InternalEntry ent /** Concrete implementation of {@link Segment} for weak keys and strong values. */ static final class WeakKeyStrongValueSegment extends Segment, WeakKeyStrongValueSegment> { - private final ReferenceQueue queueForKeys = new ReferenceQueue(); + private final ReferenceQueue queueForKeys = new ReferenceQueue<>(); WeakKeyStrongValueSegment( MapMakerInternalMap, WeakKeyStrongValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2154,14 +2168,13 @@ void maybeClearReferenceQueues() { /** Concrete implementation of {@link Segment} for weak keys and weak values. */ static final class WeakKeyWeakValueSegment extends Segment, WeakKeyWeakValueSegment> { - private final ReferenceQueue queueForKeys = new ReferenceQueue(); - private final ReferenceQueue queueForValues = new ReferenceQueue(); + private final ReferenceQueue queueForKeys = new ReferenceQueue<>(); + private final ReferenceQueue queueForValues = new ReferenceQueue<>(); WeakKeyWeakValueSegment( MapMakerInternalMap, WeakKeyWeakValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2181,7 +2194,8 @@ ReferenceQueue getValueReferenceQueueForTesting() { @SuppressWarnings("unchecked") @Override - public WeakKeyWeakValueEntry castForTesting(InternalEntry entry) { + public @Nullable WeakKeyWeakValueEntry castForTesting( + @Nullable InternalEntry entry) { return (WeakKeyWeakValueEntry) entry; } @@ -2225,13 +2239,12 @@ void maybeClearReferenceQueues() { /** Concrete implementation of {@link Segment} for weak keys and {@link Dummy} values. */ static final class WeakKeyDummyValueSegment extends Segment, WeakKeyDummyValueSegment> { - private final ReferenceQueue queueForKeys = new ReferenceQueue(); + private final ReferenceQueue queueForKeys = new ReferenceQueue<>(); WeakKeyDummyValueSegment( MapMakerInternalMap, WeakKeyDummyValueSegment> map, - int initialCapacity, - int maxSegmentSize) { - super(map, initialCapacity, maxSegmentSize); + int initialCapacity) { + super(map, initialCapacity); } @Override @@ -2265,7 +2278,7 @@ static final class CleanupMapTask implements Runnable { final WeakReference> mapReference; public CleanupMapTask(MapMakerInternalMap map) { - this.mapReference = new WeakReference>(map); + this.mapReference = new WeakReference<>(map); } @Override @@ -2339,7 +2352,7 @@ public int size() { } @Override - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { if (key == null) { return null; } @@ -2351,7 +2364,7 @@ public V get(@CheckForNull Object key) { * Returns the internal entry for the specified key. The entry may be computing or partially * collected. Does not impact recency ordering. */ - E getEntry(@CheckForNull Object key) { + @Nullable E getEntry(@Nullable Object key) { if (key == null) { return null; } @@ -2360,7 +2373,7 @@ E getEntry(@CheckForNull Object key) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { if (key == null) { return false; } @@ -2369,7 +2382,7 @@ public boolean containsKey(@CheckForNull Object key) { } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { if (value == null) { return false; } @@ -2379,7 +2392,7 @@ public boolean containsValue(@CheckForNull Object value) { // such that none of the subsequent iterations observed it, despite the fact that at every point // in time it was present somewhere int the map. This becomes increasingly unlikely as // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. - final Segment[] segments = this.segments; + Segment[] segments = this.segments; long last = -1L; for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { long sum = 0L; @@ -2408,7 +2421,7 @@ public boolean containsValue(@CheckForNull Object value) { @CanIgnoreReturnValue @Override - public V put(K key, V value) { + public @Nullable V put(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -2417,7 +2430,7 @@ public V put(K key, V value) { @CanIgnoreReturnValue @Override - public V putIfAbsent(K key, V value) { + public @Nullable V putIfAbsent(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -2433,7 +2446,7 @@ public void putAll(Map m) { @CanIgnoreReturnValue @Override - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { if (key == null) { return null; } @@ -2443,7 +2456,7 @@ public V remove(@CheckForNull Object key) { @CanIgnoreReturnValue @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { if (key == null || value == null) { return false; } @@ -2453,7 +2466,7 @@ public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { @CanIgnoreReturnValue @Override - public boolean replace(K key, @CheckForNull V oldValue, V newValue) { + public boolean replace(K key, @Nullable V oldValue, V newValue) { checkNotNull(key); checkNotNull(newValue); if (oldValue == null) { @@ -2465,7 +2478,7 @@ public boolean replace(K key, @CheckForNull V oldValue, V newValue) { @CanIgnoreReturnValue @Override - public V replace(K key, V value) { + public @Nullable V replace(K key, V value) { checkNotNull(key); checkNotNull(value); int hash = hash(key); @@ -2479,7 +2492,7 @@ public void clear() { } } - @CheckForNull transient Set keySet; + @LazyInit transient @Nullable Set keySet; @Override public Set keySet() { @@ -2487,7 +2500,7 @@ public Set keySet() { return (ks != null) ? ks : (keySet = new KeySet()); } - @CheckForNull transient Collection values; + @LazyInit transient @Nullable Collection values; @Override public Collection values() { @@ -2495,7 +2508,7 @@ public Collection values() { return (vs != null) ? vs : (values = new Values()); } - @CheckForNull transient Set> entrySet; + @LazyInit transient @Nullable Set> entrySet; @Override public Set> entrySet() { @@ -2509,11 +2522,11 @@ abstract class HashIterator implements Iterator { int nextSegmentIndex; int nextTableIndex; - @CheckForNull Segment currentSegment; - @CheckForNull AtomicReferenceArray currentTable; - @CheckForNull E nextEntry; - @CheckForNull WriteThroughEntry nextExternal; - @CheckForNull WriteThroughEntry lastReturned; + @Nullable Segment currentSegment; + @Nullable AtomicReferenceArray currentTable; + @Nullable E nextEntry; + @Nullable WriteThroughEntry nextExternal; + @Nullable WriteThroughEntry lastReturned; HashIterator() { nextSegmentIndex = segments.length - 1; @@ -2653,7 +2666,7 @@ public V getValue() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { // Cannot use key and value equivalence if (object instanceof Entry) { Entry that = (Entry) object; @@ -2685,7 +2698,7 @@ public Entry next() { } @WeakOuter - final class KeySet extends SafeToArraySet { + final class KeySet extends AbstractSet { @Override public Iterator iterator() { @@ -2745,23 +2758,10 @@ public boolean contains(Object o) { public void clear() { MapMakerInternalMap.this.clear(); } - - // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. - // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 - - @Override - public Object[] toArray() { - return toArrayList(this).toArray(); - } - - @Override - public T[] toArray(T[] a) { - return toArrayList(this).toArray(a); - } } @WeakOuter - final class EntrySet extends SafeToArraySet> { + final class EntrySet extends AbstractSet> { @Override public Iterator> iterator() { @@ -2809,28 +2809,6 @@ public void clear() { } } - private abstract static class SafeToArraySet extends AbstractSet { - // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. - // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 - - @Override - public Object[] toArray() { - return toArrayList(this).toArray(); - } - - @Override - public T[] toArray(T[] a) { - return toArrayList(this).toArray(a); - } - } - - private static ArrayList toArrayList(Collection c) { - // Avoid calling ArrayList(Collection), which may call back into toArray. - ArrayList result = new ArrayList<>(c.size()); - Iterators.addAll(result, c.iterator()); - return result; - } - // Serialization Support private static final long serialVersionUID = 5; @@ -2845,6 +2823,11 @@ Object writeReplace() { this); } + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializationProxy"); + } + /** * The actual object that gets serialized. Unfortunately, readResolve() doesn't get called when a * circular dependency is present, so the proxy must be able to behave as the map itself. @@ -2890,7 +2873,7 @@ void writeMapTo(ObjectOutputStream out) throws IOException { out.writeObject(null); // terminate entries } - @SuppressWarnings("deprecation") // serialization of deprecated feature + @J2ktIncompatible // java.io.ObjectInputStream MapMaker readMapMaker(ObjectInputStream in) throws IOException { int size = in.readInt(); return new MapMaker() @@ -2902,6 +2885,7 @@ MapMaker readMapMaker(ObjectInputStream in) throws IOException { } @SuppressWarnings("unchecked") + @J2ktIncompatible // java.io.ObjectInputStream void readEntries(ObjectInputStream in) throws IOException, ClassNotFoundException { while (true) { K key = (K) in.readObject(); @@ -2937,6 +2921,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { writeMapTo(out); } + @J2ktIncompatible // java.io.ObjectInputStream private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); MapMaker mapMaker = readMapMaker(in); diff --git a/android/guava/src/com/google/common/collect/Maps.java b/android/guava/src/com/google/common/collect/Maps.java index b415f25eda5d..37e2f4fc92a2 100644 --- a/android/guava/src/com/google/common/collect/Maps.java +++ b/android/guava/src/com/google/common/collect/Maps.java @@ -21,12 +21,16 @@ import static com.google.common.base.Predicates.compose; import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static com.google.common.collect.Sets.newHashSet; +import static java.lang.Math.ceil; +import static java.util.Collections.singletonMap; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; import com.google.common.base.Equivalence; import com.google.common.base.Function; @@ -37,6 +41,7 @@ import com.google.common.collect.MapDifference.ValueDifference; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import com.google.j2objc.annotations.Weak; import com.google.j2objc.annotations.WeakOuter; @@ -63,8 +68,10 @@ import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.BinaryOperator; +import java.util.stream.Collector; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@link Map} instances (including instances of {@link @@ -72,7 +79,7 @@ * and {@link Queues}. * *

    See the Guava User Guide article on {@code Maps}. + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#maps">{@code Maps}. * * @author Kevin Bourrillion * @author Mike Bostock @@ -81,22 +88,19 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Maps { private Maps() {} private enum EntryFunction implements Function, @Nullable Object> { KEY { @Override - @CheckForNull - public Object apply(Entry entry) { + public @Nullable Object apply(Entry entry) { return entry.getKey(); } }, VALUE { @Override - @CheckForNull - public Object apply(Entry entry) { + public @Nullable Object apply(Entry entry) { return entry.getValue(); } }; @@ -161,9 +165,8 @@ public static , V> ImmutableMap immutableEnumMap( K key1 = entry1.getKey(); V value1 = entry1.getValue(); checkEntryNotNull(key1, value1); - Class clazz = key1.getDeclaringClass(); - EnumMap enumMap = new EnumMap<>(clazz); - enumMap.put(key1, value1); + // Do something that works for j2cl, where we can't call getDeclaredClass(): + EnumMap enumMap = new EnumMap<>(singletonMap(key1, value1)); while (entryItr.hasNext()) { Entry entry = entryItr.next(); K key = entry.getKey(); @@ -174,6 +177,50 @@ public static , V> ImmutableMap immutableEnumMap( return ImmutableEnumMap.asImmutable(enumMap); } + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. The + * resulting implementation is specialized for enum key types. The returned map and its views will + * iterate over keys in their enum definition order, not encounter order. + * + *

    If the mapped keys contain duplicates, an {@code IllegalArgumentException} is thrown when + * the collection operation is performed. (This differs from the {@code Collector} returned by + * {@link java.util.stream.Collectors#toMap(java.util.function.Function, + * java.util.function.Function) Collectors.toMap(Function, Function)}, which throws an {@code + * IllegalStateException}.) + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static , V> + Collector> toImmutableEnumMap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction) { + return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. The + * resulting implementation is specialized for enum key types. The returned map and its views will + * iterate over keys in their enum definition order, not encounter order. + * + *

    If the mapped keys contain duplicates, the values are merged using the specified merging + * function. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static , V> + Collector> toImmutableEnumMap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction, mergeFunction); + } + /** * Creates a mutable, empty {@code HashMap} instance. * @@ -181,12 +228,14 @@ public static , V> ImmutableMap immutableEnumMap( * *

    Note: if {@code K} is an {@code enum} type, use {@link #newEnumMap} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code HashMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashMap} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code HashMap} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashMap newHashMap() { return new HashMap<>(); @@ -199,13 +248,15 @@ HashMap newHashMap() { * *

    Note: if {@code K} is an {@link Enum} type, use {@link #newEnumMap} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code HashMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashMap} constructor directly, taking advantage of "diamond" + * syntax. * * @param map the mappings to be placed in the new map * @return a new {@code HashMap} initialized with the mappings from {@code map} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashMap newHashMap( Map map) { return new HashMap<>(map); @@ -222,6 +273,7 @@ HashMap newHashMap() { * without resizing * @throws IllegalArgumentException if {@code expectedSize} is negative */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashMap newHashMapWithExpectedSize(int expectedSize) { return new HashMap<>(capacity(expectedSize)); @@ -237,10 +289,19 @@ static int capacity(int expectedSize) { return expectedSize + 1; } if (expectedSize < Ints.MAX_POWER_OF_TWO) { - // This is the calculation used in JDK8 to resize when a putAll - // happens; it seems to be the most conservative calculation we - // can make. 0.75 is the default load factor. - return (int) ((float) expectedSize / 0.75F + 1.0F); + // This seems to be consistent across JDKs. The capacity argument to HashMap and LinkedHashMap + // ends up being used to compute a "threshold" size, beyond which the internal table + // will be resized. That threshold is ceilingPowerOfTwo(capacity*loadFactor), where + // loadFactor is 0.75 by default. So with the calculation here we ensure that the + // threshold is equal to ceilingPowerOfTwo(expectedSize). There is a separate code + // path when the first operation on the new map is putAll(otherMap). There, prior to + // https://github.com/openjdk/jdk/commit/3e393047e12147a81e2899784b943923fc34da8e, a bug + // meant that sometimes a too-large threshold is calculated. However, this new threshold is + // independent of the initial capacity, except that it won't be lower than the threshold + // computed from that capacity. Because the internal table is only allocated on the first + // write, we won't see copying because of the new threshold. So it is always OK to use the + // calculation here. + return (int) ceil(expectedSize / 0.75); } return Integer.MAX_VALUE; // any large value } @@ -250,12 +311,14 @@ static int capacity(int expectedSize) { * *

    Note: if mutability is not required, use {@link ImmutableMap#of()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code LinkedHashMap} constructor directly, taking advantage of - * the new "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashMap} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code LinkedHashMap} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashMap newLinkedHashMap() { return new LinkedHashMap<>(); @@ -267,13 +330,15 @@ LinkedHashMap newLinkedHashMap() { * *

    Note: if mutability is not required, use {@link ImmutableMap#copyOf(Map)} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code LinkedHashMap} constructor directly, taking advantage of - * the new "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashMap} constructor directly, taking advantage of "diamond" + * syntax. * * @param map the mappings to be placed in the new map * @return a new, {@code LinkedHashMap} initialized with the mappings from {@code map} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashMap newLinkedHashMap(Map map) { return new LinkedHashMap<>(map); @@ -291,6 +356,7 @@ LinkedHashMap newLinkedHashMap(Map map) { * @throws IllegalArgumentException if {@code expectedSize} is negative * @since 19.0 */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashMap newLinkedHashMapWithExpectedSize(int expectedSize) { return new LinkedHashMap<>(capacity(expectedSize)); @@ -311,12 +377,17 @@ public static ConcurrentMap newConcurrentMap() { * *

    Note: if mutability is not required, use {@link ImmutableSortedMap#of()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code TreeMap} */ + @SuppressWarnings({ + "rawtypes", // https://github.com/google/guava/issues/989 + "NonApiType", // acts as a direct substitute for a constructor call + }) public static TreeMap newTreeMap() { return new TreeMap<>(); } @@ -328,15 +399,17 @@ public static ConcurrentMap newConcurrentMap() { *

    Note: if mutability is not required, use {@link * ImmutableSortedMap#copyOfSorted(SortedMap)} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" + * syntax. * * @param map the sorted map whose mappings are to be placed in the new map and whose comparator * is to be used to sort the new map * @return a new {@code TreeMap} initialized with the mappings from {@code map} and using the * comparator of {@code map} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static TreeMap newTreeMap( SortedMap map) { return new TreeMap<>(map); @@ -348,15 +421,17 @@ public static ConcurrentMap newConcurrentMap() { *

    Note: if mutability is not required, use {@code * ImmutableSortedMap.orderedBy(comparator).build()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" + * syntax. * * @param comparator the comparator to sort the keys with * @return a new, empty {@code TreeMap} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static - TreeMap newTreeMap(@CheckForNull Comparator comparator) { + TreeMap newTreeMap(@Nullable Comparator comparator) { // Ideally, the extra type parameter "C" shouldn't be necessary. It is a // work-around of a compiler type inference quirk that prevents the // following code from being compiled: @@ -379,9 +454,10 @@ TreeMap newTreeMap(@CheckForNull Comparator comparator) { /** * Creates an {@code EnumMap} with the same mappings as the specified map. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code EnumMap} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code EnumMap} constructor directly, taking advantage of "diamond" + * syntax. * * @param map the map from which to initialize this {@code EnumMap} * @return a new {@code EnumMap} initialized with the mappings from {@code map} @@ -396,9 +472,10 @@ TreeMap newTreeMap(@CheckForNull Comparator comparator) { /** * Creates an {@code IdentityHashMap} instance. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code IdentityHashMap} constructor directly, taking advantage of - * the new "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code IdentityHashMap} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code IdentityHashMap} */ @@ -422,28 +499,15 @@ IdentityHashMap newIdentityHashMap() { * @param right the map to treat as the "right" map for purposes of comparison * @return the difference between the two maps */ - @SuppressWarnings("unchecked") public static MapDifference difference( Map left, Map right) { if (left instanceof SortedMap) { + @SuppressWarnings("unchecked") SortedMap sortedLeft = (SortedMap) left; return difference(sortedLeft, right); } - /* - * This cast is safe: The Equivalence-accepting overload of difference() (which we call below) - * has a weird signature because Equivalence is itself a little weird. Still, we know that - * Equivalence.equals() can handle all inputs, and we know that the resulting MapDifference will - * contain only Ks and Vs (as opposed to possibly containing @Nullable objects even when K and V - * are *not* @Nullable). - * - * An alternative to suppressing the warning would be to inline the body of the other - * difference() method into this one. - */ - @SuppressWarnings("nullness") - MapDifference result = - (MapDifference) difference(left, right, Equivalence.equals()); - return result; + return difference(left, right, Equivalence.equals()); } /** @@ -460,36 +524,11 @@ MapDifference difference( * @return the difference between the two maps * @since 10.0 */ - /* - * This method should really be annotated to accept maps with @Nullable value types. Fortunately, - * no existing Google callers appear to pass null values (much less pass null values *and* run a - * nullness checker). - * - * Still, if we decide that we want to make that work, we'd need to introduce a new type parameter - * for the Equivalence input type: - * - * ... difference(..., Equivalence ...) - * - * Maybe we should, even though it will break source compatibility. - * - * Alternatively, this is a case in which it would be useful to be able to express Equivalence). - * - * As things stand now, though, we have to either: - * - * - require non-null inputs so that we can guarantee non-null outputs - * - * - accept nullable inputs but force users to cope with nullable outputs - * - * And the non-null option is far more useful to existing users. - * - * (Vaguely related: Another thing we could consider is an overload that accepts a BiPredicate: - * https://github.com/google/guava/issues/3913) - */ - public static MapDifference difference( - Map left, - Map right, - Equivalence valueEquivalence) { + public static + MapDifference difference( + Map left, + Map right, + Equivalence valueEquivalence) { Preconditions.checkNotNull(valueEquivalence); Map onlyOnLeft = newLinkedHashMap(); @@ -528,6 +567,7 @@ SortedMapDifference difference( onlyOnRight.putAll(right); // will whittle it down SortedMap onBoth = Maps.newTreeMap(comparator); SortedMap> differences = Maps.newTreeMap(comparator); + doDifference(left, right, Equivalence.equals(), onlyOnLeft, onlyOnRight, onBoth, differences); return new SortedMapDifferenceImpl<>(onlyOnLeft, onlyOnRight, onBoth, differences); } @@ -535,7 +575,7 @@ SortedMapDifference difference( private static void doDifference( Map left, Map right, - Equivalence valueEquivalence, + Equivalence valueEquivalence, Map onlyOnLeft, Map onlyOnRight, Map onBoth, @@ -619,7 +659,7 @@ public Map> entriesDiffering() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -666,7 +706,7 @@ static class ValueDifferenceImpl static ValueDifference create( @ParametricNullness V left, @ParametricNullness V right) { - return new ValueDifferenceImpl(left, right); + return new ValueDifferenceImpl<>(left, right); } private ValueDifferenceImpl(@ParametricNullness V left, @ParametricNullness V right) { @@ -687,7 +727,7 @@ public V rightValue() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof MapDifference.ValueDifference) { MapDifference.ValueDifference that = (MapDifference.ValueDifference) object; return Objects.equal(this.left, that.leftValue()) @@ -745,7 +785,7 @@ public SortedMap entriesOnlyOnRight() { */ @SuppressWarnings("unchecked") static Comparator orNaturalOrder( - @CheckForNull Comparator comparator) { + @Nullable Comparator comparator) { if (comparator != null) { // can't use ? : because of javac bug 5080917 return comparator; } @@ -869,13 +909,12 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return backingSet().contains(key); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { if (Collections2.safeContains(backingSet(), key)) { @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it K k = (K) key; @@ -886,8 +925,7 @@ public V get(@CheckForNull Object key) { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { if (backingSet().remove(key)) { @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it K k = (K) key; @@ -921,10 +959,10 @@ public Iterator> iterator() { } static - Iterator> asMapEntryIterator(Set set, final Function function) { + Iterator> asMapEntryIterator(Set set, Function function) { return new TransformedIterator>(set.iterator()) { @Override - Entry transform(@ParametricNullness final K key) { + Entry transform(@ParametricNullness K key) { return immutableEntry(key, function.apply(key)); } }; @@ -943,8 +981,7 @@ SortedSet backingSet() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return backingSet().comparator(); } @@ -1018,14 +1055,12 @@ public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusi } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return set.comparator(); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { if (Collections2.safeContains(set, key)) { @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it K k = (K) key; @@ -1066,7 +1101,7 @@ public NavigableMap descendingMap() { } } - private static Set removeOnlySet(final Set set) { + private static Set removeOnlySet(Set set) { return new ForwardingSet() { @Override protected Set delegate() { @@ -1085,8 +1120,7 @@ public boolean addAll(Collection es) { }; } - private static SortedSet removeOnlySortedSet( - final SortedSet set) { + private static SortedSet removeOnlySortedSet(SortedSet set) { return new ForwardingSortedSet() { @Override protected SortedSet delegate() { @@ -1123,7 +1157,7 @@ public SortedSet tailSet(@ParametricNullness E fromElement) { @GwtIncompatible // NavigableSet private static NavigableSet removeOnlyNavigableSet( - final NavigableSet set) { + NavigableSet set) { return new ForwardingNavigableSet() { @Override protected NavigableSet delegate() { @@ -1195,6 +1229,18 @@ public NavigableSet descendingSet() { *

    If {@code keys} is a {@link Set}, a live view can be obtained instead of a copy using {@link * Maps#asMap(Set, Function)}. * + *

    Note: on Java 8+, it is usually better to use streams. For example: + * + *

    {@code
    +   * import static com.google.common.collect.ImmutableMap.toImmutableMap;
    +   * ...
    +   * ImmutableMap colorNames =
    +   *     allColors.stream().collect(toImmutableMap(c -> c, c -> c.toString()));
    +   * }
    + * + *

    Streams provide a more standard and flexible API and the lambdas make it clear what the keys + * and values in the map are. + * * @throws NullPointerException if any element of {@code keys} is {@code null}, or if {@code * valueFunction} produces {@code null} for any key * @since 14.0 @@ -1220,13 +1266,13 @@ public static ImmutableMap toMap( public static ImmutableMap toMap( Iterator keys, Function valueFunction) { checkNotNull(valueFunction); - // Using LHM instead of a builder so as not to fail on duplicate keys - Map builder = newLinkedHashMap(); + ImmutableMap.Builder builder = ImmutableMap.builder(); while (keys.hasNext()) { K key = keys.next(); builder.put(key, valueFunction.apply(key)); } - return ImmutableMap.copyOf(builder); + // Using buildKeepingLast() so as not to fail on duplicate keys + return builder.buildKeepingLast(); } /** @@ -1240,14 +1286,26 @@ public static ImmutableMap toMap( * ... * ImmutableSet allColors = ImmutableSet.of(red, green, blue); * - * Map colorForName = - * uniqueIndex(allColors, toStringFunction()); + * ImmutableMap colorForName = + * uniqueIndex(allColors, c -> c.toString()); * assertThat(colorForName).containsEntry("red", red); * } * *

    If your index may associate multiple values with each key, use {@link * Multimaps#index(Iterable, Function) Multimaps.index}. * + *

    Note: on Java 8+, it is usually better to use streams. For example: + * + *

    {@code
    +   * import static com.google.common.collect.ImmutableMap.toImmutableMap;
    +   * ...
    +   * ImmutableMap colorForName =
    +   *     allColors.stream().collect(toImmutableMap(c -> c.toString(), c -> c));
    +   * }
    + * + *

    Streams provide a more standard and flexible API and the lambdas make it clear what the keys + * and values in the map are. + * * @param values the values to use when constructing the {@code Map} * @param keyFunction the function used to produce the key for each value * @return a map mapping the result of evaluating the function {@code keyFunction} on each value @@ -1260,7 +1318,12 @@ public static ImmutableMap toMap( @CanIgnoreReturnValue public static ImmutableMap uniqueIndex( Iterable values, Function keyFunction) { - // TODO(lowasser): consider presizing the builder if values is a Collection + if (values instanceof Collection) { + return uniqueIndex( + values.iterator(), + keyFunction, + ImmutableMap.builderWithExpectedSize(((Collection) values).size())); + } return uniqueIndex(values.iterator(), keyFunction); } @@ -1296,14 +1359,18 @@ public static ImmutableMap uniqueIndex( @CanIgnoreReturnValue public static ImmutableMap uniqueIndex( Iterator values, Function keyFunction) { + return uniqueIndex(values, keyFunction, ImmutableMap.builder()); + } + + private static ImmutableMap uniqueIndex( + Iterator values, Function keyFunction, ImmutableMap.Builder builder) { checkNotNull(keyFunction); - ImmutableMap.Builder builder = ImmutableMap.builder(); while (values.hasNext()) { V value = values.next(); builder.put(keyFunction.apply(value), value); } try { - return builder.build(); + return builder.buildOrThrow(); } catch (IllegalArgumentException duplicateKeys) { throw new IllegalArgumentException( duplicateKeys.getMessage() @@ -1321,6 +1388,7 @@ public static ImmutableMap uniqueIndex( * @throws ClassCastException if any key in {@code properties} is not a {@code String} * @throws NullPointerException if any key or value in {@code properties} is null */ + @J2ktIncompatible @GwtIncompatible // java.util.Properties public static ImmutableMap fromProperties(Properties properties) { ImmutableMap.Builder builder = ImmutableMap.builder(); @@ -1356,7 +1424,7 @@ public static ImmutableMap fromProperties(Properties properties) builder.put(key, requireNonNull(properties.getProperty(key))); } - return builder.build(); + return builder.buildOrThrow(); } /** @@ -1392,7 +1460,7 @@ Set> unmodifiableEntrySet(Set> entrySet) { /** * Returns an unmodifiable view of the specified map entry. The {@link Entry#setValue} operation - * throws an {@link UnsupportedOperationException}. This also has the side-effect of redefining + * throws an {@link UnsupportedOperationException}. This also has the side effect of redefining * {@code equals} to comply with the Entry contract, to avoid a possible nefarious implementation * of equals. * @@ -1400,7 +1468,7 @@ Set> unmodifiableEntrySet(Set> entrySet) { * @return an unmodifiable view of the entry */ static Entry unmodifiableEntry( - final Entry entry) { + Entry entry) { checkNotNull(entry); return new AbstractMapEntry() { @Override @@ -1419,7 +1487,7 @@ public V getValue() { static UnmodifiableIterator> unmodifiableEntryIterator( - final Iterator> entryIterator) { + Iterator> entryIterator) { return new UnmodifiableIterator>() { @Override public boolean hasNext() { @@ -1433,7 +1501,7 @@ public Entry next() { }; } - /** @see Multimaps#unmodifiableEntries */ + /** The implementation of {@link Multimaps#unmodifiableEntries}. */ static class UnmodifiableEntries extends ForwardingCollection> { private final Collection> entries; @@ -1455,15 +1523,14 @@ public Iterator> iterator() { // See java.util.Collections.UnmodifiableEntrySet for details on attacks. @Override - public Object[] toArray() { + public @Nullable Object[] toArray() { /* - * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it can + * standardToArray returns `@Nullable Object[]` rather than `Object[]` but because it can * be used with collections that may contain null. This collection never contains nulls, so we - * can treat it as a plain `Object[]`. + * could return `Object[]`. But this class is private and J2KT cannot change return types in + * overrides, so we declare `@Nullable Object[]` as the return type. */ - @SuppressWarnings("nullness") - Object[] result = standardToArray(); - return result; + return standardToArray(); } @Override @@ -1473,7 +1540,7 @@ public Object[] toArray() { } } - /** @see Maps#unmodifiableEntrySet(Set) */ + /** The implementation of {@link Maps#unmodifiableEntrySet(Set)}. */ static class UnmodifiableEntrySet extends UnmodifiableEntries implements Set> { UnmodifiableEntrySet(Set> entries) { @@ -1483,7 +1550,7 @@ static class UnmodifiableEntrySet Converter asConverter(final BiMap bimap) { + public static Converter asConverter(BiMap bimap) { return new BiMapConverter<>(bimap); } @@ -1531,7 +1598,7 @@ private static Y convert(BiMap bimap, X input) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof BiMapConverter) { BiMapConverter that = (BiMapConverter) object; return this.bimap.equals(that.bimap); @@ -1550,7 +1617,7 @@ public String toString() { return "Maps.asConverter(" + bimap + ")"; } - private static final long serialVersionUID = 0L; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0L; } /** @@ -1582,6 +1649,7 @@ public String toString() { * @param bimap the bimap to be wrapped in a synchronized view * @return a synchronized view of the specified bimap */ + @J2ktIncompatible // Synchronized public static BiMap synchronizedBiMap(BiMap bimap) { return Synchronized.biMap(bimap, null); @@ -1603,15 +1671,17 @@ BiMap unmodifiableBiMap(BiMap bimap) { return new UnmodifiableBiMap<>(bimap, null); } - /** @see Maps#unmodifiableBiMap(BiMap) */ + /** + * @see Maps#unmodifiableBiMap(BiMap) + */ private static class UnmodifiableBiMap extends ForwardingMap implements BiMap, Serializable { final Map unmodifiableMap; final BiMap delegate; - @RetainedWith @CheckForNull BiMap inverse; - @CheckForNull transient Set values; + @LazyInit @RetainedWith @Nullable BiMap inverse; + @LazyInit transient @Nullable Set values; - UnmodifiableBiMap(BiMap delegate, @CheckForNull BiMap inverse) { + UnmodifiableBiMap(BiMap delegate, @Nullable BiMap inverse) { unmodifiableMap = Collections.unmodifiableMap(delegate); this.delegate = delegate; this.inverse = inverse; @@ -1623,8 +1693,7 @@ protected Map delegate() { } @Override - @CheckForNull - public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) { throw new UnsupportedOperationException(); } @@ -1642,7 +1711,7 @@ public Set values() { return (result == null) ? values = Collections.unmodifiableSet(delegate.values()) : result; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -1962,53 +2031,36 @@ public interface EntryTransformer< * @throws NullPointerException if the key or value is null and this transformer does not accept * null arguments */ + @ParametricNullness V2 transformEntry(@ParametricNullness K key, @ParametricNullness V1 value); } /** Views a function as an entry transformer that ignores the entry key. */ static - EntryTransformer asEntryTransformer(final Function function) { + EntryTransformer asEntryTransformer(Function function) { checkNotNull(function); - return new EntryTransformer() { - @Override - @ParametricNullness - public V2 transformEntry(@ParametricNullness K key, @ParametricNullness V1 value) { - return function.apply(value); - } - }; + return (key, value) -> function.apply(value); } static Function asValueToValueFunction( - final EntryTransformer transformer, @ParametricNullness final K key) { + EntryTransformer transformer, @ParametricNullness K key) { checkNotNull(transformer); - return new Function() { - @Override - @ParametricNullness - public V2 apply(@ParametricNullness V1 v1) { - return transformer.transformEntry(key, v1); - } - }; + return v1 -> transformer.transformEntry(key, v1); } /** Views an entry transformer as a function from {@code Entry} to values. */ static Function, V2> asEntryToValueFunction( - final EntryTransformer transformer) { + EntryTransformer transformer) { checkNotNull(transformer); - return new Function, V2>() { - @Override - @ParametricNullness - public V2 apply(Entry entry) { - return transformer.transformEntry(entry.getKey(), entry.getValue()); - } - }; + return entry -> transformer.transformEntry(entry.getKey(), entry.getValue()); } /** Returns a view of an entry transformed by the specified transformer. */ static Entry transformEntry( - final EntryTransformer transformer, final Entry entry) { + EntryTransformer transformer, Entry entry) { checkNotNull(transformer); checkNotNull(entry); return new AbstractMapEntry() { @@ -2029,14 +2081,9 @@ public V2 getValue() { /** Views an entry transformer as a function from entries to entries. */ static Function, Entry> asEntryToEntryFunction( - final EntryTransformer transformer) { + EntryTransformer transformer) { checkNotNull(transformer); - return new Function, Entry>() { - @Override - public Entry apply(final Entry entry) { - return transformEntry(transformer, entry); - } - }; + return entry -> transformEntry(transformer, entry); } static class TransformedEntriesMap< @@ -2057,15 +2104,14 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return fromMap.containsKey(key); } // safe as long as the user followed the Warning in the javadoc @SuppressWarnings("unchecked") @Override - @CheckForNull - public V2 get(@CheckForNull Object key) { + public @Nullable V2 get(@Nullable Object key) { V1 value = fromMap.get(key); if (value != null || fromMap.containsKey(key)) { // The cast is safe because of the containsKey check. @@ -2077,8 +2123,7 @@ public V2 get(@CheckForNull Object key) { // safe as long as the user followed the Warning in the javadoc @SuppressWarnings("unchecked") @Override - @CheckForNull - public V2 remove(@CheckForNull Object key) { + public @Nullable V2 remove(@Nullable Object key) { return fromMap.containsKey(key) // The cast is safe because of the containsKey check. ? transformer.transformEntry((K) key, uncheckedCastNullableTToT(fromMap.remove(key))) @@ -2121,8 +2166,7 @@ protected SortedMap fromMap() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return fromMap().comparator(); } @@ -2165,14 +2209,12 @@ private static class TransformedEntriesNavigableMap< } @Override - @CheckForNull - public Entry ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry ceilingEntry(@ParametricNullness K key) { return transformEntry(fromMap().ceilingEntry(key)); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return fromMap().ceilingKey(key); } @@ -2187,20 +2229,17 @@ public NavigableMap descendingMap() { } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return transformEntry(fromMap().firstEntry()); } @Override - @CheckForNull - public Entry floorEntry(@ParametricNullness K key) { + public @Nullable Entry floorEntry(@ParametricNullness K key) { return transformEntry(fromMap().floorEntry(key)); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return fromMap().floorKey(key); } @@ -2215,32 +2254,27 @@ public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusiv } @Override - @CheckForNull - public Entry higherEntry(@ParametricNullness K key) { + public @Nullable Entry higherEntry(@ParametricNullness K key) { return transformEntry(fromMap().higherEntry(key)); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return fromMap().higherKey(key); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return transformEntry(fromMap().lastEntry()); } @Override - @CheckForNull - public Entry lowerEntry(@ParametricNullness K key) { + public @Nullable Entry lowerEntry(@ParametricNullness K key) { return transformEntry(fromMap().lowerEntry(key)); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return fromMap().lowerKey(key); } @@ -2250,14 +2284,12 @@ public NavigableSet navigableKeySet() { } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return transformEntry(fromMap().pollFirstEntry()); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return transformEntry(fromMap().pollLastEntry()); } @@ -2286,8 +2318,7 @@ public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclus return transformEntries(fromMap().tailMap(fromKey, inclusive), transformer); } - @CheckForNull - private Entry transformEntry(@CheckForNull Entry entry) { + private @Nullable Entry transformEntry(@Nullable Entry entry) { return (entry == null) ? null : Maps.transformEntry(transformer, entry); } @@ -2331,7 +2362,7 @@ protected NavigableMap fromMap() { * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. */ public static Map filterKeys( - Map unfiltered, final Predicate keyPredicate) { + Map unfiltered, Predicate keyPredicate) { checkNotNull(keyPredicate); Predicate> entryPredicate = keyPredicateOnEntries(keyPredicate); return (unfiltered instanceof AbstractFilteredMap) @@ -2366,7 +2397,7 @@ protected NavigableMap fromMap() { * @since 11.0 */ public static SortedMap filterKeys( - SortedMap unfiltered, final Predicate keyPredicate) { + SortedMap unfiltered, Predicate keyPredicate) { // TODO(lowasser): Return a subclass of Maps.FilteredKeyMap for slightly better // performance. return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); @@ -2401,7 +2432,7 @@ protected NavigableMap fromMap() { @GwtIncompatible // NavigableMap public static NavigableMap filterKeys( - NavigableMap unfiltered, final Predicate keyPredicate) { + NavigableMap unfiltered, Predicate keyPredicate) { // TODO(lowasser): Return a subclass of Maps.FilteredKeyMap for slightly better // performance. return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); @@ -2432,7 +2463,7 @@ NavigableMap filterKeys( * @since 14.0 */ public static BiMap filterKeys( - BiMap unfiltered, final Predicate keyPredicate) { + BiMap unfiltered, Predicate keyPredicate) { checkNotNull(keyPredicate); return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); } @@ -2461,7 +2492,7 @@ NavigableMap filterKeys( * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. */ public static Map filterValues( - Map unfiltered, final Predicate valuePredicate) { + Map unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2493,7 +2524,7 @@ NavigableMap filterKeys( */ public static SortedMap filterValues( - SortedMap unfiltered, final Predicate valuePredicate) { + SortedMap unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2526,7 +2557,7 @@ SortedMap filterValues( @GwtIncompatible // NavigableMap public static NavigableMap filterValues( - NavigableMap unfiltered, final Predicate valuePredicate) { + NavigableMap unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2558,7 +2589,7 @@ NavigableMap filterValues( * @since 14.0 */ public static BiMap filterValues( - BiMap unfiltered, final Predicate valuePredicate) { + BiMap unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2757,17 +2788,16 @@ private abstract static class AbstractFilteredMap< this.predicate = predicate; } - boolean apply(@CheckForNull Object key, @ParametricNullness V value) { + boolean apply(@Nullable Object key, @ParametricNullness V value) { // This method is called only when the key is in the map (or about to be added to the map), // implying that key is a K. @SuppressWarnings({"unchecked", "nullness"}) K k = (K) key; - return predicate.apply(Maps.immutableEntry(k, value)); + return predicate.apply(immutableEntry(k, value)); } @Override - @CheckForNull - public V put(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { checkArgument(apply(key, value)); return unfiltered.put(key, value); } @@ -2781,13 +2811,12 @@ public void putAll(Map map) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return unfiltered.containsKey(key) && apply(key, unfiltered.get(key)); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { V value = unfiltered.get(key); return ((value != null) && apply(key, value)) ? value : null; } @@ -2798,8 +2827,7 @@ public boolean isEmpty() { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { return containsKey(key) ? unfiltered.remove(key) : null; } @@ -2823,7 +2851,7 @@ private static final class FilteredMapValues< } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { Iterator> entryItr = unfiltered.entrySet().iterator(); while (entryItr.hasNext()) { Entry entry = entryItr.next(); @@ -2902,7 +2930,7 @@ Set createKeySet() { // that key is a K. @Override @SuppressWarnings("unchecked") - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return unfiltered.containsKey(key) && keyPredicate.apply((K) key); } } @@ -2936,7 +2964,7 @@ protected Set> delegate() { public Iterator> iterator() { return new TransformedIterator, Entry>(filteredEntrySet.iterator()) { @Override - Entry transform(final Entry entry) { + Entry transform(Entry entry) { return new ForwardingMapEntry() { @Override protected Entry delegate() { @@ -2995,7 +3023,7 @@ class KeySet extends Maps.KeySet { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (containsKey(o)) { unfiltered.remove(o); return true; @@ -3053,8 +3081,7 @@ SortedSet createKeySet() { @WeakOuter class SortedKeySet extends KeySet implements SortedSet { @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return sortedMap().comparator(); } @@ -3088,8 +3115,7 @@ public K last() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return sortedMap().comparator(); } @@ -3153,8 +3179,7 @@ private static class FilteredEntryNavigableMap< } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return unfiltered.comparator(); } @@ -3199,25 +3224,22 @@ public boolean isEmpty() { } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { return filteredDelegate.get(key); } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return filteredDelegate.containsKey(key); } @Override - @CheckForNull - public V put(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V put(@ParametricNullness K key, @ParametricNullness V value) { return filteredDelegate.put(key, value); } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { return filteredDelegate.remove(key); } @@ -3237,14 +3259,12 @@ public Set> entrySet() { } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return Iterables.removeFirstMatching(unfiltered.entrySet(), entryPredicate); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return Iterables.removeFirstMatching(unfiltered.descendingMap().entrySet(), entryPredicate); } @@ -3279,14 +3299,8 @@ static final class FilteredEntryBiMap inverse; private static - Predicate> inversePredicate( - final Predicate> forwardPredicate) { - return new Predicate>() { - @Override - public boolean apply(Entry input) { - return forwardPredicate.apply(Maps.immutableEntry(input.getValue(), input.getKey())); - } - }; + Predicate> inversePredicate(Predicate> forwardPredicate) { + return input -> forwardPredicate.apply(immutableEntry(input.getValue(), input.getKey())); } FilteredEntryBiMap(BiMap delegate, Predicate> predicate) { @@ -3306,8 +3320,7 @@ BiMap unfiltered() { } @Override - @CheckForNull - public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) { checkArgument(apply(key, value)); return unfiltered().forcePut(key, value); } @@ -3354,9 +3367,8 @@ NavigableMap unmodifiableNavigableMap(NavigableMap map) { } } - @CheckForNull private static - Entry unmodifiableOrNull(@CheckForNull Entry entry) { + @Nullable Entry unmodifiableOrNull(@Nullable Entry entry) { return (entry == null) ? null : Maps.unmodifiableEntry(entry); } @@ -3381,78 +3393,66 @@ protected SortedMap delegate() { } @Override - @CheckForNull - public Entry lowerEntry(@ParametricNullness K key) { + public @Nullable Entry lowerEntry(@ParametricNullness K key) { return unmodifiableOrNull(delegate.lowerEntry(key)); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return delegate.lowerKey(key); } @Override - @CheckForNull - public Entry floorEntry(@ParametricNullness K key) { + public @Nullable Entry floorEntry(@ParametricNullness K key) { return unmodifiableOrNull(delegate.floorEntry(key)); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return delegate.floorKey(key); } @Override - @CheckForNull - public Entry ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry ceilingEntry(@ParametricNullness K key) { return unmodifiableOrNull(delegate.ceilingEntry(key)); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return delegate.ceilingKey(key); } @Override - @CheckForNull - public Entry higherEntry(@ParametricNullness K key) { + public @Nullable Entry higherEntry(@ParametricNullness K key) { return unmodifiableOrNull(delegate.higherEntry(key)); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return delegate.higherKey(key); } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return unmodifiableOrNull(delegate.firstEntry()); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return unmodifiableOrNull(delegate.lastEntry()); } @Override - @CheckForNull - public final Entry pollFirstEntry() { + public final @Nullable Entry pollFirstEntry() { throw new UnsupportedOperationException(); } @Override - @CheckForNull - public final Entry pollLastEntry() { + public final @Nullable Entry pollLastEntry() { throw new UnsupportedOperationException(); } - @CheckForNull private transient UnmodifiableNavigableMap descendingMap; + @LazyInit private transient @Nullable UnmodifiableNavigableMap descendingMap; @Override public NavigableMap descendingMap() { @@ -3563,6 +3563,7 @@ public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusi * @since 13.0 */ @GwtIncompatible // NavigableMap + @J2ktIncompatible // Synchronized public static NavigableMap synchronizedNavigableMap(NavigableMap navigableMap) { return Synchronized.navigableMap(navigableMap); @@ -3582,7 +3583,7 @@ abstract static class ViewCachingAbstractMap< */ abstract Set> createEntrySet(); - @CheckForNull private transient Set> entrySet; + @LazyInit private transient @Nullable Set> entrySet; @Override public Set> entrySet() { @@ -3590,7 +3591,7 @@ public Set> entrySet() { return (result == null) ? entrySet = createEntrySet() : result; } - @CheckForNull private transient Set keySet; + @LazyInit private transient @Nullable Set keySet; @Override public Set keySet() { @@ -3602,7 +3603,7 @@ Set createKeySet() { return new KeySet<>(this); } - @CheckForNull private transient Collection values; + @LazyInit private transient @Nullable Collection values; @Override public Collection values() { @@ -3648,8 +3649,7 @@ public void clear() { * Delegates to {@link Map#get}. Returns {@code null} on {@code ClassCastException} and {@code * NullPointerException}. */ - @CheckForNull - static V safeGet(Map map, @CheckForNull Object key) { + static @Nullable V safeGet(Map map, @Nullable Object key) { checkNotNull(map); try { return map.get(key); @@ -3662,7 +3662,7 @@ public void clear() { * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code ClassCastException} and * {@code NullPointerException}. */ - static boolean safeContainsKey(Map map, @CheckForNull Object key) { + static boolean safeContainsKey(Map map, @Nullable Object key) { checkNotNull(map); try { return map.containsKey(key); @@ -3675,8 +3675,7 @@ static boolean safeContainsKey(Map map, @CheckForNull Object key) { * Delegates to {@link Map#remove}. Returns {@code null} on {@code ClassCastException} and {@code * NullPointerException}. */ - @CheckForNull - static V safeRemove(Map map, @CheckForNull Object key) { + static @Nullable V safeRemove(Map map, @Nullable Object key) { checkNotNull(map); try { return map.remove(key); @@ -3686,12 +3685,12 @@ static boolean safeContainsKey(Map map, @CheckForNull Object key) { } /** An admittedly inefficient implementation of {@link Map#containsKey}. */ - static boolean containsKeyImpl(Map map, @CheckForNull Object key) { + static boolean containsKeyImpl(Map map, @Nullable Object key) { return Iterators.contains(keyIterator(map.entrySet().iterator()), key); } /** An implementation of {@link Map#containsValue}. */ - static boolean containsValueImpl(Map map, @CheckForNull Object value) { + static boolean containsValueImpl(Map map, @Nullable Object value) { return Iterators.contains(valueIterator(map.entrySet().iterator()), value); } @@ -3708,7 +3707,7 @@ static boolean containsValueImpl(Map map, @CheckForNull Object value) { * @return {@code true} if {@code c} contains {@code o} */ static boolean containsEntryImpl( - Collection> c, @CheckForNull Object o) { + Collection> c, @Nullable Object o) { if (!(o instanceof Entry)) { return false; } @@ -3727,7 +3726,7 @@ static boolean containsValueImpl(Map map, @CheckForNull Object value) { * @return {@code true} if {@code c} was changed */ static boolean removeEntryImpl( - Collection> c, @CheckForNull Object o) { + Collection> c, @Nullable Object o) { if (!(o instanceof Entry)) { return false; } @@ -3735,7 +3734,7 @@ static boolean containsValueImpl(Map map, @CheckForNull Object value) { } /** An implementation of {@link Map#equals}. */ - static boolean equalsImpl(Map map, @CheckForNull Object object) { + static boolean equalsImpl(Map map, @Nullable Object object) { if (map == object) { return true; } else if (object instanceof Map) { @@ -3795,12 +3794,12 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return map().containsKey(o); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (contains(o)) { map().remove(o); return true; @@ -3814,13 +3813,11 @@ public void clear() { } } - @CheckForNull - static K keyOrNull(@CheckForNull Entry entry) { + static @Nullable K keyOrNull(@Nullable Entry entry) { return (entry == null) ? null : entry.getKey(); } - @CheckForNull - static V valueOrNull(@CheckForNull Entry entry) { + static @Nullable V valueOrNull(@Nullable Entry entry) { return (entry == null) ? null : entry.getValue(); } @@ -3836,8 +3833,7 @@ SortedMap map() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return map().comparator(); } @@ -3882,38 +3878,32 @@ NavigableMap map() { } @Override - @CheckForNull - public K lower(@ParametricNullness K e) { + public @Nullable K lower(@ParametricNullness K e) { return map().lowerKey(e); } @Override - @CheckForNull - public K floor(@ParametricNullness K e) { + public @Nullable K floor(@ParametricNullness K e) { return map().floorKey(e); } @Override - @CheckForNull - public K ceiling(@ParametricNullness K e) { + public @Nullable K ceiling(@ParametricNullness K e) { return map().ceilingKey(e); } @Override - @CheckForNull - public K higher(@ParametricNullness K e) { + public @Nullable K higher(@ParametricNullness K e) { return map().higherKey(e); } @Override - @CheckForNull - public K pollFirst() { + public @Nullable K pollFirst() { return keyOrNull(map().pollFirstEntry()); } @Override - @CheckForNull - public K pollLast() { + public @Nullable K pollLast() { return keyOrNull(map().pollLastEntry()); } @@ -3980,7 +3970,7 @@ public Iterator iterator() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { try { return super.remove(o); } catch (UnsupportedOperationException e) { @@ -3999,7 +3989,7 @@ public boolean removeAll(Collection c) { try { return super.removeAll(checkNotNull(c)); } catch (UnsupportedOperationException e) { - Set toRemove = Sets.newHashSet(); + Set toRemove = newHashSet(); for (Entry entry : map().entrySet()) { if (c.contains(entry.getValue())) { toRemove.add(entry.getKey()); @@ -4014,7 +4004,7 @@ public boolean retainAll(Collection c) { try { return super.retainAll(checkNotNull(c)); } catch (UnsupportedOperationException e) { - Set toRetain = Sets.newHashSet(); + Set toRetain = newHashSet(); for (Entry entry : map().entrySet()) { if (c.contains(entry.getValue())) { toRetain.add(entry.getKey()); @@ -4035,7 +4025,7 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return map().containsValue(o); } @@ -4060,7 +4050,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Entry) { Entry entry = (Entry) o; Object key = entry.getKey(); @@ -4076,7 +4066,7 @@ public boolean isEmpty() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { /* * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our * nullness checker. @@ -4131,7 +4121,7 @@ protected final Map delegate() { return forward(); } - @CheckForNull private transient Comparator comparator; + @LazyInit private transient @Nullable Comparator comparator; @SuppressWarnings("unchecked") @Override @@ -4165,74 +4155,62 @@ public K lastKey() { } @Override - @CheckForNull - public Entry lowerEntry(@ParametricNullness K key) { + public @Nullable Entry lowerEntry(@ParametricNullness K key) { return forward().higherEntry(key); } @Override - @CheckForNull - public K lowerKey(@ParametricNullness K key) { + public @Nullable K lowerKey(@ParametricNullness K key) { return forward().higherKey(key); } @Override - @CheckForNull - public Entry floorEntry(@ParametricNullness K key) { + public @Nullable Entry floorEntry(@ParametricNullness K key) { return forward().ceilingEntry(key); } @Override - @CheckForNull - public K floorKey(@ParametricNullness K key) { + public @Nullable K floorKey(@ParametricNullness K key) { return forward().ceilingKey(key); } @Override - @CheckForNull - public Entry ceilingEntry(@ParametricNullness K key) { + public @Nullable Entry ceilingEntry(@ParametricNullness K key) { return forward().floorEntry(key); } @Override - @CheckForNull - public K ceilingKey(@ParametricNullness K key) { + public @Nullable K ceilingKey(@ParametricNullness K key) { return forward().floorKey(key); } @Override - @CheckForNull - public Entry higherEntry(@ParametricNullness K key) { + public @Nullable Entry higherEntry(@ParametricNullness K key) { return forward().lowerEntry(key); } @Override - @CheckForNull - public K higherKey(@ParametricNullness K key) { + public @Nullable K higherKey(@ParametricNullness K key) { return forward().lowerKey(key); } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return forward().lastEntry(); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return forward().firstEntry(); } @Override - @CheckForNull - public Entry pollFirstEntry() { + public @Nullable Entry pollFirstEntry() { return forward().pollLastEntry(); } @Override - @CheckForNull - public Entry pollLastEntry() { + public @Nullable Entry pollLastEntry() { return forward().pollFirstEntry(); } @@ -4241,7 +4219,7 @@ public NavigableMap descendingMap() { return forward(); } - @CheckForNull private transient Set> entrySet; + @LazyInit private transient @Nullable Set> entrySet; @Override public Set> entrySet() { @@ -4272,7 +4250,7 @@ public Set keySet() { return navigableKeySet(); } - @CheckForNull private transient NavigableSet navigableKeySet; + @LazyInit private transient @Nullable NavigableSet navigableKeySet; @Override public NavigableSet navigableKeySet() { @@ -4337,7 +4315,7 @@ static ImmutableMap indexMap(Collection list) { for (E e : list) { builder.put(e, i++); } - return builder.build(); + return builder.buildOrThrow(); } /** @@ -4356,7 +4334,6 @@ static ImmutableMap indexMap(Collection list) { * * @since 20.0 */ - @Beta @GwtIncompatible // NavigableMap public static , V extends @Nullable Object> NavigableMap subMap(NavigableMap map, Range range) { diff --git a/android/guava/src/com/google/common/collect/MinMaxPriorityQueue.java b/android/guava/src/com/google/common/collect/MinMaxPriorityQueue.java index 181b3fbb50b3..d365ffb718a9 100644 --- a/android/guava/src/com/google/common/collect/MinMaxPriorityQueue.java +++ b/android/guava/src/com/google/common/collect/MinMaxPriorityQueue.java @@ -21,10 +21,13 @@ import static com.google.common.base.Preconditions.checkPositionIndex; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.CollectPreconditions.checkRemove; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.lang.System.arraycopy; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.math.IntMath; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -42,8 +45,7 @@ import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A double-ended priority queue, which provides constant-time access to both its least element and @@ -98,9 +100,7 @@ * @author Torbjorn Gannholm * @since 8.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class MinMaxPriorityQueue extends AbstractQueue { /** @@ -108,7 +108,7 @@ public final class MinMaxPriorityQueue extends AbstractQueue { * initial contents, and an initial expected size of 11. */ public static > MinMaxPriorityQueue create() { - return new Builder(Ordering.natural()).create(); + return new Builder>(Ordering.natural()).create(); } /** @@ -138,6 +138,7 @@ public static Builder orderedBy(Comparator comparator) { * Creates and returns a new builder, configured to build {@code MinMaxPriorityQueue} instances * sized appropriately to hold {@code expectedSize} elements. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static Builder expectedSize(int expectedSize) { return new Builder(Ordering.natural()).expectedSize(expectedSize); } @@ -148,6 +149,7 @@ public static Builder expectedSize(int expectedSize) { * immediately removes its greatest element (according to its comparator), which might be the * element that was just added. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static Builder maximumSize(int maximumSize) { return new Builder(Ordering.natural()).maximumSize(maximumSize); } @@ -162,7 +164,6 @@ public static Builder maximumSize(int maximumSize) { * Queue} but not a {@code Queue}). * @since 8.0 */ - @Beta public static final class Builder { /* * TODO(kevinb): when the dust settles, see if we still need this or can @@ -301,8 +302,7 @@ public boolean offer(E element) { @CanIgnoreReturnValue @Override - @CheckForNull - public E poll() { + public @Nullable E poll() { return isEmpty() ? null : removeAndGet(0); } @@ -316,8 +316,7 @@ E elementData(int index) { } @Override - @CheckForNull - public E peek() { + public @Nullable E peek() { return isEmpty() ? null : elementData(0); } @@ -340,8 +339,7 @@ private int getMaxElementIndex() { * empty. */ @CanIgnoreReturnValue - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return poll(); } @@ -359,8 +357,7 @@ public E removeFirst() { * Retrieves, but does not remove, the least element of this queue, or returns {@code null} if the * queue is empty. */ - @CheckForNull - public E peekFirst() { + public @Nullable E peekFirst() { return peek(); } @@ -369,8 +366,7 @@ public E peekFirst() { * empty. */ @CanIgnoreReturnValue - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return isEmpty() ? null : removeAndGet(getMaxElementIndex()); } @@ -391,8 +387,7 @@ public E removeLast() { * Retrieves, but does not remove, the greatest element of this queue, or returns {@code null} if * the queue is empty. */ - @CheckForNull - public E peekLast() { + public @Nullable E peekLast() { return isEmpty() ? null : elementData(getMaxElementIndex()); } @@ -411,8 +406,7 @@ public E peekLast() { */ @VisibleForTesting @CanIgnoreReturnValue - @CheckForNull - MoveDesc removeAt(int index) { + @Nullable MoveDesc removeAt(int index) { checkPositionIndex(index, size); modCount++; size--; @@ -447,8 +441,7 @@ MoveDesc removeAt(int index) { return changes; } - @CheckForNull - private MoveDesc fillHole(int index, E toTrickle) { + private @Nullable MoveDesc fillHole(int index, E toTrickle) { Heap heap = heapForIndex(index); // We consider elementData(index) a "hole", and we want to fill it // with the last element of the heap, toTrickle. @@ -518,14 +511,17 @@ boolean isIntact() { } /** - * Each instance of MinMaxPriortyQueue encapsulates two instances of Heap: a min-heap and a + * Each instance of MinMaxPriorityQueue encapsulates two instances of Heap: a min-heap and a * max-heap. Conceptually, these might each have their own array for storage, but for efficiency's * sake they are stored interleaved on alternate heap levels in the same array (MMPQ.queue). */ @WeakOuter - private class Heap { + class Heap { final Ordering ordering; - @Weak Heap otherHeap; // always initialized immediately after construction + + @SuppressWarnings("nullness:initialization.field.uninitialized") + @Weak + Heap otherHeap; // always initialized immediately after construction Heap(Ordering ordering) { this.ordering = ordering; @@ -539,8 +535,7 @@ int compareElements(int a, int b) { * Tries to move {@code toTrickle} from a min to a max level and bubble up there. If it moved * before {@code removeIndex} this method returns a pair as described in {@link #removeAt}. */ - @CheckForNull - MoveDesc tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) { + @Nullable MoveDesc tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) { int crossOver = crossOver(vacated, toTrickle); if (crossOver == vacated) { return null; @@ -608,7 +603,7 @@ int findMin(int index, int len) { return -1; } checkState(index > 0); - int limit = Math.min(index, size - len) + len; + int limit = min(index, size - len) + len; int minIndex = index; for (int i = index + 1; i < limit; i++) { if (compareElements(i, minIndex) < 0) { @@ -644,17 +639,18 @@ int crossOverUp(int index, E x) { int parentIndex = getParentIndex(index); E parentElement = elementData(parentIndex); if (parentIndex != 0) { - // This is a guard for the case of the childless uncle. - // Since the end of the array is actually the middle of the heap, - // a smaller childless uncle can become a child of x when we - // bubble up alternate levels, violating the invariant. + /* + * This is a guard for the case of the childless aunt node. Since the end of the array is + * actually the middle of the heap, a smaller childless aunt node can become a child of x + * when we bubble up alternate levels, violating the invariant. + */ int grandparentIndex = getParentIndex(parentIndex); - int uncleIndex = getRightChildIndex(grandparentIndex); - if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) { - E uncleElement = elementData(uncleIndex); - if (ordering.compare(uncleElement, parentElement) < 0) { - parentIndex = uncleIndex; - parentElement = uncleElement; + int auntIndex = getRightChildIndex(grandparentIndex); + if (auntIndex != parentIndex && getLeftChildIndex(auntIndex) >= size) { + E auntElement = elementData(auntIndex); + if (ordering.compare(auntElement, parentElement) < 0) { + parentIndex = auntIndex; + parentElement = auntElement; } } } @@ -667,26 +663,30 @@ int crossOverUp(int index, E x) { return index; } + // About the term "aunt node": it's better to leave gender out of it, but for this the English + // language has nothing for us. Except for the whimsical neologism "pibling" (!) which we + // obviously could not expect to increase anyone's understanding of the code. + /** * Swap {@code actualLastElement} with the conceptually correct last element of the heap. * Returns the index that {@code actualLastElement} now resides in. * *

    Since the last element of the array is actually in the middle of the sorted structure, a - * childless uncle node could be smaller, which would corrupt the invariant if this element - * becomes the new parent of the uncle. In that case, we first switch the last element with its - * uncle, before returning. + * childless aunt node could be smaller, which would corrupt the invariant if this element + * becomes the new parent of the aunt node. In that case, we first switch the last element with + * its aunt node, before returning. */ int swapWithConceptuallyLastElement(E actualLastElement) { int parentIndex = getParentIndex(size); if (parentIndex != 0) { int grandparentIndex = getParentIndex(parentIndex); - int uncleIndex = getRightChildIndex(grandparentIndex); - if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) { - E uncleElement = elementData(uncleIndex); - if (ordering.compare(uncleElement, actualLastElement) < 0) { - queue[uncleIndex] = actualLastElement; - queue[size] = uncleElement; - return uncleIndex; + int auntIndex = getRightChildIndex(grandparentIndex); + if (auntIndex != parentIndex && getLeftChildIndex(auntIndex) >= size) { + E auntElement = elementData(auntIndex); + if (ordering.compare(auntElement, actualLastElement) < 0) { + queue[auntIndex] = actualLastElement; + queue[size] = auntElement; + return auntIndex; } } } @@ -773,9 +773,9 @@ private class QueueIterator implements Iterator { private int expectedModCount = modCount; // The same element is not allowed in both forgetMeNot and skipMe, but duplicates are allowed in // either of them, up to the same multiplicity as the queue. - @CheckForNull private Queue forgetMeNot; - @CheckForNull private List skipMe; - @CheckForNull private E lastFromForgetMeNot; + private @Nullable Queue forgetMeNot; + private @Nullable List skipMe; + private @Nullable E lastFromForgetMeNot; private boolean canRemove; @Override @@ -911,9 +911,10 @@ public void clear() { } @Override + @J2ktIncompatible // Incompatible return type change. Use inherited (unoptimized) implementation public Object[] toArray() { Object[] copyTo = new Object[size]; - System.arraycopy(queue, 0, copyTo, 0, size); + arraycopy(queue, 0, copyTo, 0, size); return copyTo; } @@ -947,7 +948,7 @@ static int initialQueueSize( // Enlarge to contain initial contents if (initialContents instanceof Collection) { int initialSize = ((Collection) initialContents).size(); - result = Math.max(result, initialSize); + result = max(result, initialSize); } // Now cap it at maxSize + 1 @@ -958,7 +959,7 @@ private void growIfNeeded() { if (size > queue.length) { int newCapacity = calculateNewCapacity(); Object[] newQueue = new Object[newCapacity]; - System.arraycopy(queue, 0, newQueue, 0, queue.length); + arraycopy(queue, 0, newQueue, 0, queue.length); queue = newQueue; } } @@ -973,6 +974,6 @@ private int calculateNewCapacity() { /** There's no reason for the queueSize to ever be more than maxSize + 1 */ private static int capAtMaximumSize(int queueSize, int maximumSize) { - return Math.min(queueSize - 1, maximumSize) + 1; // don't overflow + return min(queueSize - 1, maximumSize) + 1; // don't overflow } } diff --git a/android/guava/src/com/google/common/collect/MoreCollectors.java b/android/guava/src/com/google/common/collect/MoreCollectors.java new file mode 100644 index 000000000000..905bf14e8ec7 --- /dev/null +++ b/android/guava/src/com/google/common/collect/MoreCollectors.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.emptyList; + +import com.google.common.annotations.GwtCompatible; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; + +/** + * Collectors not present in {@code java.util.stream.Collectors} that are not otherwise associated + * with a {@code com.google.common} type. + * + * @author Louis Wasserman + * @since 33.2.0 (available since 21.0 in guava-jre) + */ +@GwtCompatible +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // Users will use this only if they're already using streams. +public final class MoreCollectors { + + /* + * TODO(lowasser): figure out if we can convert this to a concurrent AtomicReference-based + * collector without breaking j2cl? + */ + private static final Collector> TO_OPTIONAL = + Collector.of( + ToOptionalState::new, + ToOptionalState::add, + ToOptionalState::combine, + ToOptionalState::getOptional, + Collector.Characteristics.UNORDERED); + + /** + * A collector that converts a stream of zero or one elements to an {@code Optional}. + * + * @throws IllegalArgumentException if the stream consists of two or more elements. + * @throws NullPointerException if any element in the stream is {@code null}. + * @return {@code Optional.of(onlyElement)} if the stream has exactly one element (must not be + * {@code null}) and returns {@code Optional.empty()} if it has none. + */ + @SuppressWarnings("unchecked") + public static Collector> toOptional() { + return (Collector) TO_OPTIONAL; + } + + private static final Object NULL_PLACEHOLDER = new Object(); + + private static final Collector<@Nullable Object, ?, @Nullable Object> ONLY_ELEMENT = + Collector.<@Nullable Object, ToOptionalState, @Nullable Object>of( + ToOptionalState::new, + (state, o) -> state.add((o == null) ? NULL_PLACEHOLDER : o), + ToOptionalState::combine, + state -> { + Object result = state.getElement(); + return (result == NULL_PLACEHOLDER) ? null : result; + }, + Collector.Characteristics.UNORDERED); + + /** + * A collector that takes a stream containing exactly one element and returns that element. The + * returned collector throws an {@code IllegalArgumentException} if the stream consists of two or + * more elements, and a {@code NoSuchElementException} if the stream is empty. + */ + @SuppressWarnings("unchecked") + public static Collector onlyElement() { + return (Collector) ONLY_ELEMENT; + } + + /** + * This atrocity is here to let us report several of the elements in the stream if there were more + * than one, not just two. + */ + private static final class ToOptionalState { + static final int MAX_EXTRAS = 4; + + @Nullable Object element; + List extras; + + ToOptionalState() { + element = null; + extras = emptyList(); + } + + IllegalArgumentException multiples(boolean overflow) { + StringBuilder sb = + new StringBuilder().append("expected one element but was: <").append(element); + for (Object o : extras) { + sb.append(", ").append(o); + } + if (overflow) { + sb.append(", ..."); + } + sb.append('>'); + throw new IllegalArgumentException(sb.toString()); + } + + void add(Object o) { + checkNotNull(o); + if (element == null) { + this.element = o; + } else if (extras.isEmpty()) { + // Replace immutable empty list with mutable list. + extras = new ArrayList<>(MAX_EXTRAS); + extras.add(o); + } else if (extras.size() < MAX_EXTRAS) { + extras.add(o); + } else { + throw multiples(true); + } + } + + ToOptionalState combine(ToOptionalState other) { + if (element == null) { + return other; + } else if (other.element == null) { + return this; + } else { + if (extras.isEmpty()) { + // Replace immutable empty list with mutable list. + extras = new ArrayList<>(); + } + extras.add(other.element); + extras.addAll(other.extras); + if (extras.size() > MAX_EXTRAS) { + extras.subList(MAX_EXTRAS, extras.size()).clear(); + throw multiples(true); + } + return this; + } + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + Optional getOptional() { + if (extras.isEmpty()) { + return Optional.ofNullable(element); + } else { + throw multiples(false); + } + } + + Object getElement() { + if (element == null) { + throw new NoSuchElementException(); + } else if (extras.isEmpty()) { + return element; + } else { + throw multiples(false); + } + } + } + + private MoreCollectors() {} +} diff --git a/android/guava/src/com/google/common/collect/Multimap.java b/android/guava/src/com/google/common/collect/Multimap.java index 09aa2875b3b1..a02c6ea6f6a2 100644 --- a/android/guava/src/com/google/common/collect/Multimap.java +++ b/android/guava/src/com/google/common/collect/Multimap.java @@ -25,8 +25,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A collection that maps keys to values, similar to {@link Map}, but in which each key may be @@ -132,13 +131,16 @@ * *

    Implementations

    * - *

    As always, prefer the immutable implementations, {@link ImmutableListMultimap} and {@link - * ImmutableSetMultimap}. General-purpose mutable implementations are listed above under "All Known - * Implementing Classes". You can also create a custom multimap, backed by any {@code Map} - * and {@link Collection} types, using the {@link Multimaps#newMultimap Multimaps.newMultimap} - * family of methods. Finally, another popular way to obtain a multimap is using {@link - * Multimaps#index Multimaps.index}. See the {@link Multimaps} class for these and other static - * utilities related to multimaps. + *

      + *
    • {@link ImmutableListMultimap} + *
    • {@link ImmutableSetMultimap} + *
    • Configure your own mutable multimap with {@link MultimapBuilder} + *
    • {@link LinkedListMultimap} (for one unusual kind of mutable {@code Multimap}) + *
    + * + * Guava contains a number of other multimap implementations, such as {@link ArrayListMultimap}. In + * new code, we recommend using {@link MultimapBuilder} instead: It provides better control of how + * keys and values are stored. * *

    Other Notes

    * @@ -151,15 +153,13 @@ * {@link UnsupportedOperationException}. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @since 2.0 */ @DoNotMock("Use ImmutableMultimap, HashMultimap, or another implementation") @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Multimap { // Query Operations @@ -182,21 +182,20 @@ public interface Multimap removeAll(@CompatibleWith("K") @CheckForNull Object key); + Collection removeAll(@CompatibleWith("K") @Nullable Object key); /** Removes all key-value pairs from the multimap, leaving it {@linkplain #isEmpty empty}. */ void clear(); @@ -356,7 +354,7 @@ boolean remove( * multimaps are equal, because they both have empty {@link #asMap} views. */ @Override - boolean equals(@CheckForNull Object obj); + boolean equals(@Nullable Object obj); /** * Returns the hash code for this multimap. diff --git a/android/guava/src/com/google/common/collect/MultimapBuilder.java b/android/guava/src/com/google/common/collect/MultimapBuilder.java index 3d6278aaec9d..fb829d53d9a2 100644 --- a/android/guava/src/com/google/common/collect/MultimapBuilder.java +++ b/android/guava/src/com/google/common/collect/MultimapBuilder.java @@ -34,20 +34,17 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** - * A builder for a multimap implementation that allows customization of the backing map and value - * collection implementations used in a particular multimap. - * - *

    This can be used to easily configure multimap data structure implementations not provided - * explicitly in {@code com.google.common.collect}, for example: + * An immutable builder for {@link Multimap} instances, letting you independently select the desired + * behaviors (for example, ordering) of the backing map and value-collections. Example: * *

    {@code
    - * ListMultimap treeListMultimap =
    - *     MultimapBuilder.treeKeys().arrayListValues().build();
    - * SetMultimap hashEnumMultimap =
    - *     MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();
    + * ListMultimap errorsByUser =
    + *     MultimapBuilder.linkedHashKeys().arrayListValues().build();
    + * SortedSetMultimap methodsForName =
    + *     MultimapBuilder.treeKeys().treeSetValues(this::compareMethods).build();
      * }
    * *

    {@code MultimapBuilder} instances are immutable. Invoking a configuration method has no effect @@ -62,7 +59,6 @@ * @since 16.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class MultimapBuilder { /* * Leaving K and V as upper bounds rather than the actual key and value types allows type @@ -207,6 +203,8 @@ private enum LinkedListSupplier implements Supplier> { } @Override + // We recommend against linkedListValues but need to keep it for compatibility. + @SuppressWarnings("JdkObsolete") public List get() { return new LinkedList<>(); } @@ -306,7 +304,15 @@ public abstract static class MultimapBuilderWithKeysPerformance note: {@link ArrayList} and {@link java.util.ArrayDeque} consistently + * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have + * spent a lot of time benchmarking your specific needs, use one of those instead. (However, we + * do not currently offer a {@link Multimap} implementation based on {@link + * java.util.ArrayDeque}.) + */ public ListMultimapBuilder linkedListValues() { return new ListMultimapBuilder() { @Override @@ -433,7 +439,7 @@ public abstract static class ListMultimapBuilder< @Override public ListMultimap build( Multimap multimap) { - return (ListMultimap) super.build(multimap); + return (ListMultimap) super.build(multimap); } } @@ -453,7 +459,7 @@ public abstract static class SetMultimapBuilder< @Override public SetMultimap build( Multimap multimap) { - return (SetMultimap) super.build(multimap); + return (SetMultimap) super.build(multimap); } } @@ -473,7 +479,7 @@ public abstract static class SortedSetMultimapBuilder< @Override public SortedSetMultimap build( Multimap multimap) { - return (SortedSetMultimap) super.build(multimap); + return (SortedSetMultimap) super.build(multimap); } } } diff --git a/android/guava/src/com/google/common/collect/Multimaps.java b/android/guava/src/com/google/common/collect/Multimaps.java index b0577eab1a54..4b4606f2896c 100644 --- a/android/guava/src/com/google/common/collect/Multimaps.java +++ b/android/guava/src/com/google/common/collect/Multimaps.java @@ -22,15 +22,16 @@ import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.Maps.EntryTransformer; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.Weak; import com.google.j2objc.annotations.WeakOuter; @@ -51,14 +52,15 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Collector; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; /** * Provides static methods acting on or generating a {@code Multimap}. * *

    See the Guava User Guide article on {@code + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#multimaps">{@code * Multimaps}. * * @author Jared Levy @@ -68,10 +70,107 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Multimaps { private Multimaps() {} + /** + * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the + * specified supplier. The keys and values of the entries are the result of applying the provided + * mapping functions to the input elements, accumulated in the encounter order of the stream. + * + *

    Example: + * + *

    {@code
    +   * static final ListMultimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(
    +   *             toMultimap(
    +   *                  str -> str.charAt(0),
    +   *                  str -> str.substring(1),
    +   *                  MultimapBuilder.treeKeys().arrayListValues()::build));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final ListMultimap FIRST_LETTER_MULTIMAP;
    +   *
    +   * static {
    +   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.treeKeys().arrayListValues().build();
    +   *     FIRST_LETTER_MULTIMAP.put('b', "anana");
    +   *     FIRST_LETTER_MULTIMAP.put('a', "pple");
    +   *     FIRST_LETTER_MULTIMAP.put('a', "sparagus");
    +   *     FIRST_LETTER_MULTIMAP.put('c', "arrot");
    +   *     FIRST_LETTER_MULTIMAP.put('c', "herry");
    +   * }
    +   * }
    + * + *

    To collect to an {@link ImmutableMultimap}, use either {@link + * ImmutableSetMultimap#toImmutableSetMultimap} or {@link + * ImmutableListMultimap#toImmutableListMultimap}. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap> + Collector toMultimap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction, + java.util.function.Supplier multimapSupplier) { + return CollectCollectors.toMultimap(keyFunction, valueFunction, multimapSupplier); + } + + /** + * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the + * specified supplier. Each input element is mapped to a key and a stream of values, each of which + * are put into the resulting {@code Multimap}, in the encounter order of the stream and the + * encounter order of the streams of values. + * + *

    Example: + * + *

    {@code
    +   * static final ListMultimap FIRST_LETTER_MULTIMAP =
    +   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
    +   *         .collect(
    +   *             flatteningToMultimap(
    +   *                  str -> str.charAt(0),
    +   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c),
    +   *                  MultimapBuilder.linkedHashKeys().arrayListValues()::build));
    +   *
    +   * // is equivalent to
    +   *
    +   * static final ListMultimap FIRST_LETTER_MULTIMAP;
    +   *
    +   * static {
    +   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.linkedHashKeys().arrayListValues().build();
    +   *     FIRST_LETTER_MULTIMAP.putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'));
    +   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('p', 'p', 'l', 'e'));
    +   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'));
    +   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'));
    +   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'));
    +   * }
    +   * }
    + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static < + T extends @Nullable Object, + K extends @Nullable Object, + V extends @Nullable Object, + M extends Multimap> + Collector flatteningToMultimap( + java.util.function.Function keyFunction, + java.util.function.Function> valueFunction, + java.util.function.Supplier multimapSupplier) { + return CollectCollectors.flatteningToMultimap( + keyFunction, valueFunction, multimapSupplier); + } + /** * Creates a new {@code Multimap} backed by {@code map}, whose internal value collections are * generated by {@code factory}. @@ -109,7 +208,7 @@ private Multimaps() {} * @throws IllegalArgumentException if {@code map} is not empty */ public static Multimap newMultimap( - Map> map, final Supplier> factory) { + Map> map, Supplier> factory) { return new CustomMultimap<>(map, factory); } @@ -171,8 +270,11 @@ Collection wrapCollection(@ParametricNullness K key, Collection collection // can't use Serialization writeMultimap and populateMultimap methods since // there's no way to generate the empty backing map. - /** @serialData the factory and the backing map */ + /** + * @serialData the factory and the backing map + */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(factory); @@ -180,16 +282,16 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); + factory = (Supplier>) requireNonNull(stream.readObject()); + Map> map = (Map>) requireNonNull(stream.readObject()); setMap(map); } - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -224,7 +326,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo */ public static ListMultimap newListMultimap( - Map> map, final Supplier> factory) { + Map> map, Supplier> factory) { return new CustomListMultimap<>(map, factory); } @@ -252,8 +354,11 @@ protected List createCollection() { return factory.get(); } - /** @serialData the factory and the backing map */ + /** + * @serialData the factory and the backing map + */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(factory); @@ -261,16 +366,16 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); + factory = (Supplier>) requireNonNull(stream.readObject()); + Map> map = (Map>) requireNonNull(stream.readObject()); setMap(map); } - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -304,7 +409,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo */ public static SetMultimap newSetMultimap( - Map> map, final Supplier> factory) { + Map> map, Supplier> factory) { return new CustomSetMultimap<>(map, factory); } @@ -355,8 +460,11 @@ Collection wrapCollection(@ParametricNullness K key, Collection collection } } - /** @serialData the factory and the backing map */ + /** + * @serialData the factory and the backing map + */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(factory); @@ -364,16 +472,16 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); + factory = (Supplier>) requireNonNull(stream.readObject()); + Map> map = (Map>) requireNonNull(stream.readObject()); setMap(map); } - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -407,7 +515,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo */ public static SortedSetMultimap newSortedSetMultimap( - Map> map, final Supplier> factory) { + Map> map, Supplier> factory) { return new CustomSortedSetMultimap<>(map, factory); } @@ -415,7 +523,7 @@ private static class CustomSortedSetMultimap< K extends @Nullable Object, V extends @Nullable Object> extends AbstractSortedSetMultimap { transient Supplier> factory; - @CheckForNull transient Comparator valueComparator; + transient @Nullable Comparator valueComparator; CustomSortedSetMultimap(Map> map, Supplier> factory) { super(map); @@ -439,13 +547,15 @@ protected SortedSet createCollection() { } @Override - @CheckForNull - public Comparator valueComparator() { + public @Nullable Comparator valueComparator() { return valueComparator; } - /** @serialData the factory and the backing map */ + /** + * @serialData the factory and the backing map + */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(factory); @@ -453,17 +563,17 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); + factory = (Supplier>) requireNonNull(stream.readObject()); valueComparator = factory.get().comparator(); - Map> map = (Map>) stream.readObject(); + Map> map = (Map>) requireNonNull(stream.readObject()); setMap(map); } - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -519,6 +629,7 @@ M invertFrom(Multimap source, M dest) { * @param multimap the multimap to be wrapped in a synchronized view * @return a synchronized view of the specified multimap */ + @J2ktIncompatible // Synchronized public static Multimap synchronizedMultimap(Multimap multimap) { return Synchronized.multimap(multimap, null); @@ -549,6 +660,9 @@ Multimap unmodifiableMultimap(Multimap delegate) { * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(delegate)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static Multimap unmodifiableMultimap(ImmutableMultimap delegate) { return checkNotNull(delegate); @@ -557,13 +671,13 @@ public static Multimap unmodifiableMultimap(ImmutableMultimap private static class UnmodifiableMultimap extends ForwardingMultimap implements Serializable { final Multimap delegate; - @LazyInit @CheckForNull transient Collection> entries; - @LazyInit @CheckForNull transient Multiset keys; - @LazyInit @CheckForNull transient Set keySet; - @LazyInit @CheckForNull transient Collection values; - @LazyInit @CheckForNull transient Map> map; + @LazyInit transient @Nullable Collection> entries; + @LazyInit transient @Nullable Multiset keys; + @LazyInit transient @Nullable Set keySet; + @LazyInit transient @Nullable Collection values; + @LazyInit transient @Nullable Map> map; - UnmodifiableMultimap(final Multimap delegate) { + UnmodifiableMultimap(Multimap delegate) { this.delegate = checkNotNull(delegate); } @@ -584,14 +698,7 @@ public Map> asMap() { result = map = Collections.unmodifiableMap( - Maps.transformValues( - delegate.asMap(), - new Function, Collection>() { - @Override - public Collection apply(Collection collection) { - return unmodifiableValueCollection(collection); - } - })); + Maps.transformValues(delegate.asMap(), Multimaps::unmodifiableValueCollection)); } return result; } @@ -644,12 +751,12 @@ public boolean putAll(Multimap multimap) { } @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { throw new UnsupportedOperationException(); } @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -667,7 +774,7 @@ public Collection values() { return result; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static class UnmodifiableListMultimap< @@ -688,7 +795,7 @@ public List get(@ParametricNullness K key) { } @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -697,7 +804,7 @@ public List replaceValues(@ParametricNullness K key, Iterable va throw new UnsupportedOperationException(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static class UnmodifiableSetMultimap< @@ -727,7 +834,7 @@ public Set> entries() { } @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -736,7 +843,7 @@ public Set replaceValues(@ParametricNullness K key, Iterable val throw new UnsupportedOperationException(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static class UnmodifiableSortedSetMultimap< @@ -757,7 +864,7 @@ public SortedSet get(@ParametricNullness K key) { } @Override - public SortedSet removeAll(@CheckForNull Object key) { + public SortedSet removeAll(@Nullable Object key) { throw new UnsupportedOperationException(); } @@ -767,12 +874,11 @@ public SortedSet replaceValues(@ParametricNullness K key, Iterable valueComparator() { + public @Nullable Comparator valueComparator() { return delegate().valueComparator(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -785,6 +891,7 @@ public Comparator valueComparator() { * @param multimap the multimap to be wrapped * @return a synchronized view of the specified multimap */ + @J2ktIncompatible // Synchronized public static SetMultimap synchronizedSetMultimap(SetMultimap multimap) { return Synchronized.setMultimap(multimap, null); @@ -815,6 +922,9 @@ SetMultimap unmodifiableSetMultimap(SetMultimap delegate) { * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(delegate)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static SetMultimap unmodifiableSetMultimap( ImmutableSetMultimap delegate) { @@ -832,6 +942,7 @@ public static SetMultimap unmodifiableSetMultimap( * @param multimap the multimap to be wrapped * @return a synchronized view of the specified multimap */ + @J2ktIncompatible // Synchronized public static SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { return Synchronized.sortedSetMultimap(multimap, null); @@ -864,6 +975,7 @@ SortedSetMultimap unmodifiableSortedSetMultimap(SortedSetMultimap de * @param multimap the multimap to be wrapped * @return a synchronized view of the specified multimap */ + @J2ktIncompatible // Synchronized public static ListMultimap synchronizedListMultimap(ListMultimap multimap) { return Synchronized.listMultimap(multimap, null); @@ -894,6 +1006,9 @@ ListMultimap unmodifiableListMultimap(ListMultimap delegate) { * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(delegate)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static ListMultimap unmodifiableListMultimap( ImmutableListMultimap delegate) { @@ -942,7 +1057,6 @@ Collection> unmodifiableEntries(Collection> entries) { * * @since 15.0 */ - @Beta @SuppressWarnings("unchecked") // safe by specification of ListMultimap.asMap() public static Map> asMap( @@ -956,7 +1070,6 @@ Collection> unmodifiableEntries(Collection> entries) { * * @since 15.0 */ - @Beta @SuppressWarnings("unchecked") // safe by specification of SetMultimap.asMap() public static Map> asMap( @@ -970,7 +1083,6 @@ Collection> unmodifiableEntries(Collection> entries) { * * @since 15.0 */ - @Beta @SuppressWarnings("unchecked") // safe by specification of SortedSetMultimap.asMap() public static Map> asMap( @@ -984,7 +1096,6 @@ Collection> unmodifiableEntries(Collection> entries) { * * @since 15.0 */ - @Beta public static Map> asMap(Multimap multimap) { return multimap.asMap(); @@ -1010,7 +1121,9 @@ Map> asMap(Multimap multimap) { return new MapMultimap<>(map); } - /** @see Multimaps#forMap */ + /** + * @see Multimaps#forMap + */ private static class MapMultimap extends AbstractMultimap implements SetMultimap, Serializable { final Map map; @@ -1025,22 +1138,22 @@ public int size() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return map.containsKey(key); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return map.containsValue(value); } @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { return map.entrySet().contains(Maps.immutableEntry(key, value)); } @Override - public Set get(@ParametricNullness final K key) { + public Set get(@ParametricNullness K key) { return new Sets.ImprovedAbstractSet() { @Override public Iterator iterator() { @@ -1103,13 +1216,13 @@ public Set replaceValues(@ParametricNullness K key, Iterable val } @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { return map.entrySet().remove(Maps.immutableEntry(key, value)); } @Override - public Set removeAll(@CheckForNull Object key) { - Set values = new HashSet(2); + public Set removeAll(@Nullable Object key) { + Set values = new HashSet<>(2); if (!map.containsKey(key)) { return values; } @@ -1162,6 +1275,7 @@ public int hashCode() { return map.hashCode(); } + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 7845222491160860175L; } @@ -1209,7 +1323,7 @@ public int hashCode() { public static < K extends @Nullable Object, V1 extends @Nullable Object, V2 extends @Nullable Object> Multimap transformValues( - Multimap fromMultimap, final Function function) { + Multimap fromMultimap, Function function) { checkNotNull(function); EntryTransformer transformer = Maps.asEntryTransformer(function); return transformEntries(fromMultimap, transformer); @@ -1258,7 +1372,7 @@ Multimap transformValues( public static < K extends @Nullable Object, V1 extends @Nullable Object, V2 extends @Nullable Object> ListMultimap transformValues( - ListMultimap fromMultimap, final Function function) { + ListMultimap fromMultimap, Function function) { checkNotNull(function); EntryTransformer transformer = Maps.asEntryTransformer(function); return transformEntries(fromMultimap, transformer); @@ -1384,8 +1498,7 @@ private static class TransformedEntriesMultimap< final EntryTransformer transformer; TransformedEntriesMultimap( - Multimap fromMultimap, - final EntryTransformer transformer) { + Multimap fromMultimap, EntryTransformer transformer) { this.fromMultimap = checkNotNull(fromMultimap); this.transformer = checkNotNull(transformer); } @@ -1401,14 +1514,7 @@ Collection transform(@ParametricNullness K key, Collection values) { @Override Map> createAsMap() { - return Maps.transformEntries( - fromMultimap.asMap(), - new EntryTransformer, Collection>() { - @Override - public Collection transformEntry(@ParametricNullness K key, Collection value) { - return transform(key, value); - } - }); + return Maps.transformEntries(fromMultimap.asMap(), this::transform); } @Override @@ -1417,7 +1523,7 @@ public void clear() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return fromMultimap.containsKey(key); } @@ -1433,7 +1539,7 @@ Iterator> entryIterator() { } @Override - public Collection get(@ParametricNullness final K key) { + public Collection get(@ParametricNullness K key) { return transform(key, fromMultimap.get(key)); } @@ -1469,13 +1575,13 @@ public boolean putAll(Multimap multimap) { @SuppressWarnings("unchecked") @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { return get((K) key).remove(value); } @SuppressWarnings("unchecked") @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { return transform((K) key, fromMultimap.removeAll(key)); } @@ -1517,7 +1623,7 @@ public List get(@ParametricNullness K key) { @SuppressWarnings("unchecked") @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { return transform((K) key, fromMultimap.removeAll(key)); } @@ -1628,7 +1734,7 @@ Iterator> entryIterator() { return new TransformedIterator>, Multiset.Entry>( multimap.asMap().entrySet().iterator()) { @Override - Multiset.Entry transform(final Map.Entry> backingEntry) { + Multiset.Entry transform(Map.Entry> backingEntry) { return new Multisets.AbstractEntry() { @Override @ParametricNullness @@ -1656,7 +1762,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return multimap.containsKey(element); } @@ -1666,13 +1772,13 @@ public Iterator iterator() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { Collection values = Maps.safeGet(multimap.asMap(), element); return (values == null) ? 0 : values.size(); } @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { checkNonnegative(occurrences, "occurrences"); if (occurrences == 0) { return count(element); @@ -1724,7 +1830,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Map.Entry) { Map.Entry entry = (Map.Entry) o; return multimap().containsEntry(entry.getKey(), entry.getValue()); @@ -1733,7 +1839,7 @@ public boolean contains(@CheckForNull Object o) { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (o instanceof Map.Entry) { Map.Entry entry = (Map.Entry) o; return multimap().remove(entry.getKey(), entry.getValue()); @@ -1766,7 +1872,7 @@ protected Set>> createEntrySet() { return new EntrySet(); } - void removeValuesForKey(@CheckForNull Object key) { + void removeValuesForKey(@Nullable Object key) { multimap.keySet().remove(key); } @@ -1779,18 +1885,11 @@ Map> map() { @Override public Iterator>> iterator() { - return Maps.asMapEntryIterator( - multimap.keySet(), - new Function>() { - @Override - public Collection apply(@ParametricNullness K key) { - return multimap.get(key); - } - }); + return Maps.asMapEntryIterator(multimap.keySet(), multimap::get); } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { if (!contains(o)) { return false; } @@ -1803,14 +1902,12 @@ public boolean remove(@CheckForNull Object o) { @SuppressWarnings("unchecked") @Override - @CheckForNull - public Collection get(@CheckForNull Object key) { + public @Nullable Collection get(@Nullable Object key) { return containsKey(key) ? multimap.get((K) key) : null; } @Override - @CheckForNull - public Collection remove(@CheckForNull Object key) { + public @Nullable Collection remove(@Nullable Object key) { return containsKey(key) ? multimap.removeAll(key) : null; } @@ -1825,7 +1922,7 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return multimap.containsKey(key); } @@ -1863,7 +1960,7 @@ public void clear() { * @since 11.0 */ public static Multimap filterKeys( - Multimap unfiltered, final Predicate keyPredicate) { + Multimap unfiltered, Predicate keyPredicate) { if (unfiltered instanceof SetMultimap) { return filterKeys((SetMultimap) unfiltered, keyPredicate); } else if (unfiltered instanceof ListMultimap) { @@ -1909,7 +2006,7 @@ public void clear() { */ public static SetMultimap filterKeys( - SetMultimap unfiltered, final Predicate keyPredicate) { + SetMultimap unfiltered, Predicate keyPredicate) { if (unfiltered instanceof FilteredKeySetMultimap) { FilteredKeySetMultimap prev = (FilteredKeySetMultimap) unfiltered; return new FilteredKeySetMultimap<>( @@ -1951,7 +2048,7 @@ SetMultimap filterKeys( */ public static ListMultimap filterKeys( - ListMultimap unfiltered, final Predicate keyPredicate) { + ListMultimap unfiltered, Predicate keyPredicate) { if (unfiltered instanceof FilteredKeyListMultimap) { FilteredKeyListMultimap prev = (FilteredKeyListMultimap) unfiltered; return new FilteredKeyListMultimap<>( @@ -1989,8 +2086,7 @@ ListMultimap filterKeys( * @since 11.0 */ public static - Multimap filterValues( - Multimap unfiltered, final Predicate valuePredicate) { + Multimap filterValues(Multimap unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2023,7 +2119,7 @@ Multimap filterValues( */ public static SetMultimap filterValues( - SetMultimap unfiltered, final Predicate valuePredicate) { + SetMultimap unfiltered, Predicate valuePredicate) { return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); } @@ -2126,7 +2222,7 @@ SetMultimap filterFiltered( return new FilteredEntrySetMultimap<>(multimap.unfiltered(), predicate); } - static boolean equalsImpl(Multimap multimap, @CheckForNull Object object) { + static boolean equalsImpl(Multimap multimap, @Nullable Object object) { if (object == multimap) { return true; } diff --git a/android/guava/src/com/google/common/collect/Multiset.java b/android/guava/src/com/google/common/collect/Multiset.java index 8dfb1af24bcc..79255a9f9b74 100644 --- a/android/guava/src/com/google/common/collect/Multiset.java +++ b/android/guava/src/com/google/common/collect/Multiset.java @@ -24,8 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A collection that supports order-independent equality, like {@link Set}, but may have duplicate @@ -51,8 +50,7 @@ *

    In addition to these required methods, implementations of {@code Multiset} are expected to * provide two {@code static} creation methods: {@code create()}, returning an empty multiset, and * {@code create(Iterable)}, returning a multiset containing the given initial - * elements. This is simply a refinement of {@code Collection}'s constructor recommendations, - * reflecting the new developments of Java 5. + * elements. This is simply a refinement of {@code Collection}'s constructor recommendations. * *

    As with other collection types, the modification operations are optional, and should throw * {@link UnsupportedOperationException} when they are not implemented. Most implementations should @@ -66,22 +64,29 @@ * element (in a way that affects its {@link Object#equals} behavior) while it is contained in a * multiset. Undefined behavior and bugs will result. * - *

    Common implementations include {@link ImmutableMultiset}, {@link HashMultiset}, and {@link - * ConcurrentHashMultiset}. + *

    Implementations

    + * + *
      + *
    • {@link ImmutableMultiset} + *
    • {@link ImmutableSortedMultiset} + *
    • {@link HashMultiset} + *
    • {@link LinkedHashMultiset} + *
    • {@link TreeMultiset} + *
    • {@link EnumMultiset} + *
    • {@link ConcurrentHashMultiset} + *
    * *

    If your values may be zero, negative, or outside the range of an int, you may wish to use * {@link com.google.common.util.concurrent.AtomicLongMap} instead. Note, however, that unlike * {@code Multiset}, {@code AtomicLongMap} does not automatically remove zeros. * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Kevin Bourrillion * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Multiset extends Collection { // Query Operations @@ -107,7 +112,7 @@ public interface Multiset extends Collection { * @return the number of occurrences of the element in this multiset; possibly zero but never * negative */ - int count(@CompatibleWith("E") @CheckForNull Object element); + int count(@CompatibleWith("E") @Nullable Object element); // Bulk Operations @@ -168,7 +173,7 @@ public interface Multiset extends Collection { * @throws IllegalArgumentException if {@code occurrences} is negative */ @CanIgnoreReturnValue - int remove(@CompatibleWith("E") @CheckForNull Object element, int occurrences); + int remove(@CompatibleWith("E") @Nullable Object element, int occurrences); /** * Removes a single occurrence of the specified element from this multiset, if present. @@ -184,7 +189,7 @@ public interface Multiset extends Collection { */ @CanIgnoreReturnValue @Override - boolean remove(@CheckForNull Object element); + boolean remove(@Nullable Object element); /** * Adds or removes the necessary occurrences of an element such that the element attains the @@ -301,7 +306,7 @@ interface Entry { */ @Override // TODO(kevinb): check this wrt TreeMultiset? - boolean equals(@CheckForNull Object o); + boolean equals(@Nullable Object o); /** * {@inheritDoc} @@ -335,7 +340,7 @@ interface Entry { */ @Override // TODO(kevinb): caveats about equivalence-relation? - boolean equals(@CheckForNull Object object); + boolean equals(@Nullable Object object); /** * Returns the hash code for this multiset. This is defined as the sum of @@ -381,7 +386,7 @@ interface Entry { * @return {@code true} if this multiset contains at least one occurrence of the element */ @Override - boolean contains(@CheckForNull Object element); + boolean contains(@Nullable Object element); /** * Returns {@code true} if this multiset contains at least one occurrence of each element in the diff --git a/android/guava/src/com/google/common/collect/Multisets.java b/android/guava/src/com/google/common/collect/Multisets.java index 02febcac7bee..f43e98c1e355 100644 --- a/android/guava/src/com/google/common/collect/Multisets.java +++ b/android/guava/src/com/google/common/collect/Multisets.java @@ -20,10 +20,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; import static com.google.common.collect.CollectPreconditions.checkRemove; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -31,6 +35,8 @@ import com.google.common.math.IntMath; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; @@ -39,14 +45,17 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * Provides static utility methods for creating and working with {@link Multiset} instances. * *

    See the Guava User Guide article on {@code + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#multisets">{@code * Multisets}. * * @author Kevin Bourrillion @@ -55,10 +64,36 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Multisets { private Multisets() {} + /** + * Returns a {@code Collector} that accumulates elements into a multiset created via the specified + * {@code Supplier}, whose elements are the result of applying {@code elementFunction} to the + * inputs, with counts equal to the result of applying {@code countFunction} to the inputs. + * Elements are added in encounter order. + * + *

    If the mapped elements contain duplicates (according to {@link Object#equals}), the element + * will be added more than once, with the count summed over all appearances of the element. + * + *

    Note that {@code stream.collect(toMultiset(function, e -> 1, supplier))} is equivalent to + * {@code stream.map(function).collect(Collectors.toCollection(supplier))}. + * + *

    To collect to an {@link ImmutableMultiset}, use {@link + * ImmutableMultiset#toImmutableMultiset}. + * + * @since 33.2.0 (available since 22.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static > + Collector toMultiset( + Function elementFunction, + ToIntFunction countFunction, + Supplier multisetSupplier) { + return CollectCollectors.toMultiset(elementFunction, countFunction, multisetSupplier); + } + /** * Returns an unmodifiable view of the specified multiset. Query operations on the returned * multiset "read through" to the specified multiset, and attempts to modify the returned multiset @@ -76,7 +111,7 @@ private Multisets() {} Multiset result = (Multiset) multiset; return result; } - return new UnmodifiableMultiset(checkNotNull(multiset)); + return new UnmodifiableMultiset<>(checkNotNull(multiset)); } /** @@ -85,6 +120,9 @@ private Multisets() {} * @deprecated no need to use this * @since 10.0 */ + @InlineMe( + replacement = "checkNotNull(multiset)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static Multiset unmodifiableMultiset(ImmutableMultiset multiset) { return checkNotNull(multiset); @@ -105,7 +143,7 @@ protected Multiset delegate() { return (Multiset) delegate; } - @CheckForNull transient Set elementSet; + @LazyInit transient @Nullable Set elementSet; Set createElementSet() { return Collections.unmodifiableSet(delegate.elementSet()); @@ -117,7 +155,7 @@ public Set elementSet() { return (es == null) ? elementSet = createElementSet() : es; } - @CheckForNull transient Set> entrySet; + @LazyInit transient @Nullable Set> entrySet; @SuppressWarnings("unchecked") @Override @@ -151,12 +189,12 @@ public boolean addAll(Collection elementsToAdd) { } @Override - public boolean remove(@CheckForNull Object element) { + public boolean remove(@Nullable Object element) { throw new UnsupportedOperationException(); } @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { throw new UnsupportedOperationException(); } @@ -185,7 +223,7 @@ public boolean setCount(@ParametricNullness E element, int oldCount, int newCoun throw new UnsupportedOperationException(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -199,11 +237,10 @@ public boolean setCount(@ParametricNullness E element, int oldCount, int newCoun * @return an unmodifiable view of the multiset * @since 11.0 */ - @Beta public static SortedMultiset unmodifiableSortedMultiset( SortedMultiset sortedMultiset) { // it's in its own file so it can be emulated for GWT - return new UnmodifiableSortedMultiset(checkNotNull(sortedMultiset)); + return new UnmodifiableSortedMultiset<>(checkNotNull(sortedMultiset)); } /** @@ -216,7 +253,7 @@ public boolean setCount(@ParametricNullness E element, int oldCount, int newCoun */ public static Multiset.Entry immutableEntry( @ParametricNullness E e, int n) { - return new ImmutableEntry(e, n); + return new ImmutableEntry<>(e, n); } static class ImmutableEntry extends AbstractEntry @@ -241,12 +278,11 @@ public final int getCount() { return count; } - @CheckForNull - public ImmutableEntry nextInBucket() { + public @Nullable ImmutableEntry nextInBucket() { return null; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -274,17 +310,16 @@ public ImmutableEntry nextInBucket() { * * @since 14.0 */ - @Beta public static Multiset filter( Multiset unfiltered, Predicate predicate) { if (unfiltered instanceof FilteredMultiset) { // Support clear(), removeAll(), and retainAll() when filtering a filtered // collection. FilteredMultiset filtered = (FilteredMultiset) unfiltered; - Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); - return new FilteredMultiset(filtered.unfiltered, combinedPredicate); + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredMultiset<>(filtered.unfiltered, combinedPredicate); } - return new FilteredMultiset(unfiltered, predicate); + return new FilteredMultiset<>(unfiltered, predicate); } private static final class FilteredMultiset extends ViewMultiset { @@ -313,14 +348,7 @@ Iterator elementIterator() { @Override Set> createEntrySet() { - return Sets.filter( - unfiltered.entrySet(), - new Predicate>() { - @Override - public boolean apply(Entry entry) { - return predicate.apply(entry.getElement()); - } - }); + return Sets.filter(unfiltered.entrySet(), entry -> predicate.apply(entry.getElement())); } @Override @@ -329,7 +357,7 @@ Iterator> entryIterator() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { int count = unfiltered.count(element); if (count > 0) { @SuppressWarnings("unchecked") // element is equal to an E @@ -347,7 +375,7 @@ public int add(@ParametricNullness E element, int occurrences) { } @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { checkNonnegative(occurrences, "occurrences"); if (occurrences == 0) { return count(element); @@ -381,15 +409,14 @@ static int inferDistinctElements(Iterable elements) { * * @since 14.0 */ - @Beta public static Multiset union( - final Multiset multiset1, final Multiset multiset2) { + Multiset multiset1, Multiset multiset2) { checkNotNull(multiset1); checkNotNull(multiset2); return new ViewMultiset() { @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return multiset1.contains(element) || multiset2.contains(element); } @@ -399,8 +426,8 @@ public boolean isEmpty() { } @Override - public int count(@CheckForNull Object element) { - return Math.max(multiset1.count(element), multiset2.count(element)); + public int count(@Nullable Object element) { + return max(multiset1.count(element), multiset2.count(element)); } @Override @@ -415,17 +442,16 @@ Iterator elementIterator() { @Override Iterator> entryIterator() { - final Iterator> iterator1 = multiset1.entrySet().iterator(); - final Iterator> iterator2 = multiset2.entrySet().iterator(); + Iterator> iterator1 = multiset1.entrySet().iterator(); + Iterator> iterator2 = multiset2.entrySet().iterator(); // TODO(lowasser): consider making the entries live views return new AbstractIterator>() { @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { if (iterator1.hasNext()) { Entry entry1 = iterator1.next(); E element = entry1.getElement(); - int count = Math.max(entry1.getCount(), multiset2.count(element)); + int count = max(entry1.getCount(), multiset2.count(element)); return immutableEntry(element, count); } while (iterator2.hasNext()) { @@ -455,15 +481,15 @@ protected Entry computeNext() { * @since 2.0 */ public static Multiset intersection( - final Multiset multiset1, final Multiset multiset2) { + Multiset multiset1, Multiset multiset2) { checkNotNull(multiset1); checkNotNull(multiset2); return new ViewMultiset() { @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { int count1 = multiset1.count(element); - return (count1 == 0) ? 0 : Math.min(count1, multiset2.count(element)); + return (count1 == 0) ? 0 : min(count1, multiset2.count(element)); } @Override @@ -478,16 +504,15 @@ Iterator elementIterator() { @Override Iterator> entryIterator() { - final Iterator> iterator1 = multiset1.entrySet().iterator(); + Iterator> iterator1 = multiset1.entrySet().iterator(); // TODO(lowasser): consider making the entries live views return new AbstractIterator>() { @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { while (iterator1.hasNext()) { Entry entry1 = iterator1.next(); E element = entry1.getElement(); - int count = Math.min(entry1.getCount(), multiset2.count(element)); + int count = min(entry1.getCount(), multiset2.count(element)); if (count > 0) { return immutableEntry(element, count); } @@ -511,16 +536,15 @@ protected Entry computeNext() { * * @since 14.0 */ - @Beta public static Multiset sum( - final Multiset multiset1, final Multiset multiset2) { + Multiset multiset1, Multiset multiset2) { checkNotNull(multiset1); checkNotNull(multiset2); // TODO(lowasser): consider making the entries live views return new ViewMultiset() { @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return multiset1.contains(element) || multiset2.contains(element); } @@ -535,7 +559,7 @@ public int size() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { return multiset1.count(element) + multiset2.count(element); } @@ -551,12 +575,11 @@ Iterator elementIterator() { @Override Iterator> entryIterator() { - final Iterator> iterator1 = multiset1.entrySet().iterator(); - final Iterator> iterator2 = multiset2.entrySet().iterator(); + Iterator> iterator1 = multiset1.entrySet().iterator(); + Iterator> iterator2 = multiset2.entrySet().iterator(); return new AbstractIterator>() { @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { if (iterator1.hasNext()) { Entry entry1 = iterator1.next(); E element = entry1.getElement(); @@ -589,18 +612,17 @@ protected Entry computeNext() { * * @since 14.0 */ - @Beta public static Multiset difference( - final Multiset multiset1, final Multiset multiset2) { + Multiset multiset1, Multiset multiset2) { checkNotNull(multiset1); checkNotNull(multiset2); // TODO(lowasser): consider making the entries live views return new ViewMultiset() { @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { int count1 = multiset1.count(element); - return (count1 == 0) ? 0 : Math.max(0, count1 - multiset2.count(element)); + return (count1 == 0) ? 0 : max(0, count1 - multiset2.count(element)); } @Override @@ -610,11 +632,10 @@ public void clear() { @Override Iterator elementIterator() { - final Iterator> iterator1 = multiset1.entrySet().iterator(); + Iterator> iterator1 = multiset1.entrySet().iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { while (iterator1.hasNext()) { Entry entry1 = iterator1.next(); E element = entry1.getElement(); @@ -629,11 +650,10 @@ protected E computeNext() { @Override Iterator> entryIterator() { - final Iterator> iterator1 = multiset1.entrySet().iterator(); + Iterator> iterator1 = multiset1.entrySet().iterator(); return new AbstractIterator>() { @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { while (iterator1.hasNext()) { Entry entry1 = iterator1.next(); E element = entry1.getElement(); @@ -810,7 +830,7 @@ abstract static class AbstractEntry implements Multi * Multiset.Entry#equals}. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Multiset.Entry) { Multiset.Entry that = (Multiset.Entry) object; return this.getCount() == that.getCount() @@ -844,7 +864,7 @@ public String toString() { } /** An implementation of {@link Multiset#equals}. */ - static boolean equalsImpl(Multiset multiset, @CheckForNull Object object) { + static boolean equalsImpl(Multiset multiset, @Nullable Object object) { if (object == multiset) { return true; } @@ -875,7 +895,7 @@ static boolean equalsImpl(Multiset multiset, @CheckForNull Object object) { checkNotNull(self); checkNotNull(elements); if (elements instanceof Multiset) { - return addAllImpl(self, cast(elements)); + return addAllImpl(self, (Multiset) elements); } else if (elements.isEmpty()) { return false; } else { @@ -985,7 +1005,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { return multiset().contains(o); } @@ -1003,7 +1023,7 @@ public boolean isEmpty() { public abstract Iterator iterator(); @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { return multiset().remove(o, Integer.MAX_VALUE) > 0; } @@ -1018,12 +1038,8 @@ abstract static class EntrySet abstract Multiset multiset(); @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Entry) { - /* - * The GWT compiler wrongly issues a warning here. - */ - @SuppressWarnings("cast") Entry entry = (Entry) o; if (entry.getCount() <= 0) { return false; @@ -1034,10 +1050,8 @@ public boolean contains(@CheckForNull Object o) { return false; } - // GWT compiler warning; see contains(). - @SuppressWarnings("cast") @Override - public boolean remove(@CheckForNull Object object) { + public boolean remove(@Nullable Object object) { if (object instanceof Multiset.Entry) { Entry entry = (Entry) object; Object element = entry.getElement(); @@ -1061,13 +1075,13 @@ public void clear() { /** An implementation of {@link Multiset#iterator}. */ static Iterator iteratorImpl(Multiset multiset) { - return new MultisetIteratorImpl(multiset, multiset.entrySet().iterator()); + return new MultisetIteratorImpl<>(multiset, multiset.entrySet().iterator()); } static final class MultisetIteratorImpl implements Iterator { private final Multiset multiset; private final Iterator> entryIterator; - @CheckForNull private Entry currentEntry; + private @Nullable Entry currentEntry; /** Count of subsequent elements equal to current element */ private int laterCount; @@ -1132,26 +1146,22 @@ static int linearTimeSizeImpl(Multiset multiset) { return Ints.saturatedCast(size); } - /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ - static Multiset cast(Iterable iterable) { - return (Multiset) iterable; - } - /** - * Returns a copy of {@code multiset} as an {@link ImmutableMultiset} whose iteration order is - * highest count first, with ties broken by the iteration order of the original multiset. + * Returns a copy of {@code multiset} as an {@link ImmutableMultiset} whose iteration order puts + * the highest count first, with ties broken by the iteration order of the original multiset. * * @since 11.0 */ - @Beta public static ImmutableMultiset copyHighestCountFirst(Multiset multiset) { - Entry[] entries = (Entry[]) multiset.entrySet().toArray(new Entry[0]); + @SuppressWarnings("unchecked") // generics+arrays + // TODO(cpovirk): Consider storing an Entry instead of Entry. + Entry[] entries = (Entry[]) multiset.entrySet().toArray((Entry[]) new Entry[0]); Arrays.sort(entries, DecreasingCount.INSTANCE); - return ImmutableMultiset.copyFromEntries(Arrays.asList(entries)); + return ImmutableMultiset.copyFromEntries(asList(entries)); } private static final class DecreasingCount implements Comparator> { - static final DecreasingCount INSTANCE = new DecreasingCount(); + static final Comparator> INSTANCE = new DecreasingCount(); @Override public int compare(Entry entry1, Entry entry2) { diff --git a/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java b/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java index 2fcf0d664e3d..41768fd8c121 100644 --- a/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java +++ b/android/guava/src/com/google/common/collect/MutableClassToInstanceMap.java @@ -19,16 +19,19 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Primitives; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link @@ -38,24 +41,22 @@ * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#classtoinstancemap">{@code * ClassToInstanceMap}. * - *

    This implementation does support null values, despite how it is annotated; see - * discussion at {@link ClassToInstanceMap}. - * * @author Kevin Bourrillion * @since 2.0 */ +@J2ktIncompatible @GwtIncompatible @SuppressWarnings("serial") // using writeReplace instead of standard serialization -@ElementTypesAreNonnullByDefault -public final class MutableClassToInstanceMap extends ForwardingMap, B> +public final class MutableClassToInstanceMap + extends ForwardingMap, B> implements ClassToInstanceMap, Serializable { /** * Returns a new {@code MutableClassToInstanceMap} instance backed by a {@link HashMap} using the * default initial capacity and load factor. */ - public static MutableClassToInstanceMap create() { - return new MutableClassToInstanceMap(new HashMap, B>()); + public static MutableClassToInstanceMap create() { + return new MutableClassToInstanceMap<>(new HashMap, B>()); } /** @@ -63,50 +64,59 @@ public static MutableClassToInstanceMap create() { * backingMap}. The caller surrenders control of the backing map, and thus should not allow any * direct references to it to remain accessible. */ - public static MutableClassToInstanceMap create(Map, B> backingMap) { - return new MutableClassToInstanceMap(backingMap); + public static MutableClassToInstanceMap create( + Map, B> backingMap) { + return new MutableClassToInstanceMap<>(backingMap); } - private final Map, B> delegate; + private final Map, B> delegate; - private MutableClassToInstanceMap(Map, B> delegate) { + private MutableClassToInstanceMap(Map, B> delegate) { this.delegate = checkNotNull(delegate); } @Override - protected Map, B> delegate() { + protected Map, B> delegate() { return delegate; } - static Entry, B> checkedEntry(final Entry, B> entry) { - return new ForwardingMapEntry, B>() { + /** + * Wraps the {@code setValue} implementation of an {@code Entry} to enforce the class constraint. + */ + private static Entry, B> checkedEntry( + Entry, B> entry) { + return new ForwardingMapEntry, B>() { @Override - protected Entry, B> delegate() { + protected Entry, B> delegate() { return entry; } @Override - public B setValue(B value) { - return super.setValue(cast(getKey(), value)); + @ParametricNullness + public B setValue(@ParametricNullness B value) { + cast(getKey(), value); + return super.setValue(value); } }; } @Override - public Set, B>> entrySet() { - return new ForwardingSet, B>>() { + public Set, B>> entrySet() { + return new ForwardingSet, B>>() { @Override - protected Set, B>> delegate() { + protected Set, B>> delegate() { return MutableClassToInstanceMap.this.delegate().entrySet(); } @Override - public Iterator, B>> iterator() { - return new TransformedIterator, B>, Entry, B>>( + public Iterator, B>> iterator() { + return new TransformedIterator< + Entry, B>, Entry, B>>( delegate().iterator()) { @Override - Entry, B> transform(Entry, B> from) { + Entry, B> transform( + Entry, B> from) { return checkedEntry(from); } }; @@ -135,15 +145,15 @@ public Object[] toArray() { @Override @CanIgnoreReturnValue - @CheckForNull - public B put(Class key, B value) { - return super.put(key, cast(key, value)); + public @Nullable B put(Class key, @ParametricNullness B value) { + cast(key, value); + return super.put(key, value); } @Override - public void putAll(Map, ? extends B> map) { - Map, B> copy = new LinkedHashMap<>(map); - for (Entry, B> entry : copy.entrySet()) { + public void putAll(Map, ? extends B> map) { + Map, B> copy = new LinkedHashMap<>(map); + for (Entry, B> entry : copy.entrySet()) { cast(entry.getKey(), entry.getValue()); } super.putAll(copy); @@ -151,32 +161,34 @@ public void putAll(Map, ? extends B> map) { @CanIgnoreReturnValue @Override - @CheckForNull - public T putInstance(Class type, T value) { + public @Nullable T putInstance( + Class<@NonNull T> type, @ParametricNullness T value) { return cast(type, put(type, value)); } @Override - @CheckForNull - public T getInstance(Class type) { + public @Nullable T getInstance(Class type) { return cast(type, get(type)); } @CanIgnoreReturnValue - @CheckForNull - private static T cast(Class type, @CheckForNull B value) { + private static @Nullable T cast(Class type, @Nullable Object value) { return Primitives.wrap(type).cast(value); } private Object writeReplace() { - return new SerializedForm(delegate()); + return new SerializedForm<>(delegate()); + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); } /** Serialized form of the map, to avoid serializing the constraint. */ - private static final class SerializedForm implements Serializable { - private final Map, B> backingMap; + private static final class SerializedForm implements Serializable { + private final Map, B> backingMap; - SerializedForm(Map, B> backingMap) { + SerializedForm(Map, B> backingMap) { this.backingMap = backingMap; } diff --git a/android/guava/src/com/google/common/collect/NaturalOrdering.java b/android/guava/src/com/google/common/collect/NaturalOrdering.java index 8cb8aef9a0da..9908f5f4c3d0 100644 --- a/android/guava/src/com/google/common/collect/NaturalOrdering.java +++ b/android/guava/src/com/google/common/collect/NaturalOrdering.java @@ -19,21 +19,23 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** An ordering that uses the natural order of the values. */ @GwtCompatible(serializable = true) -@SuppressWarnings({"unchecked", "rawtypes"}) // TODO(kevinb): the right way to explain this?? -@ElementTypesAreNonnullByDefault final class NaturalOrdering extends Ordering> implements Serializable { static final NaturalOrdering INSTANCE = new NaturalOrdering(); - @CheckForNull private transient Ordering<@Nullable Comparable> nullsFirst; - @CheckForNull private transient Ordering<@Nullable Comparable> nullsLast; + // TODO: b/287198172 - Consider eagerly initializing these (but think about serialization). + @LazyInit private transient @Nullable Ordering<@Nullable Comparable> nullsFirst; + @LazyInit private transient @Nullable Ordering<@Nullable Comparable> nullsLast; @Override + @SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? public int compare(Comparable left, Comparable right) { checkNotNull(left); // for GWT checkNotNull(right); @@ -41,6 +43,7 @@ public int compare(Comparable left, Comparable right) { } @Override + @SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? public > Ordering<@Nullable S> nullsFirst() { Ordering<@Nullable Comparable> result = nullsFirst; if (result == null) { @@ -50,6 +53,7 @@ public int compare(Comparable left, Comparable right) { } @Override + @SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? public > Ordering<@Nullable S> nullsLast() { Ordering<@Nullable Comparable> result = nullsLast; if (result == null) { @@ -59,6 +63,7 @@ public int compare(Comparable left, Comparable right) { } @Override + @SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? public > Ordering reverse() { return (Ordering) ReverseNaturalOrdering.INSTANCE; } @@ -75,5 +80,5 @@ public String toString() { private NaturalOrdering() {} - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/NullnessCasts.java b/android/guava/src/com/google/common/collect/NullnessCasts.java index 6ceeda75ffbc..82bcc2dbeaef 100644 --- a/android/guava/src/com/google/common/collect/NullnessCasts.java +++ b/android/guava/src/com/google/common/collect/NullnessCasts.java @@ -15,12 +15,10 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class NullnessCasts { /** * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that @@ -52,12 +50,12 @@ final class NullnessCasts { */ @ParametricNullness @SuppressWarnings("nullness") - static T uncheckedCastNullableTToT(@CheckForNull T t) { + static T uncheckedCastNullableTToT(@Nullable T t) { return t; } /** Returns {@code null} as any type, even one that does not include {@code null}. */ - @SuppressWarnings({"nullness", "TypeParameterUnusedInFormals"}) + @SuppressWarnings({"nullness", "TypeParameterUnusedInFormals", "ReturnMissingNullable"}) // The warnings are legitimate. Each time we use this method, we document why. @ParametricNullness static T unsafeNull() { diff --git a/android/guava/src/com/google/common/collect/NullsFirstOrdering.java b/android/guava/src/com/google/common/collect/NullsFirstOrdering.java index ce8be2fa3cc5..9fa1f43e79ed 100644 --- a/android/guava/src/com/google/common/collect/NullsFirstOrdering.java +++ b/android/guava/src/com/google/common/collect/NullsFirstOrdering.java @@ -17,13 +17,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** An ordering that treats {@code null} as less than all other values. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class NullsFirstOrdering extends Ordering<@Nullable T> implements Serializable { final Ordering ordering; @@ -33,7 +34,7 @@ final class NullsFirstOrdering extends Ordering<@Nul } @Override - public int compare(@CheckForNull T left, @CheckForNull T right) { + public int compare(@Nullable T left, @Nullable T right) { if (left == right) { return 0; } @@ -50,22 +51,22 @@ public int compare(@CheckForNull T left, @CheckForNull T right) { @SuppressWarnings("nullness") // should be safe, but not sure if we can avoid the warning public Ordering reverse() { // ordering.reverse() might be optimized, so let it do its thing - return ordering.reverse().nullsLast(); + return ordering.reverse().<@NonNull S>nullsLast(); } @SuppressWarnings("unchecked") // still need the right way to explain this @Override - public Ordering<@Nullable S> nullsFirst() { + public Ordering<@Nullable S> nullsFirst() { return (Ordering<@Nullable S>) this; } @Override - public Ordering<@Nullable S> nullsLast() { - return ordering.nullsLast(); + public Ordering<@Nullable S> nullsLast() { + return ordering.<@NonNull S>nullsLast(); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -86,5 +87,5 @@ public String toString() { return ordering + ".nullsFirst()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/NullsLastOrdering.java b/android/guava/src/com/google/common/collect/NullsLastOrdering.java index 6f8f74cdd623..427848abbcea 100644 --- a/android/guava/src/com/google/common/collect/NullsLastOrdering.java +++ b/android/guava/src/com/google/common/collect/NullsLastOrdering.java @@ -17,13 +17,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** An ordering that treats {@code null} as greater than all other values. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class NullsLastOrdering extends Ordering<@Nullable T> implements Serializable { final Ordering ordering; @@ -33,7 +34,7 @@ final class NullsLastOrdering extends Ordering<@Null } @Override - public int compare(@CheckForNull T left, @CheckForNull T right) { + public int compare(@Nullable T left, @Nullable T right) { if (left == right) { return 0; } @@ -50,22 +51,22 @@ public int compare(@CheckForNull T left, @CheckForNull T right) { @SuppressWarnings("nullness") // should be safe, but not sure if we can avoid the warning public Ordering reverse() { // ordering.reverse() might be optimized, so let it do its thing - return ordering.reverse().nullsFirst(); + return ordering.reverse().<@NonNull S>nullsFirst(); } @Override - public Ordering<@Nullable S> nullsFirst() { - return ordering.nullsFirst(); + public Ordering<@Nullable S> nullsFirst() { + return ordering.<@NonNull S>nullsFirst(); } @SuppressWarnings("unchecked") // still need the right way to explain this @Override - public Ordering<@Nullable S> nullsLast() { + public Ordering<@Nullable S> nullsLast() { return (Ordering<@Nullable S>) this; } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -86,5 +87,5 @@ public String toString() { return ordering + ".nullsLast()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ObjectArrays.java b/android/guava/src/com/google/common/collect/ObjectArrays.java index 0f928e170275..3505037bf971 100644 --- a/android/guava/src/com/google/common/collect/ObjectArrays.java +++ b/android/guava/src/com/google/common/collect/ObjectArrays.java @@ -17,6 +17,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.System.arraycopy; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; @@ -24,8 +25,8 @@ import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to object arrays. @@ -34,7 +35,7 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@SuppressWarnings("AvoidObjectArrays") public final class ObjectArrays { private ObjectArrays() {} @@ -47,7 +48,7 @@ private ObjectArrays() {} */ @GwtIncompatible // Array.newInstance(Class, int) @SuppressWarnings("unchecked") - public static T[] newArray(Class type, int length) { + public static T[] newArray(Class<@NonNull T> type, int length) { return (T[]) Array.newInstance(type, length); } @@ -69,10 +70,11 @@ public static T[] newArray(Class type, int length) { * @param type the component type of the returned array */ @GwtIncompatible // Array.newInstance(Class, int) - public static T[] concat(T[] first, T[] second, Class type) { + public static T[] concat( + T[] first, T[] second, Class<@NonNull T> type) { T[] result = newArray(type, first.length + second.length); - System.arraycopy(first, 0, result, 0, first.length); - System.arraycopy(second, 0, result, first.length, second.length); + arraycopy(first, 0, result, 0, first.length); + arraycopy(second, 0, result, first.length, second.length); return result; } @@ -87,7 +89,7 @@ public static T[] concat(T[] first, T[] second, Class type) { public static T[] concat(@ParametricNullness T element, T[] array) { T[] result = newArray(array, array.length + 1); result[0] = element; - System.arraycopy(array, 0, result, 1, array.length); + arraycopy(array, 0, result, 1, array.length); return result; } @@ -159,7 +161,7 @@ public static T[] concat(T[] first, T[] second, Class type) { @Nullable Object[] unsoundlyCovariantArray = dst; unsoundlyCovariantArray[len] = null; } - System.arraycopy(src, offset, dst, 0, len); + arraycopy(src, offset, dst, 0, len); return dst; } @@ -189,7 +191,7 @@ public static T[] concat(T[] first, T[] second, Class type) { return new Object[0]; } @Nullable Object[] result = new Object[length]; - System.arraycopy(elements, offset, result, 0, length); + arraycopy(elements, offset, result, 0, length); return result; } @@ -226,7 +228,7 @@ static Object[] checkElementsNotNull(Object... array) { // We do this instead of Preconditions.checkNotNull to save boxing and array // creation cost. @CanIgnoreReturnValue - static Object checkElementNotNull(@CheckForNull Object element, int index) { + static Object checkElementNotNull(@Nullable Object element, int index) { if (element == null) { throw new NullPointerException("at index " + index); } diff --git a/android/guava/src/com/google/common/collect/ObjectCountHashMap.java b/android/guava/src/com/google/common/collect/ObjectCountHashMap.java index 29d5bcde4039..250fcb77d415 100644 --- a/android/guava/src/com/google/common/collect/ObjectCountHashMap.java +++ b/android/guava/src/com/google/common/collect/ObjectCountHashMap.java @@ -28,8 +28,8 @@ import com.google.common.collect.Multisets.AbstractEntry; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Arrays; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * {@code ObjectCountHashMap} uses arrays to store key objects and count values. Comparing to using @@ -39,7 +39,7 @@ *

    In the absence of element deletions, this will iterate over elements in insertion order. */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault +@NullMarked class ObjectCountHashMap { /** Creates an empty {@code ObjectCountHashMap} instance. */ @@ -386,7 +386,7 @@ private void resizeTable(int newCapacity) { // newCapacity always a power of two this.table = newTable; } - int indexOf(@CheckForNull Object key) { + int indexOf(@Nullable Object key) { int hash = smearedHash(key); int next = table[hash & hashTableMask()]; while (next != UNSET) { @@ -399,21 +399,21 @@ int indexOf(@CheckForNull Object key) { return -1; } - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return indexOf(key) != -1; } - public int get(@CheckForNull Object key) { + public int get(@Nullable Object key) { int index = indexOf(key); return (index == -1) ? 0 : values[index]; } @CanIgnoreReturnValue - public int remove(@CheckForNull Object key) { + public int remove(@Nullable Object key) { return remove(key, smearedHash(key)); } - private int remove(@CheckForNull Object key, int hash) { + private int remove(@Nullable Object key, int hash) { int tableIndex = hash & hashTableMask(); int next = table[tableIndex]; if (next == UNSET) { // empty bucket diff --git a/android/guava/src/com/google/common/collect/ObjectCountLinkedHashMap.java b/android/guava/src/com/google/common/collect/ObjectCountLinkedHashMap.java index 266606a153fc..1e8b092b9feb 100644 --- a/android/guava/src/com/google/common/collect/ObjectCountLinkedHashMap.java +++ b/android/guava/src/com/google/common/collect/ObjectCountLinkedHashMap.java @@ -18,7 +18,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; import java.util.Arrays; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * {@code ObjectCountLinkedHashMap} is a subclass of {@code ObjectCountHashMap} with insertion @@ -28,7 +29,7 @@ * footprint. */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault +@NullMarked class ObjectCountLinkedHashMap extends ObjectCountHashMap { /** Creates an empty {@code ObjectCountLinkedHashMap} instance. */ static ObjectCountLinkedHashMap create() { diff --git a/android/guava/src/com/google/common/collect/Ordering.java b/android/guava/src/com/google/common/collect/Ordering.java index 025f95fb7053..b3f806e748ef 100644 --- a/android/guava/src/com/google/common/collect/Ordering.java +++ b/android/guava/src/com/google/common/collect/Ordering.java @@ -18,11 +18,19 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static java.util.Arrays.asList; +import static java.util.Arrays.sort; +import static java.util.Collections.emptyList; +import static java.util.Collections.sort; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; +import com.google.errorprone.annotations.InlineMeValidationDisabled; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -38,8 +46,8 @@ import java.util.TreeSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A comparator, with additional methods to support common operations. This is an "enriched" version @@ -122,12 +130,12 @@ * {@code function} can themselves be serialized, then {@code ordering.onResultOf(function)} can as * well. * - *

    For Java 8 users

    + *

    Java 8+ users

    * - *

    If you are using Java 8, this class is now obsolete. Most of its functionality is now provided - * by {@link java.util.stream.Stream Stream} and by {@link Comparator} itself, and the rest can now - * be found as static methods in our new {@link Comparators} class. See each method below for - * further instructions. Whenever possible, you should change any references of type {@code + *

    If you are using Java 8+, this class is now obsolete. Most of its functionality is now + * provided by {@link java.util.stream.Stream Stream} and by {@link Comparator} itself, and the rest + * can now be found as static methods in our new {@link Comparators} class. See each method below + * for further instructions. Whenever possible, you should change any references of type {@code * Ordering} to be of type {@code Comparator} instead. However, at this time we have no plan to * deprecate this class. * @@ -145,7 +153,6 @@ * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class Ordering implements Comparator { // Natural order @@ -156,10 +163,12 @@ public abstract class Ordering implements Comparator *

    The type specification is {@code }, instead of the technically correct * {@code >}, to support legacy types from before Java 5. * - *

    Java 8 users: use {@link Comparator#naturalOrder} instead. + *

    Java 8+ users: use {@link Comparator#naturalOrder} instead. */ @GwtCompatible(serializable = true) - @SuppressWarnings("unchecked") // TODO(kevinb): right way to explain this?? + @SuppressWarnings({"unchecked", "rawtypes"}) + // TODO(kevinb): right way to explain this?? + // plus https://github.com/google/guava/issues/989 public static Ordering natural() { return (Ordering) NaturalOrdering.INSTANCE; } @@ -172,7 +181,9 @@ public static Ordering natural() { * to pass it in here. Instead, simply subclass {@code Ordering} and implement its {@code compare} * method directly. * - *

    Java 8 users: this class is now obsolete as explained in the class documentation, so + *

    The returned object is serializable if {@code comparator} is serializable. + * + *

    Java 8+ users: this class is now obsolete as explained in the class documentation, so * there is no need to use this method. * * @param comparator the comparator that defines the order @@ -191,6 +202,9 @@ public static Ordering natural() { * * @deprecated no need to use this */ + @InlineMe( + replacement = "checkNotNull(ordering)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @GwtCompatible(serializable = true) @Deprecated public static Ordering from(Ordering ordering) { @@ -219,7 +233,7 @@ public static Ordering natural() { // TODO(kevinb): provide replacement @GwtCompatible(serializable = true) public static Ordering explicit(List valuesInOrder) { - return new ExplicitOrdering(valuesInOrder); + return new ExplicitOrdering<>(valuesInOrder); } /** @@ -273,13 +287,12 @@ public static Ordering explicit(T leastValue, T... remainingValuesInOrder * *

    The returned comparator is serializable. * - *

    Java 8 users: Use the lambda expression {@code (a, b) -> 0} instead (in certain cases - * you may need to cast that to {@code Comparator}). + *

    Java 8+ users: Use the lambda expression {@code (a, b) -> 0} instead (in certain + * cases you may need to cast that to {@code Comparator}). * * @since 13.0 */ @GwtCompatible(serializable = true) - @SuppressWarnings("unchecked") public static Ordering<@Nullable Object> allEqual() { return AllEqualOrdering.INSTANCE; } @@ -290,7 +303,7 @@ public static Ordering explicit(T leastValue, T... remainingValuesInOrder * *

    The comparator is serializable. * - *

    Java 8 users: Use {@code Comparator.comparing(Object::toString)} instead. + *

    Java 8+ users: Use {@code Comparator.comparing(Object::toString)} instead. */ @GwtCompatible(serializable = true) public static Ordering usingToString() { @@ -313,14 +326,17 @@ public static Ordering usingToString() { * @since 2.0 */ // TODO(kevinb): copy to Comparators, etc. + @J2ktIncompatible // MapMaker public static Ordering<@Nullable Object> arbitrary() { return ArbitraryOrderingHolder.ARBITRARY_ORDERING; } + @J2ktIncompatible // MapMaker private static class ArbitraryOrderingHolder { static final Ordering<@Nullable Object> ARBITRARY_ORDERING = new ArbitraryOrdering(); } + @J2ktIncompatible // MapMaker @VisibleForTesting static class ArbitraryOrdering extends Ordering<@Nullable Object> { @@ -344,7 +360,7 @@ private Integer getUid(Object obj) { } @Override - public int compare(@CheckForNull Object left, @CheckForNull Object right) { + public int compare(@Nullable Object left, @Nullable Object right) { if (left == right) { return 0; } else if (left == null) { @@ -398,20 +414,22 @@ protected Ordering() {} * Returns the reverse of this ordering; the {@code Ordering} equivalent to {@link * Collections#reverseOrder(Comparator)}. * - *

    Java 8 users: Use {@code thisComparator.reversed()} instead. + *

    Java 8+ users: Use {@code thisComparator.reversed()} instead. */ // type parameter lets us avoid the extra in statements like: // Ordering o = Ordering.natural().reverse(); @GwtCompatible(serializable = true) public Ordering reverse() { - return new ReverseOrdering(this); + return new ReverseOrdering<>(this); } /** * Returns an ordering that treats {@code null} as less than all other values and uses {@code * this} to compare non-null values. * - *

    Java 8 users: Use {@code Comparator.nullsFirst(thisComparator)} instead. + *

    The returned object is serializable if this object is serializable. + * + *

    Java 8+ users: Use {@code Comparator.nullsFirst(thisComparator)} instead. */ // type parameter lets us avoid the extra in statements like: // Ordering o = Ordering.natural().nullsFirst(); @@ -424,7 +442,9 @@ public Ordering reverse() { * Returns an ordering that treats {@code null} as greater than all other values and uses this * ordering to compare non-null values. * - *

    Java 8 users: Use {@code Comparator.nullsLast(thisComparator)} instead. + *

    The returned object is serializable if this object is serializable. + * + *

    Java 8+ users: Use {@code Comparator.nullsLast(thisComparator)} instead. */ // type parameter lets us avoid the extra in statements like: // Ordering o = Ordering.natural().nullsLast(); @@ -443,8 +463,8 @@ public Ordering reverse() { * .onResultOf(Functions.toStringFunction()) * } * - *

    Java 8 users: Use {@code Comparator.comparing(function, thisComparator)} instead (you - * can omit the comparator if it is the natural order). + *

    Java 8+ users: Use {@code Comparator.comparing(function, thisComparator)} instead + * (you can omit the comparator if it is the natural order). */ @GwtCompatible(serializable = true) public Ordering onResultOf(Function function) { @@ -464,13 +484,16 @@ public Ordering reverse() { *

    An ordering produced by this method, or a chain of calls to this method, is equivalent to * one created using {@link Ordering#compound(Iterable)} on the same component comparators. * - *

    Java 8 users: Use {@code thisComparator.thenComparing(secondaryComparator)} instead. + *

    The returned object is serializable if this object and {@code secondaryComparator} are both + * serializable. + * + *

    Java 8+ users: Use {@code thisComparator.thenComparing(secondaryComparator)} instead. * Depending on what {@code secondaryComparator} is, one of the other overloads of {@code * thenComparing} may be even more useful. */ @GwtCompatible(serializable = true) public Ordering compound(Comparator secondaryComparator) { - return new CompoundOrdering(this, checkNotNull(secondaryComparator)); + return new CompoundOrdering<>(this, checkNotNull(secondaryComparator)); } /** @@ -482,10 +505,12 @@ public Ordering compound(Comparator secondaryCompara *

    The returned ordering is equivalent to that produced using {@code * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. * + *

    The returned object is serializable if each of the {@code comparators} is serializable. + * *

    Warning: Supplying an argument with undefined iteration order, such as a {@link * HashSet}, will produce non-deterministic results. * - *

    Java 8 users: Use a chain of calls to {@link Comparator#thenComparing(Comparator)}, + *

    Java 8+ users: Use a chain of calls to {@link Comparator#thenComparing(Comparator)}, * or {@code comparatorCollection.stream().reduce(Comparator::thenComparing).get()} (if the * collection might be empty, also provide a default comparator as the {@code identity} parameter * to {@code reduce}). @@ -495,7 +520,7 @@ public Ordering compound(Comparator secondaryCompara @GwtCompatible(serializable = true) public static Ordering compound( Iterable> comparators) { - return new CompoundOrdering(comparators); + return new CompoundOrdering<>(comparators); } /** @@ -509,7 +534,7 @@ public Ordering compound(Comparator secondaryCompara * ordering.reverse().lexicographical()} (consider how each would order {@code [1]} and {@code [1, * 1]}). * - *

    Java 8 users: Use {@link Comparators#lexicographical(Comparator)} instead. + *

    Java 8+ users: Use {@link Comparators#lexicographical(Comparator)} instead. * * @since 2.0 */ @@ -530,7 +555,6 @@ public Ordering> lexicographical() { // Regular instance methods - @CanIgnoreReturnValue // TODO(kak): Consider removing this @Override public abstract int compare(@ParametricNullness T left, @ParametricNullness T right); @@ -539,7 +563,7 @@ public Ordering> lexicographical() { * least values, the first of those is returned. The iterator will be left exhausted: its {@code * hasNext()} method will return {@code false}. * - *

    Java 8 users: Use {@code Streams.stream(iterator).min(thisComparator).get()} instead + *

    Java 8+ users: Use {@code Streams.stream(iterator).min(thisComparator).get()} instead * (but note that it does not guarantee which tied minimum element is returned). * * @param iterator the iterator whose minimum element is to be determined @@ -554,7 +578,7 @@ public E min(Iterator iterator) { E minSoFar = iterator.next(); while (iterator.hasNext()) { - minSoFar = min(minSoFar, iterator.next()); + minSoFar = this.min(minSoFar, iterator.next()); } return minSoFar; @@ -564,10 +588,10 @@ public E min(Iterator iterator) { * Returns the least of the specified values according to this ordering. If there are multiple * least values, the first of those is returned. * - *

    Java 8 users: If {@code iterable} is a {@link Collection}, use {@code + *

    Java 8+ users: If {@code iterable} is a {@link Collection}, use {@code * Collections.min(collection, thisComparator)} instead. Otherwise, use {@code * Streams.stream(iterable).min(thisComparator).get()} instead. Note that these alternatives do - * not guarantee which tied minimum element is returned) + * not guarantee which tied minimum element is returned. * * @param iterable the iterable whose minimum element is to be determined * @throws NoSuchElementException if {@code iterable} is empty @@ -603,7 +627,7 @@ public E min(@ParametricNullness E a, @ParametricNullness E b) { * Returns the least of the specified values according to this ordering. If there are multiple * least values, the first of those is returned. * - *

    Java 8 users: Use {@code Collections.min(Arrays.asList(a, b, c...), thisComparator)} + *

    Java 8+ users: Use {@code Collections.min(Arrays.asList(a, b, c...), thisComparator)} * instead (but note that it does not guarantee which tied minimum element is returned). * * @param a value to compare, returned if less than or equal to the rest. @@ -630,7 +654,7 @@ public E min( * greatest values, the first of those is returned. The iterator will be left exhausted: its * {@code hasNext()} method will return {@code false}. * - *

    Java 8 users: Use {@code Streams.stream(iterator).max(thisComparator).get()} instead + *

    Java 8+ users: Use {@code Streams.stream(iterator).max(thisComparator).get()} instead * (but note that it does not guarantee which tied maximum element is returned). * * @param iterator the iterator whose maximum element is to be determined @@ -645,7 +669,7 @@ public E max(Iterator iterator) { E maxSoFar = iterator.next(); while (iterator.hasNext()) { - maxSoFar = max(maxSoFar, iterator.next()); + maxSoFar = this.max(maxSoFar, iterator.next()); } return maxSoFar; @@ -655,10 +679,10 @@ public E max(Iterator iterator) { * Returns the greatest of the specified values according to this ordering. If there are multiple * greatest values, the first of those is returned. * - *

    Java 8 users: If {@code iterable} is a {@link Collection}, use {@code + *

    Java 8+ users: If {@code iterable} is a {@link Collection}, use {@code * Collections.max(collection, thisComparator)} instead. Otherwise, use {@code * Streams.stream(iterable).max(thisComparator).get()} instead. Note that these alternatives do - * not guarantee which tied maximum element is returned) + * not guarantee which tied maximum element is returned. * * @param iterable the iterable whose maximum element is to be determined * @throws NoSuchElementException if {@code iterable} is empty @@ -694,7 +718,7 @@ public E max(@ParametricNullness E a, @ParametricNullness E b) { * Returns the greatest of the specified values according to this ordering. If there are multiple * greatest values, the first of those is returned. * - *

    Java 8 users: Use {@code Collections.max(Arrays.asList(a, b, c...), thisComparator)} + *

    Java 8+ users: Use {@code Collections.max(Arrays.asList(a, b, c...), thisComparator)} * instead (but note that it does not guarantee which tied maximum element is returned). * * @param a value to compare, returned if greater than or equal to the rest. @@ -724,8 +748,8 @@ public E max( *

    The implementation does not necessarily use a stable sorting algorithm; when multiple * elements are equivalent, it is undefined which will come first. * - *

    Java 8 users: Continue to use this method for now. After the next release of Guava, - * use {@code Streams.stream(iterable).collect(Comparators.least(k, thisComparator))} instead. + *

    Java 8+ users: Use {@code Streams.stream(iterable).collect(Comparators.least(k, + * thisComparator))} instead. * * @return an immutable {@code RandomAccess} list of the {@code k} least elements in ascending * order @@ -742,11 +766,11 @@ public List leastOf(Iterable iterable, int k) { @SuppressWarnings("unchecked") // c only contains E's and doesn't escape E[] array = (E[]) collection.toArray(); - Arrays.sort(array, this); + sort(array, this); if (array.length > k) { array = Arrays.copyOf(array, k); } - return Collections.unmodifiableList(Arrays.asList(array)); + return unmodifiableList(asList(array)); } } return leastOf(iterable.iterator(), k); @@ -760,7 +784,7 @@ public List leastOf(Iterable iterable, int k) { *

    The implementation does not necessarily use a stable sorting algorithm; when multiple * elements are equivalent, it is undefined which will come first. * - *

    Java 8 users: Use {@code Streams.stream(iterator).collect(Comparators.least(k, + *

    Java 8+ users: Use {@code Streams.stream(iterator).collect(Comparators.least(k, * thisComparator))} instead. * * @return an immutable {@code RandomAccess} list of the {@code k} least elements in ascending @@ -773,16 +797,16 @@ public List leastOf(Iterator iterator, int k) { checkNonnegative(k, "k"); if (k == 0 || !iterator.hasNext()) { - return Collections.emptyList(); + return emptyList(); } else if (k >= Integer.MAX_VALUE / 2) { // k is really large; just do a straightforward sorted-copy-and-sublist ArrayList list = Lists.newArrayList(iterator); - Collections.sort(list, this); + sort(list, this); if (list.size() > k) { list.subList(k, list.size()).clear(); } list.trimToSize(); - return Collections.unmodifiableList(list); + return unmodifiableList(list); } else { TopKSelector selector = TopKSelector.least(k, this); selector.offerAll(iterator); @@ -798,8 +822,8 @@ public List leastOf(Iterator iterator, int k) { *

    The implementation does not necessarily use a stable sorting algorithm; when multiple * elements are equivalent, it is undefined which will come first. * - *

    Java 8 users: Continue to use this method for now. After the next release of Guava, - * use {@code Streams.stream(iterable).collect(Comparators.greatest(k, thisComparator))} instead. + *

    Java 8+ users: Use {@code Streams.stream(iterable).collect(Comparators.greatest(k, + * thisComparator))} instead. * * @return an immutable {@code RandomAccess} list of the {@code k} greatest elements in * descending order @@ -809,7 +833,7 @@ public List leastOf(Iterator iterator, int k) { public List greatestOf(Iterable iterable, int k) { // TODO(kevinb): see if delegation is hurting performance noticeably // TODO(kevinb): if we change this implementation, add full unit tests. - return reverse().leastOf(iterable, k); + return this.reverse().leastOf(iterable, k); } /** @@ -820,7 +844,7 @@ public List greatestOf(Iterable iterable, int k) { *

    The implementation does not necessarily use a stable sorting algorithm; when multiple * elements are equivalent, it is undefined which will come first. * - *

    Java 8 users: Use {@code Streams.stream(iterator).collect(Comparators.greatest(k, + *

    Java 8+ users: Use {@code Streams.stream(iterator).collect(Comparators.greatest(k, * thisComparator))} instead. * * @return an immutable {@code RandomAccess} list of the {@code k} greatest elements in @@ -829,7 +853,7 @@ public List greatestOf(Iterable iterable, int k) { * @since 14.0 */ public List greatestOf(Iterator iterator, int k) { - return reverse().leastOf(iterator, k); + return this.reverse().leastOf(iterator, k); } /** @@ -851,8 +875,8 @@ public List greatestOf(Iterator iterator, int k) { public List sortedCopy(Iterable elements) { @SuppressWarnings("unchecked") // does not escape, and contains only E's E[] array = (E[]) Iterables.toArray(elements); - Arrays.sort(array, this); - return Lists.newArrayList(Arrays.asList(array)); + sort(array, this); + return Lists.newArrayList(asList(array)); } /** @@ -871,8 +895,7 @@ public List sortedCopy(Iterable elements) { * @since 3.0 */ // TODO(kevinb): rerun benchmarks including new options - @SuppressWarnings("nullness") // unsafe, but there's not much we can do about it now - public ImmutableList immutableSortedCopy(Iterable elements) { + public ImmutableList immutableSortedCopy(Iterable elements) { return ImmutableList.sortedCopyOf(this, elements); } @@ -881,7 +904,7 @@ public ImmutableList immutableSortedCopy(Iterable elements) * equal to the element that preceded it, according to this ordering. Note that this is always * true when the iterable has fewer than two elements. * - *

    Java 8 users: Use the equivalent {@link Comparators#isInOrder(Iterable, Comparator)} + *

    Java 8+ users: Use the equivalent {@link Comparators#isInOrder(Iterable, Comparator)} * instead, since the rest of {@code Ordering} is mostly obsolete (as explained in the class * documentation). */ @@ -905,7 +928,7 @@ public boolean isOrdered(Iterable iterable) { * greater than the element that preceded it, according to this ordering. Note that this is always * true when the iterable has fewer than two elements. * - *

    Java 8 users: Use the equivalent {@link Comparators#isInStrictOrder(Iterable, + *

    Java 8+ users: Use the equivalent {@link Comparators#isInStrictOrder(Iterable, * Comparator)} instead, since the rest of {@code Ordering} is mostly obsolete (as explained in * the class documentation). */ @@ -932,6 +955,13 @@ public boolean isStrictlyOrdered(Iterable iterable) { * @param key the key to be searched for * @deprecated Use {@link Collections#binarySearch(List, Object, Comparator)} directly. */ + @InlineMe( + replacement = "Collections.binarySearch(sortedList, key, this)", + imports = "java.util.Collections") + // We can't compatibly make this `final` now. + @InlineMeValidationDisabled( + "While binarySearch() is not final, the inlining is still safe as long as any overrides" + + " follow the contract.") @Deprecated public int binarySearch( List sortedList, @ParametricNullness T key) { @@ -943,7 +973,6 @@ public int binarySearch( * Object[])} comparator when comparing a value outside the set of values it can compare. * Extending {@link ClassCastException} may seem odd, but it is required. */ - @VisibleForTesting static class IncomparableValueException extends ClassCastException { final Object value; @@ -952,7 +981,7 @@ static class IncomparableValueException extends ClassCastException { this.value = value; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } // Never make these public diff --git a/android/guava/src/com/google/common/collect/ParametricNullness.java b/android/guava/src/com/google/common/collect/ParametricNullness.java index 94b1bcd3aaa2..d3d67ef6a186 100644 --- a/android/guava/src/com/google/common/collect/ParametricNullness.java +++ b/android/guava/src/com/google/common/collect/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/collect/PeekingIterator.java b/android/guava/src/com/google/common/collect/PeekingIterator.java index a274fe454a87..2c214c1ec809 100644 --- a/android/guava/src/com/google/common/collect/PeekingIterator.java +++ b/android/guava/src/com/google/common/collect/PeekingIterator.java @@ -21,13 +21,13 @@ import com.google.errorprone.annotations.DoNotMock; import java.util.Iterator; import java.util.NoSuchElementException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An iterator that supports a one-element lookahead while iterating. * *

    See the Guava User Guide article on {@code + * "https://github.com/google/guava/wiki/CollectionHelpersExplained#peekingiterator">{@code * PeekingIterator}. * * @author Mick Killianey @@ -35,7 +35,6 @@ */ @DoNotMock("Use Iterators.peekingIterator") @GwtCompatible -@ElementTypesAreNonnullByDefault public interface PeekingIterator extends Iterator { /** * Returns the next element in the iteration, without advancing the iteration. diff --git a/android/guava/src/com/google/common/collect/Platform.java b/android/guava/src/com/google/common/collect/Platform.java index 0b18e1ff396e..141bff4c4d70 100644 --- a/android/guava/src/com/google/common/collect/Platform.java +++ b/android/guava/src/com/google/common/collect/Platform.java @@ -17,11 +17,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.lang.reflect.Array; +import com.google.common.annotations.J2ktIncompatible; import java.util.Arrays; import java.util.Map; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Methods factored out so that they can be emulated differently in GWT. @@ -29,7 +29,6 @@ * @author Hayward Chan */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class Platform { /** Returns the platform preferred implementation of a map based on a hash table. */ static @@ -68,6 +67,15 @@ Map preservesInsertionOrderOnPutsMap() { return CompactHashMap.create(); } + /** + * Returns the platform preferred map implementation that preserves insertion order when used only + * for insertions, with a hint for how many entries to expect. + */ + static + Map preservesInsertionOrderOnPutsMapWithExpectedSize(int expectedSize) { + return Maps.newLinkedHashMapWithExpectedSize(expectedSize); + } + /** * Returns the platform preferred set implementation that preserves insertion order when used only * for insertions. @@ -89,13 +97,8 @@ Map preservesInsertionOrderOnPutsMap() { * ObjectArrays, which is the main caller of this method.) */ static T[] newArray(T[] reference, int length) { - Class type = reference.getClass().getComponentType(); - - // the cast is safe because - // result.getClass() == reference.getClass().getComponentType() - @SuppressWarnings("unchecked") - T[] result = (T[]) Array.newInstance(type, length); - return result; + T[] empty = reference.length == 0 ? reference : Arrays.copyOf(reference, 0); + return Arrays.copyOf(empty, length); } /** Equivalent to Arrays.copyOfRange(source, from, to, arrayOfType.getClass()). */ @@ -107,7 +110,11 @@ Map preservesInsertionOrderOnPutsMap() { * * - https://github.com/jspecify/jdk/commit/71d826792b8c7ef95d492c50a274deab938f2552 */ - @SuppressWarnings("nullness") + /* + * TODO(cpovirk): Is the unchecked cast avoidable? Would System.arraycopy be similarly fast (if + * likewise not type-checked)? Could our single caller do something different? + */ + @SuppressWarnings({"nullness", "unchecked"}) static T[] copy(Object[] source, int from, int to, T[] arrayOfType) { return Arrays.copyOfRange(source, from, to, (Class) arrayOfType.getClass()); } @@ -117,10 +124,15 @@ Map preservesInsertionOrderOnPutsMap() { * GWT). This is sometimes acceptable, when only server-side code could generate enough volume * that reclamation becomes important. */ + @J2ktIncompatible static MapMaker tryWeakKeys(MapMaker mapMaker) { return mapMaker.weakKeys(); } + static > Class getDeclaringClassOrObjectForJ2cl(E e) { + return e.getDeclaringClass(); + } + static int reduceIterationsIfGwt(int iterations) { return iterations; } @@ -129,7 +141,5 @@ static int reduceExponentIfGwt(int exponent) { return exponent; } - static void checkGwtRpcEnabled() {} - private Platform() {} } diff --git a/android/guava/src/com/google/common/collect/Queues.java b/android/guava/src/com/google/common/collect/Queues.java index 6a4dbd227ec0..7d050f33f5cb 100644 --- a/android/guava/src/com/google/common/collect/Queues.java +++ b/android/guava/src/com/google/common/collect/Queues.java @@ -14,11 +14,14 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; @@ -32,7 +35,7 @@ import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@link Queue} and {@link Deque} instances. Also see this @@ -42,7 +45,6 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Queues { private Queues() {} @@ -52,9 +54,10 @@ private Queues() {} * Creates an empty {@code ArrayBlockingQueue} with the given (fixed) capacity and nonfair access * policy. */ + @J2ktIncompatible @GwtIncompatible // ArrayBlockingQueue public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { - return new ArrayBlockingQueue(capacity); + return new ArrayBlockingQueue<>(capacity); } // ArrayDeque @@ -65,7 +68,7 @@ public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { * @since 12.0 */ public static ArrayDeque newArrayDeque() { - return new ArrayDeque(); + return new ArrayDeque<>(); } /** @@ -76,9 +79,9 @@ public static ArrayDeque newArrayDeque() { */ public static ArrayDeque newArrayDeque(Iterable elements) { if (elements instanceof Collection) { - return new ArrayDeque((Collection) elements); + return new ArrayDeque<>((Collection) elements); } - ArrayDeque deque = new ArrayDeque(); + ArrayDeque deque = new ArrayDeque<>(); Iterables.addAll(deque, elements); return deque; } @@ -86,22 +89,24 @@ public static ArrayDeque newArrayDeque(Iterable elements) { // ConcurrentLinkedQueue /** Creates an empty {@code ConcurrentLinkedQueue}. */ + @J2ktIncompatible @GwtIncompatible // ConcurrentLinkedQueue public static ConcurrentLinkedQueue newConcurrentLinkedQueue() { - return new ConcurrentLinkedQueue(); + return new ConcurrentLinkedQueue<>(); } /** * Creates a {@code ConcurrentLinkedQueue} containing the elements of the specified iterable, in * the order they are returned by the iterable's iterator. */ + @J2ktIncompatible @GwtIncompatible // ConcurrentLinkedQueue public static ConcurrentLinkedQueue newConcurrentLinkedQueue( Iterable elements) { if (elements instanceof Collection) { - return new ConcurrentLinkedQueue((Collection) elements); + return new ConcurrentLinkedQueue<>((Collection) elements); } - ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); Iterables.addAll(queue, elements); return queue; } @@ -113,9 +118,10 @@ public static ConcurrentLinkedQueue newConcurrentLinkedQueue( * * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // LinkedBlockingDeque public static LinkedBlockingDeque newLinkedBlockingDeque() { - return new LinkedBlockingDeque(); + return new LinkedBlockingDeque<>(); } /** @@ -124,9 +130,10 @@ public static LinkedBlockingDeque newLinkedBlockingDeque() { * @throws IllegalArgumentException if {@code capacity} is less than 1 * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // LinkedBlockingDeque public static LinkedBlockingDeque newLinkedBlockingDeque(int capacity) { - return new LinkedBlockingDeque(capacity); + return new LinkedBlockingDeque<>(capacity); } /** @@ -136,12 +143,13 @@ public static LinkedBlockingDeque newLinkedBlockingDeque(int capacity) { * * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // LinkedBlockingDeque public static LinkedBlockingDeque newLinkedBlockingDeque(Iterable elements) { if (elements instanceof Collection) { - return new LinkedBlockingDeque((Collection) elements); + return new LinkedBlockingDeque<>((Collection) elements); } - LinkedBlockingDeque deque = new LinkedBlockingDeque(); + LinkedBlockingDeque deque = new LinkedBlockingDeque<>(); Iterables.addAll(deque, elements); return deque; } @@ -149,9 +157,10 @@ public static LinkedBlockingDeque newLinkedBlockingDeque(Iterable LinkedBlockingQueue newLinkedBlockingQueue() { - return new LinkedBlockingQueue(); + return new LinkedBlockingQueue<>(); } /** @@ -159,9 +168,10 @@ public static LinkedBlockingQueue newLinkedBlockingQueue() { * * @throws IllegalArgumentException if {@code capacity} is less than 1 */ + @J2ktIncompatible @GwtIncompatible // LinkedBlockingQueue public static LinkedBlockingQueue newLinkedBlockingQueue(int capacity) { - return new LinkedBlockingQueue(capacity); + return new LinkedBlockingQueue<>(capacity); } /** @@ -172,12 +182,13 @@ public static LinkedBlockingQueue newLinkedBlockingQueue(int capacity) { * @param elements the elements that the queue should contain, in order * @return a new {@code LinkedBlockingQueue} containing those elements */ + @J2ktIncompatible @GwtIncompatible // LinkedBlockingQueue public static LinkedBlockingQueue newLinkedBlockingQueue(Iterable elements) { if (elements instanceof Collection) { - return new LinkedBlockingQueue((Collection) elements); + return new LinkedBlockingQueue<>((Collection) elements); } - LinkedBlockingQueue queue = new LinkedBlockingQueue(); + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); Iterables.addAll(queue, elements); return queue; } @@ -193,9 +204,11 @@ public static LinkedBlockingQueue newLinkedBlockingQueue(Iterable PriorityBlockingQueue newPriorityBlockingQueue() { - return new PriorityBlockingQueue(); + return new PriorityBlockingQueue<>(); } /** @@ -207,13 +220,15 @@ public static PriorityBlockingQueue newPriorityBlockin * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} * in 15.0) */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 + @J2ktIncompatible @GwtIncompatible // PriorityBlockingQueue public static PriorityBlockingQueue newPriorityBlockingQueue( Iterable elements) { if (elements instanceof Collection) { - return new PriorityBlockingQueue((Collection) elements); + return new PriorityBlockingQueue<>((Collection) elements); } - PriorityBlockingQueue queue = new PriorityBlockingQueue(); + PriorityBlockingQueue queue = new PriorityBlockingQueue<>(); Iterables.addAll(queue, elements); return queue; } @@ -227,8 +242,9 @@ public static PriorityBlockingQueue newPriorityBlockin * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} * in 15.0) */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static PriorityQueue newPriorityQueue() { - return new PriorityQueue(); + return new PriorityQueue<>(); } /** @@ -240,12 +256,13 @@ public static PriorityQueue newPriorityQueue() { * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} * in 15.0) */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static PriorityQueue newPriorityQueue( Iterable elements) { if (elements instanceof Collection) { - return new PriorityQueue((Collection) elements); + return new PriorityQueue<>((Collection) elements); } - PriorityQueue queue = new PriorityQueue(); + PriorityQueue queue = new PriorityQueue<>(); Iterables.addAll(queue, elements); return queue; } @@ -253,9 +270,34 @@ public static PriorityQueue newPriorityQueue( // SynchronousQueue /** Creates an empty {@code SynchronousQueue} with nonfair access policy. */ + @J2ktIncompatible @GwtIncompatible // SynchronousQueue public static SynchronousQueue newSynchronousQueue() { - return new SynchronousQueue(); + return new SynchronousQueue<>(); + } + + /** + * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested {@code + * numElements} elements are not available, it will wait for them up to the specified timeout. + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up + * @return the number of elements transferred + * @throws InterruptedException if interrupted while waiting + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @CanIgnoreReturnValue + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration + public static int drain( + BlockingQueue q, Collection buffer, int numElements, Duration timeout) + throws InterruptedException { + // TODO(b/126049426): Consider using saturateToNanos(timeout) instead. + return drain(q, buffer, numElements, timeout.toNanos(), NANOSECONDS); } /** @@ -270,8 +312,8 @@ public static SynchronousQueue newSynchronousQueue() { * @return the number of elements transferred * @throws InterruptedException if interrupted while waiting */ - @Beta @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // BlockingQueue @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static int drain( @@ -294,7 +336,7 @@ public static int drain( // elements already available (e.g. LinkedBlockingQueue#drainTo locks only once) added += q.drainTo(buffer, numElements - added); if (added < numElements) { // not enough elements immediately available; will have to poll - E e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + E e = q.poll(deadline - System.nanoTime(), NANOSECONDS); if (e == null) { break; // we already waited enough, and there are no more elements in sight } @@ -305,6 +347,30 @@ public static int drain( return added; } + /** + * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, Duration)}, but with a + * different behavior in case it is interrupted while waiting. In that case, the operation will + * continue as usual, and in the end the thread's interruption status will be set (no {@code + * InterruptedException} is thrown). + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up + * @return the number of elements transferred + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @CanIgnoreReturnValue + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration + public static int drainUninterruptibly( + BlockingQueue q, Collection buffer, int numElements, Duration timeout) { + // TODO(b/126049426): Consider using saturateToNanos(timeout) instead. + return drainUninterruptibly(q, buffer, numElements, timeout.toNanos(), NANOSECONDS); + } + /** * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, but * with a different behavior in case it is interrupted while waiting. In that case, the operation @@ -318,8 +384,8 @@ public static int drain( * @param unit a {@code TimeUnit} determining how to interpret the timeout parameter * @return the number of elements transferred */ - @Beta @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // BlockingQueue @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static int drainUninterruptibly( @@ -341,7 +407,7 @@ public static int drainUninterruptibly( E e; // written exactly once, by a successful (uninterrupted) invocation of #poll while (true) { try { - e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + e = q.poll(deadline - System.nanoTime(), NANOSECONDS); break; } catch (InterruptedException ex) { interrupted = true; // note interruption and retry @@ -391,6 +457,7 @@ public static int drainUninterruptibly( * @return a synchronized view of the specified queue * @since 14.0 */ + @J2ktIncompatible // Synchronized public static Queue synchronizedQueue(Queue queue) { return Synchronized.queue(queue, null); } @@ -424,6 +491,7 @@ public static int drainUninterruptibly( * @return a synchronized view of the specified deque * @since 15.0 */ + @J2ktIncompatible // Synchronized public static Deque synchronizedDeque(Deque deque) { return Synchronized.deque(deque, null); } diff --git a/android/guava/src/com/google/common/collect/Range.java b/android/guava/src/com/google/common/collect/Range.java index 2a5d464dc2a8..e32091409c21 100644 --- a/android/guava/src/com/google/common/collect/Range.java +++ b/android/guava/src/com/google/common/collect/Range.java @@ -20,15 +20,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Equivalence; -import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.SortedSet; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A range (or "interval") defines the boundaries around a contiguous span of values of some @@ -105,6 +108,7 @@ * P if, for all ranges {@code b} also having property P, {@code a.encloses(b)}. * Likewise, {@code a} is minimal when {@code b.encloses(a)} for all {@code b} having * property P. See, for example, the definition of {@link #intersection intersection}. + *

  • A {@code Range} is serializable if it has no bounds, or if each bound is serializable. * * *

    Further reading

    @@ -117,41 +121,13 @@ * @since 10.0 */ @GwtCompatible -@SuppressWarnings("rawtypes") -@ElementTypesAreNonnullByDefault +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 +@Immutable(containerOf = "C") public final class Range extends RangeGwtSerializationDependencies implements Predicate, Serializable { - - static class LowerBoundFn implements Function { - static final LowerBoundFn INSTANCE = new LowerBoundFn(); - - @Override - public Cut apply(Range range) { - return range.lowerBound; - } - } - - static class UpperBoundFn implements Function { - static final UpperBoundFn INSTANCE = new UpperBoundFn(); - - @Override - public Cut apply(Range range) { - return range.upperBound; - } - } - @SuppressWarnings("unchecked") - static > Function, Cut> lowerBoundFn() { - return (Function) LowerBoundFn.INSTANCE; - } - - @SuppressWarnings("unchecked") - static > Function, Cut> upperBoundFn() { - return (Function) UpperBoundFn.INSTANCE; - } - static > Ordering> rangeLexOrdering() { - return (Ordering>) (Ordering) RangeLexOrdering.INSTANCE; + return (Ordering>) RangeLexOrdering.INSTANCE; } static > Range create(Cut lowerBound, Cut upperBound) { @@ -257,9 +233,8 @@ public static > Range upTo(C endpoint, BoundType boun return lessThan(endpoint); case CLOSED: return atMost(endpoint); - default: - throw new AssertionError(); } + throw new AssertionError(); } /** @@ -292,9 +267,8 @@ public static > Range downTo(C endpoint, BoundType bo return greaterThan(endpoint); case CLOSED: return atLeast(endpoint); - default: - throw new AssertionError(); } + throw new AssertionError(); } private static final Range ALL = new Range<>(Cut.belowAll(), Cut.aboveAll()); @@ -333,7 +307,7 @@ public static > Range encloseAll(Iterable values) if (values instanceof SortedSet) { SortedSet set = (SortedSet) values; Comparator comparator = set.comparator(); - if (Ordering.natural().equals(comparator) || comparator == null) { + if (Ordering.natural().equals(comparator) || comparator == null) { return closed(set.first(), set.last()); } } @@ -342,8 +316,8 @@ public static > Range encloseAll(Iterable values) C max = min; while (valueIterator.hasNext()) { C value = checkNotNull(valueIterator.next()); - min = Ordering.natural().min(min, value); - max = Ordering.natural().max(max, value); + min = Ordering.natural().min(min, value); + max = Ordering.natural().max(max, value); } return closed(min, max); } @@ -441,6 +415,7 @@ public boolean contains(C value) { * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #contains} * instead. */ + @InlineMe(replacement = "this.contains(input)") @Deprecated @Override public boolean apply(C input) { @@ -674,7 +649,7 @@ public Range canonical(DiscreteDomain domain) { * {@code [3..3)}, {@code (3..3]}, {@code (4..4]} are all unequal. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Range) { Range other = (Range) object; return lowerBound.equals(other.lowerBound) && upperBound.equals(other.upperBound); @@ -705,6 +680,16 @@ private static String toString(Cut lowerBound, Cut upperBound) { return sb.toString(); } + // We declare accessors so that we can use method references like `Range::lowerBound`. + + Cut lowerBound() { + return lowerBound; + } + + Cut upperBound() { + return upperBound; + } + Object readResolve() { if (this.equals(ALL)) { return all(); @@ -720,7 +705,7 @@ static int compareOrThrow(Comparable left, Comparable right) { /** Needed to serialize sorted collections of Ranges. */ private static class RangeLexOrdering extends Ordering> implements Serializable { - static final Ordering> INSTANCE = new RangeLexOrdering(); + static final Ordering INSTANCE = new RangeLexOrdering(); @Override public int compare(Range left, Range right) { @@ -730,8 +715,8 @@ public int compare(Range left, Range right) { .result(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/RangeGwtSerializationDependencies.java b/android/guava/src/com/google/common/collect/RangeGwtSerializationDependencies.java index 222c1285fb4b..b0b67d4071f7 100644 --- a/android/guava/src/com/google/common/collect/RangeGwtSerializationDependencies.java +++ b/android/guava/src/com/google/common/collect/RangeGwtSerializationDependencies.java @@ -28,5 +28,6 @@ * *

    TODO(cpovirk): Consider applying this subclass approach to our other types. */ +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtCompatible(emulated = true) abstract class RangeGwtSerializationDependencies implements Serializable {} diff --git a/android/guava/src/com/google/common/collect/RangeMap.java b/android/guava/src/com/google/common/collect/RangeMap.java index 14adb00c18c0..083a12cc5c87 100644 --- a/android/guava/src/com/google/common/collect/RangeMap.java +++ b/android/guava/src/com/google/common/collect/RangeMap.java @@ -16,14 +16,13 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.errorprone.annotations.DoNotMock; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A mapping from disjoint nonempty ranges to non-null values. Queries look up the value associated @@ -35,10 +34,9 @@ * @author Louis Wasserman * @since 14.0 */ -@Beta +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @DoNotMock("Use ImmutableRangeMap or TreeRangeMap") @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface RangeMap { /* * TODO(cpovirk): These docs sometimes say "map" and sometimes say "range map." Pick one, or at @@ -51,15 +49,13 @@ public interface RangeMap { *

    Specifically, if any range in this range map contains the specified key, the value * associated with that range is returned. */ - @CheckForNull - V get(K key); + @Nullable V get(K key); /** * Returns the range containing this key and its associated value, if such a range is present in * the range map, or {@code null} otherwise. */ - @CheckForNull - Entry, V> getEntry(K key); + @Nullable Entry, V> getEntry(K key); /** * Returns the minimal range {@linkplain Range#encloses(Range) enclosing} the ranges in this @@ -101,7 +97,7 @@ public interface RangeMap { void putCoalescing(Range range, V value); /** Puts all the associations from {@code rangeMap} into this range map (optional operation). */ - void putAll(RangeMap rangeMap); + void putAll(RangeMap rangeMap); /** Removes all associations from this range map (optional operation). */ void clear(); @@ -160,7 +156,7 @@ public interface RangeMap { * #asMapOfRanges()}. */ @Override - boolean equals(@CheckForNull Object o); + boolean equals(@Nullable Object o); /** Returns {@code asMapOfRanges().hashCode()}. */ @Override diff --git a/android/guava/src/com/google/common/collect/RangeSet.java b/android/guava/src/com/google/common/collect/RangeSet.java index edb0f792a4b0..76f2098b9d52 100644 --- a/android/guava/src/com/google/common/collect/RangeSet.java +++ b/android/guava/src/com/google/common/collect/RangeSet.java @@ -14,12 +14,11 @@ package com.google.common.collect; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.errorprone.annotations.DoNotMock; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A set comprising zero or more {@linkplain Range#isEmpty nonempty}, {@linkplain @@ -43,16 +42,15 @@ *

    For a {@link Set} whose contents are specified by a {@link Range}, see {@link ContiguousSet}. * *

    See the Guava User Guide article on RangeSets. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#rangeset">RangeSets. * * @author Kevin Bourrillion * @author Louis Wasserman * @since 14.0 */ -@Beta +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @DoNotMock("Use ImmutableRangeSet or TreeRangeSet") @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface RangeSet { // Query methods @@ -64,8 +62,7 @@ public interface RangeSet { * Returns the unique range from this range set that {@linkplain Range#contains contains} {@code * value}, or {@code null} if this range set does not contain {@code value}. */ - @CheckForNull - Range rangeContaining(C value); + @Nullable Range rangeContaining(C value); /** * Returns {@code true} if there exists a non-empty range enclosed by both a member range in this @@ -248,7 +245,7 @@ public interface RangeSet { * according to {@link Range#equals(Object)}. */ @Override - boolean equals(@CheckForNull Object obj); + boolean equals(@Nullable Object obj); /** Returns {@code asRanges().hashCode()}. */ @Override diff --git a/android/guava/src/com/google/common/collect/RegularContiguousSet.java b/android/guava/src/com/google/common/collect/RegularContiguousSet.java index 787606eb1f81..26b5ce6e51df 100644 --- a/android/guava/src/com/google/common/collect/RegularContiguousSet.java +++ b/android/guava/src/com/google/common/collect/RegularContiguousSet.java @@ -22,9 +22,12 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Collection; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link ContiguousSet} that contains one or more elements. @@ -32,8 +35,7 @@ * @author Gregory Kick */ @GwtCompatible(emulated = true) -@SuppressWarnings("unchecked") // allow ungenerified Comparable types -@ElementTypesAreNonnullByDefault +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 final class RegularContiguousSet extends ContiguousSet { private final Range range; @@ -54,6 +56,7 @@ ContiguousSet headSetImpl(C toElement, boolean inclusive) { } @Override + @SuppressWarnings("unchecked") // TODO(cpovirk): Use a shared unsafeCompare method. ContiguousSet subSetImpl( C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { if (fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive) { @@ -73,9 +76,15 @@ ContiguousSet tailSetImpl(C fromElement, boolean inclusive) { @GwtIncompatible // not used by GWT emulation @Override - int indexOf(@CheckForNull Object target) { + int indexOf(@Nullable Object target) { + if (!contains(target)) { + return -1; + } + // The cast is safe because of the contains check—at least for any reasonable Comparable class. + @SuppressWarnings("unchecked") // requireNonNull is safe because of the contains check. - return contains(target) ? (int) domain.distance(first(), (C) requireNonNull(target)) : -1; + C c = (C) requireNonNull(target); + return (int) domain.distance(first(), c); } @Override @@ -84,8 +93,7 @@ public UnmodifiableIterator iterator() { final C last = last(); @Override - @CheckForNull - protected C computeNext(C previous) { + protected @Nullable C computeNext(C previous) { return equalsOrThrow(previous, last) ? null : domain.next(previous); } }; @@ -98,14 +106,13 @@ public UnmodifiableIterator descendingIterator() { final C first = first(); @Override - @CheckForNull - protected C computeNext(C previous) { + protected @Nullable C computeNext(C previous) { return equalsOrThrow(previous, first) ? null : domain.previous(previous); } }; } - private static boolean equalsOrThrow(Comparable left, @CheckForNull Comparable right) { + private static boolean equalsOrThrow(Comparable left, @Nullable Comparable right) { return right != null && Range.compareOrThrow(left, right) == 0; } @@ -140,6 +147,15 @@ public C get(int i) { checkElementIndex(i, size()); return domain.offset(first(), i); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } else { return super.createAsList(); @@ -153,12 +169,14 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object == null) { return false; } try { - return range.contains((C) object); + @SuppressWarnings("unchecked") // The worst case is usually CCE, which we catch. + C c = (C) object; + return range.contains(c); } catch (ClassCastException e) { return false; } @@ -175,14 +193,15 @@ public boolean isEmpty() { } @Override + @SuppressWarnings("unchecked") // TODO(cpovirk): Use a shared unsafeCompare method. public ContiguousSet intersection(ContiguousSet other) { checkNotNull(other); checkArgument(this.domain.equals(other.domain)); if (other.isEmpty()) { return other; } else { - C lowerEndpoint = Ordering.natural().max(this.first(), other.first()); - C upperEndpoint = Ordering.natural().min(this.last(), other.last()); + C lowerEndpoint = Ordering.natural().max(this.first(), other.first()); + C upperEndpoint = Ordering.natural().min(this.last(), other.last()); return (lowerEndpoint.compareTo(upperEndpoint) <= 0) ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain) : new EmptyContiguousSet(domain); @@ -202,7 +221,7 @@ public Range range(BoundType lowerBoundType, BoundType upperBoundType) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } else if (object instanceof RegularContiguousSet) { @@ -221,6 +240,7 @@ public int hashCode() { } @GwtIncompatible // serialization + @J2ktIncompatible private static final class SerializedForm implements Serializable { final Range range; final DiscreteDomain domain; @@ -236,10 +256,17 @@ private Object readResolve() { } @GwtIncompatible // serialization + @J2ktIncompatible @Override Object writeReplace() { return new SerializedForm<>(range, domain); } - private static final long serialVersionUID = 0; + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableAsList.java b/android/guava/src/com/google/common/collect/RegularImmutableAsList.java index e344db1eab17..557359ad753d 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableAsList.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableAsList.java @@ -18,8 +18,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * An {@link ImmutableAsList} implementation specialized for when the delegate collection is already @@ -29,7 +29,6 @@ */ @GwtCompatible(emulated = true) @SuppressWarnings("serial") // uses writeReplace, not default serialization -@ElementTypesAreNonnullByDefault class RegularImmutableAsList extends ImmutableAsList { private final ImmutableCollection delegate; private final ImmutableList delegateList; @@ -69,9 +68,7 @@ int copyIntoArray(@Nullable Object[] dst, int offset) { } @Override - @CheckForNull - @Nullable - Object[] internalArray() { + @Nullable Object @Nullable [] internalArray() { return delegateList.internalArray(); } @@ -89,4 +86,13 @@ int internalArrayEnd() { public E get(int index) { return delegateList.get(index); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableBiMap.java b/android/guava/src/com/google/common/collect/RegularImmutableBiMap.java index 460747747e2c..253af71b056e 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableBiMap.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableBiMap.java @@ -17,9 +17,10 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Bimap with zero or more mappings. @@ -28,11 +29,10 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization -@ElementTypesAreNonnullByDefault final class RegularImmutableBiMap extends ImmutableBiMap { static final RegularImmutableBiMap EMPTY = new RegularImmutableBiMap<>(); - @CheckForNull private final transient Object keyHashTable; + private final transient @Nullable Object keyHashTable; @VisibleForTesting final transient @Nullable Object[] alternatingKeysAndValues; private final transient int keyOffset; // 0 for K-to-V, 1 for V-to-K private final transient int size; @@ -55,16 +55,16 @@ private RegularImmutableBiMap() { this.keyOffset = 0; int tableSize = (size >= 2) ? ImmutableSet.chooseTableSize(size) : 0; this.keyHashTable = - RegularImmutableMap.createHashTable(alternatingKeysAndValues, size, tableSize, 0); + RegularImmutableMap.createHashTableOrThrow(alternatingKeysAndValues, size, tableSize, 0); Object valueHashTable = - RegularImmutableMap.createHashTable(alternatingKeysAndValues, size, tableSize, 1); + RegularImmutableMap.createHashTableOrThrow(alternatingKeysAndValues, size, tableSize, 1); this.inverse = new RegularImmutableBiMap(valueHashTable, alternatingKeysAndValues, size, this); } /** V-to-K constructor. */ private RegularImmutableBiMap( - @CheckForNull Object valueHashTable, + @Nullable Object valueHashTable, @Nullable Object[] alternatingKeysAndValues, int size, RegularImmutableBiMap inverse) { @@ -87,8 +87,7 @@ public ImmutableBiMap inverse() { @SuppressWarnings("unchecked") @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { Object result = RegularImmutableMap.get(keyHashTable, alternatingKeysAndValues, size, keyOffset, key); /* @@ -120,4 +119,13 @@ ImmutableSet createKeySet() { boolean isPartialView() { return false; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableList.java b/android/guava/src/com/google/common/collect/RegularImmutableList.java index baf1d66ed804..2f167282a143 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableList.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableList.java @@ -17,11 +17,14 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkElementIndex; +import static java.lang.System.arraycopy; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableList} backed by a simple array. @@ -30,7 +33,6 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization -@ElementTypesAreNonnullByDefault class RegularImmutableList extends ImmutableList { static final ImmutableList EMPTY = new RegularImmutableList<>(new Object[0], 0); @@ -54,8 +56,7 @@ boolean isPartialView() { } @Override - @Nullable - Object[] internalArray() { + @Nullable Object[] internalArray() { return array; } @@ -71,7 +72,7 @@ int internalArrayEnd() { @Override int copyIntoArray(@Nullable Object[] dst, int dstOff) { - System.arraycopy(array, 0, dst, dstOff, size); + arraycopy(array, 0, dst, dstOff, size); return dstOff + size; } @@ -85,4 +86,13 @@ public E get(int index) { } // TODO(lowasser): benchmark optimizations for equals() and see if they're worthwhile + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableMap.java b/android/guava/src/com/google/common/collect/RegularImmutableMap.java index d8f9f3b27faf..18b32fd58096 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableMap.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableMap.java @@ -22,12 +22,13 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.util.AbstractMap; import java.util.Arrays; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A hash-based implementation of {@link ImmutableMap}. @@ -35,7 +36,6 @@ * @author Louis Wasserman */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault final class RegularImmutableMap extends ImmutableMap { private static final byte ABSENT = -1; @@ -67,15 +67,42 @@ final class RegularImmutableMap extends ImmutableMap { * & (table.length - 1) instead of % table.length, though. */ - @CheckForNull private final transient Object hashTable; + private final transient @Nullable Object hashTable; @VisibleForTesting final transient @Nullable Object[] alternatingKeysAndValues; private final transient int size; - @SuppressWarnings("unchecked") + /* + * We have some considerable complexity in these create methods because of + * Builder.buildKeepingLast(). The same Builder might be called with buildKeepingLast() and then + * buildOrThrow(), or vice versa. So in particular, if we modify alternatingKeysAndValues to + * eliminate duplicate keys (for buildKeepingLast()) then we have to ensure that a later call to + * buildOrThrow() will still throw as if the duplicates had not been eliminated. And the exception + * message must mention two values that were associated with the duplicate key in two different + * calls to Builder.put (though we don't really care *which* two values if there were more than + * two). These considerations lead us to have a field of type DuplicateKey in the Builder, which + * will remember the first duplicate key we encountered. All later calls to buildOrThrow() can + * mention that key with its values. Further duplicates might be added in the meantime but since + * builders only ever accumulate entries it will always be valid to throw from buildOrThrow() with + * the first duplicate. + */ + + // This entry point is for callers other than ImmutableMap.Builder. static RegularImmutableMap create( int n, @Nullable Object[] alternatingKeysAndValues) { + return create(n, alternatingKeysAndValues, /* builder= */ null); + } + + // This entry point is used by the other create method but also directly by + // ImmutableMap.Builder, so that it can remember any DuplicateKey encountered and produce an + // exception for a later buildOrThrow(). If builder is null that means that a duplicate + // key will lead to an immediate exception. If it is not null then a duplicate key will instead be + // stored in the builder, which may use it to throw an exception later. + static RegularImmutableMap create( + int n, @Nullable Object[] alternatingKeysAndValues, @Nullable Builder builder) { if (n == 0) { - return (RegularImmutableMap) EMPTY; + @SuppressWarnings("unchecked") + RegularImmutableMap empty = (RegularImmutableMap) EMPTY; + return empty; } else if (n == 1) { // requireNonNull is safe because the first `2*n` elements have been filled in. checkEntryNotNull( @@ -84,16 +111,43 @@ static RegularImmutableMap create( } checkPositionIndex(n, alternatingKeysAndValues.length >> 1); int tableSize = ImmutableSet.chooseTableSize(n); - Object hashTable = createHashTable(alternatingKeysAndValues, n, tableSize, 0); + // If there are no duplicate keys, hashTablePlus is the final hashTable value. If there *are* + // duplicate keys, hashTablePlus consists of 3 elements: [0] the hashTable; [1] the number of + // entries in alternatingKeysAndValues that are still valid after rewriting to remove + // duplicates; [2] a Builder.DuplicateKey that records the first duplicate key we encountered + // for possible later use in exceptions, perhaps straight away. + Object hashTablePlus = createHashTable(alternatingKeysAndValues, n, tableSize, 0); + Object hashTable; + if (hashTablePlus instanceof Object[]) { + Object[] hashTableAndSizeAndDuplicate = (Object[]) hashTablePlus; + Builder.DuplicateKey duplicateKey = (Builder.DuplicateKey) hashTableAndSizeAndDuplicate[2]; + if (builder == null) { + throw duplicateKey.exception(); + } + builder.duplicateKey = duplicateKey; + hashTable = hashTableAndSizeAndDuplicate[0]; + n = (Integer) hashTableAndSizeAndDuplicate[1]; + alternatingKeysAndValues = Arrays.copyOf(alternatingKeysAndValues, n * 2); + } else { + hashTable = hashTablePlus; + } return new RegularImmutableMap(hashTable, alternatingKeysAndValues, n); } /** * Returns a hash table for the specified keys and values, and ensures that neither keys nor - * values are null. + * values are null. This method may update {@code alternatingKeysAndValues} if there are duplicate + * keys. If so, the return value will indicate how many entries are still valid, and will also + * include a {@link Builder.DuplicateKey} in case duplicate keys are not allowed now or will not + * be allowed on a later {@link Builder#buildOrThrow()} call. + * + * @param keyOffset 1 if this is the reverse direction of a BiMap, 0 otherwise. + * @return an {@code Object} that is a {@code byte[]}, {@code short[]}, or {@code int[]}, the + * smallest possible to fit {@code tableSize}; or an {@code Object[]} where [0] is one of + * these; [1] indicates how many element pairs in {@code alternatingKeysAndValues} are valid; + * and [2] is a {@link Builder.DuplicateKey} for the first duplicate key encountered. */ - @CheckForNull - static Object createHashTable( + private static @Nullable Object createHashTable( @Nullable Object[] alternatingKeysAndValues, int n, int tableSize, int keyOffset) { if (n == 1) { // for n=1 we don't create a hash table, but we need to do the checkEntryNotNull check! @@ -104,6 +158,7 @@ static Object createHashTable( return null; } int mask = tableSize - 1; + Builder.DuplicateKey duplicateKey = null; if (tableSize <= BYTE_MAX_SIZE) { /* * Use 8 bits per entry. The value is unsigned to allow use up to a size of 2^8. @@ -114,8 +169,11 @@ static Object createHashTable( byte[] hashTable = new byte[tableSize]; Arrays.fill(hashTable, ABSENT); + int outI = 0; + entries: for (int i = 0; i < n; i++) { int keyIndex = 2 * i + keyOffset; + int outKeyIndex = 2 * outI + keyOffset; // requireNonNull is safe because the first `2*n` elements have been filled in. Object key = requireNonNull(alternatingKeysAndValues[keyIndex]); Object value = requireNonNull(alternatingKeysAndValues[keyIndex ^ 1]); @@ -124,14 +182,23 @@ static Object createHashTable( h &= mask; int previousKeyIndex = hashTable[h] & BYTE_MASK; // unsigned read if (previousKeyIndex == BYTE_MASK) { // -1 signed becomes 255 unsigned - hashTable[h] = (byte) keyIndex; + hashTable[h] = (byte) outKeyIndex; break; } else if (key.equals(alternatingKeysAndValues[previousKeyIndex])) { - throw duplicateKeyException(key, value, alternatingKeysAndValues, previousKeyIndex); + duplicateKey = + new Builder.DuplicateKey( + key, value, requireNonNull(alternatingKeysAndValues[previousKeyIndex ^ 1])); + alternatingKeysAndValues[previousKeyIndex ^ 1] = value; + continue entries; } } + if (outI < i) { // if outI == i don't bother writing the values back where they came from + alternatingKeysAndValues[outKeyIndex] = key; + alternatingKeysAndValues[outKeyIndex ^ 1] = value; + } + outI++; } - return hashTable; + return outI == n ? hashTable : new Object[] {hashTable, outI, duplicateKey}; } else if (tableSize <= SHORT_MAX_SIZE) { /* * Use 16 bits per entry. The value is unsigned to allow use up to a size of 2^16. @@ -142,8 +209,11 @@ static Object createHashTable( short[] hashTable = new short[tableSize]; Arrays.fill(hashTable, ABSENT); + int outI = 0; + entries: for (int i = 0; i < n; i++) { int keyIndex = 2 * i + keyOffset; + int outKeyIndex = 2 * outI + keyOffset; // requireNonNull is safe because the first `2*n` elements have been filled in. Object key = requireNonNull(alternatingKeysAndValues[keyIndex]); Object value = requireNonNull(alternatingKeysAndValues[keyIndex ^ 1]); @@ -152,14 +222,23 @@ static Object createHashTable( h &= mask; int previousKeyIndex = hashTable[h] & SHORT_MASK; // unsigned read if (previousKeyIndex == SHORT_MASK) { // -1 signed becomes 65_535 unsigned - hashTable[h] = (short) keyIndex; + hashTable[h] = (short) outKeyIndex; break; } else if (key.equals(alternatingKeysAndValues[previousKeyIndex])) { - throw duplicateKeyException(key, value, alternatingKeysAndValues, previousKeyIndex); + duplicateKey = + new Builder.DuplicateKey( + key, value, requireNonNull(alternatingKeysAndValues[previousKeyIndex ^ 1])); + alternatingKeysAndValues[previousKeyIndex ^ 1] = value; + continue entries; } } + if (outI < i) { // if outI == i don't bother writing the values back where they came from + alternatingKeysAndValues[outKeyIndex] = key; + alternatingKeysAndValues[outKeyIndex ^ 1] = value; + } + outI++; } - return hashTable; + return outI == n ? hashTable : new Object[] {hashTable, outI, duplicateKey}; } else { /* * Use 32 bits per entry. @@ -167,8 +246,11 @@ static Object createHashTable( int[] hashTable = new int[tableSize]; Arrays.fill(hashTable, ABSENT); + int outI = 0; + entries: for (int i = 0; i < n; i++) { int keyIndex = 2 * i + keyOffset; + int outKeyIndex = 2 * outI + keyOffset; // requireNonNull is safe because the first `2*n` elements have been filled in. Object key = requireNonNull(alternatingKeysAndValues[keyIndex]); Object value = requireNonNull(alternatingKeysAndValues[keyIndex ^ 1]); @@ -177,32 +259,39 @@ static Object createHashTable( h &= mask; int previousKeyIndex = hashTable[h]; if (previousKeyIndex == ABSENT) { - hashTable[h] = keyIndex; + hashTable[h] = outKeyIndex; break; } else if (key.equals(alternatingKeysAndValues[previousKeyIndex])) { - throw duplicateKeyException(key, value, alternatingKeysAndValues, previousKeyIndex); + duplicateKey = + new Builder.DuplicateKey( + key, value, requireNonNull(alternatingKeysAndValues[previousKeyIndex ^ 1])); + alternatingKeysAndValues[previousKeyIndex ^ 1] = value; + continue entries; } } + if (outI < i) { // if outI == i don't bother writing the values back where they came from + alternatingKeysAndValues[outKeyIndex] = key; + alternatingKeysAndValues[outKeyIndex ^ 1] = value; + } + outI++; } - return hashTable; + return outI == n ? hashTable : new Object[] {hashTable, outI, duplicateKey}; } } - private static IllegalArgumentException duplicateKeyException( - Object key, Object value, @Nullable Object[] alternatingKeysAndValues, int previousKeyIndex) { - return new IllegalArgumentException( - "Multiple entries with same key: " - + key - + "=" - + value - + " and " - + alternatingKeysAndValues[previousKeyIndex] - + "=" - + alternatingKeysAndValues[previousKeyIndex ^ 1]); + static @Nullable Object createHashTableOrThrow( + @Nullable Object[] alternatingKeysAndValues, int n, int tableSize, int keyOffset) { + Object hashTablePlus = createHashTable(alternatingKeysAndValues, n, tableSize, keyOffset); + if (hashTablePlus instanceof Object[]) { + Object[] hashTableAndSizeAndDuplicate = (Object[]) hashTablePlus; + Builder.DuplicateKey duplicateKey = (Builder.DuplicateKey) hashTableAndSizeAndDuplicate[2]; + throw duplicateKey.exception(); + } + return hashTablePlus; } private RegularImmutableMap( - @CheckForNull Object hashTable, @Nullable Object[] alternatingKeysAndValues, int size) { + @Nullable Object hashTable, @Nullable Object[] alternatingKeysAndValues, int size) { this.hashTable = hashTable; this.alternatingKeysAndValues = alternatingKeysAndValues; this.size = size; @@ -215,8 +304,7 @@ public int size() { @SuppressWarnings("unchecked") @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { Object result = get(hashTable, alternatingKeysAndValues, size, 0, key); /* * We can't simply cast the result of `RegularImmutableMap.get` to V because of a bug in our @@ -229,13 +317,12 @@ public V get(@CheckForNull Object key) { } } - @CheckForNull - static Object get( - @CheckForNull Object hashTableObject, + static @Nullable Object get( + @Nullable Object hashTableObject, @Nullable Object[] alternatingKeysAndValues, int size, int keyOffset, - @CheckForNull Object key) { + @Nullable Object key) { if (key == null) { return null; } else if (size == 1) { @@ -313,7 +400,7 @@ public UnmodifiableIterator> iterator() { } @Override - int copyIntoArray(Object[] dst, int offset) { + int copyIntoArray(@Nullable Object[] dst, int offset) { return asList().copyIntoArray(dst, offset); } @@ -343,11 +430,19 @@ public int size() { public boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object instanceof Entry) { Entry entry = (Entry) object; Object k = entry.getKey(); @@ -366,6 +461,15 @@ boolean isPartialView() { public int size() { return size; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override @@ -403,6 +507,13 @@ boolean isPartialView() { public int size() { return size; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + Object writeReplace() { + return super.writeReplace(); + } } static final class KeySet extends ImmutableSet { @@ -420,7 +531,7 @@ public UnmodifiableIterator iterator() { } @Override - int copyIntoArray(Object[] dst, int offset) { + int copyIntoArray(@Nullable Object[] dst, int offset) { return asList().copyIntoArray(dst, offset); } @@ -430,7 +541,7 @@ public ImmutableList asList() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return map.get(object) != null; } @@ -443,6 +554,15 @@ boolean isPartialView() { public int size() { return map.size(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @SuppressWarnings("unchecked") @@ -456,7 +576,16 @@ boolean isPartialView() { return false; } + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } + // This class is never actually serialized directly, but we have to make the // warning go away (and suppressing would suppress for all nested classes too) - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableMultiset.java b/android/guava/src/com/google/common/collect/RegularImmutableMultiset.java index 6fbc099c8708..2dba8ffbeb1b 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableMultiset.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableMultiset.java @@ -16,12 +16,13 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.Multiset.Entry; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableMultiset} with zero or more elements. @@ -31,7 +32,6 @@ */ @GwtCompatible(emulated = true, serializable = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization -@ElementTypesAreNonnullByDefault class RegularImmutableMultiset extends ImmutableMultiset { static final RegularImmutableMultiset EMPTY = new RegularImmutableMultiset<>(ObjectCountHashMap.create()); @@ -39,7 +39,7 @@ class RegularImmutableMultiset extends ImmutableMultiset { final transient ObjectCountHashMap contents; private final transient int size; - @LazyInit @CheckForNull private transient ImmutableSet elementSet; + @LazyInit private transient @Nullable ImmutableSet elementSet; RegularImmutableMultiset(ObjectCountHashMap contents) { this.contents = contents; @@ -56,7 +56,7 @@ boolean isPartialView() { } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { return contents.get(element); } @@ -80,7 +80,7 @@ E get(int index) { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return RegularImmutableMultiset.this.contains(object); } @@ -93,6 +93,15 @@ boolean isPartialView() { public int size() { return contents.size(); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } @Override @@ -127,11 +136,12 @@ Object readResolve() { return builder.build(); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - @GwtIncompatible @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization Object writeReplace() { return new SerializedForm(this); } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableSet.java b/android/guava/src/com/google/common/collect/RegularImmutableSet.java index 2382ef62e798..b62931a2e302 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableSet.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableSet.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static java.lang.System.arraycopy; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableSet} with two or more elements. @@ -28,7 +31,6 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization -@ElementTypesAreNonnullByDefault final class RegularImmutableSet extends ImmutableSet { private static final Object[] EMPTY_ARRAY = new Object[0]; static final RegularImmutableSet EMPTY = @@ -53,7 +55,7 @@ final class RegularImmutableSet extends ImmutableSet { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { @Nullable Object[] table = this.table; if (target == null || table.length == 0) { return false; @@ -74,14 +76,16 @@ public int size() { return size; } + // We're careful to put only E instances into the array in the mainline. + // (In the backport, we don't need this suppression, but we keep it to minimize diffs.) + @SuppressWarnings("unchecked") @Override public UnmodifiableIterator iterator() { return asList().iterator(); } @Override - @Nullable - Object[] internalArray() { + @Nullable Object[] internalArray() { return elements; } @@ -97,7 +101,7 @@ int internalArrayEnd() { @Override int copyIntoArray(@Nullable Object[] dst, int offset) { - System.arraycopy(elements, 0, dst, offset, size); + arraycopy(elements, 0, dst, offset, size); return offset + size; } @@ -120,4 +124,13 @@ public int hashCode() { boolean isHashCodeFast() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java b/android/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java index 3b6d79c44c34..c5fa21534e88 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java @@ -19,10 +19,11 @@ import static com.google.common.collect.BoundType.CLOSED; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import java.util.Comparator; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable sorted multiset with one or more distinct elements. @@ -31,11 +32,10 @@ */ @SuppressWarnings("serial") // uses writeReplace, not default serialization @GwtIncompatible -@ElementTypesAreNonnullByDefault final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset { - private static final long[] ZERO_CUMULATIVE_COUNTS = {0}; + private static final long[] zeroCumulativeCounts = {0}; - static final ImmutableSortedMultiset NATURAL_EMPTY_MULTISET = + static final ImmutableSortedMultiset NATURAL_EMPTY_MULTISET = new RegularImmutableSortedMultiset<>(Ordering.natural()); @VisibleForTesting final transient RegularImmutableSortedSet elementSet; @@ -45,7 +45,7 @@ final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset RegularImmutableSortedMultiset(Comparator comparator) { this.elementSet = ImmutableSortedSet.emptySet(comparator); - this.cumulativeCounts = ZERO_CUMULATIVE_COUNTS; + this.cumulativeCounts = zeroCumulativeCounts; this.offset = 0; this.length = 0; } @@ -68,19 +68,17 @@ Entry getEntry(int index) { } @Override - @CheckForNull - public Entry firstEntry() { + public @Nullable Entry firstEntry() { return isEmpty() ? null : getEntry(0); } @Override - @CheckForNull - public Entry lastEntry() { + public @Nullable Entry lastEntry() { return isEmpty() ? null : getEntry(length - 1); } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { int index = elementSet.indexOf(element); return (index >= 0) ? getCount(index) : 0; } @@ -115,7 +113,7 @@ ImmutableSortedMultiset getSubMultiset(int from, int to) { return this; } else { RegularImmutableSortedSet subElementSet = elementSet.getSubSet(from, to); - return new RegularImmutableSortedMultiset( + return new RegularImmutableSortedMultiset<>( subElementSet, cumulativeCounts, offset + from, to - from); } } @@ -124,4 +122,12 @@ ImmutableSortedMultiset getSubMultiset(int from, int to) { boolean isPartialView() { return offset > 0 || length < cumulativeCounts.length - 1; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableSortedSet.java b/android/guava/src/com/google/common/collect/RegularImmutableSortedSet.java index dd987988b87d..f7d2804f08bb 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableSortedSet.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableSortedSet.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.util.Collection; import java.util.Collections; @@ -27,8 +28,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An immutable sorted set with one or more elements. TODO(jlevy): Consider separate class for a @@ -39,7 +39,6 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings({"serial", "rawtypes"}) -@ElementTypesAreNonnullByDefault final class RegularImmutableSortedSet extends ImmutableSortedSet { static final RegularImmutableSortedSet NATURAL_EMPTY_SET = new RegularImmutableSortedSet<>(ImmutableList.of(), Ordering.natural()); @@ -52,9 +51,7 @@ final class RegularImmutableSortedSet extends ImmutableSortedSet { } @Override - @CheckForNull - @Nullable - Object[] internalArray() { + @Nullable Object @Nullable [] internalArray() { return elements.internalArray(); } @@ -85,7 +82,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { try { return o != null && unsafeBinarySearch(o) >= 0; } catch (ClassCastException e) { @@ -155,12 +152,12 @@ boolean isPartialView() { } @Override - int copyIntoArray(Object[] dst, int offset) { + int copyIntoArray(@Nullable Object[] dst, int offset) { return elements.copyIntoArray(dst, offset); } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -213,29 +210,25 @@ public E last() { } @Override - @CheckForNull - public E lower(E element) { + public @Nullable E lower(E element) { int index = headIndex(element, false) - 1; return (index == -1) ? null : elements.get(index); } @Override - @CheckForNull - public E floor(E element) { + public @Nullable E floor(E element) { int index = headIndex(element, true) - 1; return (index == -1) ? null : elements.get(index); } @Override - @CheckForNull - public E ceiling(E element) { + public @Nullable E ceiling(E element) { int index = tailIndex(element, true); return (index == size()) ? null : elements.get(index); } @Override - @CheckForNull - public E higher(E element) { + public @Nullable E higher(E element) { int index = tailIndex(element, false); return (index == size()) ? null : elements.get(index); } @@ -286,7 +279,7 @@ RegularImmutableSortedSet getSubSet(int newFromIndex, int newToIndex) { if (newFromIndex == 0 && newToIndex == size()) { return this; } else if (newFromIndex < newToIndex) { - return new RegularImmutableSortedSet( + return new RegularImmutableSortedSet<>( elements.subList(newFromIndex, newToIndex), comparator); } else { return emptySet(comparator); @@ -294,7 +287,7 @@ RegularImmutableSortedSet getSubSet(int newFromIndex, int newToIndex) { } @Override - int indexOf(@CheckForNull Object target) { + int indexOf(@Nullable Object target) { if (target == null) { return -1; } @@ -319,4 +312,13 @@ ImmutableSortedSet createDescendingSet() { ? emptySet(reversedOrder) : new RegularImmutableSortedSet(elements.reverse(), reversedOrder); } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/RegularImmutableTable.java b/android/guava/src/com/google/common/collect/RegularImmutableTable.java index 337f123b698d..820bd100e09b 100644 --- a/android/guava/src/com/google/common/collect/RegularImmutableTable.java +++ b/android/guava/src/com/google/common/collect/RegularImmutableTable.java @@ -16,15 +16,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.sort; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.j2objc.annotations.WeakOuter; -import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link ImmutableTable} holding an arbitrary number of cells. @@ -32,7 +34,6 @@ * @author Gregory Kick */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class RegularImmutableTable extends ImmutableTable { RegularImmutableTable() {} @@ -56,7 +57,7 @@ Cell get(int index) { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (object instanceof Cell) { Cell cell = (Cell) object; Object value = RegularImmutableTable.this.get(cell.getRowKey(), cell.getColumnKey()); @@ -69,6 +70,15 @@ public boolean contains(@CheckForNull Object object) { boolean isPartialView() { return false; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } abstract V getValue(int iterationIndex); @@ -94,12 +104,21 @@ public V get(int index) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } static RegularImmutableTable forCells( List> cells, - @CheckForNull Comparator rowComparator, - @CheckForNull Comparator columnComparator) { + @Nullable Comparator rowComparator, + @Nullable Comparator columnComparator) { checkNotNull(cells); if (rowComparator != null || columnComparator != null) { /* @@ -122,7 +141,7 @@ static RegularImmutableTable forCells( ? 0 : columnComparator.compare(cell1.getColumnKey(), cell2.getColumnKey()); }; - Collections.sort(cells, comparator); + sort(cells, comparator); } return forCellsInternal(cells, rowComparator, columnComparator); } @@ -133,8 +152,8 @@ static RegularImmutableTable forCells(Iterable> private static RegularImmutableTable forCellsInternal( Iterable> cells, - @CheckForNull Comparator rowComparator, - @CheckForNull Comparator columnComparator) { + @Nullable Comparator rowComparator, + @Nullable Comparator columnComparator) { Set rowSpaceBuilder = new LinkedHashSet<>(); Set columnSpaceBuilder = new LinkedHashSet<>(); ImmutableList> cellList = ImmutableList.copyOf(cells); @@ -167,12 +186,14 @@ static RegularImmutableTable forOrderedComponents( : new SparseImmutableTable(cellList, rowSpace, columnSpace); } - /** @throws IllegalArgumentException if {@code existingValue} is not null. */ + /** + * @throws IllegalArgumentException if {@code existingValue} is not null. + */ /* * We could have declared this method 'static' but the additional compile-time checks achieved by * referencing the type variables seem worthwhile. */ - final void checkNoDuplicate(R rowKey, C columnKey, @CheckForNull V existingValue, V newValue) { + final void checkNoDuplicate(R rowKey, C columnKey, @Nullable V existingValue, V newValue) { checkArgument( existingValue == null, "Duplicate key: (row=%s, column=%s), values: [%s, %s].", @@ -181,4 +202,10 @@ final void checkNoDuplicate(R rowKey, C columnKey, @CheckForNull V existingValue newValue, existingValue); } + + // redeclare to satisfy our test for b/310253115 + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + abstract Object writeReplace(); } diff --git a/android/guava/src/com/google/common/collect/ReverseNaturalOrdering.java b/android/guava/src/com/google/common/collect/ReverseNaturalOrdering.java index f0c40cb61262..f95f7e421305 100644 --- a/android/guava/src/com/google/common/collect/ReverseNaturalOrdering.java +++ b/android/guava/src/com/google/common/collect/ReverseNaturalOrdering.java @@ -19,17 +19,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Iterator; /** An ordering that uses the reverse of the natural order of the values. */ @GwtCompatible(serializable = true) -@SuppressWarnings({"unchecked", "rawtypes"}) // TODO(kevinb): the right way to explain this?? -@ElementTypesAreNonnullByDefault final class ReverseNaturalOrdering extends Ordering> implements Serializable { static final ReverseNaturalOrdering INSTANCE = new ReverseNaturalOrdering(); @Override + @SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? public int compare(Comparable left, Comparable right) { checkNotNull(left); // right null is caught later if (left == right) { @@ -98,5 +99,5 @@ public String toString() { private ReverseNaturalOrdering() {} - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/ReverseOrdering.java b/android/guava/src/com/google/common/collect/ReverseOrdering.java index 6c1e74dd73d5..f8883ff1fcb4 100644 --- a/android/guava/src/com/google/common/collect/ReverseOrdering.java +++ b/android/guava/src/com/google/common/collect/ReverseOrdering.java @@ -19,14 +19,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** An ordering that uses the reverse of a given order. */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault final class ReverseOrdering extends Ordering implements Serializable { final Ordering forwardOrder; @@ -96,7 +96,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -112,5 +112,5 @@ public String toString() { return forwardOrder + ".reverse()"; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/RowSortedTable.java b/android/guava/src/com/google/common/collect/RowSortedTable.java index 2c2d773f78db..99e59b9edd41 100644 --- a/android/guava/src/com/google/common/collect/RowSortedTable.java +++ b/android/guava/src/com/google/common/collect/RowSortedTable.java @@ -21,7 +21,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Interface that extends {@code Table} and whose rows are sorted. @@ -34,7 +34,6 @@ * @since 8.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface RowSortedTable< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends Table { diff --git a/android/guava/src/com/google/common/collect/Serialization.java b/android/guava/src/com/google/common/collect/Serialization.java index 4ab53f2d7808..81035be120a9 100644 --- a/android/guava/src/com/google/common/collect/Serialization.java +++ b/android/guava/src/com/google/common/collect/Serialization.java @@ -17,13 +17,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Collection; import java.util.Map; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides static methods for serializing collection classes. @@ -34,7 +35,7 @@ * @author Jared Levy */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible final class Serialization { private Serialization() {} diff --git a/android/guava/src/com/google/common/collect/SetMultimap.java b/android/guava/src/com/google/common/collect/SetMultimap.java index 216533ea2356..4b21111f9959 100644 --- a/android/guava/src/com/google/common/collect/SetMultimap.java +++ b/android/guava/src/com/google/common/collect/SetMultimap.java @@ -22,8 +22,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a key-value pair that's @@ -46,14 +45,12 @@ * that affects its {@link Object#equals} behavior. Undefined behavior and bugs will result. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface SetMultimap extends Multimap { /** @@ -75,7 +72,7 @@ public interface SetMultimap removeAll(@CheckForNull Object key); + Set removeAll(@Nullable Object key); /** * {@inheritDoc} @@ -120,5 +117,5 @@ public interface SetMultimapSee the Guava User Guide article on {@code Sets}. + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#sets">{@code Sets}. * * @author Kevin Bourrillion * @author Jared Levy @@ -65,7 +70,6 @@ * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Sets { private Sets() {} @@ -96,7 +100,6 @@ public boolean retainAll(Collection c) { * @param otherElements the rest of the elements the set should contain * @return an immutable set containing those elements, minus duplicates */ - // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 @GwtCompatible(serializable = true) public static > ImmutableSet immutableEnumSet( E anElement, E... otherElements) { @@ -113,7 +116,6 @@ public static > ImmutableSet immutableEnumSet( * @param elements the elements, all of the same {@code enum} type, that the set should contain * @return an immutable set containing those elements, minus duplicates */ - // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 @GwtCompatible(serializable = true) public static > ImmutableSet immutableEnumSet(Iterable elements) { if (elements instanceof ImmutableEnumSet) { @@ -137,6 +139,19 @@ public static > ImmutableSet immutableEnumSet(Iterable e } } + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet} + * with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the + * resulting set will iterate over elements in their enum definition order, not encounter order. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static > Collector> toImmutableEnumSet() { + return CollectCollectors.toImmutableEnumSet(); + } + /** * Returns a new, mutable {@code EnumSet} instance containing the given elements in their * natural order. This method behaves identically to {@link EnumSet#copyOf(Collection)}, but also @@ -159,12 +174,14 @@ public static > EnumSet newEnumSet( * using a {@code LinkedHashSet} instead, at the cost of increased memory footprint, to get * deterministic iteration behavior. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code HashSet} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashSet} constructor directly, taking advantage of "diamond" + * syntax. */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashSet newHashSet() { - return new HashSet(); + return new HashSet<>(); } /** @@ -180,6 +197,7 @@ public static > EnumSet newEnumSet( * asList}{@code (...))}, or for creating an empty set then calling {@link Collections#addAll}. * This method is not actually very useful and will likely be deprecated in the future. */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashSet newHashSet(E... elements) { HashSet set = newHashSetWithExpectedSize(elements.length); Collections.addAll(set, elements); @@ -198,12 +216,14 @@ public static > EnumSet newEnumSet( *

    Note: if {@code E} is an {@link Enum} type, use {@link #newEnumSet(Iterable, Class)} * instead. * - *

    Note for Java 7 and later: if {@code elements} is a {@link Collection}, you don't - * need this method. Instead, use the {@code HashSet} constructor directly, taking advantage of - * the new "diamond" syntax. + *

    Note: if {@code elements} is a {@link Collection}, you don't need this method. + * Instead, use the {@code HashSet} constructor directly, taking advantage of "diamond" + * syntax. * *

    Overall, this method is not very useful and will likely be deprecated in the future. */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashSet newHashSet(Iterable elements) { return (elements instanceof Collection) ? new HashSet((Collection) elements) @@ -222,6 +242,7 @@ public static > EnumSet newEnumSet( * *

    Overall, this method is not very useful and will likely be deprecated in the future. */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashSet newHashSet(Iterator elements) { HashSet set = newHashSet(); Iterators.addAll(set, elements); @@ -240,9 +261,10 @@ public static > EnumSet newEnumSet( * without resizing * @throws IllegalArgumentException if {@code expectedSize} is negative */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static HashSet newHashSetWithExpectedSize( int expectedSize) { - return new HashSet(Maps.capacity(expectedSize)); + return new HashSet<>(Maps.capacity(expectedSize)); } /** @@ -285,14 +307,16 @@ public static Set newConcurrentHashSet(Iterable elements) { * *

    Note: if mutability is not required, use {@link ImmutableSet#of()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code LinkedHashSet} constructor directly, taking advantage of - * the new "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashSet} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code LinkedHashSet} */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashSet newLinkedHashSet() { - return new LinkedHashSet(); + return new LinkedHashSet<>(); } /** @@ -301,19 +325,21 @@ public static Set newConcurrentHashSet(Iterable elements) { *

    Note: if mutability is not required and the elements are non-null, use {@link * ImmutableSet#copyOf(Iterable)} instead. * - *

    Note for Java 7 and later: if {@code elements} is a {@link Collection}, you don't - * need this method. Instead, use the {@code LinkedHashSet} constructor directly, taking advantage - * of the new "diamond" syntax. + *

    Note: if {@code elements} is a {@link Collection}, you don't need this method. + * Instead, use the {@code LinkedHashSet} constructor directly, taking advantage of "diamond" + * syntax. * *

    Overall, this method is not very useful and will likely be deprecated in the future. * * @param elements the elements that the set should contain, in order * @return a new {@code LinkedHashSet} containing those elements (minus duplicates) */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashSet newLinkedHashSet( Iterable elements) { if (elements instanceof Collection) { - return new LinkedHashSet((Collection) elements); + return new LinkedHashSet<>((Collection) elements); } LinkedHashSet set = newLinkedHashSet(); Iterables.addAll(set, elements); @@ -332,9 +358,10 @@ public static Set newConcurrentHashSet(Iterable elements) { * @throws IllegalArgumentException if {@code expectedSize} is negative * @since 11.0 */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static LinkedHashSet newLinkedHashSetWithExpectedSize( int expectedSize) { - return new LinkedHashSet(Maps.capacity(expectedSize)); + return new LinkedHashSet<>(Maps.capacity(expectedSize)); } // TreeSet @@ -345,14 +372,19 @@ public static Set newConcurrentHashSet(Iterable elements) { * *

    Note: if mutability is not required, use {@link ImmutableSortedSet#of()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeSet} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" + * syntax. * * @return a new, empty {@code TreeSet} */ + @SuppressWarnings({ + "rawtypes", // https://github.com/google/guava/issues/989 + "NonApiType", // acts as a direct substitute for a constructor call + }) public static TreeSet newTreeSet() { - return new TreeSet(); + return new TreeSet<>(); } /** @@ -366,9 +398,10 @@ public static TreeSet newTreeSet() { * method has different behavior than {@link TreeSet#TreeSet(SortedSet)}, which returns a {@code * TreeSet} with that comparator. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeSet} constructor directly, taking advantage of the new - * "diamond" syntax. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" + * syntax. * *

    This method is just a small convenience for creating an empty set and then calling {@link * Iterables#addAll}. This method is not very useful and will likely be deprecated in the future. @@ -376,6 +409,10 @@ public static TreeSet newTreeSet() { * @param elements the elements that the set should contain * @return a new {@code TreeSet} containing those elements (minus duplicates) */ + @SuppressWarnings({ + "rawtypes", // https://github.com/google/guava/issues/989 + "NonApiType", // acts as a direct substitute for a constructor call + }) public static TreeSet newTreeSet(Iterable elements) { TreeSet set = newTreeSet(); Iterables.addAll(set, elements); @@ -388,19 +425,21 @@ public static TreeSet newTreeSet(Iterable *

    Note: if mutability is not required, use {@code * ImmutableSortedSet.orderedBy(comparator).build()} instead. * - *

    Note for Java 7 and later: this method is now unnecessary and should be treated as - * deprecated. Instead, use the {@code TreeSet} constructor directly, taking advantage of the new - * "diamond" syntax. One caveat to this is that the {@code - * TreeSet} constructor uses a null {@code Comparator} to mean "natural ordering," whereas this - * factory rejects null. Clean your code accordingly. + *

    Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" + * syntax. One caveat to this is that the {@code TreeSet} constructor uses a null {@code + * Comparator} to mean "natural ordering," whereas this factory rejects null. Clean your code + * accordingly. * * @param comparator the comparator to use to sort the set * @return a new, empty {@code TreeSet} * @throws NullPointerException if {@code comparator} is null */ + @SuppressWarnings("NonApiType") // acts as a direct substitute for a constructor call public static TreeSet newTreeSet( Comparator comparator) { - return new TreeSet(checkNotNull(comparator)); + return new TreeSet<>(checkNotNull(comparator)); } /** @@ -425,9 +464,10 @@ public static TreeSet newTreeSet(Iterable * @return a new, empty {@code CopyOnWriteArraySet} * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArraySet public static CopyOnWriteArraySet newCopyOnWriteArraySet() { - return new CopyOnWriteArraySet(); + return new CopyOnWriteArraySet<>(); } /** @@ -437,6 +477,7 @@ public static TreeSet newTreeSet(Iterable * @return a new {@code CopyOnWriteArraySet} containing those elements * @since 12.0 */ + @J2ktIncompatible @GwtIncompatible // CopyOnWriteArraySet public static CopyOnWriteArraySet newCopyOnWriteArraySet( Iterable elements) { @@ -446,7 +487,7 @@ public static TreeSet newTreeSet(Iterable (elements instanceof Collection) ? (Collection) elements : Lists.newArrayList(elements); - return new CopyOnWriteArraySet(elementsCollection); + return new CopyOnWriteArraySet<>(elementsCollection); } /** @@ -462,6 +503,8 @@ public static TreeSet newTreeSet(Iterable * @throws IllegalArgumentException if {@code collection} is not an {@code EnumSet} instance and * contains no elements */ + @J2ktIncompatible + @GwtIncompatible // EnumSet.complementOf public static > EnumSet complementOf(Collection collection) { if (collection instanceof EnumSet) { return EnumSet.complementOf((EnumSet) collection); @@ -482,6 +525,8 @@ public static > EnumSet complementOf(Collection collecti * @return a new, modifiable {@code EnumSet} initially containing all the values of the enum not * present in the given collection */ + @J2ktIncompatible + @GwtIncompatible // EnumSet.complementOf public static > EnumSet complementOf( Collection collection, Class type) { checkNotNull(collection); @@ -490,6 +535,8 @@ public static > EnumSet complementOf( : makeComplementByHand(collection, type); } + @J2ktIncompatible + @GwtIncompatible private static > EnumSet makeComplementByHand( Collection collection, Class type) { EnumSet result = EnumSet.allOf(type); @@ -526,6 +573,7 @@ private static > EnumSet makeComplementByHand( * @throws IllegalArgumentException if {@code map} is not empty * @deprecated Use {@link Collections#newSetFromMap} instead. */ + @InlineMe(replacement = "Collections.newSetFromMap(map)", imports = "java.util.Collections") @Deprecated public static Set newSetFromMap( Map map) { @@ -552,9 +600,18 @@ private SetView() {} // no subclasses but our own * nonstandard notion of equivalence, for example if it is a {@link TreeSet} using a comparator * that is inconsistent with {@link Object#equals(Object)}. */ - @SuppressWarnings("nullness") // Unsafe, but we can't fix it now. - public ImmutableSet immutableCopy() { - return ImmutableSet.copyOf(this); + public ImmutableSet<@NonNull E> immutableCopy() { + // Not using ImmutableSet.copyOf() to avoid iterating thrice (isEmpty, size, iterator). + int upperBoundSize = upperBoundSize(); + if (upperBoundSize == 0) { + return ImmutableSet.of(); + } + ImmutableSet.Builder<@NonNull E> builder = + ImmutableSet.builderWithExpectedSize(upperBoundSize); + for (E element : this) { + builder.add(checkNotNull(element)); + } + return builder.build(); } /** @@ -596,7 +653,7 @@ public final boolean add(@ParametricNullness E e) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public final boolean remove(@CheckForNull Object object) { + public final boolean remove(@Nullable Object object) { throw new UnsupportedOperationException(); } @@ -662,6 +719,18 @@ public final void clear() { */ @Override public abstract UnmodifiableIterator iterator(); + + /** + * Returns the upper bound on the size of this set view. + * + *

    This method is used to presize the underlying collection when converting to an {@link + * ImmutableSet}. + */ + abstract int upperBoundSize(); + + static int upperBoundSize(Set set) { + return set instanceof SetView ? ((SetView) set).upperBoundSize() : set.size(); + } } /** @@ -675,7 +744,7 @@ public final void clear() { * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. */ public static SetView union( - final Set set1, final Set set2) { + Set set1, Set set2) { checkNotNull(set1, "set1"); checkNotNull(set2, "set2"); @@ -703,8 +772,7 @@ public UnmodifiableIterator iterator() { final Iterator itr2 = set2.iterator(); @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { if (itr1.hasNext()) { return itr1.next(); } @@ -720,7 +788,7 @@ protected E computeNext() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return set1.contains(object) || set2.contains(object); } @@ -732,9 +800,8 @@ public > S copyInto(S set) { } @Override - @SuppressWarnings("nullness") // see supertype - public ImmutableSet immutableCopy() { - return new ImmutableSet.Builder().addAll(set1).addAll(set2).build(); + int upperBoundSize() { + return upperBoundSize(set1) + upperBoundSize(set2); } }; } @@ -766,8 +833,7 @@ public ImmutableSet immutableCopy() { * *

    This is unfortunate, but should come up only very rarely. */ - public static SetView intersection( - final Set set1, final Set set2) { + public static SetView intersection(Set set1, Set set2) { checkNotNull(set1, "set1"); checkNotNull(set2, "set2"); @@ -778,8 +844,7 @@ public UnmodifiableIterator iterator() { final Iterator itr = set1.iterator(); @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { while (itr.hasNext()) { E e = itr.next(); if (set2.contains(e)) { @@ -808,7 +873,7 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { return set1.contains(object) && set2.contains(object); } @@ -816,6 +881,11 @@ public boolean contains(@CheckForNull Object object) { public boolean containsAll(Collection collection) { return set1.containsAll(collection) && set2.containsAll(collection); } + + @Override + int upperBoundSize() { + return min(upperBoundSize(set1), upperBoundSize(set2)); + } }; } @@ -829,8 +899,7 @@ public boolean containsAll(Collection collection) { * equivalence relations, for example if {@code set1} is a {@link HashSet} and {@code set2} is a * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. */ - public static SetView difference( - final Set set1, final Set set2) { + public static SetView difference(Set set1, Set set2) { checkNotNull(set1, "set1"); checkNotNull(set2, "set2"); @@ -841,8 +910,7 @@ public UnmodifiableIterator iterator() { final Iterator itr = set1.iterator(); @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { while (itr.hasNext()) { E e = itr.next(); if (!set2.contains(e)) { @@ -871,9 +939,14 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return set1.contains(element) && !set2.contains(element); } + + @Override + int upperBoundSize() { + return upperBoundSize(set1); + } }; } @@ -889,19 +962,18 @@ public boolean contains(@CheckForNull Object element) { * @since 3.0 */ public static SetView symmetricDifference( - final Set set1, final Set set2) { + Set set1, Set set2) { checkNotNull(set1, "set1"); checkNotNull(set2, "set2"); return new SetView() { @Override public UnmodifiableIterator iterator() { - final Iterator itr1 = set1.iterator(); - final Iterator itr2 = set2.iterator(); + Iterator itr1 = set1.iterator(); + Iterator itr2 = set2.iterator(); return new AbstractIterator() { @Override - @CheckForNull - public E computeNext() { + public @Nullable E computeNext() { while (itr1.hasNext()) { E elem1 = itr1.next(); if (!set2.contains(elem1)) { @@ -941,9 +1013,14 @@ public boolean isEmpty() { } @Override - public boolean contains(@CheckForNull Object element) { + public boolean contains(@Nullable Object element) { return set1.contains(element) ^ set2.contains(element); } + + @Override + int upperBoundSize() { + return upperBoundSize(set1) + upperBoundSize(set2); + } }; } @@ -969,7 +1046,7 @@ public boolean contains(@CheckForNull Object element) { * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link * Iterables#filter(Iterable, Class)} for related functionality.) * - *

    Java 8 users: many use cases for this method are better addressed by {@link + *

    Java 8+ users: many use cases for this method are better addressed by {@link * java.util.stream.Stream#filter}. This method is not being deprecated, but we gently encourage * you to migrate to streams. */ @@ -983,11 +1060,11 @@ public boolean contains(@CheckForNull Object element) { // Support clear(), removeAll(), and retainAll() when filtering a filtered // collection. FilteredSet filtered = (FilteredSet) unfiltered; - Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); - return new FilteredSet((Set) filtered.unfiltered, combinedPredicate); + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredSet<>((Set) filtered.unfiltered, combinedPredicate); } - return new FilteredSet(checkNotNull(unfiltered), checkNotNull(predicate)); + return new FilteredSet<>(checkNotNull(unfiltered), checkNotNull(predicate)); } /** @@ -1020,11 +1097,11 @@ public boolean contains(@CheckForNull Object element) { // Support clear(), removeAll(), and retainAll() when filtering a filtered // collection. FilteredSet filtered = (FilteredSet) unfiltered; - Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); - return new FilteredSortedSet((SortedSet) filtered.unfiltered, combinedPredicate); + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredSortedSet<>((SortedSet) filtered.unfiltered, combinedPredicate); } - return new FilteredSortedSet(checkNotNull(unfiltered), checkNotNull(predicate)); + return new FilteredSortedSet<>(checkNotNull(unfiltered), checkNotNull(predicate)); } /** @@ -1052,18 +1129,17 @@ public boolean contains(@CheckForNull Object element) { * @since 14.0 */ @GwtIncompatible // NavigableSet - @SuppressWarnings("unchecked") public static NavigableSet filter( NavigableSet unfiltered, Predicate predicate) { if (unfiltered instanceof FilteredSet) { // Support clear(), removeAll(), and retainAll() when filtering a filtered // collection. FilteredSet filtered = (FilteredSet) unfiltered; - Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); - return new FilteredNavigableSet((NavigableSet) filtered.unfiltered, combinedPredicate); + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredNavigableSet<>((NavigableSet) filtered.unfiltered, combinedPredicate); } - return new FilteredNavigableSet(checkNotNull(unfiltered), checkNotNull(predicate)); + return new FilteredNavigableSet<>(checkNotNull(unfiltered), checkNotNull(predicate)); } private static class FilteredSet extends FilteredCollection @@ -1073,7 +1149,7 @@ private static class FilteredSet extends FilteredCol } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { return equalsImpl(this, object); } @@ -1091,25 +1167,24 @@ private static class FilteredSortedSet extends Filte } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return ((SortedSet) unfiltered).comparator(); } @Override public SortedSet subSet(@ParametricNullness E fromElement, @ParametricNullness E toElement) { - return new FilteredSortedSet( + return new FilteredSortedSet<>( ((SortedSet) unfiltered).subSet(fromElement, toElement), predicate); } @Override public SortedSet headSet(@ParametricNullness E toElement) { - return new FilteredSortedSet(((SortedSet) unfiltered).headSet(toElement), predicate); + return new FilteredSortedSet<>(((SortedSet) unfiltered).headSet(toElement), predicate); } @Override public SortedSet tailSet(@ParametricNullness E fromElement) { - return new FilteredSortedSet(((SortedSet) unfiltered).tailSet(fromElement), predicate); + return new FilteredSortedSet<>(((SortedSet) unfiltered).tailSet(fromElement), predicate); } @Override @@ -1144,38 +1219,32 @@ NavigableSet unfiltered() { } @Override - @CheckForNull - public E lower(@ParametricNullness E e) { + public @Nullable E lower(@ParametricNullness E e) { return Iterators.find(unfiltered().headSet(e, false).descendingIterator(), predicate, null); } @Override - @CheckForNull - public E floor(@ParametricNullness E e) { + public @Nullable E floor(@ParametricNullness E e) { return Iterators.find(unfiltered().headSet(e, true).descendingIterator(), predicate, null); } @Override - @CheckForNull - public E ceiling(@ParametricNullness E e) { + public @Nullable E ceiling(@ParametricNullness E e) { return Iterables.find(unfiltered().tailSet(e, true), predicate, null); } @Override - @CheckForNull - public E higher(@ParametricNullness E e) { + public @Nullable E higher(@ParametricNullness E e) { return Iterables.find(unfiltered().tailSet(e, false), predicate, null); } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return Iterables.removeFirstMatching(unfiltered(), predicate); } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return Iterables.removeFirstMatching(unfiltered().descendingSet(), predicate); } @@ -1328,7 +1397,7 @@ public static Set> cartesianProduct(List> */ @SafeVarargs public static Set> cartesianProduct(Set... sets) { - return cartesianProduct(Arrays.asList(sets)); + return cartesianProduct(asList(sets)); } private static final class CartesianSet extends ForwardingCollection> @@ -1345,7 +1414,7 @@ static Set> create(List> sets) { } axesBuilder.add(copy); } - final ImmutableList> axes = axesBuilder.build(); + ImmutableList> axes = axesBuilder.build(); ImmutableList> listAxes = new ImmutableList>() { @Override @@ -1362,6 +1431,15 @@ public List get(int index) { boolean isPartialView() { return true; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } }; return new CartesianSet(axes, new CartesianList(listAxes)); } @@ -1377,7 +1455,7 @@ protected Collection> delegate() { } @Override - public boolean contains(@CheckForNull Object object) { + public boolean contains(@Nullable Object object) { if (!(object instanceof List)) { return false; } @@ -1396,14 +1474,18 @@ public boolean contains(@CheckForNull Object object) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { // Warning: this is broken if size() == 0, so it is critical that we // substitute an empty ImmutableSet to the user in place of this if (object instanceof CartesianSet) { CartesianSet that = (CartesianSet) object; return this.axes.equals(that.axes); } - return super.equals(object); + if (object instanceof Set) { + Set that = (Set) object; + return this.size() == that.size() && this.containsAll(that); + } + return false; } @Override @@ -1496,7 +1578,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { Integer index = inputSet.get(o); return index != null && (mask & (1 << index)) != 0; } @@ -1525,14 +1607,14 @@ public boolean isEmpty() { public Iterator> iterator() { return new AbstractIndexedListIterator>(size()) { @Override - protected Set get(final int setBits) { - return new SubSet(inputSet, setBits); + protected Set get(int setBits) { + return new SubSet<>(inputSet, setBits); } }; } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (obj instanceof Set) { Set set = (Set) obj; return inputSet.keySet().containsAll(set); @@ -1541,7 +1623,7 @@ public boolean contains(@CheckForNull Object obj) { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof PowerSet) { PowerSet that = (PowerSet) obj; return inputSet.keySet().equals(that.inputSet.keySet()); @@ -1589,9 +1671,8 @@ public String toString() { * @throws NullPointerException if {@code set} is or contains {@code null} * @since 23.0 */ - @Beta - public static Set> combinations(Set set, final int size) { - final ImmutableMap index = Maps.indexMap(set); + public static Set> combinations(Set set, int size) { + ImmutableMap index = Maps.indexMap(set); checkNonnegative(size, "size"); checkArgument(size <= index.size(), "size (%s) must be <= set.size() (%s)", size, index.size()); if (size == 0) { @@ -1601,7 +1682,7 @@ public static Set> combinations(Set set, final int size) { } return new AbstractSet>() { @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Set) { Set s = (Set) o; return s.size() == size && index.keySet().containsAll(s); @@ -1615,8 +1696,7 @@ public Iterator> iterator() { final BitSet bits = new BitSet(index.size()); @Override - @CheckForNull - protected Set computeNext() { + protected @Nullable Set computeNext() { if (bits.isEmpty()) { bits.set(0, size); } else { @@ -1626,6 +1706,7 @@ protected Set computeNext() { if (bitToFlip == index.size()) { return endOfData(); } + /* * The current set in sorted order looks like * {firstSetBit, firstSetBit + 1, ..., bitToFlip - 1, ...} @@ -1643,10 +1724,10 @@ protected Set computeNext() { bits.clear(bitToFlip - firstSetBit - 1, bitToFlip); bits.set(bitToFlip); } - final BitSet copy = (BitSet) bits.clone(); + BitSet copy = (BitSet) bits.clone(); return new AbstractSet() { @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { Integer i = index.get(o); return i != null && copy.get(i); } @@ -1657,8 +1738,7 @@ public Iterator iterator() { int i = -1; @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { i = copy.nextSetBit(i + 1); if (i == -1) { return endOfData(); @@ -1702,7 +1782,7 @@ static int hashCodeImpl(Set s) { } /** An implementation for {@link Set#equals(Object)}. */ - static boolean equalsImpl(Set s, @CheckForNull Object object) { + static boolean equalsImpl(Set s, @Nullable Object object) { if (s == object) { return true; } @@ -1727,6 +1807,8 @@ static boolean equalsImpl(Set s, @CheckForNull Object object) { *

    The returned navigable set will be serializable if the specified navigable set is * serializable. * + *

    Java 8+ users and later: Prefer {@link Collections#unmodifiableNavigableSet}. + * * @param set the navigable set for which an unmodifiable view is to be returned * @return an unmodifiable view of the specified navigable set * @since 12.0 @@ -1736,7 +1818,7 @@ static boolean equalsImpl(Set s, @CheckForNull Object object) { if (set instanceof ImmutableCollection || set instanceof UnmodifiableNavigableSet) { return set; } - return new UnmodifiableNavigableSet(set); + return new UnmodifiableNavigableSet<>(set); } static final class UnmodifiableNavigableSet @@ -1755,48 +1837,42 @@ protected SortedSet delegate() { } @Override - @CheckForNull - public E lower(@ParametricNullness E e) { + public @Nullable E lower(@ParametricNullness E e) { return delegate.lower(e); } @Override - @CheckForNull - public E floor(@ParametricNullness E e) { + public @Nullable E floor(@ParametricNullness E e) { return delegate.floor(e); } @Override - @CheckForNull - public E ceiling(@ParametricNullness E e) { + public @Nullable E ceiling(@ParametricNullness E e) { return delegate.ceiling(e); } @Override - @CheckForNull - public E higher(@ParametricNullness E e) { + public @Nullable E higher(@ParametricNullness E e) { return delegate.higher(e); } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { throw new UnsupportedOperationException(); } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { throw new UnsupportedOperationException(); } - @CheckForNull private transient UnmodifiableNavigableSet descendingSet; + @LazyInit private transient @Nullable UnmodifiableNavigableSet descendingSet; @Override public NavigableSet descendingSet() { UnmodifiableNavigableSet result = descendingSet; if (result == null) { - result = descendingSet = new UnmodifiableNavigableSet(delegate.descendingSet()); + result = descendingSet = new UnmodifiableNavigableSet<>(delegate.descendingSet()); result.descendingSet = this; } return result; @@ -1827,7 +1903,7 @@ public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclus return unmodifiableNavigableSet(delegate.tailSet(fromElement, inclusive)); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -1871,11 +1947,14 @@ public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclus *

    The returned navigable set will be serializable if the specified navigable set is * serializable. * + *

    Java 8+ users and later: Prefer {@link Collections#synchronizedNavigableSet}. + * * @param navigableSet the navigable set to be "wrapped" in a synchronized navigable set. * @return a synchronized view of the specified navigable set. * @since 13.0 */ @GwtIncompatible // NavigableSet + @J2ktIncompatible // Synchronized public static NavigableSet synchronizedNavigableSet( NavigableSet navigableSet) { return Synchronized.navigableSet(navigableSet); @@ -1900,7 +1979,7 @@ static boolean removeAllImpl(Set set, Collection collection) { * is just more than the set's size. We augment the test by * assuming that sets have fast contains() performance, and other * collections don't. See - * http://code.google.com/p/guava-libraries/issues/detail?id=1013 + * https://github.com/google/guava/issues/1013 */ if (collection instanceof Set && collection.size() > set.size()) { return Iterators.removeAll(set.iterator(), collection); @@ -1923,38 +2002,32 @@ protected NavigableSet delegate() { } @Override - @CheckForNull - public E lower(@ParametricNullness E e) { + public @Nullable E lower(@ParametricNullness E e) { return forward.higher(e); } @Override - @CheckForNull - public E floor(@ParametricNullness E e) { + public @Nullable E floor(@ParametricNullness E e) { return forward.ceiling(e); } @Override - @CheckForNull - public E ceiling(@ParametricNullness E e) { + public @Nullable E ceiling(@ParametricNullness E e) { return forward.floor(e); } @Override - @CheckForNull - public E higher(@ParametricNullness E e) { + public @Nullable E higher(@ParametricNullness E e) { return forward.lower(e); } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return forward.pollLast(); } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return forward.pollFirst(); } @@ -2068,7 +2141,6 @@ public String toString() { * * @since 20.0 */ - @Beta @GwtIncompatible // NavigableSet public static > NavigableSet subSet( NavigableSet set, Range range) { diff --git a/android/guava/src/com/google/common/collect/SingletonImmutableSet.java b/android/guava/src/com/google/common/collect/SingletonImmutableSet.java index 088cb802f27f..09894261d31e 100644 --- a/android/guava/src/com/google/common/collect/SingletonImmutableSet.java +++ b/android/guava/src/com/google/common/collect/SingletonImmutableSet.java @@ -16,10 +16,13 @@ package com.google.common.collect; +import static com.google.common.collect.Iterators.singletonIterator; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@link ImmutableSet} with exactly one element. @@ -29,7 +32,6 @@ */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization -@ElementTypesAreNonnullByDefault final class SingletonImmutableSet extends ImmutableSet { // We deliberately avoid caching the asList and hashCode here, to ensure that with // compressed oops, a SingletonImmutableSet packs all the way down to the optimal 16 bytes. @@ -46,13 +48,13 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { return element.equals(target); } @Override public UnmodifiableIterator iterator() { - return Iterators.singletonIterator(element); + return singletonIterator(element); } @Override @@ -80,4 +82,13 @@ public final int hashCode() { public String toString() { return '[' + element.toString() + ']'; } + + // redeclare to help optimizers with b/310253115 + @SuppressWarnings("RedundantOverride") + @Override + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { + return super.writeReplace(); + } } diff --git a/android/guava/src/com/google/common/collect/SingletonImmutableTable.java b/android/guava/src/com/google/common/collect/SingletonImmutableTable.java index cfaeadb41df7..5e4975ce2895 100644 --- a/android/guava/src/com/google/common/collect/SingletonImmutableTable.java +++ b/android/guava/src/com/google/common/collect/SingletonImmutableTable.java @@ -19,6 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Map; /** @@ -27,7 +29,6 @@ * @author Gregory Kick */ @GwtCompatible -@ElementTypesAreNonnullByDefault class SingletonImmutableTable extends ImmutableTable { final R singleRowKey; final C singleColumnKey; @@ -77,7 +78,9 @@ ImmutableCollection createValues() { } @Override - SerializedForm createSerializedForm() { + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { return SerializedForm.create(this, new int[] {0}, new int[] {0}); } } diff --git a/android/guava/src/com/google/common/collect/SneakyThrows.java b/android/guava/src/com/google/common/collect/SneakyThrows.java new file mode 100644 index 000000000000..911fbc725a50 --- /dev/null +++ b/android/guava/src/com/google/common/collect/SneakyThrows.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +/** Static utility method for unchecked throwing of any {@link Throwable}. */ +@GwtCompatible +final class SneakyThrows { + /** + * Throws {@code t} as if it were an unchecked {@link Throwable}. + * + *

    This method is useful primarily when we make a reflective call to a method with no {@code + * throws} clause: Java forces us to handle an arbitrary {@link Throwable} from that method, + * rather than just the {@link RuntimeException} or {@link Error} that should be possible. (And in + * fact the static type of {@link Throwable} is occasionally justified even for a method with no + * {@code throws} clause: Some such methods can in fact throw a checked exception (e.g., by + * calling code written in Kotlin).) Typically, we want to let a {@link Throwable} from such a + * method propagate untouched, just as we'd typically let it do for a non-reflective call. + * However, we can't usually write {@code throw t;} when {@code t} has a static type of {@link + * Throwable}. But we can write {@code sneakyThrow(t);}. + * + *

    We sometimes also use {@code sneakyThrow} for testing how our code responds to + * sneaky checked exception. + * + * @return never; this method declares a return type of {@link Error} only so that callers can + * write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always + * throw. + */ + @CanIgnoreReturnValue + static Error sneakyThrow(Throwable t) { + throw new SneakyThrows().throwIt(t); + } + + @SuppressWarnings("unchecked") // not really safe, but that's the point + private Error throwIt(Throwable t) throws T { + throw (T) t; + } + + private SneakyThrows() {} +} diff --git a/android/guava/src/com/google/common/collect/SortedIterable.java b/android/guava/src/com/google/common/collect/SortedIterable.java index 64ec08ef315b..0b17fe6fa8c8 100644 --- a/android/guava/src/com/google/common/collect/SortedIterable.java +++ b/android/guava/src/com/google/common/collect/SortedIterable.java @@ -17,7 +17,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An {@code Iterable} whose elements are sorted relative to a {@code Comparator}, typically @@ -26,7 +26,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault interface SortedIterable extends Iterable { /** * Returns the {@code Comparator} by which the elements of this iterable are ordered, or {@code diff --git a/android/guava/src/com/google/common/collect/SortedIterables.java b/android/guava/src/com/google/common/collect/SortedIterables.java index 68b231a381e5..a1acb8f2811f 100644 --- a/android/guava/src/com/google/common/collect/SortedIterables.java +++ b/android/guava/src/com/google/common/collect/SortedIterables.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.SortedSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Utilities for dealing with sorted collections of all types. @@ -27,7 +27,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class SortedIterables { private SortedIterables() {} diff --git a/android/guava/src/com/google/common/collect/SortedLists.java b/android/guava/src/com/google/common/collect/SortedLists.java index 0ebaab20f938..5ea430648869 100644 --- a/android/guava/src/com/google/common/collect/SortedLists.java +++ b/android/guava/src/com/google/common/collect/SortedLists.java @@ -15,15 +15,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Lists.transform; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static methods pertaining to sorted {@link List} instances. @@ -35,8 +36,6 @@ * @author Louis Wasserman */ @GwtCompatible -@Beta -@ElementTypesAreNonnullByDefault final class SortedLists { private SortedLists() {} @@ -200,6 +199,7 @@ public int resultIndex(int higherIndex) { *

    Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, * KeyAbsentBehavior)} using {@link Ordering#natural}. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static int binarySearch( List list, E e, @@ -215,6 +215,7 @@ public static int binarySearch( *

    Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, * KeyAbsentBehavior)} using {@link Ordering#natural}. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static int binarySearch( List list, Function keyFunction, @@ -241,7 +242,7 @@ public static int binarySearch( KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { return binarySearch( - Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); + transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); } /** @@ -278,7 +279,7 @@ public static int binarySearch( checkNotNull(presentBehavior); checkNotNull(absentBehavior); if (!(list instanceof RandomAccess)) { - list = Lists.newArrayList(list); + list = new ArrayList<>(list); } // TODO(lowasser): benchmark when it's best to do a linear search diff --git a/android/guava/src/com/google/common/collect/SortedMapDifference.java b/android/guava/src/com/google/common/collect/SortedMapDifference.java index 46cac8a3da54..ba4fd80b779c 100644 --- a/android/guava/src/com/google/common/collect/SortedMapDifference.java +++ b/android/guava/src/com/google/common/collect/SortedMapDifference.java @@ -18,7 +18,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.SortedMap; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An object representing the differences between two sorted maps. @@ -27,7 +27,6 @@ * @since 8.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface SortedMapDifference extends MapDifference { diff --git a/android/guava/src/com/google/common/collect/SortedMultiset.java b/android/guava/src/com/google/common/collect/SortedMultiset.java index 68932f585902..8042b36703fb 100644 --- a/android/guava/src/com/google/common/collect/SortedMultiset.java +++ b/android/guava/src/com/google/common/collect/SortedMultiset.java @@ -22,8 +22,7 @@ import java.util.Iterator; import java.util.NavigableSet; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link Multiset} which maintains the ordering of its elements, according to either their @@ -37,14 +36,12 @@ * Collection} contract, which is specified in terms of {@link Object#equals}. * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Louis Wasserman * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public interface SortedMultiset extends SortedMultisetBridge, SortedIterable { /** @@ -58,29 +55,25 @@ public interface SortedMultiset * Returns the entry of the first element in this multiset, or {@code null} if this multiset is * empty. */ - @CheckForNull - Entry firstEntry(); + @Nullable Entry firstEntry(); /** * Returns the entry of the last element in this multiset, or {@code null} if this multiset is * empty. */ - @CheckForNull - Entry lastEntry(); + @Nullable Entry lastEntry(); /** * Returns and removes the entry associated with the lowest element in this multiset, or returns * {@code null} if this multiset is empty. */ - @CheckForNull - Entry pollFirstEntry(); + @Nullable Entry pollFirstEntry(); /** * Returns and removes the entry associated with the greatest element in this multiset, or returns * {@code null} if this multiset is empty. */ - @CheckForNull - Entry pollLastEntry(); + @Nullable Entry pollLastEntry(); /** * Returns a {@link NavigableSet} view of the distinct elements in this multiset. @@ -93,8 +86,8 @@ public interface SortedMultiset /** * {@inheritDoc} * - *

    The {@code entrySet}'s iterator returns entries in ascending element order according to the - * this multiset's comparator. + *

    The {@code entrySet}'s iterator returns entries in ascending element order according to this + * multiset's comparator. */ @Override Set> entrySet(); diff --git a/android/guava/src/com/google/common/collect/SortedMultisetBridge.java b/android/guava/src/com/google/common/collect/SortedMultisetBridge.java index 72d50beb692f..6c46c73fb195 100644 --- a/android/guava/src/com/google/common/collect/SortedMultisetBridge.java +++ b/android/guava/src/com/google/common/collect/SortedMultisetBridge.java @@ -18,7 +18,7 @@ import com.google.common.annotations.GwtIncompatible; import java.util.SortedSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Superinterface of {@link SortedMultiset} to introduce a bridge method for {@code elementSet()}, @@ -28,7 +28,6 @@ * @author Louis Wasserman */ @GwtIncompatible -@ElementTypesAreNonnullByDefault interface SortedMultisetBridge extends Multiset { @Override SortedSet elementSet(); diff --git a/android/guava/src/com/google/common/collect/SortedMultisets.java b/android/guava/src/com/google/common/collect/SortedMultisets.java index f602a42a1145..530b663be6cc 100644 --- a/android/guava/src/com/google/common/collect/SortedMultisets.java +++ b/android/guava/src/com/google/common/collect/SortedMultisets.java @@ -28,8 +28,7 @@ import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides static utility methods for creating and working with {@link SortedMultiset} instances. @@ -37,7 +36,6 @@ * @author Louis Wasserman */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class SortedMultisets { private SortedMultisets() {} @@ -103,26 +101,22 @@ static class NavigableElementSet extends ElementSet< } @Override - @CheckForNull - public E lower(@ParametricNullness E e) { + public @Nullable E lower(@ParametricNullness E e) { return getElementOrNull(multiset().headMultiset(e, OPEN).lastEntry()); } @Override - @CheckForNull - public E floor(@ParametricNullness E e) { + public @Nullable E floor(@ParametricNullness E e) { return getElementOrNull(multiset().headMultiset(e, CLOSED).lastEntry()); } @Override - @CheckForNull - public E ceiling(@ParametricNullness E e) { + public @Nullable E ceiling(@ParametricNullness E e) { return getElementOrNull(multiset().tailMultiset(e, CLOSED).firstEntry()); } @Override - @CheckForNull - public E higher(@ParametricNullness E e) { + public @Nullable E higher(@ParametricNullness E e) { return getElementOrNull(multiset().tailMultiset(e, OPEN).firstEntry()); } @@ -137,14 +131,12 @@ public Iterator descendingIterator() { } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { return getElementOrNull(multiset().pollFirstEntry()); } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { return getElementOrNull(multiset().pollLastEntry()); } @@ -174,15 +166,15 @@ public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclus } } - private static E getElementOrThrow(@CheckForNull Entry entry) { + private static E getElementOrThrow(@Nullable Entry entry) { if (entry == null) { throw new NoSuchElementException(); } return entry.getElement(); } - @CheckForNull - private static E getElementOrNull(@CheckForNull Entry entry) { + private static @Nullable E getElementOrNull( + @Nullable Entry entry) { return (entry == null) ? null : entry.getElement(); } } diff --git a/android/guava/src/com/google/common/collect/SortedSetMultimap.java b/android/guava/src/com/google/common/collect/SortedSetMultimap.java index 8449014674a3..ab3f499910fc 100644 --- a/android/guava/src/com/google/common/collect/SortedSetMultimap.java +++ b/android/guava/src/com/google/common/collect/SortedSetMultimap.java @@ -24,8 +24,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@code SetMultimap} whose set of values for a given key are kept sorted; that is, they comprise @@ -43,14 +42,12 @@ * position in the order of the values). Undefined behavior and bugs will result. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @since 2.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface SortedSetMultimap extends SetMultimap { // Following Javadoc copied from Multimap. @@ -77,7 +74,7 @@ public interface SortedSetMultimap removeAll(@CheckForNull Object key); + SortedSet removeAll(@Nullable Object key); /** * Stores a collection of values with the same key, replacing any existing values for that key. @@ -116,6 +113,5 @@ public interface SortedSetMultimap valueComparator(); + @Nullable Comparator valueComparator(); } diff --git a/android/guava/src/com/google/common/collect/SparseImmutableTable.java b/android/guava/src/com/google/common/collect/SparseImmutableTable.java index b314f7bb3396..f6ab2c94c45a 100644 --- a/android/guava/src/com/google/common/collect/SparseImmutableTable.java +++ b/android/guava/src/com/google/common/collect/SparseImmutableTable.java @@ -17,6 +17,8 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.Immutable; import java.util.LinkedHashMap; import java.util.Map; @@ -25,7 +27,6 @@ /** A {@code RegularImmutableTable} optimized for sparse data. */ @GwtCompatible @Immutable(containerOf = {"R", "C", "V"}) -@ElementTypesAreNonnullByDefault final class SparseImmutableTable extends RegularImmutableTable { static final ImmutableTable EMPTY = new SparseImmutableTable<>( @@ -82,14 +83,14 @@ final class SparseImmutableTable extends RegularImmutableTable for (Entry> row : rows.entrySet()) { rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue())); } - this.rowMap = rowBuilder.build(); + this.rowMap = rowBuilder.buildOrThrow(); ImmutableMap.Builder> columnBuilder = new ImmutableMap.Builder<>(columns.size()); for (Entry> col : columns.entrySet()) { columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue())); } - this.columnMap = columnBuilder.build(); + this.columnMap = columnBuilder.buildOrThrow(); } @Override @@ -130,7 +131,9 @@ V getValue(int index) { } @Override - SerializedForm createSerializedForm() { + @J2ktIncompatible // serialization + @GwtIncompatible // serialization + Object writeReplace() { Map columnKeyToIndex = Maps.indexMap(columnKeySet()); int[] cellColumnIndices = new int[cellSet().size()]; int i = 0; diff --git a/android/guava/src/com/google/common/collect/StandardRowSortedTable.java b/android/guava/src/com/google/common/collect/StandardRowSortedTable.java index bea5b7bb3a86..2e0550f4ea08 100644 --- a/android/guava/src/com/google/common/collect/StandardRowSortedTable.java +++ b/android/guava/src/com/google/common/collect/StandardRowSortedTable.java @@ -19,6 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; import com.google.j2objc.annotations.WeakOuter; import java.util.Comparator; @@ -26,7 +28,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Table} whose iteration ordering across row keys is sorted by their @@ -46,7 +48,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault class StandardRowSortedTable extends StandardTable implements RowSortedTable { /* @@ -104,8 +105,7 @@ SortedSet createKeySet() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { return sortedBackingMap().comparator(); } @@ -142,5 +142,5 @@ public SortedMap> tailMap(R fromKey) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/StandardTable.java b/android/guava/src/com/google/common/collect/StandardTable.java index 4c1ffe1843a3..79d1bdef23b2 100644 --- a/android/guava/src/com/google/common/collect/StandardTable.java +++ b/android/guava/src/com/google/common/collect/StandardTable.java @@ -21,19 +21,25 @@ import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Maps.asMapEntryIterator; +import static com.google.common.collect.Maps.immutableEntry; import static com.google.common.collect.Maps.safeContainsKey; import static com.google.common.collect.Maps.safeGet; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static com.google.common.collect.Tables.immutableCell; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Function; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Maps.IteratorBasedAbstractMap; import com.google.common.collect.Maps.ViewCachingAbstractMap; import com.google.common.collect.Sets.ImprovedAbstractSet; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.WeakOuter; import java.io.Serializable; import java.util.Collection; @@ -42,7 +48,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * {@link Table} implementation backed by a map that associates row keys with column key / value @@ -64,7 +70,6 @@ * @author Jared Levy */ @GwtCompatible -@ElementTypesAreNonnullByDefault class StandardTable extends AbstractTable implements Serializable { @GwtTransient final Map> backingMap; @GwtTransient final Supplier> factory; @@ -77,12 +82,12 @@ class StandardTable extends AbstractTable implements Serializa // Accessors @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); } @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { if (columnKey == null) { return false; } @@ -95,18 +100,17 @@ public boolean containsColumn(@CheckForNull Object columnKey) { } @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { return rowKey != null && safeContainsKey(backingMap, rowKey); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return value != null && super.containsValue(value); } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { return (rowKey == null || columnKey == null) ? null : super.get(rowKey, columnKey); } @@ -142,8 +146,7 @@ private Map getOrCreate(R rowKey) { @CanIgnoreReturnValue @Override - @CheckForNull - public V put(R rowKey, C columnKey, V value) { + public @Nullable V put(R rowKey, C columnKey, V value) { checkNotNull(rowKey); checkNotNull(columnKey); checkNotNull(value); @@ -152,8 +155,7 @@ public V put(R rowKey, C columnKey, V value) { @CanIgnoreReturnValue @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { if ((rowKey == null) || (columnKey == null)) { return null; } @@ -169,7 +171,7 @@ public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { } @CanIgnoreReturnValue - private Map removeColumn(@CheckForNull Object column) { + private Map removeColumn(@Nullable Object column) { Map output = new LinkedHashMap<>(); Iterator>> iterator = backingMap.entrySet().iterator(); while (iterator.hasNext()) { @@ -186,13 +188,13 @@ private Map removeColumn(@CheckForNull Object column) { } private boolean containsMapping( - @CheckForNull Object rowKey, @CheckForNull Object columnKey, @CheckForNull Object value) { + @Nullable Object rowKey, @Nullable Object columnKey, @Nullable Object value) { return value != null && value.equals(get(rowKey, columnKey)); } /** Remove a row key / column key / value mapping, if present. */ private boolean removeMapping( - @CheckForNull Object rowKey, @CheckForNull Object columnKey, @CheckForNull Object value) { + @Nullable Object rowKey, @Nullable Object columnKey, @Nullable Object value) { if (containsMapping(rowKey, columnKey, value)) { remove(rowKey, columnKey); return true; @@ -240,7 +242,7 @@ Iterator> cellIterator() { private class CellIterator implements Iterator> { final Iterator>> rowIterator = backingMap.entrySet().iterator(); - @CheckForNull Entry> rowEntry; + @Nullable Entry> rowEntry; Iterator> columnIterator = Iterators.emptyModifiableIterator(); @Override @@ -269,7 +271,7 @@ public Cell next() { */ requireNonNull(rowEntry); Entry columnEntry = columnIterator.next(); - return Tables.immutableCell(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); + return immutableCell(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); } @Override @@ -304,7 +306,7 @@ class Row extends IteratorBasedAbstractMap { this.rowKey = checkNotNull(rowKey); } - @CheckForNull Map backingRowMap; + @Nullable Map backingRowMap; final void updateBackingRowMapField() { if (backingRowMap == null || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) { @@ -312,8 +314,7 @@ final void updateBackingRowMapField() { } } - @CheckForNull - Map computeBackingRowMap() { + @Nullable Map computeBackingRowMap() { return backingMap.get(rowKey); } @@ -327,21 +328,19 @@ void maintainEmptyInvariant() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { updateBackingRowMapField(); return (key != null && backingRowMap != null) && Maps.safeContainsKey(backingRowMap, key); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { updateBackingRowMapField(); - return (key != null && backingRowMap != null) ? Maps.safeGet(backingRowMap, key) : null; + return (key != null && backingRowMap != null) ? safeGet(backingRowMap, key) : null; } @Override - @CheckForNull - public V put(C key, V value) { + public @Nullable V put(C key, V value) { checkNotNull(key); checkNotNull(value); if (backingRowMap != null && !backingRowMap.isEmpty()) { @@ -351,8 +350,7 @@ public V put(C key, V value) { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { updateBackingRowMapField(); if (backingRowMap == null) { return null; @@ -383,7 +381,7 @@ Iterator> entryIterator() { if (backingRowMap == null) { return Iterators.emptyModifiableIterator(); } - final Iterator> iterator = backingRowMap.entrySet().iterator(); + Iterator> iterator = backingRowMap.entrySet().iterator(); return new Iterator>() { @Override public boolean hasNext() { @@ -403,7 +401,7 @@ public void remove() { }; } - Entry wrapEntry(final Entry entry) { + Entry wrapEntry(Entry entry) { return new ForwardingMapEntry() { @Override protected Entry delegate() { @@ -416,7 +414,7 @@ public V setValue(V value) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { // TODO(lowasser): identify why this affects GWT tests return standardEquals(object); } @@ -442,25 +440,22 @@ private class Column extends ViewCachingAbstractMap { } @Override - @CheckForNull - public V put(R key, V value) { + public @Nullable V put(R key, V value) { return StandardTable.this.put(key, columnKey, value); } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { return StandardTable.this.get(key, columnKey); } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return StandardTable.this.contains(key, columnKey); } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { return StandardTable.this.remove(key, columnKey); } @@ -473,7 +468,7 @@ boolean removeFromColumnIf(Predicate> predicate) { Entry> entry = iterator.next(); Map map = entry.getValue(); V value = map.get(columnKey); - if (value != null && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { + if (value != null && predicate.apply(immutableEntry(entry.getKey(), value))) { map.remove(columnKey); changed = true; if (map.isEmpty()) { @@ -518,7 +513,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { if (o instanceof Entry) { Entry entry = (Entry) o; return containsMapping(entry.getKey(), columnKey, entry.getValue()); @@ -527,7 +522,7 @@ public boolean contains(@CheckForNull Object o) { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return removeMapping(entry.getKey(), columnKey, entry.getValue()); @@ -545,10 +540,9 @@ private class EntrySetIterator extends AbstractIterator> { final Iterator>> iterator = backingMap.entrySet().iterator(); @Override - @CheckForNull - protected Entry computeNext() { + protected @Nullable Entry computeNext() { while (iterator.hasNext()) { - final Entry> entry = iterator.next(); + Entry> entry = iterator.next(); if (entry.getValue().containsKey(columnKey)) { @WeakOuter class EntryImpl extends AbstractMapEntry { @@ -601,17 +595,17 @@ private class KeySet extends Maps.KeySet { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return StandardTable.this.contains(obj, columnKey); } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { return StandardTable.this.remove(obj, columnKey) != null; } @Override - public boolean retainAll(final Collection c) { + public boolean retainAll(Collection c) { return removeFromColumnIf(Maps.keyPredicateOnEntries(not(in(c)))); } } @@ -628,17 +622,17 @@ private class Values extends Maps.Values { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { return obj != null && removeFromColumnIf(Maps.valuePredicateOnEntries(equalTo(obj))); } @Override - public boolean removeAll(final Collection c) { + public boolean removeAll(Collection c) { return removeFromColumnIf(Maps.valuePredicateOnEntries(in(c))); } @Override - public boolean retainAll(final Collection c) { + public boolean retainAll(Collection c) { return removeFromColumnIf(Maps.valuePredicateOnEntries(not(in(c)))); } } @@ -649,7 +643,7 @@ public Set rowKeySet() { return rowMap().keySet(); } - @CheckForNull private transient Set columnKeySet; + @LazyInit private transient @Nullable Set columnKeySet; /** * {@inheritDoc} @@ -678,7 +672,7 @@ public int size() { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { if (obj == null) { return false; } @@ -733,7 +727,7 @@ public boolean retainAll(Collection c) { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return containsColumn(obj); } } @@ -748,11 +742,10 @@ private class ColumnKeyIterator extends AbstractIterator { // consistent with equals(). final Map seen = factory.get(); final Iterator> mapIterator = backingMap.values().iterator(); - Iterator> entryIterator = Iterators.emptyIterator(); + Iterator> entryIterator = emptyIterator(); @Override - @CheckForNull - protected C computeNext() { + protected @Nullable C computeNext() { while (true) { if (entryIterator.hasNext()) { Entry entry = entryIterator.next(); @@ -780,7 +773,7 @@ public Collection values() { return super.values(); } - @CheckForNull private transient Map> rowMap; + @LazyInit private transient @Nullable Map> rowMap; @Override public Map> rowMap() { @@ -795,22 +788,20 @@ Map> createRowMap() { @WeakOuter class RowMap extends ViewCachingAbstractMap> { @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return containsRow(key); } // performing cast only when key is in backing map and has the correct type @SuppressWarnings("unchecked") @Override - @CheckForNull - public Map get(@CheckForNull Object key) { + public @Nullable Map get(@Nullable Object key) { // requireNonNull is safe because of the containsRow check. return containsRow(key) ? row((R) requireNonNull(key)) : null; } @Override - @CheckForNull - public Map remove(@CheckForNull Object key) { + public @Nullable Map remove(@Nullable Object key) { return (key == null) ? null : backingMap.remove(key); } @@ -820,17 +811,10 @@ protected Set>> createEntrySet() { } @WeakOuter - class EntrySet extends TableSet>> { + private final class EntrySet extends TableSet>> { @Override public Iterator>> iterator() { - return Maps.asMapEntryIterator( - backingMap.keySet(), - new Function>() { - @Override - public Map apply(R rowKey) { - return row(rowKey); - } - }); + return asMapEntryIterator(backingMap.keySet(), StandardTable.this::row); } @Override @@ -839,7 +823,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return entry.getKey() != null @@ -850,7 +834,7 @@ public boolean contains(@CheckForNull Object obj) { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return entry.getKey() != null @@ -862,7 +846,7 @@ public boolean remove(@CheckForNull Object obj) { } } - @CheckForNull private transient ColumnMap columnMap; + @LazyInit private transient @Nullable ColumnMap columnMap; @Override public Map> columnMap() { @@ -876,20 +860,18 @@ private class ColumnMap extends ViewCachingAbstractMap> { // has the correct type. @SuppressWarnings("unchecked") @Override - @CheckForNull - public Map get(@CheckForNull Object key) { + public @Nullable Map get(@Nullable Object key) { // requireNonNull is safe because of the containsColumn check. return containsColumn(key) ? column((C) requireNonNull(key)) : null; } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return containsColumn(key); } @Override - @CheckForNull - public Map remove(@CheckForNull Object key) { + public @Nullable Map remove(@Nullable Object key) { return containsColumn(key) ? removeColumn(key) : null; } @@ -909,17 +891,10 @@ Collection> createValues() { } @WeakOuter - class ColumnMapEntrySet extends TableSet>> { + private final class ColumnMapEntrySet extends TableSet>> { @Override public Iterator>> iterator() { - return Maps.asMapEntryIterator( - columnKeySet(), - new Function>() { - @Override - public Map apply(C columnKey) { - return column(columnKey); - } - }); + return asMapEntryIterator(columnKeySet(), StandardTable.this::column); } @Override @@ -928,7 +903,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; if (containsColumn(entry.getKey())) { @@ -940,7 +915,7 @@ public boolean contains(@CheckForNull Object obj) { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { /* * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our * nullness checker. @@ -957,7 +932,7 @@ public boolean remove(@CheckForNull Object obj) { public boolean removeAll(Collection c) { /* * We can't inherit the normal implementation (which calls - * Sets.removeAllImpl(Set, *Collection*) because, under some + * Sets.removeAllImpl(Set, *Collection*)) because, under some * circumstances, it attempts to call columnKeySet().iterator().remove, * which is unsupported. */ @@ -970,7 +945,7 @@ public boolean retainAll(Collection c) { checkNotNull(c); boolean changed = false; for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { - if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { + if (!c.contains(immutableEntry(columnKey, column(columnKey)))) { removeColumn(columnKey); changed = true; } @@ -986,7 +961,7 @@ private class ColumnMapValues extends Maps.Values> { } @Override - public boolean remove(@CheckForNull Object obj) { + public boolean remove(@Nullable Object obj) { for (Entry> entry : ColumnMap.this.entrySet()) { if (entry.getValue().equals(obj)) { removeColumn(entry.getKey()); @@ -1024,5 +999,5 @@ public boolean retainAll(Collection c) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/Streams.java b/android/guava/src/com/google/common/collect/Streams.java new file mode 100644 index 000000000000..8b3b25ec27cc --- /dev/null +++ b/android/guava/src/com/google/common/collect/Streams.java @@ -0,0 +1,1000 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static com.google.common.collect.SneakyThrows.sneakyThrow; +import static java.lang.Math.min; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.math.LongMath; +import com.google.errorprone.annotations.InlineMe; +import com.google.errorprone.annotations.InlineMeValidationDisabled; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.Spliterators.AbstractSpliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; + +/** + * Static utility methods related to {@code Stream} instances. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ +@GwtCompatible +@SuppressWarnings("Java7ApiChecker") +/* + * Users will use most of these methods only if they're already using Stream. For a few other + * methods, like stream(Iterable), we have to rely on users not to call them without library + * desugaring. + */ +@IgnoreJRERequirement +public final class Streams { + /** + * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link + * Collection#stream} if possible. + */ + public static Stream stream(Iterable iterable) { + return (iterable instanceof Collection) + ? ((Collection) iterable).stream() + : StreamSupport.stream(iterable.spliterator(), false); + } + + /** + * Returns {@link Collection#stream}. + * + * @deprecated There is no reason to use this; just invoke {@code collection.stream()} directly. + */ + @Deprecated + @InlineMe(replacement = "collection.stream()") + public static Stream stream(Collection collection) { + return collection.stream(); + } + + /** + * Returns a sequential {@link Stream} of the remaining contents of {@code iterator}. Do not use + * {@code iterator} directly after passing it to this method. + */ + public static Stream stream(Iterator iterator) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + */ + public static Stream stream(com.google.common.base.Optional optional) { + return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

    Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + @InlineMe(replacement = "optional.stream()") + @InlineMeValidationDisabled("Java 9+ API only") + public static Stream stream(java.util.Optional optional) { + return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

    Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + @InlineMe(replacement = "optional.stream()") + @InlineMeValidationDisabled("Java 9+ API only") + public static IntStream stream(OptionalInt optional) { + return optional.isPresent() ? IntStream.of(optional.getAsInt()) : IntStream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

    Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + @InlineMe(replacement = "optional.stream()") + @InlineMeValidationDisabled("Java 9+ API only") + public static LongStream stream(OptionalLong optional) { + return optional.isPresent() ? LongStream.of(optional.getAsLong()) : LongStream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

    Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + @InlineMe(replacement = "optional.stream()") + @InlineMeValidationDisabled("Java 9+ API only") + public static DoubleStream stream(OptionalDouble optional) { + return optional.isPresent() ? DoubleStream.of(optional.getAsDouble()) : DoubleStream.empty(); + } + + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception + private static void closeAll(BaseStream[] toClose) { + // If one of the streams throws an exception, continue closing the others, then throw the + // exception later. If more than one stream throws an exception, the later ones are added to the + // first as suppressed exceptions. We don't catch Error on the grounds that it should be allowed + // to propagate immediately. + Exception exception = null; + for (BaseStream stream : toClose) { + try { + stream.close(); + } catch (Exception e) { // sneaky checked exception + if (exception == null) { + exception = e; + } else { + exception.addSuppressed(e); + } + } + } + if (exception != null) { + // Normally this is a RuntimeException that doesn't need sneakyThrow. + // But theoretically we could see sneaky checked exception + sneakyThrow(exception); + } + } + + /** + * Returns a {@link Stream} containing the elements of the first stream, followed by the elements + * of the second stream, and so on. + * + *

    This is equivalent to {@code Stream.of(streams).flatMap(stream -> stream)}, but the returned + * stream may perform better. + * + * @see Stream#concat(Stream, Stream) + */ + @SuppressWarnings("unchecked") // could probably be avoided with a forwarding Spliterator + @SafeVarargs + public static Stream concat(Stream... streams) { + // TODO(lowasser): consider an implementation that can support SUBSIZED + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder> splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (Stream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.stream( + CollectSpliterators.flatMap( + splitrsBuilder.build().spliterator(), + splitr -> (Spliterator) splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns an {@link IntStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

    This is equivalent to {@code Stream.of(streams).flatMapToInt(stream -> stream)}, but the + * returned stream may perform better. + * + * @see IntStream#concat(IntStream, IntStream) + */ + public static IntStream concat(IntStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (IntStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfInt splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.intStream( + CollectSpliterators.flatMapToInt( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a {@link LongStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

    This is equivalent to {@code Stream.of(streams).flatMapToLong(stream -> stream)}, but the + * returned stream may perform better. + * + * @see LongStream#concat(LongStream, LongStream) + */ + public static LongStream concat(LongStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (LongStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfLong splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.longStream( + CollectSpliterators.flatMapToLong( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a {@link DoubleStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

    This is equivalent to {@code Stream.of(streams).flatMapToDouble(stream -> stream)}, but the + * returned stream may perform better. + * + * @see DoubleStream#concat(DoubleStream, DoubleStream) + */ + public static DoubleStream concat(DoubleStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (DoubleStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfDouble splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.doubleStream( + CollectSpliterators.flatMapToDouble( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a stream in which each element is the result of passing the corresponding element of + * each of {@code streamA} and {@code streamB} to {@code function}. + * + *

    For example: + * + *

    {@code
    +   * Streams.zip(
    +   *   Stream.of("foo1", "foo2", "foo3"),
    +   *   Stream.of("bar1", "bar2"),
    +   *   (arg1, arg2) -> arg1 + ":" + arg2)
    +   * }
    + * + *

    will return {@code Stream.of("foo1:bar1", "foo2:bar2")}. + * + *

    The resulting stream will only be as long as the shorter of the two input streams; if one + * stream is longer, its extra elements will be ignored. + * + *

    Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want + * to consider using {@link #forEachPair} instead of this method. + * + *

    Performance note: The resulting stream is not efficiently splittable. + * This may harm parallel performance. + */ + @Beta + public static + Stream zip( + Stream streamA, Stream streamB, BiFunction function) { + checkNotNull(streamA); + checkNotNull(streamB); + checkNotNull(function); + boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat + Spliterator splitrA = streamA.spliterator(); + Spliterator splitrB = streamB.spliterator(); + int characteristics = + splitrA.characteristics() + & splitrB.characteristics() + & (Spliterator.SIZED | Spliterator.ORDERED); + Iterator itrA = Spliterators.iterator(splitrA); + Iterator itrB = Spliterators.iterator(splitrB); + return StreamSupport.stream( + new AbstractSpliterator( + min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) { + @Override + public boolean tryAdvance(Consumer action) { + if (itrA.hasNext() && itrB.hasNext()) { + action.accept(function.apply(itrA.next(), itrB.next())); + return true; + } + return false; + } + }, + isParallel) + .onClose(streamA::close) + .onClose(streamB::close); + } + + /** + * Invokes {@code consumer} once for each pair of corresponding elements in {@code streamA} + * and {@code streamB}. If one stream is longer than the other, the extra elements are silently + * ignored. Elements passed to the consumer are guaranteed to come from the same position in their + * respective source streams. For example: + * + *

    {@code
    +   * Streams.forEachPair(
    +   *   Stream.of("foo1", "foo2", "foo3"),
    +   *   Stream.of("bar1", "bar2"),
    +   *   (arg1, arg2) -> System.out.println(arg1 + ":" + arg2)
    +   * }
    + * + *

    will print: + * + *

    {@code
    +   * foo1:bar1
    +   * foo2:bar2
    +   * }
    + * + *

    Warning: If either supplied stream is a parallel stream, the same correspondence + * between elements will be made, but the order in which those pairs of elements are passed to the + * consumer is not defined. + * + *

    Note that many usages of this method can be replaced with simpler calls to {@link #zip}. + * This method behaves equivalently to {@linkplain #zip zipping} the stream elements into + * temporary pair objects and then using {@link Stream#forEach} on that stream. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @Beta + public static void forEachPair( + Stream streamA, Stream streamB, BiConsumer consumer) { + checkNotNull(consumer); + + if (streamA.isParallel() || streamB.isParallel()) { + zip(streamA, streamB, TemporaryPair::new).forEach(pair -> consumer.accept(pair.a, pair.b)); + } else { + Iterator iterA = streamA.iterator(); + Iterator iterB = streamB.iterator(); + while (iterA.hasNext() && iterB.hasNext()) { + consumer.accept(iterA.next(), iterB.next()); + } + } + } + + // Use this carefully - it doesn't implement value semantics + private static class TemporaryPair { + @ParametricNullness final A a; + @ParametricNullness final B b; + + TemporaryPair(@ParametricNullness A a, @ParametricNullness B b) { + this.a = a; + this.b = b; + } + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indices in the stream. For example, + * + *

    {@code
    +   * mapWithIndex(
    +   *     Stream.of("a", "b", "c"),
    +   *     (e, index) -> index + ":" + e)
    +   * }
    + * + *

    would return {@code Stream.of("0:a", "1:b", "2:c")}. + * + *

    The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

    The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + public static Stream mapWithIndex( + Stream stream, FunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + Iterator fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.next(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator, R, Splitr> implements Consumer { + @Nullable T holder; + + Splitr(Spliterator splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(@ParametricNullness T t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + try { + // The cast is safe because tryAdvance puts a T into `holder`. + action.accept(function.apply(uncheckedCastNullableTToT(holder), index++)); + return true; + } finally { + holder = null; + } + } + return false; + } + + @Override + Splitr createSplit(Spliterator from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

    {@code
    +   * mapWithIndex(
    +   *     IntStream.of(10, 11, 12),
    +   *     (e, index) -> index + ":" + e)
    +   * }
    + * + *

    ...would return {@code Stream.of("0:10", "1:11", "2:12")}. + * + *

    The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

    The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + @SuppressWarnings("AndroidJdkLibsChecker") // b/229998664 + public static Stream mapWithIndex( + IntStream stream, IntFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfInt fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfInt fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextInt(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements IntConsumer { + int holder; + + Splitr(Spliterator.OfInt splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(int t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfInt from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

    {@code
    +   * mapWithIndex(
    +   *     LongStream.of(10, 11, 12),
    +   *     (e, index) -> index + ":" + e)
    +   * }
    + * + *

    ...would return {@code Stream.of("0:10", "1:11", "2:12")}. + * + *

    The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

    The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + @SuppressWarnings("AndroidJdkLibsChecker") // b/229998664 + public static Stream mapWithIndex( + LongStream stream, LongFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfLong fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfLong fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextLong(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements LongConsumer { + long holder; + + Splitr(Spliterator.OfLong splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(long t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfLong from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

    {@code
    +   * mapWithIndex(
    +   *     DoubleStream.of(0.0, 1.0, 2.0)
    +   *     (e, index) -> index + ":" + e)
    +   * }
    + * + *

    ...would return {@code Stream.of("0:0.0", "1:1.0", "2:2.0")}. + * + *

    The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

    The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + @SuppressWarnings("AndroidJdkLibsChecker") // b/229998664 + public static Stream mapWithIndex( + DoubleStream stream, DoubleFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfDouble fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfDouble fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextDouble(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements DoubleConsumer { + double holder; + + Splitr(Spliterator.OfDouble splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(double t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfDouble from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * An analogue of {@link java.util.function.Function} also accepting an index. + * + *

    This interface is only intended for use by callers of {@link #mapWithIndex(Stream, + * FunctionWithIndex)}. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + public interface FunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(@ParametricNullness T from, long index); + } + + /* + * @IgnoreJRERequirement should be redundant with the one on Streams itself, but it's necessary as + * of Animal Sniffer 1.24. Maybe Animal Sniffer processes this nested class before it processes + * Streams and thus hasn't had a chance to see Streams's annotation? + */ + @IgnoreJRERequirement + private abstract static class MapWithIndexSpliterator< + F extends Spliterator, + R extends @Nullable Object, + S extends MapWithIndexSpliterator> + implements Spliterator { + final F fromSpliterator; + long index; + + MapWithIndexSpliterator(F fromSpliterator, long index) { + this.fromSpliterator = fromSpliterator; + this.index = index; + } + + abstract S createSplit(F from, long i); + + @Override + public @Nullable S trySplit() { + Spliterator splitOrNull = fromSpliterator.trySplit(); + if (splitOrNull == null) { + return null; + } + @SuppressWarnings("unchecked") + F split = (F) splitOrNull; + S result = createSplit(split, index); + this.index += split.getExactSizeIfKnown(); + return result; + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & (Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED); + } + } + + /** + * An analogue of {@link java.util.function.IntFunction} also accepting an index. + * + *

    This interface is only intended for use by callers of {@link #mapWithIndex(IntStream, + * IntFunctionWithIndex)}. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + public interface IntFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(int from, long index); + } + + /** + * An analogue of {@link java.util.function.LongFunction} also accepting an index. + * + *

    This interface is only intended for use by callers of {@link #mapWithIndex(LongStream, + * LongFunctionWithIndex)}. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + public interface LongFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(long from, long index); + } + + /** + * An analogue of {@link java.util.function.DoubleFunction} also accepting an index. + * + *

    This interface is only intended for use by callers of {@link #mapWithIndex(DoubleStream, + * DoubleFunctionWithIndex)}. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + public interface DoubleFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(double from, long index); + } + + /** + * Returns the last element of the specified stream, or {@link java.util.Optional#empty} if the + * stream is empty. + * + *

    Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + *

    If the stream has nondeterministic order, this has equivalent semantics to {@link + * Stream#findAny} (which you might as well use). + * + * @see Stream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + /* + * By declaring instead of , we declare this method as requiring a + * stream whose elements are non-null. However, the method goes out of its way to still handle + * nulls in the stream. This means that the method can safely be used with a stream that contains + * nulls as long as the *last* element is *not* null. + * + * (To "go out of its way," the method tracks a `set` bit so that it can distinguish "the final + * split has a last element of null, so throw NPE" from "the final split was empty, so look for an + * element in the prior one.") + */ + public static java.util.Optional findLast(Stream stream) { + class OptionalState { + boolean set = false; + @Nullable T value = null; + + void set(T value) { + this.set = true; + this.value = value; + } + + T get() { + /* + * requireNonNull is safe because we call get() only if we've previously called set(). + * + * (For further discussion of nullness, see the comment above the method.) + */ + return requireNonNull(value); + } + } + OptionalState state = new OptionalState(); + + Deque> splits = new ArrayDeque<>(); + splits.addLast(stream.spliterator()); + + while (!splits.isEmpty()) { + Spliterator spliterator = splits.removeLast(); + + if (spliterator.getExactSizeIfKnown() == 0) { + continue; // drop this split + } + + // Many spliterators will have trySplits that are SUBSIZED even if they are not themselves + // SUBSIZED. + if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + // we can drill down to exactly the smallest nonempty spliterator + while (true) { + Spliterator prefix = spliterator.trySplit(); + if (prefix == null || prefix.getExactSizeIfKnown() == 0) { + break; + } else if (spliterator.getExactSizeIfKnown() == 0) { + spliterator = prefix; + break; + } + } + + // spliterator is known to be nonempty now + spliterator.forEachRemaining(state::set); + return java.util.Optional.of(state.get()); + } + + Spliterator prefix = spliterator.trySplit(); + if (prefix == null || prefix.getExactSizeIfKnown() == 0) { + // we can't split this any further + spliterator.forEachRemaining(state::set); + if (state.set) { + return java.util.Optional.of(state.get()); + } + // fall back to the last split + continue; + } + splits.addLast(prefix); + splits.addLast(spliterator); + } + return java.util.Optional.empty(); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalInt#empty} if the stream is + * empty. + * + *

    Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see IntStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalInt findLast(IntStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalInt::of).orElse(OptionalInt.empty()); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalLong#empty} if the stream + * is empty. + * + *

    Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see LongStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalLong findLast(LongStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalLong::of).orElse(OptionalLong.empty()); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalDouble#empty} if the stream + * is empty. + * + *

    Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see DoubleStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalDouble findLast(DoubleStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalDouble::of).orElse(OptionalDouble.empty()); + } + + private Streams() {} +} diff --git a/android/guava/src/com/google/common/collect/Synchronized.java b/android/guava/src/com/google/common/collect/Synchronized.java index 42bf6114c840..64d7474cfd53 100644 --- a/android/guava/src/com/google/common/collect/Synchronized.java +++ b/android/guava/src/com/google/common/collect/Synchronized.java @@ -17,9 +17,11 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Maps.transformValues; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.j2objc.annotations.RetainedWith; import java.io.IOException; @@ -39,8 +41,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Synchronized collection views. The returned synchronized collection views are serializable if the @@ -54,8 +55,8 @@ * @author Mike Bostock * @author Jared Levy */ +@J2ktIncompatible @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault /* * I have decided not to bother adding @ParametricNullness annotations in this class. Adding them is * a lot of busy work, and the annotation matters only when the APIs to be annotated are visible to @@ -72,7 +73,7 @@ static class SynchronizedObject implements Serializable { final Object delegate; final Object mutex; - SynchronizedObject(Object delegate, @CheckForNull Object mutex) { + SynchronizedObject(Object delegate, @Nullable Object mutex) { this.delegate = checkNotNull(delegate); this.mutex = (mutex == null) ? this : mutex; } @@ -96,25 +97,25 @@ public String toString() { // following writeObject() handles the SynchronizedObject members. @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { synchronized (mutex) { stream.defaultWriteObject(); } } - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static Collection collection( - Collection collection, @CheckForNull Object mutex) { - return new SynchronizedCollection(collection, mutex); + Collection collection, @Nullable Object mutex) { + return new SynchronizedCollection<>(collection, mutex); } @VisibleForTesting static class SynchronizedCollection extends SynchronizedObject implements Collection { - private SynchronizedCollection(Collection delegate, @CheckForNull Object mutex) { + private SynchronizedCollection(Collection delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -146,7 +147,7 @@ public void clear() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { synchronized (mutex) { return delegate().contains(o); } @@ -172,7 +173,7 @@ public Iterator iterator() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { synchronized (mutex) { return delegate().remove(o); } @@ -214,18 +215,18 @@ public int size() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @VisibleForTesting - static Set set(Set set, @CheckForNull Object mutex) { - return new SynchronizedSet(set, mutex); + static Set set(Set set, @Nullable Object mutex) { + return new SynchronizedSet<>(set, mutex); } static class SynchronizedSet extends SynchronizedCollection implements Set { - SynchronizedSet(Set delegate, @CheckForNull Object mutex) { + SynchronizedSet(Set delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -235,7 +236,7 @@ Set delegate() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -251,17 +252,17 @@ public int hashCode() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static SortedSet sortedSet( - SortedSet set, @CheckForNull Object mutex) { - return new SynchronizedSortedSet(set, mutex); + SortedSet set, @Nullable Object mutex) { + return new SynchronizedSortedSet<>(set, mutex); } static class SynchronizedSortedSet extends SynchronizedSet implements SortedSet { - SynchronizedSortedSet(SortedSet delegate, @CheckForNull Object mutex) { + SynchronizedSortedSet(SortedSet delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -271,8 +272,7 @@ SortedSet delegate() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { synchronized (mutex) { return delegate().comparator(); } @@ -313,19 +313,18 @@ public E last() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static List list( - List list, @CheckForNull Object mutex) { + private static List list(List list, @Nullable Object mutex) { return (list instanceof RandomAccess) ? new SynchronizedRandomAccessList(list, mutex) : new SynchronizedList(list, mutex); } - private static class SynchronizedList - extends SynchronizedCollection implements List { - SynchronizedList(List delegate, @CheckForNull Object mutex) { + static class SynchronizedList extends SynchronizedCollection + implements List { + SynchronizedList(List delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -356,14 +355,14 @@ public E get(int index) { } @Override - public int indexOf(@CheckForNull Object o) { + public int indexOf(@Nullable Object o) { synchronized (mutex) { return delegate().indexOf(o); } } @Override - public int lastIndexOf(@CheckForNull Object o) { + public int lastIndexOf(@Nullable Object o) { synchronized (mutex) { return delegate().lastIndexOf(o); } @@ -401,7 +400,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -417,32 +416,32 @@ public int hashCode() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static class SynchronizedRandomAccessList + static final class SynchronizedRandomAccessList extends SynchronizedList implements RandomAccess { - SynchronizedRandomAccessList(List list, @CheckForNull Object mutex) { + SynchronizedRandomAccessList(List list, @Nullable Object mutex) { super(list, mutex); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static Multiset multiset( - Multiset multiset, @CheckForNull Object mutex) { + Multiset multiset, @Nullable Object mutex) { if (multiset instanceof SynchronizedMultiset || multiset instanceof ImmutableMultiset) { return multiset; } - return new SynchronizedMultiset(multiset, mutex); + return new SynchronizedMultiset<>(multiset, mutex); } - private static class SynchronizedMultiset + static final class SynchronizedMultiset extends SynchronizedCollection implements Multiset { - @CheckForNull transient Set elementSet; - @CheckForNull transient Set> entrySet; + transient @Nullable Set elementSet; + transient @Nullable Set> entrySet; - SynchronizedMultiset(Multiset delegate, @CheckForNull Object mutex) { + SynchronizedMultiset(Multiset delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -452,35 +451,35 @@ Multiset delegate() { } @Override - public int count(@CheckForNull Object o) { + public int count(@Nullable Object o) { synchronized (mutex) { return delegate().count(o); } } @Override - public int add(E e, int n) { + public int add(@ParametricNullness E e, int n) { synchronized (mutex) { return delegate().add(e, n); } } @Override - public int remove(@CheckForNull Object o, int n) { + public int remove(@Nullable Object o, int n) { synchronized (mutex) { return delegate().remove(o, n); } } @Override - public int setCount(E element, int count) { + public int setCount(@ParametricNullness E element, int count) { synchronized (mutex) { return delegate().setCount(element, count); } } @Override - public boolean setCount(E element, int oldCount, int newCount) { + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { synchronized (mutex) { return delegate().setCount(element, oldCount, newCount); } @@ -507,7 +506,7 @@ public Set> entrySet() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -523,24 +522,24 @@ public int hashCode() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static Multimap multimap( - Multimap multimap, @CheckForNull Object mutex) { + Multimap multimap, @Nullable Object mutex) { if (multimap instanceof SynchronizedMultimap || multimap instanceof BaseImmutableMultimap) { return multimap; } return new SynchronizedMultimap<>(multimap, mutex); } - private static class SynchronizedMultimap + static class SynchronizedMultimap extends SynchronizedObject implements Multimap { - @CheckForNull transient Set keySet; - @CheckForNull transient Collection valuesCollection; - @CheckForNull transient Collection> entries; - @CheckForNull transient Map> asMap; - @CheckForNull transient Multiset keys; + transient @Nullable Set keySet; + transient @Nullable Collection valuesCollection; + transient @Nullable Collection> entries; + transient @Nullable Map> asMap; + transient @Nullable Multiset keys; @SuppressWarnings("unchecked") @Override @@ -548,7 +547,7 @@ Multimap delegate() { return (Multimap) super.delegate(); } - SynchronizedMultimap(Multimap delegate, @CheckForNull Object mutex) { + SynchronizedMultimap(Multimap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -567,42 +566,42 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { synchronized (mutex) { return delegate().containsKey(key); } } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { synchronized (mutex) { return delegate().containsValue(value); } } @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { synchronized (mutex) { return delegate().containsEntry(key, value); } } @Override - public Collection get(K key) { + public Collection get(@ParametricNullness K key) { synchronized (mutex) { return typePreservingCollection(delegate().get(key), mutex); } } @Override - public boolean put(K key, V value) { + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { synchronized (mutex) { return delegate().put(key, value); } } @Override - public boolean putAll(K key, Iterable values) { + public boolean putAll(@ParametricNullness K key, Iterable values) { synchronized (mutex) { return delegate().putAll(key, values); } @@ -616,21 +615,21 @@ public boolean putAll(Multimap multimap) { } @Override - public Collection replaceValues(K key, Iterable values) { + public Collection replaceValues(@ParametricNullness K key, Iterable values) { synchronized (mutex) { return delegate().replaceValues(key, values); // copy not synchronized } } @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { + public boolean remove(@Nullable Object key, @Nullable Object value) { synchronized (mutex) { return delegate().remove(key, value); } } @Override - public Collection removeAll(@CheckForNull Object key) { + public Collection removeAll(@Nullable Object key) { synchronized (mutex) { return delegate().removeAll(key); // copy not synchronized } @@ -694,7 +693,9 @@ public Multiset keys() { } @Override - public boolean equals(@CheckForNull Object o) { + // A forwarding implementation can't do any better than the underlying object. + @SuppressWarnings("UndefinedEquals") + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -710,21 +711,21 @@ public int hashCode() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static ListMultimap listMultimap( - ListMultimap multimap, @CheckForNull Object mutex) { + ListMultimap multimap, @Nullable Object mutex) { if (multimap instanceof SynchronizedListMultimap || multimap instanceof BaseImmutableMultimap) { return multimap; } return new SynchronizedListMultimap<>(multimap, mutex); } - private static class SynchronizedListMultimap< + static final class SynchronizedListMultimap< K extends @Nullable Object, V extends @Nullable Object> extends SynchronizedMultimap implements ListMultimap { - SynchronizedListMultimap(ListMultimap delegate, @CheckForNull Object mutex) { + SynchronizedListMultimap(ListMultimap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -741,7 +742,7 @@ public List get(K key) { } @Override - public List removeAll(@CheckForNull Object key) { + public List removeAll(@Nullable Object key) { synchronized (mutex) { return delegate().removeAll(key); // copy not synchronized } @@ -754,23 +755,22 @@ public List replaceValues(K key, Iterable values) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static SetMultimap setMultimap( - SetMultimap multimap, @CheckForNull Object mutex) { + SetMultimap multimap, @Nullable Object mutex) { if (multimap instanceof SynchronizedSetMultimap || multimap instanceof BaseImmutableMultimap) { return multimap; } return new SynchronizedSetMultimap<>(multimap, mutex); } - private static class SynchronizedSetMultimap< - K extends @Nullable Object, V extends @Nullable Object> + static class SynchronizedSetMultimap extends SynchronizedMultimap implements SetMultimap { - @CheckForNull transient Set> entrySet; + transient @Nullable Set> entrySet; - SynchronizedSetMultimap(SetMultimap delegate, @CheckForNull Object mutex) { + SynchronizedSetMultimap(SetMultimap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -787,7 +787,7 @@ public Set get(K key) { } @Override - public Set removeAll(@CheckForNull Object key) { + public Set removeAll(@Nullable Object key) { synchronized (mutex) { return delegate().removeAll(key); // copy not synchronized } @@ -810,22 +810,22 @@ public Set> entries() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static SortedSetMultimap sortedSetMultimap( - SortedSetMultimap multimap, @CheckForNull Object mutex) { + SortedSetMultimap multimap, @Nullable Object mutex) { if (multimap instanceof SynchronizedSortedSetMultimap) { return multimap; } return new SynchronizedSortedSetMultimap<>(multimap, mutex); } - private static class SynchronizedSortedSetMultimap< + static final class SynchronizedSortedSetMultimap< K extends @Nullable Object, V extends @Nullable Object> extends SynchronizedSetMultimap implements SortedSetMultimap { - SynchronizedSortedSetMultimap(SortedSetMultimap delegate, @CheckForNull Object mutex) { + SynchronizedSortedSetMultimap(SortedSetMultimap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -842,7 +842,7 @@ public SortedSet get(K key) { } @Override - public SortedSet removeAll(@CheckForNull Object key) { + public SortedSet removeAll(@Nullable Object key) { synchronized (mutex) { return delegate().removeAll(key); // copy not synchronized } @@ -856,18 +856,17 @@ public SortedSet replaceValues(K key, Iterable values) { } @Override - @CheckForNull - public Comparator valueComparator() { + public @Nullable Comparator valueComparator() { synchronized (mutex) { return delegate().valueComparator(); } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } private static Collection typePreservingCollection( - Collection collection, @CheckForNull Object mutex) { + Collection collection, @Nullable Object mutex) { if (collection instanceof SortedSet) { return sortedSet((SortedSet) collection, mutex); } @@ -881,7 +880,7 @@ public Comparator valueComparator() { } private static Set typePreservingSet( - Set set, @CheckForNull Object mutex) { + Set set, @Nullable Object mutex) { if (set instanceof SortedSet) { return sortedSet((SortedSet) set, mutex); } else { @@ -889,11 +888,10 @@ public Comparator valueComparator() { } } - private static class SynchronizedAsMapEntries< + static final class SynchronizedAsMapEntries< K extends @Nullable Object, V extends @Nullable Object> extends SynchronizedSet>> { - SynchronizedAsMapEntries( - Set>> delegate, @CheckForNull Object mutex) { + SynchronizedAsMapEntries(Set>> delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -903,7 +901,7 @@ public Iterator>> iterator() { return new TransformedIterator>, Map.Entry>>( super.iterator()) { @Override - Map.Entry> transform(final Map.Entry> entry) { + Map.Entry> transform(Map.Entry> entry) { return new ForwardingMapEntry>() { @Override protected Map.Entry> delegate() { @@ -922,16 +920,15 @@ public Collection getValue() { // See Collections.CheckedMap.CheckedEntrySet for details on attacks. @Override - public Object[] toArray() { + public @Nullable Object[] toArray() { synchronized (mutex) { /* * toArrayImpl returns `@Nullable Object[]` rather than `Object[]` but only because it can * be used with collections that may contain null. This collection never contains nulls, so - * we can treat it as a plain `Object[]`. + * we could return `Object[]`. But this class is private and J2KT cannot change return types + * in overrides, so we declare `@Nullable Object[]` as the return type. */ - @SuppressWarnings("nullness") - Object[] result = (Object[]) ObjectArrays.toArrayImpl(delegate()); - return result; + return ObjectArrays.toArrayImpl(delegate()); } } @@ -944,7 +941,7 @@ public Object[] toArray() { } @Override - public boolean contains(@CheckForNull Object o) { + public boolean contains(@Nullable Object o) { synchronized (mutex) { return Maps.containsEntryImpl(delegate(), o); } @@ -958,7 +955,7 @@ public boolean containsAll(Collection c) { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -968,7 +965,7 @@ public boolean equals(@CheckForNull Object o) { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { synchronized (mutex) { return Maps.removeEntryImpl(delegate(), o); } @@ -988,22 +985,22 @@ public boolean retainAll(Collection c) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @VisibleForTesting static Map map( - Map map, @CheckForNull Object mutex) { + Map map, @Nullable Object mutex) { return new SynchronizedMap<>(map, mutex); } - private static class SynchronizedMap + static class SynchronizedMap extends SynchronizedObject implements Map { - @CheckForNull transient Set keySet; - @CheckForNull transient Collection values; - @CheckForNull transient Set> entrySet; + transient @Nullable Set keySet; + transient @Nullable Collection values; + transient @Nullable Set> entrySet; - SynchronizedMap(Map delegate, @CheckForNull Object mutex) { + SynchronizedMap(Map delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1021,14 +1018,14 @@ public void clear() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { synchronized (mutex) { return delegate().containsKey(key); } } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { synchronized (mutex) { return delegate().containsValue(value); } @@ -1045,8 +1042,7 @@ public Set> entrySet() { } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { synchronized (mutex) { return delegate().get(key); } @@ -1070,8 +1066,7 @@ public Set keySet() { } @Override - @CheckForNull - public V put(K key, V value) { + public @Nullable V put(K key, V value) { synchronized (mutex) { return delegate().put(key, value); } @@ -1085,8 +1080,7 @@ public void putAll(Map map) { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { synchronized (mutex) { return delegate().remove(key); } @@ -1110,7 +1104,7 @@ public Collection values() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -1126,18 +1120,18 @@ public int hashCode() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static SortedMap sortedMap( - SortedMap sortedMap, @CheckForNull Object mutex) { + SortedMap sortedMap, @Nullable Object mutex) { return new SynchronizedSortedMap<>(sortedMap, mutex); } static class SynchronizedSortedMap extends SynchronizedMap implements SortedMap { - SynchronizedSortedMap(SortedMap delegate, @CheckForNull Object mutex) { + SynchronizedSortedMap(SortedMap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1147,8 +1141,7 @@ SortedMap delegate() { } @Override - @CheckForNull - public Comparator comparator() { + public @Nullable Comparator comparator() { synchronized (mutex) { return delegate().comparator(); } @@ -1189,25 +1182,24 @@ public SortedMap tailMap(K fromKey) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static BiMap biMap( - BiMap bimap, @CheckForNull Object mutex) { + BiMap bimap, @Nullable Object mutex) { if (bimap instanceof SynchronizedBiMap || bimap instanceof ImmutableBiMap) { return bimap; } return new SynchronizedBiMap<>(bimap, mutex, null); } - @VisibleForTesting - static class SynchronizedBiMap - extends SynchronizedMap implements BiMap, Serializable { - @CheckForNull private transient Set valueSet; - @RetainedWith @CheckForNull private transient BiMap inverse; + static final class SynchronizedBiMap + extends SynchronizedMap implements BiMap { + private transient @Nullable Set valueSet; + @RetainedWith private transient @Nullable BiMap inverse; private SynchronizedBiMap( - BiMap delegate, @CheckForNull Object mutex, @CheckForNull BiMap inverse) { + BiMap delegate, @Nullable Object mutex, @Nullable BiMap inverse) { super(delegate, mutex); this.inverse = inverse; } @@ -1228,8 +1220,7 @@ public Set values() { } @Override - @CheckForNull - public V forcePut(K key, V value) { + public @Nullable V forcePut(@ParametricNullness K key, @ParametricNullness V value) { synchronized (mutex) { return delegate().forcePut(key, value); } @@ -1245,21 +1236,20 @@ public BiMap inverse() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static class SynchronizedAsMap + static final class SynchronizedAsMap extends SynchronizedMap> { - @CheckForNull transient Set>> asMapEntrySet; - @CheckForNull transient Collection> asMapValues; + transient @Nullable Set>> asMapEntrySet; + transient @Nullable Collection> asMapValues; - SynchronizedAsMap(Map> delegate, @CheckForNull Object mutex) { + SynchronizedAsMap(Map> delegate, @Nullable Object mutex) { super(delegate, mutex); } @Override - @CheckForNull - public Collection get(@CheckForNull Object key) { + public @Nullable Collection get(@Nullable Object key) { synchronized (mutex) { Collection collection = super.get(key); return (collection == null) ? null : typePreservingCollection(collection, mutex); @@ -1287,17 +1277,19 @@ public Collection> values() { } @Override - public boolean containsValue(@CheckForNull Object o) { + // A forwarding implementation can't do any better than the underlying object. + @SuppressWarnings("CollectionUndefinedEquality") + public boolean containsValue(@Nullable Object o) { // values() and its contains() method are both synchronized. return values().contains(o); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - private static class SynchronizedAsMapValues + static final class SynchronizedAsMapValues extends SynchronizedCollection> { - SynchronizedAsMapValues(Collection> delegate, @CheckForNull Object mutex) { + SynchronizedAsMapValues(Collection> delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1312,14 +1304,14 @@ Collection transform(Collection from) { }; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible // NavigableSet @VisibleForTesting - static class SynchronizedNavigableSet extends SynchronizedSortedSet - implements NavigableSet { - SynchronizedNavigableSet(NavigableSet delegate, @CheckForNull Object mutex) { + static final class SynchronizedNavigableSet + extends SynchronizedSortedSet implements NavigableSet { + SynchronizedNavigableSet(NavigableSet delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1329,8 +1321,7 @@ NavigableSet delegate() { } @Override - @CheckForNull - public E ceiling(E e) { + public @Nullable E ceiling(E e) { synchronized (mutex) { return delegate().ceiling(e); } @@ -1341,7 +1332,7 @@ public Iterator descendingIterator() { return delegate().descendingIterator(); // manually synchronized } - @CheckForNull transient NavigableSet descendingSet; + transient @Nullable NavigableSet descendingSet; @Override public NavigableSet descendingSet() { @@ -1356,8 +1347,7 @@ public NavigableSet descendingSet() { } @Override - @CheckForNull - public E floor(E e) { + public @Nullable E floor(E e) { synchronized (mutex) { return delegate().floor(e); } @@ -1376,32 +1366,28 @@ public SortedSet headSet(E toElement) { } @Override - @CheckForNull - public E higher(E e) { + public @Nullable E higher(E e) { synchronized (mutex) { return delegate().higher(e); } } @Override - @CheckForNull - public E lower(E e) { + public @Nullable E lower(E e) { synchronized (mutex) { return delegate().lower(e); } } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { synchronized (mutex) { return delegate().pollFirst(); } } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { synchronized (mutex) { return delegate().pollLast(); } @@ -1433,13 +1419,13 @@ public SortedSet tailSet(E fromElement) { return tailSet(fromElement, true); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible // NavigableSet static NavigableSet navigableSet( - NavigableSet navigableSet, @CheckForNull Object mutex) { - return new SynchronizedNavigableSet(navigableSet, mutex); + NavigableSet navigableSet, @Nullable Object mutex) { + return new SynchronizedNavigableSet<>(navigableSet, mutex); } @GwtIncompatible // NavigableSet @@ -1455,16 +1441,17 @@ public SortedSet tailSet(E fromElement) { @GwtIncompatible // NavigableMap static NavigableMap navigableMap( - NavigableMap navigableMap, @CheckForNull Object mutex) { + NavigableMap navigableMap, @Nullable Object mutex) { return new SynchronizedNavigableMap<>(navigableMap, mutex); } @GwtIncompatible // NavigableMap @VisibleForTesting - static class SynchronizedNavigableMap + static final class SynchronizedNavigableMap< + K extends @Nullable Object, V extends @Nullable Object> extends SynchronizedSortedMap implements NavigableMap { - SynchronizedNavigableMap(NavigableMap delegate, @CheckForNull Object mutex) { + SynchronizedNavigableMap(NavigableMap delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1474,22 +1461,20 @@ NavigableMap delegate() { } @Override - @CheckForNull - public Map.Entry ceilingEntry(K key) { + public Map.@Nullable Entry ceilingEntry(K key) { synchronized (mutex) { return nullableSynchronizedEntry(delegate().ceilingEntry(key), mutex); } } @Override - @CheckForNull - public K ceilingKey(K key) { + public @Nullable K ceilingKey(K key) { synchronized (mutex) { return delegate().ceilingKey(key); } } - @CheckForNull transient NavigableSet descendingKeySet; + transient @Nullable NavigableSet descendingKeySet; @Override public NavigableSet descendingKeySet() { @@ -1501,7 +1486,7 @@ public NavigableSet descendingKeySet() { } } - @CheckForNull transient NavigableMap descendingMap; + transient @Nullable NavigableMap descendingMap; @Override public NavigableMap descendingMap() { @@ -1514,24 +1499,21 @@ public NavigableMap descendingMap() { } @Override - @CheckForNull - public Map.Entry firstEntry() { + public Map.@Nullable Entry firstEntry() { synchronized (mutex) { return nullableSynchronizedEntry(delegate().firstEntry(), mutex); } } @Override - @CheckForNull - public Map.Entry floorEntry(K key) { + public Map.@Nullable Entry floorEntry(K key) { synchronized (mutex) { return nullableSynchronizedEntry(delegate().floorEntry(key), mutex); } } @Override - @CheckForNull - public K floorKey(K key) { + public @Nullable K floorKey(K key) { synchronized (mutex) { return delegate().floorKey(key); } @@ -1550,40 +1532,35 @@ public SortedMap headMap(K toKey) { } @Override - @CheckForNull - public Map.Entry higherEntry(K key) { + public Map.@Nullable Entry higherEntry(K key) { synchronized (mutex) { return nullableSynchronizedEntry(delegate().higherEntry(key), mutex); } } @Override - @CheckForNull - public K higherKey(K key) { + public @Nullable K higherKey(K key) { synchronized (mutex) { return delegate().higherKey(key); } } @Override - @CheckForNull - public Map.Entry lastEntry() { + public Map.@Nullable Entry lastEntry() { synchronized (mutex) { return nullableSynchronizedEntry(delegate().lastEntry(), mutex); } } @Override - @CheckForNull - public Map.Entry lowerEntry(K key) { + public Map.@Nullable Entry lowerEntry(K key) { synchronized (mutex) { return nullableSynchronizedEntry(delegate().lowerEntry(key), mutex); } } @Override - @CheckForNull - public K lowerKey(K key) { + public @Nullable K lowerKey(K key) { synchronized (mutex) { return delegate().lowerKey(key); } @@ -1594,7 +1571,7 @@ public Set keySet() { return navigableKeySet(); } - @CheckForNull transient NavigableSet navigableKeySet; + transient @Nullable NavigableSet navigableKeySet; @Override public NavigableSet navigableKeySet() { @@ -1607,16 +1584,14 @@ public NavigableSet navigableKeySet() { } @Override - @CheckForNull - public Map.Entry pollFirstEntry() { + public Map.@Nullable Entry pollFirstEntry() { synchronized (mutex) { return nullableSynchronizedEntry(delegate().pollFirstEntry(), mutex); } } @Override - @CheckForNull - public Map.Entry pollLastEntry() { + public Map.@Nullable Entry pollLastEntry() { synchronized (mutex) { return nullableSynchronizedEntry(delegate().pollLastEntry(), mutex); } @@ -1647,14 +1622,13 @@ public SortedMap tailMap(K fromKey) { return tailMap(fromKey, true); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } @GwtIncompatible // works but is needed only for NavigableMap - @CheckForNull private static - Map.Entry nullableSynchronizedEntry( - @CheckForNull Map.Entry entry, @CheckForNull Object mutex) { + Map.@Nullable Entry nullableSynchronizedEntry( + Map.@Nullable Entry entry, @Nullable Object mutex) { if (entry == null) { return null; } @@ -1662,10 +1636,10 @@ Map.Entry nullableSynchronizedEntry( } @GwtIncompatible // works but is needed only for NavigableMap - private static class SynchronizedEntry + static final class SynchronizedEntry extends SynchronizedObject implements Map.Entry { - SynchronizedEntry(Map.Entry delegate, @CheckForNull Object mutex) { + SynchronizedEntry(Map.Entry delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1676,7 +1650,7 @@ Map.Entry delegate() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { synchronized (mutex) { return delegate().equals(obj); } @@ -1710,17 +1684,17 @@ public V setValue(V value) { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - static Queue queue(Queue queue, @CheckForNull Object mutex) { + static Queue queue(Queue queue, @Nullable Object mutex) { return (queue instanceof SynchronizedQueue) ? queue : new SynchronizedQueue(queue, mutex); } - private static class SynchronizedQueue - extends SynchronizedCollection implements Queue { + static class SynchronizedQueue extends SynchronizedCollection + implements Queue { - SynchronizedQueue(Queue delegate, @CheckForNull Object mutex) { + SynchronizedQueue(Queue delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1744,16 +1718,14 @@ public boolean offer(E e) { } @Override - @CheckForNull - public E peek() { + public @Nullable E peek() { synchronized (mutex) { return delegate().peek(); } } @Override - @CheckForNull - public E poll() { + public @Nullable E poll() { synchronized (mutex) { return delegate().poll(); } @@ -1766,17 +1738,17 @@ public E remove() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - static Deque deque(Deque deque, @CheckForNull Object mutex) { - return new SynchronizedDeque(deque, mutex); + static Deque deque(Deque deque, @Nullable Object mutex) { + return new SynchronizedDeque<>(deque, mutex); } - private static final class SynchronizedDeque - extends SynchronizedQueue implements Deque { + static final class SynchronizedDeque extends SynchronizedQueue + implements Deque { - SynchronizedDeque(Deque delegate, @CheckForNull Object mutex) { + SynchronizedDeque(Deque delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1828,16 +1800,14 @@ public E removeLast() { } @Override - @CheckForNull - public E pollFirst() { + public @Nullable E pollFirst() { synchronized (mutex) { return delegate().pollFirst(); } } @Override - @CheckForNull - public E pollLast() { + public @Nullable E pollLast() { synchronized (mutex) { return delegate().pollLast(); } @@ -1858,30 +1828,28 @@ public E getLast() { } @Override - @CheckForNull - public E peekFirst() { + public @Nullable E peekFirst() { synchronized (mutex) { return delegate().peekFirst(); } } @Override - @CheckForNull - public E peekLast() { + public @Nullable E peekLast() { synchronized (mutex) { return delegate().peekLast(); } } @Override - public boolean removeFirstOccurrence(@CheckForNull Object o) { + public boolean removeFirstOccurrence(@Nullable Object o) { synchronized (mutex) { return delegate().removeFirstOccurrence(o); } } @Override - public boolean removeLastOccurrence(@CheckForNull Object o) { + public boolean removeLastOccurrence(@Nullable Object o) { synchronized (mutex) { return delegate().removeLastOccurrence(o); } @@ -1908,19 +1876,19 @@ public Iterator descendingIterator() { } } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } static - Table table(Table table, @CheckForNull Object mutex) { + Table table(Table table, @Nullable Object mutex) { return new SynchronizedTable<>(table, mutex); } - private static final class SynchronizedTable< + static final class SynchronizedTable< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends SynchronizedObject implements Table { - SynchronizedTable(Table delegate, @CheckForNull Object mutex) { + SynchronizedTable(Table delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -1931,36 +1899,35 @@ Table delegate() { } @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { synchronized (mutex) { return delegate().contains(rowKey, columnKey); } } @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { synchronized (mutex) { return delegate().containsRow(rowKey); } } @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { synchronized (mutex) { return delegate().containsColumn(columnKey); } } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { synchronized (mutex) { return delegate().containsValue(value); } } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { synchronized (mutex) { return delegate().get(rowKey, columnKey); } @@ -1988,8 +1955,10 @@ public void clear() { } @Override - @CheckForNull - public V put(R rowKey, C columnKey, V value) { + public @Nullable V put( + @ParametricNullness R rowKey, + @ParametricNullness C columnKey, + @ParametricNullness V value) { synchronized (mutex) { return delegate().put(rowKey, columnKey, value); } @@ -2003,22 +1972,21 @@ public void putAll(Table table) { } @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { synchronized (mutex) { return delegate().remove(rowKey, columnKey); } } @Override - public Map row(R rowKey) { + public Map row(@ParametricNullness R rowKey) { synchronized (mutex) { return map(delegate().row(rowKey), mutex); } } @Override - public Map column(C columnKey) { + public Map column(@ParametricNullness C columnKey) { synchronized (mutex) { return map(delegate().column(columnKey), mutex); } @@ -2055,32 +2023,14 @@ public Collection values() { @Override public Map> rowMap() { synchronized (mutex) { - return map( - Maps.transformValues( - delegate().rowMap(), - new com.google.common.base.Function, Map>() { - @Override - public Map apply(Map t) { - return map(t, mutex); - } - }), - mutex); + return map(transformValues(delegate().rowMap(), m -> map(m, mutex)), mutex); } } @Override public Map> columnMap() { synchronized (mutex) { - return map( - Maps.transformValues( - delegate().columnMap(), - new com.google.common.base.Function, Map>() { - @Override - public Map apply(Map t) { - return map(t, mutex); - } - }), - mutex); + return map(transformValues(delegate().columnMap(), m -> map(m, mutex)), mutex); } } @@ -2092,7 +2042,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/android/guava/src/com/google/common/collect/Table.java b/android/guava/src/com/google/common/collect/Table.java index 97d3f7027730..054ec519d282 100644 --- a/android/guava/src/com/google/common/collect/Table.java +++ b/android/guava/src/com/google/common/collect/Table.java @@ -24,8 +24,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A collection that associates an ordered pair of keys, called a row key and a column key, with a @@ -45,8 +44,18 @@ * not be modifiable. When modification isn't supported, those methods will throw an {@link * UnsupportedOperationException}. * + *

    Implementations

    + * + *
      + *
    • {@link ImmutableTable} + *
    • {@link HashBasedTable} + *
    • {@link TreeBasedTable} + *
    • {@link ArrayTable} + *
    • {@link Tables#newCustomTable Tables.newCustomTable} + *
    + * *

    See the Guava User Guide article on {@code Table}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#table">{@code Table}. * * @author Jared Levy * @param the type of the table row keys @@ -56,7 +65,6 @@ */ @DoNotMock("Use ImmutableTable, HashBasedTable, or another implementation") @GwtCompatible -@ElementTypesAreNonnullByDefault public interface Table< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> { // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. @@ -70,29 +78,29 @@ public interface Table< * @param columnKey key of column to search for */ boolean contains( - @CompatibleWith("R") @CheckForNull Object rowKey, - @CompatibleWith("C") @CheckForNull Object columnKey); + @CompatibleWith("R") @Nullable Object rowKey, + @CompatibleWith("C") @Nullable Object columnKey); /** * Returns {@code true} if the table contains a mapping with the specified row key. * * @param rowKey key of row to search for */ - boolean containsRow(@CompatibleWith("R") @CheckForNull Object rowKey); + boolean containsRow(@CompatibleWith("R") @Nullable Object rowKey); /** * Returns {@code true} if the table contains a mapping with the specified column. * * @param columnKey key of column to search for */ - boolean containsColumn(@CompatibleWith("C") @CheckForNull Object columnKey); + boolean containsColumn(@CompatibleWith("C") @Nullable Object columnKey); /** * Returns {@code true} if the table contains a mapping with the specified value. * * @param value value to search for */ - boolean containsValue(@CompatibleWith("V") @CheckForNull Object value); + boolean containsValue(@CompatibleWith("V") @Nullable Object value); /** * Returns the value corresponding to the given row and column keys, or {@code null} if no such @@ -101,10 +109,9 @@ boolean contains( * @param rowKey key of row to search for * @param columnKey key of column to search for */ - @CheckForNull - V get( - @CompatibleWith("R") @CheckForNull Object rowKey, - @CompatibleWith("C") @CheckForNull Object columnKey); + @Nullable V get( + @CompatibleWith("R") @Nullable Object rowKey, + @CompatibleWith("C") @Nullable Object columnKey); /** Returns {@code true} if the table contains no mappings. */ boolean isEmpty(); @@ -117,7 +124,7 @@ V get( * cell views, as returned by {@link #cellSet}, are equal. */ @Override - boolean equals(@CheckForNull Object obj); + boolean equals(@Nullable Object obj); /** * Returns the hash code for this table. The hash code of a table is defined as the hash code of @@ -142,8 +149,8 @@ V get( * for the keys */ @CanIgnoreReturnValue - @CheckForNull - V put(@ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value); + @Nullable V put( + @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value); /** * Copies all mappings from the specified table to this table. The effect is equivalent to calling @@ -161,10 +168,9 @@ V get( * @return the value previously associated with the keys, or {@code null} if no such value existed */ @CanIgnoreReturnValue - @CheckForNull - V remove( - @CompatibleWith("R") @CheckForNull Object rowKey, - @CompatibleWith("C") @CheckForNull Object columnKey); + @Nullable V remove( + @CompatibleWith("R") @Nullable Object rowKey, + @CompatibleWith("C") @Nullable Object columnKey); // Views @@ -275,7 +281,7 @@ interface Cell< * equal row keys, column keys, and values. */ @Override - boolean equals(@CheckForNull Object obj); + boolean equals(@Nullable Object obj); /** * Returns the hash code of this cell. diff --git a/android/guava/src/com/google/common/collect/TableCollectors.java b/android/guava/src/com/google/common/collect/TableCollectors.java new file mode 100644 index 000000000000..7a8da0f652d0 --- /dev/null +++ b/android/guava/src/com/google/common/collect/TableCollectors.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.Tables.AbstractCell; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; + +/** Collectors utilities for {@code common.collect.Table} internals. */ +@GwtCompatible +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // used only from APIs with Java 8 types in them +final class TableCollectors { + + static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction) { + checkNotNull(rowFunction, "rowFunction"); + checkNotNull(columnFunction, "columnFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + (Supplier>) ImmutableTable.Builder::new, + (builder, t) -> + builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)), + ImmutableTable.Builder::combine, + ImmutableTable.Builder::buildOrThrow); + } + + static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + + checkNotNull(rowFunction, "rowFunction"); + checkNotNull(columnFunction, "columnFunction"); + checkNotNull(valueFunction, "valueFunction"); + checkNotNull(mergeFunction, "mergeFunction"); + + /* + * No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but + * the Builder can't efficiently support merging of duplicate values. Getting around this + * requires some work. + */ + + return Collector.of( + ImmutableTableCollectorState::new, + (state, input) -> + state.put( + rowFunction.apply(input), + columnFunction.apply(input), + valueFunction.apply(input), + mergeFunction), + (s1, s2) -> s1.combine(s2, mergeFunction), + state -> state.toTable()); + } + + static < + T extends @Nullable Object, + R extends @Nullable Object, + C extends @Nullable Object, + V, + I extends Table> + Collector toTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + Supplier tableSupplier) { + return TableCollectors.toTable( + rowFunction, + columnFunction, + valueFunction, + (v1, v2) -> { + throw new IllegalStateException("Conflicting values " + v1 + " and " + v2); + }, + tableSupplier); + } + + static < + T extends @Nullable Object, + R extends @Nullable Object, + C extends @Nullable Object, + V, + I extends Table> + Collector toTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + BinaryOperator mergeFunction, + Supplier tableSupplier) { + checkNotNull(rowFunction); + checkNotNull(columnFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + checkNotNull(tableSupplier); + return Collector.of( + tableSupplier, + (table, input) -> + mergeTables( + table, + rowFunction.apply(input), + columnFunction.apply(input), + valueFunction.apply(input), + mergeFunction), + (table1, table2) -> { + for (Table.Cell cell2 : table2.cellSet()) { + mergeTables( + table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction); + } + return table1; + }); + } + + private static final class ImmutableTableCollectorState { + final List> insertionOrder = new ArrayList<>(); + final Table> table = HashBasedTable.create(); + + void put(R row, C column, V value, BinaryOperator merger) { + MutableCell oldCell = table.get(row, column); + if (oldCell == null) { + MutableCell cell = new MutableCell<>(row, column, value); + insertionOrder.add(cell); + table.put(row, column, cell); + } else { + oldCell.merge(value, merger); + } + } + + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Anyway, the purpose of this method is to implement a BinaryOperator combiner for a Collector, + * so its return value will get used naturally. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") + ImmutableTableCollectorState combine( + ImmutableTableCollectorState other, BinaryOperator merger) { + for (MutableCell cell : other.insertionOrder) { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger); + } + return this; + } + + ImmutableTable toTable() { + return ImmutableTable.copyOf(insertionOrder); + } + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private static final class MutableCell extends AbstractCell { + private final R row; + private final C column; + private V value; + + MutableCell(R row, C column, V value) { + this.row = checkNotNull(row, "row"); + this.column = checkNotNull(column, "column"); + this.value = checkNotNull(value, "value"); + } + + @Override + public R getRowKey() { + return row; + } + + @Override + public C getColumnKey() { + return column; + } + + @Override + public V getValue() { + return value; + } + + void merge(V value, BinaryOperator mergeFunction) { + checkNotNull(value, "value"); + this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply"); + } + } + + private static void mergeTables( + Table table, + @ParametricNullness R row, + @ParametricNullness C column, + V value, + BinaryOperator mergeFunction) { + checkNotNull(value); + V oldValue = table.get(row, column); + if (oldValue == null) { + table.put(row, column, value); + } else { + V newValue = mergeFunction.apply(oldValue, value); + if (newValue == null) { + table.remove(row, column); + } else { + table.put(row, column, newValue); + } + } + } + + private TableCollectors() {} +} diff --git a/android/guava/src/com/google/common/collect/Tables.java b/android/guava/src/com/google/common/collect/Tables.java index 772a75de76e6..ae60fddc34e6 100644 --- a/android/guava/src/com/google/common/collect/Tables.java +++ b/android/guava/src/com/google/common/collect/Tables.java @@ -19,9 +19,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.util.Collections.unmodifiableMap; +import static java.util.Collections.unmodifiableSortedMap; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Supplier; @@ -34,24 +37,85 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.BinaryOperator; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * Provides static methods that involve a {@code Table}. * *

    See the Guava User Guide article on {@code Tables}. + * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#tables">{@code Tables}. * * @author Jared Levy * @author Louis Wasserman * @since 7.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Tables { private Tables() {} + /** + * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the + * specified supplier, whose cells are generated by applying the provided mapping functions to the + * input elements. Cells are inserted into the generated {@code Table} in encounter order. + * + *

    If multiple input elements map to the same row and column, an {@code IllegalStateException} + * is thrown when the collection operation is performed. + * + *

    To collect to an {@link ImmutableTable}, use {@link ImmutableTable#toImmutableTable}. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static < + T extends @Nullable Object, + R extends @Nullable Object, + C extends @Nullable Object, + V, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + java.util.function.Supplier tableSupplier) { + return TableCollectors.toTable( + rowFunction, columnFunction, valueFunction, tableSupplier); + } + + /** + * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the + * specified supplier, whose cells are generated by applying the provided mapping functions to the + * input elements. Cells are inserted into the generated {@code Table} in encounter order. + * + *

    If multiple input elements map to the same row and column, the specified merging function is + * used to combine the values. Like {@link + * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function, + * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code + * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls + * returned from {@code mergeFunction} as removals of that row/column pair. + * + * @since 33.2.0 (available since 21.0 in guava-jre) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static < + T extends @Nullable Object, + R extends @Nullable Object, + C extends @Nullable Object, + V, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + BinaryOperator mergeFunction, + java.util.function.Supplier tableSupplier) { + return TableCollectors.toTable( + rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier); + } + /** * Returns an immutable cell with the specified row key, column key, and value. * @@ -103,7 +167,7 @@ public V getValue() { return value; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } abstract static class AbstractCell< @@ -113,7 +177,7 @@ abstract static class AbstractCell< AbstractCell() {} @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -186,34 +250,32 @@ public Map> columnMap() { } @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return original.contains(columnKey, rowKey); } @Override - public boolean containsColumn(@CheckForNull Object columnKey) { + public boolean containsColumn(@Nullable Object columnKey) { return original.containsRow(columnKey); } @Override - public boolean containsRow(@CheckForNull Object rowKey) { + public boolean containsRow(@Nullable Object rowKey) { return original.containsColumn(rowKey); } @Override - public boolean containsValue(@CheckForNull Object value) { + public boolean containsValue(@Nullable Object value) { return original.containsValue(value); } @Override - @CheckForNull - public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V get(@Nullable Object rowKey, @Nullable Object columnKey) { return original.get(columnKey, rowKey); } @Override - @CheckForNull - public V put( + public @Nullable V put( @ParametricNullness C rowKey, @ParametricNullness R columnKey, @ParametricNullness V value) { @@ -226,8 +288,7 @@ public void putAll(Table table) { } @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { return original.remove(columnKey, rowKey); } @@ -256,22 +317,18 @@ public Collection values() { return original.values(); } - // Will cast TRANSPOSE_CELL to a type that always succeeds - private static final Function, Cell> TRANSPOSE_CELL = - new Function, Cell>() { - @Override - public Cell apply(Cell cell) { - return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); - } - }; - - @SuppressWarnings("unchecked") @Override Iterator> cellIterator() { - return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL); + return Iterators.transform(original.cellSet().iterator(), Tables::transposeCell); } } + private static < + R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> + Cell transposeCell(Cell cell) { + return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); + } + /** * Creates a table that uses the specified backing map and factory. It can generate a table based * on arbitrary {@link Map} classes. @@ -310,7 +367,6 @@ Iterator> cellIterator() { * @throws IllegalArgumentException if {@code backingMap} is not empty * @since 10.0 */ - @Beta public static Table newCustomTable( Map> backingMap, Supplier> factory) { checkArgument(backingMap.isEmpty()); @@ -340,7 +396,6 @@ public static Table newCustomTable( * * @since 10.0 */ - @Beta public static < R extends @Nullable Object, C extends @Nullable Object, @@ -366,13 +421,12 @@ private static class TransformedTable< } @Override - public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { return fromTable.contains(rowKey, columnKey); } @Override - @CheckForNull - public V2 get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V2 get(@Nullable Object rowKey, @Nullable Object columnKey) { // The function is passed a null input only when the table contains a null // value. // The cast is safe because of the contains() check. @@ -392,8 +446,7 @@ public void clear() { } @Override - @CheckForNull - public V2 put( + public @Nullable V2 put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V2 value) { @@ -406,8 +459,7 @@ public void putAll(Table table) { } @Override - @CheckForNull - public V2 remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V2 remove(@Nullable Object rowKey, @Nullable Object columnKey) { return contains(rowKey, columnKey) // The cast is safe because of the contains() check. ? function.apply(uncheckedCastNullableTToT(fromTable.remove(rowKey, columnKey))) @@ -425,13 +477,8 @@ public Map column(@ParametricNullness C columnKey) { } Function, Cell> cellFunction() { - return new Function, Cell>() { - @Override - public Cell apply(Cell cell) { - return immutableCell( - cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); - } - }; + return cell -> + immutableCell(cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); } @Override @@ -456,26 +503,13 @@ Collection createValues() { @Override public Map> rowMap() { - Function, Map> rowFunction = - new Function, Map>() { - @Override - public Map apply(Map row) { - return Maps.transformValues(row, function); - } - }; - return Maps.transformValues(fromTable.rowMap(), rowFunction); + return Maps.transformValues(fromTable.rowMap(), row -> Maps.transformValues(row, function)); } @Override public Map> columnMap() { - Function, Map> columnFunction = - new Function, Map>() { - @Override - public Map apply(Map column) { - return Maps.transformValues(column, function); - } - }; - return Maps.transformValues(fromTable.columnMap(), columnFunction); + return Maps.transformValues( + fromTable.columnMap(), column -> Maps.transformValues(column, function)); } } @@ -533,13 +567,11 @@ public Set columnKeySet() { @Override public Map> columnMap() { - Function, Map> wrapper = unmodifiableWrapper(); - return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); + return unmodifiableMap(Maps.transformValues(super.columnMap(), Collections::unmodifiableMap)); } @Override - @CheckForNull - public V put( + public @Nullable V put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { @@ -552,8 +584,7 @@ public void putAll(Table table) { } @Override - @CheckForNull - public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { + public @Nullable V remove(@Nullable Object rowKey, @Nullable Object columnKey) { throw new UnsupportedOperationException(); } @@ -569,8 +600,7 @@ public Set rowKeySet() { @Override public Map> rowMap() { - Function, Map> wrapper = unmodifiableWrapper(); - return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); + return unmodifiableMap(Maps.transformValues(super.rowMap(), Collections::unmodifiableMap)); } @Override @@ -578,7 +608,7 @@ public Collection values() { return Collections.unmodifiableCollection(super.values()); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -593,7 +623,6 @@ public Collection values() { * @return an unmodifiable view of the specified table * @since 11.0 */ - @Beta public static RowSortedTable unmodifiableRowSortedTable( RowSortedTable table) { @@ -605,7 +634,7 @@ RowSortedTable unmodifiableRowSortedTable( return new UnmodifiableRowSortedMap<>(table); } - static final class UnmodifiableRowSortedMap< + private static final class UnmodifiableRowSortedMap< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends UnmodifiableTable implements RowSortedTable { @@ -620,8 +649,8 @@ protected RowSortedTable delegate() { @Override public SortedMap> rowMap() { - Function, Map> wrapper = unmodifiableWrapper(); - return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); + return unmodifiableSortedMap( + Maps.transformValues(delegate().rowMap(), Collections::unmodifiableMap)); } @Override @@ -629,23 +658,9 @@ public SortedSet rowKeySet() { return Collections.unmodifiableSortedSet(delegate().rowKeySet()); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } - @SuppressWarnings("unchecked") - private static - Function, Map> unmodifiableWrapper() { - return (Function) UNMODIFIABLE_WRAPPER; - } - - private static final Function, ? extends Map> UNMODIFIABLE_WRAPPER = - new Function, Map>() { - @Override - public Map apply(Map input) { - return Collections.unmodifiableMap(input); - } - }; - /** * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee * serial access, it is critical that all access to the backing table is accomplished @@ -675,12 +690,13 @@ public Map apply(Map input) { * @return a synchronized view of the specified table * @since 22.0 */ + @J2ktIncompatible // Synchronized public static Table synchronizedTable(Table table) { return Synchronized.table(table, null); } - static boolean equalsImpl(Table table, @CheckForNull Object obj) { + static boolean equalsImpl(Table table, @Nullable Object obj) { if (obj == table) { return true; } else if (obj instanceof Table) { diff --git a/android/guava/src/com/google/common/collect/TopKSelector.java b/android/guava/src/com/google/common/collect/TopKSelector.java index f8cca0deaff9..7da3772492d6 100644 --- a/android/guava/src/com/google/common/collect/TopKSelector.java +++ b/android/guava/src/com/google/common/collect/TopKSelector.java @@ -19,17 +19,19 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.lang.Math.max; +import static java.util.Arrays.asList; +import static java.util.Arrays.sort; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.GwtCompatible; import com.google.common.math.IntMath; import java.math.RoundingMode; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An accumulator that selects the "top" {@code k} elements added to it, relative to a provided @@ -53,7 +55,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class TopKSelector< T extends @Nullable Object> { @@ -76,7 +77,7 @@ public static > TopKSelector least(int k) { */ public static TopKSelector least( int k, Comparator comparator) { - return new TopKSelector(comparator, k); + return new TopKSelector<>(comparator, k); } /** @@ -98,7 +99,7 @@ public static > TopKSelector greatest(int k) */ public static TopKSelector greatest( int k, Comparator comparator) { - return new TopKSelector(Ordering.from(comparator).reverse(), k); + return new TopKSelector<>(Ordering.from(comparator).reverse(), k); } private final int k; @@ -116,8 +117,9 @@ public static > TopKSelector greatest(int k) * The largest of the lowest k elements we've seen so far relative to this comparator. If * bufferSize ≥ k, then we can ignore any elements greater than this value. */ - @CheckForNull private T threshold; + private @Nullable T threshold; + @SuppressWarnings("unchecked") // TODO(cpovirk): Consider storing Object[] instead of T[]. private TopKSelector(Comparator comparator, int k) { this.comparator = checkNotNull(comparator, "comparator"); this.k = k; @@ -177,15 +179,17 @@ private void trim() { if (pivotNewIndex > k) { right = pivotNewIndex - 1; } else if (pivotNewIndex < k) { - left = Math.max(pivotNewIndex, left + 1); + left = max(pivotNewIndex, left + 1); minThresholdPosition = pivotNewIndex; } else { break; } iterations++; if (iterations >= maxIterations) { + @SuppressWarnings("nullness") // safe because we pass sort() a range that contains real Ts + T[] castBuffer = (T[]) buffer; // We've already taken O(k log k), let's make sure we don't take longer than O(k log k). - Arrays.sort(buffer, left, right + 1, comparator); + sort(castBuffer, left, right + 1, comparator); break; } } @@ -229,6 +233,19 @@ private void swap(int i, int j) { buffer[j] = tmp; } + /* + * While the current implementation returns `this`, that's not something we mean to guarantee. + * Anyway, the purpose of this method is to implement a BinaryOperator combiner for a Collector, + * so its return value will get used naturally. + */ + @SuppressWarnings("CanIgnoreReturnValueSuggester") + TopKSelector combine(TopKSelector other) { + for (int i = 0; i < other.bufferSize; i++) { + this.offer(uncheckedCastNullableTToT(other.buffer[i])); + } + return this; + } + /** * Adds each member of {@code elements} as a candidate for the top {@code k} elements. This * operation takes amortized linear time in the length of {@code elements}. @@ -263,13 +280,17 @@ public void offerAll(Iterator elements) { * this {@code TopKSelector}. This method returns in O(k log k) time. */ public List topK() { - Arrays.sort(buffer, 0, bufferSize, comparator); + @SuppressWarnings("nullness") // safe because we pass sort() a range that contains real Ts + T[] castBuffer = (T[]) buffer; + sort(castBuffer, 0, bufferSize, comparator); if (bufferSize > k) { Arrays.fill(buffer, k, buffer.length, null); bufferSize = k; threshold = buffer[k - 1]; } + // Up to bufferSize, all elements of buffer are real Ts (not null unless T includes null) + T[] topK = Arrays.copyOf(castBuffer, bufferSize); // we have to support null elements, so no ImmutableList for us - return Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(buffer, bufferSize))); + return unmodifiableList(asList(topK)); } } diff --git a/android/guava/src/com/google/common/collect/TransformedIterator.java b/android/guava/src/com/google/common/collect/TransformedIterator.java index 2456cecde991..6499e7d80b49 100644 --- a/android/guava/src/com/google/common/collect/TransformedIterator.java +++ b/android/guava/src/com/google/common/collect/TransformedIterator.java @@ -20,7 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An iterator that transforms a backing iterator; for internal use. This avoids the object overhead @@ -29,7 +29,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class TransformedIterator implements Iterator { final Iterator backingIterator; diff --git a/android/guava/src/com/google/common/collect/TransformedListIterator.java b/android/guava/src/com/google/common/collect/TransformedListIterator.java index 66b42e4c6048..111588987ee5 100644 --- a/android/guava/src/com/google/common/collect/TransformedListIterator.java +++ b/android/guava/src/com/google/common/collect/TransformedListIterator.java @@ -19,7 +19,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import java.util.ListIterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An iterator that transforms a backing list iterator; for internal use. This avoids the object @@ -28,7 +28,6 @@ * @author Louis Wasserman */ @GwtCompatible -@ElementTypesAreNonnullByDefault abstract class TransformedListIterator extends TransformedIterator implements ListIterator { TransformedListIterator(ListIterator backingIterator) { @@ -36,7 +35,7 @@ abstract class TransformedListIterator backingIterator() { - return Iterators.cast(backingIterator); + return (ListIterator) backingIterator; } @Override diff --git a/android/guava/src/com/google/common/collect/TreeBasedTable.java b/android/guava/src/com/google/common/collect/TreeBasedTable.java index 98d33567af25..4b507d5ea8a5 100644 --- a/android/guava/src/com/google/common/collect/TreeBasedTable.java +++ b/android/guava/src/com/google/common/collect/TreeBasedTable.java @@ -18,10 +18,15 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Iterators.mergeSorted; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; @@ -31,7 +36,7 @@ import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Table} whose row keys and column keys are ordered by their natural @@ -59,18 +64,17 @@ * concurrently and one of the threads modifies the table, it must be synchronized externally. * *

    See the Guava User Guide article on {@code Table}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#table">{@code Table}. * * @author Jared Levy * @author Louis Wasserman * @since 7.0 */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault public class TreeBasedTable extends StandardRowSortedTable { private final Comparator columnComparator; - private static class Factory implements Supplier>, Serializable { + private static class Factory implements Supplier>, Serializable { final Comparator comparator; Factory(Comparator comparator) { @@ -78,11 +82,11 @@ private static class Factory implements Supplier>, Serializa } @Override - public TreeMap get() { + public Map get() { return new TreeMap<>(comparator); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -93,6 +97,7 @@ public TreeMap get() { * instead of {@code R extends Comparable}, and the same for {@code C}. That's * necessary to support classes defined without generics. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static TreeBasedTable create() { return new TreeBasedTable<>(Ordering.natural(), Ordering.natural()); } @@ -116,7 +121,9 @@ public static TreeBasedTable create( */ public static TreeBasedTable create(TreeBasedTable table) { TreeBasedTable result = - new TreeBasedTable<>(table.rowComparator(), table.columnComparator()); + // requireNonNull is safe, as discussed in rowComparator() below. + new TreeBasedTable<>( + requireNonNull(table.rowKeySet().comparator()), table.columnComparator()); result.putAll(table); return result; } @@ -134,8 +141,11 @@ public static TreeBasedTable create(TreeBasedTable rowComparator() { + public final Comparator rowComparator() { /* * requireNonNull is safe because the factories require non-null Comparators, which they pass on * to the backing collections. @@ -174,14 +184,14 @@ public SortedMap row(R rowKey) { } private class TreeRow extends Row implements SortedMap { - @CheckForNull final C lowerBound; - @CheckForNull final C upperBound; + final @Nullable C lowerBound; + final @Nullable C upperBound; TreeRow(R rowKey) { this(rowKey, null, null); } - TreeRow(R rowKey, @CheckForNull C lowerBound, @CheckForNull C upperBound) { + TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound) { super(rowKey); this.lowerBound = lowerBound; this.upperBound = upperBound; @@ -206,7 +216,7 @@ int compare(Object a, Object b) { return cmp.compare(a, b); } - boolean rangeContains(@CheckForNull Object o) { + boolean rangeContains(@Nullable Object o) { return o != null && (lowerBound == null || compare(lowerBound, o) <= 0) && (upperBound == null || compare(upperBound, o) > 0); @@ -248,7 +258,7 @@ public C lastKey() { return ((SortedMap) backingRowMap).lastKey(); } - @CheckForNull transient SortedMap wholeRow; + transient @Nullable SortedMap wholeRow; // If the row was previously empty, we check if there's a new row here every time we're queried. void updateWholeRowField() { @@ -258,8 +268,7 @@ void updateWholeRowField() { } @Override - @CheckForNull - SortedMap computeBackingRowMap() { + @Nullable SortedMap computeBackingRowMap() { updateWholeRowField(); SortedMap map = wholeRow; if (map != null) { @@ -285,47 +294,32 @@ void maintainEmptyInvariant() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return rangeContains(key) && super.containsKey(key); } @Override - @CheckForNull - public V put(C key, V value) { + public @Nullable V put(C key, V value) { checkArgument(rangeContains(checkNotNull(key))); return super.put(key, value); } } - // rowKeySet() and rowMap() are defined here so they appear in the Javadoc. - - @Override - public SortedSet rowKeySet() { - return super.rowKeySet(); - } - - @Override - public SortedMap> rowMap() { - return super.rowMap(); - } - /** Overridden column iterator to return columns values in globally sorted order. */ @Override Iterator createColumnKeyIterator() { Comparator comparator = columnComparator(); Iterator merged = - Iterators.mergeSorted( - Iterables.transform( - backingMap.values(), (Map input) -> input.keySet().iterator()), + mergeSorted( + transform(backingMap.values(), (Map input) -> input.keySet().iterator()), comparator); return new AbstractIterator() { - @CheckForNull C lastValue; + @Nullable C lastValue; @Override - @CheckForNull - protected C computeNext() { + protected @Nullable C computeNext() { while (merged.hasNext()) { C next = merged.next(); boolean duplicate = lastValue != null && comparator.compare(next, lastValue) == 0; @@ -343,5 +337,5 @@ protected C computeNext() { }; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/TreeMultimap.java b/android/guava/src/com/google/common/collect/TreeMultimap.java index 8b5362191696..11509a091368 100644 --- a/android/guava/src/com/google/common/collect/TreeMultimap.java +++ b/android/guava/src/com/google/common/collect/TreeMultimap.java @@ -17,9 +17,11 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -31,7 +33,7 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Multimap} whose keys and values are ordered by their natural ordering or @@ -64,15 +66,13 @@ * with a call to {@link Multimaps#synchronizedSortedSetMultimap}. * *

    See the Guava User Guide article on {@code - * Multimap}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap">{@code Multimap}. * * @author Jared Levy * @author Louis Wasserman * @since 2.0 */ @GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault public class TreeMultimap extends AbstractSortedKeySortedSetMultimap { private transient Comparator keyComparator; @@ -81,6 +81,7 @@ public class TreeMultimap TreeMultimap create() { return new TreeMultimap<>(Ordering.natural(), Ordering.natural()); } @@ -103,6 +104,7 @@ public static TreeMultimap cr * * @param multimap the multimap whose contents are copied to this multimap */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static TreeMultimap create( Multimap multimap) { return new TreeMultimap<>(Ordering.natural(), Ordering.natural(), multimap); @@ -142,7 +144,7 @@ SortedSet createCollection() { @Override Collection createCollection(@ParametricNullness K key) { if (key == null) { - keyComparator().compare(key, key); + int unused = keyComparator().compare(key, key); } return super.createCollection(key); } @@ -162,7 +164,9 @@ public Comparator valueComparator() { return valueComparator; } - /** @since 14.0 (present with return type {@code SortedSet} since 2.0) */ + /** + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ @Override @GwtIncompatible // NavigableSet public NavigableSet get(@ParametricNullness K key) { @@ -202,6 +206,7 @@ public NavigableMap> asMap() { * distinct key: the key, number of values for that key, and key values */ @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(keyComparator()); @@ -210,15 +215,15 @@ private void writeObject(ObjectOutputStream stream) throws IOException { } @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible @SuppressWarnings("unchecked") // reading data stored by writeObject private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - keyComparator = checkNotNull((Comparator) stream.readObject()); - valueComparator = checkNotNull((Comparator) stream.readObject()); + keyComparator = requireNonNull((Comparator) stream.readObject()); + valueComparator = requireNonNull((Comparator) stream.readObject()); setMap(new TreeMap>(keyComparator)); Serialization.populateMultimap(this, stream); } - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/collect/TreeMultiset.java b/android/guava/src/com/google/common/collect/TreeMultiset.java index c64c35a6a3b0..16b5ab975c0a 100644 --- a/android/guava/src/com/google/common/collect/TreeMultiset.java +++ b/android/guava/src/com/google/common/collect/TreeMultiset.java @@ -20,10 +20,12 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.CollectPreconditions.checkNonnegative; import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static java.lang.Math.max; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.common.primitives.Ints; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -35,8 +37,7 @@ import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A multiset which maintains the ordering of its elements, according to either their natural order @@ -49,15 +50,13 @@ * java.util.Collection} contract, which is specified in terms of {@link Object#equals}. * *

    See the Guava User Guide article on {@code - * Multiset}. + * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#multiset">{@code Multiset}. * * @author Louis Wasserman * @author Jared Levy * @since 2.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class TreeMultiset extends AbstractSortedMultiset implements Serializable { @@ -73,8 +72,9 @@ public final class TreeMultiset extends AbstractSort *

    The type specification is {@code }, instead of the more specific * {@code >}, to support classes defined without generics. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static TreeMultiset create() { - return new TreeMultiset(Ordering.natural()); + return new TreeMultiset<>(Ordering.natural()); } /** @@ -90,7 +90,7 @@ public static TreeMultiset create() { */ @SuppressWarnings("unchecked") public static TreeMultiset create( - @CheckForNull Comparator comparator) { + @Nullable Comparator comparator) { return (comparator == null) ? new TreeMultiset((Comparator) Ordering.natural()) : new TreeMultiset(comparator); @@ -105,6 +105,7 @@ public static TreeMultiset create() { *

    The type specification is {@code }, instead of the more specific * {@code >}, to support classes defined without generics. */ + @SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 public static TreeMultiset create(Iterable elements) { TreeMultiset multiset = create(); Iterables.addAll(multiset, elements); @@ -139,7 +140,7 @@ int nodeAggregate(AvlNode node) { } @Override - long treeAggregate(@CheckForNull AvlNode root) { + long treeAggregate(@Nullable AvlNode root) { return (root == null) ? 0 : root.totalCount; } }, @@ -150,14 +151,14 @@ int nodeAggregate(AvlNode node) { } @Override - long treeAggregate(@CheckForNull AvlNode root) { + long treeAggregate(@Nullable AvlNode root) { return (root == null) ? 0 : root.distinctElements; } }; abstract int nodeAggregate(AvlNode node); - abstract long treeAggregate(@CheckForNull AvlNode root); + abstract long treeAggregate(@Nullable AvlNode root); } private long aggregateForEntries(Aggregate aggr) { @@ -172,7 +173,7 @@ private long aggregateForEntries(Aggregate aggr) { return total; } - private long aggregateBelowRange(Aggregate aggr, @CheckForNull AvlNode node) { + private long aggregateBelowRange(Aggregate aggr, @Nullable AvlNode node) { if (node == null) { return 0; } @@ -188,9 +189,8 @@ private long aggregateBelowRange(Aggregate aggr, @CheckForNull AvlNode node) return aggr.nodeAggregate(node) + aggr.treeAggregate(node.left); case CLOSED: return aggr.treeAggregate(node.left); - default: - throw new AssertionError(); } + throw new AssertionError(); } else { return aggr.treeAggregate(node.left) + aggr.nodeAggregate(node) @@ -198,7 +198,7 @@ private long aggregateBelowRange(Aggregate aggr, @CheckForNull AvlNode node) } } - private long aggregateAboveRange(Aggregate aggr, @CheckForNull AvlNode node) { + private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode node) { if (node == null) { return 0; } @@ -214,9 +214,8 @@ private long aggregateAboveRange(Aggregate aggr, @CheckForNull AvlNode node) return aggr.nodeAggregate(node) + aggr.treeAggregate(node.right); case CLOSED: return aggr.treeAggregate(node.right); - default: - throw new AssertionError(); } + throw new AssertionError(); } else { return aggr.treeAggregate(node.right) + aggr.nodeAggregate(node) @@ -234,12 +233,12 @@ int distinctElements() { return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT)); } - static int distinctElements(@CheckForNull AvlNode node) { + static int distinctElements(@Nullable AvlNode node) { return (node == null) ? 0 : node.distinctElements; } @Override - public int count(@CheckForNull Object element) { + public int count(@Nullable Object element) { try { @SuppressWarnings("unchecked") E e = (E) element; @@ -263,8 +262,8 @@ public int add(@ParametricNullness E element, int occurrences) { checkArgument(range.contains(element)); AvlNode root = rootReference.get(); if (root == null) { - comparator().compare(element, element); - AvlNode newRoot = new AvlNode(element, occurrences); + int unused = comparator().compare(element, element); + AvlNode newRoot = new AvlNode<>(element, occurrences); successor(header, newRoot, header); rootReference.checkAndSet(root, newRoot); return 0; @@ -277,7 +276,7 @@ public int add(@ParametricNullness E element, int occurrences) { @CanIgnoreReturnValue @Override - public int remove(@CheckForNull Object element, int occurrences) { + public int remove(@Nullable Object element, int occurrences) { checkNonnegative(occurrences, "occurrences"); if (occurrences == 0) { return count(element); @@ -369,7 +368,7 @@ public void clear() { } } - private Entry wrapEntry(final AvlNode baseEntry) { + private Entry wrapEntry(AvlNode baseEntry) { return new Multisets.AbstractEntry() { @Override @ParametricNullness @@ -390,8 +389,7 @@ public int getCount() { } /** Returns the first node in the tree that is in range. */ - @CheckForNull - private AvlNode firstNode() { + private @Nullable AvlNode firstNode() { AvlNode root = rootReference.get(); if (root == null) { return null; @@ -414,8 +412,7 @@ && comparator().compare(endpoint, node.getElement()) == 0) { return (node == header || !range.contains(node.getElement())) ? null : node; } - @CheckForNull - private AvlNode lastNode() { + private @Nullable AvlNode lastNode() { AvlNode root = rootReference.get(); if (root == null) { return null; @@ -446,8 +443,8 @@ Iterator elementIterator() { @Override Iterator> entryIterator() { return new Iterator>() { - @CheckForNull AvlNode current = firstNode(); - @CheckForNull Entry prevEntry; + @Nullable AvlNode current = firstNode(); + @Nullable Entry prevEntry; @Override public boolean hasNext() { @@ -489,8 +486,8 @@ public void remove() { @Override Iterator> descendingEntryIterator() { return new Iterator>() { - @CheckForNull AvlNode current = lastNode(); - @CheckForNull Entry prevEntry = null; + @Nullable AvlNode current = lastNode(); + @Nullable Entry prevEntry = null; @Override public boolean hasNext() { @@ -537,7 +534,7 @@ public Iterator iterator() { @Override public SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundType boundType) { - return new TreeMultiset( + return new TreeMultiset<>( rootReference, range.intersect(GeneralRange.upTo(comparator(), upperBound, boundType)), header); @@ -545,21 +542,20 @@ public SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundTyp @Override public SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundType boundType) { - return new TreeMultiset( + return new TreeMultiset<>( rootReference, range.intersect(GeneralRange.downTo(comparator(), lowerBound, boundType)), header); } private static final class Reference { - @CheckForNull private T value; + private @Nullable T value; - @CheckForNull - public T get() { + public @Nullable T get() { return value; } - public void checkAndSet(@CheckForNull T expected, @CheckForNull T newValue) { + public void checkAndSet(@Nullable T expected, @Nullable T newValue) { if (value != expected) { throw new ConcurrentModificationException(); } @@ -582,7 +578,7 @@ private static final class AvlNode { * Most code that operates on an AvlNode never operates on the header node. Such code can access * the elem field without a null check by calling getElement(). */ - @CheckForNull private final E elem; + private final @Nullable E elem; // elemCount is 0 iff this node has been deleted. private int elemCount; @@ -590,8 +586,8 @@ private static final class AvlNode { private int distinctElements; private long totalCount; private int height; - @CheckForNull private AvlNode left; - @CheckForNull private AvlNode right; + private @Nullable AvlNode left; + private @Nullable AvlNode right; /* * pred and succ are nullable after construction, but we always call successor() to initialize * them immediately thereafter. @@ -603,8 +599,8 @@ private static final class AvlNode { * To access these fields when you know that they are not null, call the pred() and succ() * methods, which perform null checks before returning the fields. */ - @CheckForNull private AvlNode pred; - @CheckForNull private AvlNode succ; + private @Nullable AvlNode pred; + private @Nullable AvlNode succ; AvlNode(@ParametricNullness E elem, int elemCount) { checkArgument(elemCount > 0); @@ -644,19 +640,21 @@ int count(Comparator comparator, @ParametricNullness E e) { } } + @CanIgnoreReturnValue private AvlNode addRightChild(@ParametricNullness E e, int count) { - right = new AvlNode(e, count); + right = new AvlNode<>(e, count); successor(this, right, succ()); - height = Math.max(2, height); + height = max(2, height); distinctElements++; totalCount += count; return this; } + @CanIgnoreReturnValue private AvlNode addLeftChild(@ParametricNullness E e, int count) { - left = new AvlNode(e, count); + left = new AvlNode<>(e, count); successor(pred(), left, this); - height = Math.max(2, height); + height = max(2, height); distinctElements++; totalCount += count; return this; @@ -708,8 +706,7 @@ AvlNode add( return this; } - @CheckForNull - AvlNode remove( + @Nullable AvlNode remove( Comparator comparator, @ParametricNullness E e, int count, int[] result) { int cmp = comparator.compare(e, getElement()); if (cmp < 0) { @@ -761,8 +758,7 @@ AvlNode remove( } } - @CheckForNull - AvlNode setCount( + @Nullable AvlNode setCount( Comparator comparator, @ParametricNullness E e, int count, int[] result) { int cmp = comparator.compare(e, getElement()); if (cmp < 0) { @@ -811,8 +807,7 @@ AvlNode setCount( return this; } - @CheckForNull - AvlNode setCount( + @Nullable AvlNode setCount( Comparator comparator, @ParametricNullness E e, int expectedCount, @@ -875,8 +870,7 @@ AvlNode setCount( return this; } - @CheckForNull - private AvlNode deleteMe() { + private @Nullable AvlNode deleteMe() { int oldElemCount = this.elemCount; this.elemCount = 0; successor(pred(), succ()); @@ -903,8 +897,7 @@ private AvlNode deleteMe() { } // Removes the minimum node from this subtree to be reused elsewhere - @CheckForNull - private AvlNode removeMin(AvlNode node) { + private @Nullable AvlNode removeMin(AvlNode node) { if (left == null) { return right; } else { @@ -916,8 +909,7 @@ private AvlNode removeMin(AvlNode node) { } // Removes the maximum node from this subtree to be reused elsewhere - @CheckForNull - private AvlNode removeMax(AvlNode node) { + private @Nullable AvlNode removeMax(AvlNode node) { if (right == null) { return left; } else { @@ -935,7 +927,7 @@ private void recomputeMultiset() { } private void recomputeHeight() { - this.height = 1 + Math.max(height(left), height(right)); + this.height = 1 + max(height(left), height(right)); } private void recompute() { @@ -993,16 +985,16 @@ private AvlNode rotateRight() { return newTop; } - private static long totalCount(@CheckForNull AvlNode node) { + private static long totalCount(@Nullable AvlNode node) { return (node == null) ? 0 : node.totalCount; } - private static int height(@CheckForNull AvlNode node) { + private static int height(@Nullable AvlNode node) { return (node == null) ? 0 : node.height; } - @CheckForNull - private AvlNode ceiling(Comparator comparator, @ParametricNullness E e) { + private @Nullable AvlNode ceiling( + Comparator comparator, @ParametricNullness E e) { int cmp = comparator.compare(e, getElement()); if (cmp < 0) { return (left == null) ? this : MoreObjects.firstNonNull(left.ceiling(comparator, e), this); @@ -1013,8 +1005,7 @@ private AvlNode ceiling(Comparator comparator, @ParametricNullness } } - @CheckForNull - private AvlNode floor(Comparator comparator, @ParametricNullness E e) { + private @Nullable AvlNode floor(Comparator comparator, @ParametricNullness E e) { int cmp = comparator.compare(e, getElement()); if (cmp > 0) { return (right == null) ? this : MoreObjects.firstNonNull(right.floor(comparator, e), this); @@ -1062,6 +1053,7 @@ public String toString() { * @serialData the comparator, the number of distinct elements, the first element, its count, the * second element, its count, and so on */ + @J2ktIncompatible @GwtIncompatible // java.io.ObjectOutputStream private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); @@ -1069,12 +1061,13 @@ private void writeObject(ObjectOutputStream stream) throws IOException { Serialization.writeMultiset(this, stream); } + @J2ktIncompatible @GwtIncompatible // java.io.ObjectInputStream private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); @SuppressWarnings("unchecked") // reading data stored by writeObject - Comparator comparator = (Comparator) stream.readObject(); + Comparator comparator = (Comparator) requireNonNull(stream.readObject()); Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); Serialization.getFieldSetter(TreeMultiset.class, "range") .set(this, GeneralRange.all(comparator)); @@ -1086,6 +1079,5 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo Serialization.populateMultiset(this, stream); } - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } diff --git a/android/guava/src/com/google/common/collect/TreeRangeMap.java b/android/guava/src/com/google/common/collect/TreeRangeMap.java index 0ce8e89551cf..1086df9deb28 100644 --- a/android/guava/src/com/google/common/collect/TreeRangeMap.java +++ b/android/guava/src/com/google/common/collect/TreeRangeMap.java @@ -21,16 +21,17 @@ import static com.google.common.base.Predicates.compose; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Maps.immutableEntry; +import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; import com.google.common.collect.Maps.IteratorBasedAbstractMap; import java.util.AbstractMap; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -38,7 +39,7 @@ import java.util.NavigableMap; import java.util.NoSuchElementException; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting all optional @@ -49,21 +50,47 @@ * @author Louis Wasserman * @since 14.0 */ -@Beta +@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989 @GwtIncompatible // NavigableMap -@ElementTypesAreNonnullByDefault public final class TreeRangeMap implements RangeMap { private final NavigableMap, RangeMapEntry> entriesByLowerBound; + /** Returns a new, empty {@link TreeRangeMap}. */ public static TreeRangeMap create() { return new TreeRangeMap<>(); } + /** + * Returns a new {@link TreeRangeMap} containing the same ranges as the given {@code RangeMap}. + * + * @since 33.4.0 + */ + @SuppressWarnings("unchecked") + public static , V> TreeRangeMap copyOf( + RangeMap rangeMap) { + if (rangeMap instanceof TreeRangeMap) { + NavigableMap, RangeMapEntry> entriesByLowerBound = Maps.newTreeMap(); + entriesByLowerBound.putAll(((TreeRangeMap) rangeMap).entriesByLowerBound); + return new TreeRangeMap<>(entriesByLowerBound); + } else { + NavigableMap, RangeMapEntry> entriesByLowerBound = Maps.newTreeMap(); + for (Entry, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { + entriesByLowerBound.put( + entry.getKey().lowerBound(), new RangeMapEntry(entry.getKey(), entry.getValue())); + } + return new TreeRangeMap<>(entriesByLowerBound); + } + } + private TreeRangeMap() { this.entriesByLowerBound = Maps.newTreeMap(); } + private TreeRangeMap(NavigableMap, RangeMapEntry> entriesByLowerBound) { + this.entriesByLowerBound = entriesByLowerBound; + } + private static final class RangeMapEntry extends AbstractMapEntry, V> { private final Range range; @@ -102,15 +129,13 @@ Cut getUpperBound() { } @Override - @CheckForNull - public V get(K key) { + public @Nullable V get(K key) { Entry, V> entry = getEntry(key); return (entry == null) ? null : entry.getValue(); } @Override - @CheckForNull - public Entry, V> getEntry(K key) { + public @Nullable Entry, V> getEntry(K key) { Entry, RangeMapEntry> mapEntry = entriesByLowerBound.floorEntry(Cut.belowValue(key)); if (mapEntry != null && mapEntry.getValue().contains(key)) { @@ -157,7 +182,7 @@ private Range coalescedRange(Range range, V value) { /** Returns the range that spans the given range and entry, if the entry can be coalesced. */ private static Range coalesce( - Range range, V value, @CheckForNull Entry, RangeMapEntry> entry) { + Range range, V value, @Nullable Entry, RangeMapEntry> entry) { if (entry != null && entry.getValue().getKey().isConnected(range) && entry.getValue().getValue().equals(value)) { @@ -167,8 +192,8 @@ private static Range coalesce( } @Override - public void putAll(RangeMap rangeMap) { - for (Entry, V> entry : rangeMap.asMapOfRanges().entrySet()) { + public void putAll(RangeMap rangeMap) { + for (Entry, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { put(entry.getKey(), entry.getValue()); } } @@ -264,13 +289,12 @@ private final class AsMapOfRanges extends IteratorBasedAbstractMap, V> } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { if (key instanceof Range) { Range range = (Range) key; RangeMapEntry rangeMapEntry = entriesByLowerBound.get(range.lowerBound); @@ -310,14 +334,12 @@ private RangeMap emptySubRangeMap() { private static final RangeMap, Object> EMPTY_SUB_RANGE_MAP = new RangeMap, Object>() { @Override - @CheckForNull - public Object get(Comparable key) { + public @Nullable Object get(Comparable key) { return null; } @Override - @CheckForNull - public Entry>, Object> getEntry(Comparable key) { + public @Nullable Entry>, Object> getEntry(Comparable key) { return null; } @@ -341,7 +363,7 @@ public void putCoalescing(Range> range, Object value) { } @Override - public void putAll(RangeMap, Object> rangeMap) { + public void putAll(RangeMap, ? extends Object> rangeMap) { if (!rangeMap.asMapOfRanges().isEmpty()) { throw new IllegalArgumentException( "Cannot putAll(nonEmptyRangeMap) into an empty subRangeMap"); @@ -358,12 +380,12 @@ public void remove(Range> range) { @Override public Map>, Object> asMapOfRanges() { - return Collections.emptyMap(); + return emptyMap(); } @Override public Map>, Object> asDescendingMapOfRanges() { - return Collections.emptyMap(); + return emptyMap(); } @Override @@ -382,18 +404,16 @@ private class SubRangeMap implements RangeMap { } @Override - @CheckForNull - public V get(K key) { + public @Nullable V get(K key) { return subRange.contains(key) ? TreeRangeMap.this.get(key) : null; } @Override - @CheckForNull - public Entry, V> getEntry(K key) { + public @Nullable Entry, V> getEntry(K key) { if (subRange.contains(key)) { Entry, V> entry = TreeRangeMap.this.getEntry(key); if (entry != null) { - return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + return immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); } } return null; @@ -447,7 +467,7 @@ public void putCoalescing(Range range, V value) { } @Override - public void putAll(RangeMap rangeMap) { + public void putAll(RangeMap rangeMap) { if (rangeMap.asMapOfRanges().isEmpty()) { return; } @@ -493,9 +513,9 @@ public Map, V> asDescendingMapOfRanges() { @Override Iterator, V>> entryIterator() { if (subRange.isEmpty()) { - return Iterators.emptyIterator(); + return emptyIterator(); } - final Iterator> backingItr = + Iterator> backingItr = entriesByLowerBound .headMap(subRange.upperBound, false) .descendingMap() @@ -504,14 +524,13 @@ Iterator, V>> entryIterator() { return new AbstractIterator, V>>() { @Override - @CheckForNull - protected Entry, V> computeNext() { + protected @Nullable Entry, V> computeNext() { if (backingItr.hasNext()) { RangeMapEntry entry = backingItr.next(); if (entry.getUpperBound().compareTo(subRange.lowerBound) <= 0) { return endOfData(); } - return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + return immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); } return endOfData(); } @@ -521,7 +540,7 @@ protected Entry, V> computeNext() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof RangeMap) { RangeMap rangeMap = (RangeMap) o; return asMapOfRanges().equals(rangeMap.asMapOfRanges()); @@ -542,13 +561,12 @@ public String toString() { class SubRangeMapAsMap extends AbstractMap, V> { @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override - @CheckForNull - public V get(@CheckForNull Object key) { + public @Nullable V get(@Nullable Object key) { try { if (key instanceof Range) { @SuppressWarnings("unchecked") // we catch ClassCastExceptions @@ -581,8 +599,7 @@ public V get(@CheckForNull Object key) { } @Override - @CheckForNull - public V remove(@CheckForNull Object key) { + public @Nullable V remove(@Nullable Object key) { V value = get(key); if (value != null) { // it's definitely in the map, so the cast and requireNonNull are safe @@ -616,7 +633,7 @@ private boolean removeEntryIf(Predicate, V>> predicate) { public Set> keySet() { return new Maps.KeySet, V>(SubRangeMapAsMap.this) { @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { return SubRangeMapAsMap.this.remove(o) != null; } @@ -659,25 +676,24 @@ public boolean isEmpty() { Iterator, V>> entryIterator() { if (subRange.isEmpty()) { - return Iterators.emptyIterator(); + return emptyIterator(); } Cut cutToStart = MoreObjects.firstNonNull( entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound); - final Iterator> backingItr = + Iterator> backingItr = entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); return new AbstractIterator, V>>() { @Override - @CheckForNull - protected Entry, V> computeNext() { + protected @Nullable Entry, V> computeNext() { while (backingItr.hasNext()) { RangeMapEntry entry = backingItr.next(); if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { return endOfData(); } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { // this might not be true e.g. at the start of the iteration - return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + return immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); } } return endOfData(); @@ -703,7 +719,7 @@ public boolean retainAll(Collection c) { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof RangeMap) { RangeMap rangeMap = (RangeMap) o; return asMapOfRanges().equals(rangeMap.asMapOfRanges()); diff --git a/android/guava/src/com/google/common/collect/TreeRangeSet.java b/android/guava/src/com/google/common/collect/TreeRangeSet.java index 7ba71139eb96..11f585c35ef3 100644 --- a/android/guava/src/com/google/common/collect/TreeRangeSet.java +++ b/android/guava/src/com/google/common/collect/TreeRangeSet.java @@ -16,11 +16,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterators.emptyIterator; +import static com.google.common.collect.Maps.immutableEntry; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; import java.util.Collection; import java.util.Comparator; @@ -30,7 +32,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link RangeSet} backed by a {@link TreeMap}. @@ -38,9 +40,7 @@ * @author Louis Wasserman * @since 14.0 */ -@Beta @GwtIncompatible // uses NavigableMap -@ElementTypesAreNonnullByDefault public class TreeRangeSet> extends AbstractRangeSet implements Serializable { @@ -77,8 +77,8 @@ private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) { this.rangesByLowerBound = rangesByLowerCut; } - @CheckForNull private transient Set> asRanges; - @CheckForNull private transient Set> asDescendingSetOfRanges; + @LazyInit private transient @Nullable Set> asRanges; + @LazyInit private transient @Nullable Set> asDescendingSetOfRanges; @Override public Set> asRanges() { @@ -113,14 +113,13 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { return Sets.equalsImpl(this, o); } } @Override - @CheckForNull - public Range rangeContaining(C value) { + public @Nullable Range rangeContaining(C value) { checkNotNull(value); Entry, Range> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); if (floorEntry != null && floorEntry.getValue().contains(value)) { @@ -153,8 +152,7 @@ public boolean encloses(Range range) { return floorEntry != null && floorEntry.getValue().encloses(range); } - @CheckForNull - private Range rangeEnclosing(Range range) { + private @Nullable Range rangeEnclosing(Range range) { checkNotNull(range); Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); return (floorEntry != null && floorEntry.getValue().encloses(range)) @@ -189,31 +187,31 @@ public void add(Range rangeToAdd) { Cut lbToAdd = rangeToAdd.lowerBound; Cut ubToAdd = rangeToAdd.upperBound; - Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); - if (entryBelowLB != null) { + Entry, Range> entryBelowLb = rangesByLowerBound.lowerEntry(lbToAdd); + if (entryBelowLb != null) { // { < - Range rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { + Range rangeBelowLb = entryBelowLb.getValue(); + if (rangeBelowLb.upperBound.compareTo(lbToAdd) >= 0) { // { < }, and we will need to coalesce - if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { + if (rangeBelowLb.upperBound.compareTo(ubToAdd) >= 0) { // { < > } - ubToAdd = rangeBelowLB.upperBound; + ubToAdd = rangeBelowLb.upperBound; /* * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If * not, add tests to demonstrate the problem with each approach */ } - lbToAdd = rangeBelowLB.lowerBound; + lbToAdd = rangeBelowLb.lowerBound; } } - Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); - if (entryBelowUB != null) { + Entry, Range> entryBelowUb = rangesByLowerBound.floorEntry(ubToAdd); + if (entryBelowUb != null) { // { > - Range rangeBelowUB = entryBelowUB.getValue(); - if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { + Range rangeBelowUb = entryBelowUb.getValue(); + if (rangeBelowUb.upperBound.compareTo(ubToAdd) >= 0) { // { > }, and we need to coalesce - ubToAdd = rangeBelowUB.upperBound; + ubToAdd = rangeBelowUb.upperBound; } } @@ -234,32 +232,32 @@ public void remove(Range rangeToRemove) { // We will use { } to illustrate ranges currently in the range set, and < > // to illustrate rangeToRemove. - Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); - if (entryBelowLB != null) { + Entry, Range> entryBelowLb = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (entryBelowLb != null) { // { < - Range rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { + Range rangeBelowLb = entryBelowLb.getValue(); + if (rangeBelowLb.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { // { < }, and we will need to subdivide if (rangeToRemove.hasUpperBound() - && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + && rangeBelowLb.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { // { < > } replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); + Range.create(rangeToRemove.upperBound, rangeBelowLb.upperBound)); } replaceRangeWithSameLowerBound( - Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); + Range.create(rangeBelowLb.lowerBound, rangeToRemove.lowerBound)); } } - Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); - if (entryBelowUB != null) { + Entry, Range> entryBelowUb = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); + if (entryBelowUb != null) { // { > - Range rangeBelowUB = entryBelowUB.getValue(); + Range rangeBelowUb = entryBelowUb.getValue(); if (rangeToRemove.hasUpperBound() - && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + && rangeBelowUb.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { // { > } replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); + Range.create(rangeToRemove.upperBound, rangeBelowUb.upperBound)); } } @@ -274,7 +272,7 @@ private void replaceRangeWithSameLowerBound(Range range) { } } - @CheckForNull private transient RangeSet complement; + @LazyInit private transient @Nullable RangeSet complement; @Override public RangeSet complement() { @@ -337,13 +335,12 @@ public Comparator> comparator() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override - @CheckForNull - public Range get(@CheckForNull Object key) { + public @Nullable Range get(@Nullable Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCEs @@ -388,8 +385,7 @@ Iterator, Range>> entryIterator() { } return new AbstractIterator, Range>>() { @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } @@ -397,7 +393,7 @@ protected Entry, Range> computeNext() { if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { return endOfData(); } else { - return Maps.immutableEntry(range.upperBound, range); + return immutableEntry(range.upperBound, range); } } }; @@ -422,14 +418,13 @@ Iterator, Range>> descendingEntryIterator() { } return new AbstractIterator, Range>>() { @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (!backingItr.hasNext()) { return endOfData(); } Range range = backingItr.next(); return upperBoundWindow.lowerBound.isLessThan(range.upperBound) - ? Maps.immutableEntry(range.upperBound, range) + ? immutableEntry(range.upperBound, range) : endOfData(); } }; @@ -537,14 +532,13 @@ Iterator, Range>> entryIterator() { } else if (positiveItr.hasNext()) { firstComplementRangeLowerBound = positiveItr.next().upperBound; } else { - return Iterators.emptyIterator(); + return emptyIterator(); } return new AbstractIterator, Range>>() { Cut nextComplementRangeLowerBound = firstComplementRangeLowerBound; @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) || nextComplementRangeLowerBound == Cut.aboveAll()) { return endOfData(); @@ -558,7 +552,7 @@ protected Entry, Range> computeNext() { negativeRange = Range.create(nextComplementRangeLowerBound, Cut.aboveAll()); nextComplementRangeLowerBound = Cut.aboveAll(); } - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + return immutableEntry(negativeRange.lowerBound, negativeRange); } }; } @@ -595,7 +589,7 @@ Iterator, Range>> descendingEntryIterator() { : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); } else if (!complementLowerBoundWindow.contains(Cut.belowAll()) || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { - return Iterators.emptyIterator(); + return emptyIterator(); } else { cut = positiveRangesByLowerBound.higherKey(Cut.belowAll()); } @@ -604,8 +598,7 @@ Iterator, Range>> descendingEntryIterator() { Cut nextComplementRangeUpperBound = firstComplementRangeUpperBound; @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (nextComplementRangeUpperBound == Cut.belowAll()) { return endOfData(); } else if (positiveItr.hasNext()) { @@ -614,12 +607,12 @@ protected Entry, Range> computeNext() { Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); nextComplementRangeUpperBound = positiveRange.lowerBound; if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + return immutableEntry(negativeRange.lowerBound, negativeRange); } } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.belowAll())) { Range negativeRange = Range.create(Cut.belowAll(), nextComplementRangeUpperBound); nextComplementRangeUpperBound = Cut.belowAll(); - return Maps.immutableEntry(Cut.belowAll(), negativeRange); + return immutableEntry(Cut.belowAll(), negativeRange); } return endOfData(); } @@ -632,8 +625,7 @@ public int size() { } @Override - @CheckForNull - public Range get(@CheckForNull Object key) { + public @Nullable Range get(@Nullable Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") @@ -651,7 +643,7 @@ public Range get(@CheckForNull Object key) { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } } @@ -745,13 +737,12 @@ public Comparator> comparator() { } @Override - public boolean containsKey(@CheckForNull Object key) { + public boolean containsKey(@Nullable Object key) { return get(key) != null; } @Override - @CheckForNull - public Range get(@CheckForNull Object key) { + public @Nullable Range get(@Nullable Object key) { if (key instanceof Cut) { try { @SuppressWarnings("unchecked") // we catch CCE's @@ -782,11 +773,11 @@ public Range get(@CheckForNull Object key) { @Override Iterator, Range>> entryIterator() { if (restriction.isEmpty()) { - return Iterators.emptyIterator(); + return emptyIterator(); } Iterator> completeRangeItr; if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { - return Iterators.emptyIterator(); + return emptyIterator(); } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { // starts at the first range with upper bound strictly greater than restriction.lowerBound completeRangeItr = @@ -802,12 +793,11 @@ Iterator, Range>> entryIterator() { .iterator(); } Cut> upperBoundOnLowerBounds = - Ordering.natural() + Ordering.>>natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); return new AbstractIterator, Range>>() { @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } @@ -816,7 +806,7 @@ protected Entry, Range> computeNext() { return endOfData(); } else { nextRange = nextRange.intersection(restriction); - return Maps.immutableEntry(nextRange.lowerBound, nextRange); + return immutableEntry(nextRange.lowerBound, nextRange); } } }; @@ -825,10 +815,10 @@ protected Entry, Range> computeNext() { @Override Iterator, Range>> descendingEntryIterator() { if (restriction.isEmpty()) { - return Iterators.emptyIterator(); + return emptyIterator(); } Cut> upperBoundOnLowerBounds = - Ordering.natural() + Ordering.>>natural() .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); Iterator> completeRangeItr = rangesByLowerBound @@ -840,8 +830,7 @@ Iterator, Range>> descendingEntryIterator() { .iterator(); return new AbstractIterator, Range>>() { @Override - @CheckForNull - protected Entry, Range> computeNext() { + protected @Nullable Entry, Range> computeNext() { if (!completeRangeItr.hasNext()) { return endOfData(); } @@ -851,7 +840,7 @@ protected Entry, Range> computeNext() { } nextRange = nextRange.intersection(restriction); if (lowerBoundWindow.contains(nextRange.lowerBound)) { - return Maps.immutableEntry(nextRange.lowerBound, nextRange); + return immutableEntry(nextRange.lowerBound, nextRange); } else { return endOfData(); } @@ -890,8 +879,7 @@ public boolean encloses(Range range) { } @Override - @CheckForNull - public Range rangeContaining(C value) { + public @Nullable Range rangeContaining(C value) { if (!restriction.contains(value)) { return null; } diff --git a/android/guava/src/com/google/common/collect/TreeTraverser.java b/android/guava/src/com/google/common/collect/TreeTraverser.java index 73f8154f2a79..8a899acd11cb 100644 --- a/android/guava/src/com/google/common/collect/TreeTraverser.java +++ b/android/guava/src/com/google/common/collect/TreeTraverser.java @@ -17,6 +17,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterators.singletonIterator; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; @@ -25,7 +26,7 @@ import java.util.Deque; import java.util.Iterator; import java.util.Queue; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Views elements of a type {@code T} as nodes in a tree, and provides methods to traverse the trees @@ -48,8 +49,8 @@ * *

    Null nodes are strictly forbidden. * - *

    For Java 8 users: Because this is an abstract class, not an interface, you can't use a - * lambda expression to extend it: + *

    Because this is an abstract class, not an interface, you can't use a lambda expression to + * implement it: * *

    {@code
      * // won't work
    @@ -74,8 +75,9 @@
     @Deprecated
     @Beta
     @GwtCompatible
    -@ElementTypesAreNonnullByDefault
     public abstract class TreeTraverser {
    +  /** Constructor for use by subclasses. */
    +  public TreeTraverser() {}
     
       /**
        * Returns a tree traverser that uses the given function to navigate from a node to its children.
    @@ -89,7 +91,7 @@ public abstract class TreeTraverser {
        */
       @Deprecated
       public static  TreeTraverser using(
    -      final Function> nodeToChildrenFunction) {
    +      Function> nodeToChildrenFunction) {
         checkNotNull(nodeToChildrenFunction);
         return new TreeTraverser() {
           @Override
    @@ -113,7 +115,7 @@ public Iterable children(T root) {
        *     the same behavior.
        */
       @Deprecated
    -  public final FluentIterable preOrderTraversal(final T root) {
    +  public final FluentIterable preOrderTraversal(T root) {
         checkNotNull(root);
         return new FluentIterable() {
           @Override
    @@ -132,7 +134,7 @@ private final class PreOrderIterator extends UnmodifiableIterator {
     
         PreOrderIterator(T root) {
           this.stack = new ArrayDeque<>();
    -      stack.addLast(Iterators.singletonIterator(checkNotNull(root)));
    +      stack.addLast(singletonIterator(checkNotNull(root)));
         }
     
         @Override
    @@ -166,7 +168,7 @@ public T next() {
        *     has the same behavior.
        */
       @Deprecated
    -  public final FluentIterable postOrderTraversal(final T root) {
    +  public final FluentIterable postOrderTraversal(T root) {
         checkNotNull(root);
         return new FluentIterable() {
           @Override
    @@ -199,8 +201,7 @@ private final class PostOrderIterator extends AbstractIterator {
         }
     
         @Override
    -    @CheckForNull
    -    protected T computeNext() {
    +    protected @Nullable T computeNext() {
           while (!stack.isEmpty()) {
             PostOrderNode top = stack.getLast();
             if (top.childIterator.hasNext()) {
    @@ -215,7 +216,7 @@ protected T computeNext() {
         }
     
         private PostOrderNode expand(T t) {
    -      return new PostOrderNode(t, children(t).iterator());
    +      return new PostOrderNode<>(t, children(t).iterator());
         }
       }
     
    @@ -230,7 +231,7 @@ private PostOrderNode expand(T t) {
        *     same behavior.
        */
       @Deprecated
    -  public final FluentIterable breadthFirstTraversal(final T root) {
    +  public final FluentIterable breadthFirstTraversal(T root) {
         checkNotNull(root);
         return new FluentIterable() {
           @Override
    @@ -245,7 +246,7 @@ private final class BreadthFirstIterator extends UnmodifiableIterator
         private final Queue queue;
     
         BreadthFirstIterator(T root) {
    -      this.queue = new ArrayDeque();
    +      this.queue = new ArrayDeque<>();
           queue.add(root);
         }
     
    diff --git a/android/guava/src/com/google/common/collect/UnmodifiableIterator.java b/android/guava/src/com/google/common/collect/UnmodifiableIterator.java
    index 03e52aa2037d..a159121a7b90 100644
    --- a/android/guava/src/com/google/common/collect/UnmodifiableIterator.java
    +++ b/android/guava/src/com/google/common/collect/UnmodifiableIterator.java
    @@ -19,7 +19,7 @@
     import com.google.common.annotations.GwtCompatible;
     import com.google.errorprone.annotations.DoNotCall;
     import java.util.Iterator;
    -import org.checkerframework.checker.nullness.qual.Nullable;
    +import org.jspecify.annotations.Nullable;
     
     /**
      * An iterator that does not support {@link #remove}.
    @@ -32,7 +32,6 @@
      * @since 2.0
      */
     @GwtCompatible
    -@ElementTypesAreNonnullByDefault
     public abstract class UnmodifiableIterator implements Iterator {
       /** Constructor for use by subclasses. */
       protected UnmodifiableIterator() {}
    diff --git a/android/guava/src/com/google/common/collect/UnmodifiableListIterator.java b/android/guava/src/com/google/common/collect/UnmodifiableListIterator.java
    index f3d3b921b7dc..2917d5914b60 100644
    --- a/android/guava/src/com/google/common/collect/UnmodifiableListIterator.java
    +++ b/android/guava/src/com/google/common/collect/UnmodifiableListIterator.java
    @@ -19,7 +19,7 @@
     import com.google.common.annotations.GwtCompatible;
     import com.google.errorprone.annotations.DoNotCall;
     import java.util.ListIterator;
    -import org.checkerframework.checker.nullness.qual.Nullable;
    +import org.jspecify.annotations.Nullable;
     
     /**
      * A list iterator that does not support {@link #remove}, {@link #add}, or {@link #set}.
    @@ -28,7 +28,6 @@
      * @author Louis Wasserman
      */
     @GwtCompatible
    -@ElementTypesAreNonnullByDefault
     public abstract class UnmodifiableListIterator
         extends UnmodifiableIterator implements ListIterator {
       /** Constructor for use by subclasses. */
    diff --git a/android/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java b/android/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java
    index 31f3c7197776..0b602c1d0eb2 100644
    --- a/android/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java
    +++ b/android/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java
    @@ -16,12 +16,16 @@
     
     package com.google.common.collect;
     
    +import static com.google.common.collect.Sets.unmodifiableNavigableSet;
    +
     import com.google.common.annotations.GwtCompatible;
    +import com.google.common.annotations.GwtIncompatible;
    +import com.google.common.annotations.J2ktIncompatible;
     import com.google.common.collect.Multisets.UnmodifiableMultiset;
    +import com.google.errorprone.annotations.concurrent.LazyInit;
     import java.util.Comparator;
     import java.util.NavigableSet;
    -import javax.annotation.CheckForNull;
    -import org.checkerframework.checker.nullness.qual.Nullable;
    +import org.jspecify.annotations.Nullable;
     
     /**
      * Implementation of {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}, split out into
    @@ -31,7 +35,6 @@
      * @author Louis Wasserman
      */
     @GwtCompatible(emulated = true)
    -@ElementTypesAreNonnullByDefault
     final class UnmodifiableSortedMultiset extends UnmodifiableMultiset
         implements SortedMultiset {
       UnmodifiableSortedMultiset(SortedMultiset delegate) {
    @@ -50,7 +53,7 @@ public Comparator comparator() {
     
       @Override
       NavigableSet createElementSet() {
    -    return Sets.unmodifiableNavigableSet(delegate().elementSet());
    +    return unmodifiableNavigableSet(delegate().elementSet());
       }
     
       @Override
    @@ -58,7 +61,7 @@ public NavigableSet elementSet() {
         return (NavigableSet) super.elementSet();
       }
     
    -  @CheckForNull private transient UnmodifiableSortedMultiset descendingMultiset;
    +  @LazyInit private transient @Nullable UnmodifiableSortedMultiset descendingMultiset;
     
       @Override
       public SortedMultiset descendingMultiset() {
    @@ -72,26 +75,22 @@ public SortedMultiset descendingMultiset() {
       }
     
       @Override
    -  @CheckForNull
    -  public Entry firstEntry() {
    +  public @Nullable Entry firstEntry() {
         return delegate().firstEntry();
       }
     
       @Override
    -  @CheckForNull
    -  public Entry lastEntry() {
    +  public @Nullable Entry lastEntry() {
         return delegate().lastEntry();
       }
     
       @Override
    -  @CheckForNull
    -  public Entry pollFirstEntry() {
    +  public @Nullable Entry pollFirstEntry() {
         throw new UnsupportedOperationException();
       }
     
       @Override
    -  @CheckForNull
    -  public Entry pollLastEntry() {
    +  public @Nullable Entry pollLastEntry() {
         throw new UnsupportedOperationException();
       }
     
    @@ -115,5 +114,5 @@ public SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundTyp
         return Multisets.unmodifiableSortedMultiset(delegate().tailMultiset(lowerBound, boundType));
       }
     
    -  private static final long serialVersionUID = 0;
    +  @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0;
     }
    diff --git a/android/guava/src/com/google/common/collect/UsingToStringOrdering.java b/android/guava/src/com/google/common/collect/UsingToStringOrdering.java
    index 3443b43e7223..7839aff833c2 100644
    --- a/android/guava/src/com/google/common/collect/UsingToStringOrdering.java
    +++ b/android/guava/src/com/google/common/collect/UsingToStringOrdering.java
    @@ -17,11 +17,12 @@
     package com.google.common.collect;
     
     import com.google.common.annotations.GwtCompatible;
    +import com.google.common.annotations.GwtIncompatible;
    +import com.google.common.annotations.J2ktIncompatible;
     import java.io.Serializable;
     
     /** An ordering that uses the natural order of the string representation of the values. */
     @GwtCompatible(serializable = true)
    -@ElementTypesAreNonnullByDefault
     final class UsingToStringOrdering extends Ordering implements Serializable {
       static final UsingToStringOrdering INSTANCE = new UsingToStringOrdering();
     
    @@ -42,5 +43,5 @@ public String toString() {
     
       private UsingToStringOrdering() {}
     
    -  private static final long serialVersionUID = 0;
    +  @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0;
     }
    diff --git a/android/guava/src/com/google/common/collect/package-info.java b/android/guava/src/com/google/common/collect/package-info.java
    index d46e65fd3a13..d9f2331e1a64 100644
    --- a/android/guava/src/com/google/common/collect/package-info.java
    +++ b/android/guava/src/com/google/common/collect/package-info.java
    @@ -15,206 +15,113 @@
      */
     
     /**
    - * This package contains generic collection interfaces and implementations, and other utilities for
    - * working with collections. It is a part of the open-source Guava library.
    + * Collection interfaces and implementations, and other utilities for collections. This package is a
    + * part of the open-source Guava library.
      *
    - * 

    Collection Types

    + *

    The classes in this package include: + * + *

    Immutable collections

    + * + * These are collections whose contents will never change. They also offer a few additional + * guarantees (see {@link ImmutableCollection} for details). Implementations are available for both + * the JDK collection types and the Guava collection types (listed below). + * + *

    Collection types

    * *
    - *
    {@link com.google.common.collect.BiMap} + *
    {@link Multimap} + *
    A new type, which is similar to {@link java.util.Map}, but may contain multiple entries + * with the same key. Some behaviors of {@link Multimap} are left unspecified and are provided + * only by the subtypes mentioned below. + *
    {@link ListMultimap} + *
    An extension of {@link Multimap} which permits duplicate entries, supports random access of + * values for a particular key, and has partially order-dependent equality as defined + * by {@link ListMultimap#equals(Object)}. {@code ListMultimap} takes its name from the fact + * that the {@linkplain ListMultimap#get collection of values} associated with a given key + * fulfills the {@link java.util.List} contract. + *
    {@link SetMultimap} + *
    An extension of {@link Multimap} which has order-independent equality and does not allow + * duplicate entries; that is, while a key may appear twice in a {@code SetMultimap}, each + * must map to a different value. {@code SetMultimap} takes its name from the fact that the + * {@linkplain SetMultimap#get collection of values} associated with a given key fulfills the + * {@link java.util.Set} contract. + *
    {@link SortedSetMultimap} + *
    An extension of {@link SetMultimap} for which the {@linkplain SortedSetMultimap#get + * collection values} associated with a given key is a {@link java.util.SortedSet}. + *
    {@link BiMap} *
    An extension of {@link java.util.Map} that guarantees the uniqueness of its values as well * as that of its keys. This is sometimes called an "invertible map," since the restriction on - * values enables it to support an {@linkplain com.google.common.collect.BiMap#inverse inverse - * view} -- which is another instance of {@code BiMap}. - *
    {@link com.google.common.collect.Multiset} + * values enables it to support an {@linkplain BiMap#inverse inverse view} -- which is another + * instance of {@code BiMap}. + *
    {@link Table} + *
    A new type, which is similar to {@link java.util.Map}, but which indexes its values by an + * ordered pair of keys, a row key and column key. + *
    {@link Multiset} *
    An extension of {@link java.util.Collection} that may contain duplicate values like a * {@link java.util.List}, yet has order-independent equality like a {@link java.util.Set}. * One typical use for a multiset is to represent a histogram. - *
    {@link com.google.common.collect.Multimap} - *
    A new type, which is similar to {@link java.util.Map}, but may contain multiple entries - * with the same key. Some behaviors of {@link com.google.common.collect.Multimap} are left - * unspecified and are provided only by the subtypes mentioned below. - *
    {@link com.google.common.collect.ListMultimap} - *
    An extension of {@link com.google.common.collect.Multimap} which permits duplicate entries, - * supports random access of values for a particular key, and has partially order-dependent - * equality as defined by {@link com.google.common.collect.ListMultimap#equals(Object)}. - * {@code ListMultimap} takes its name from the fact that the {@linkplain - * com.google.common.collect.ListMultimap#get collection of values} associated with a given - * key fulfills the {@link java.util.List} contract. - *
    {@link com.google.common.collect.SetMultimap} - *
    An extension of {@link com.google.common.collect.Multimap} which has order-independent - * equality and does not allow duplicate entries; that is, while a key may appear twice in a - * {@code SetMultimap}, each must map to a different value. {@code SetMultimap} takes its name - * from the fact that the {@linkplain com.google.common.collect.SetMultimap#get collection of - * values} associated with a given key fulfills the {@link java.util.Set} contract. - *
    {@link com.google.common.collect.SortedSetMultimap} - *
    An extension of {@link com.google.common.collect.SetMultimap} for which the {@linkplain - * com.google.common.collect.SortedSetMultimap#get collection values} associated with a given - * key is a {@link java.util.SortedSet}. - *
    {@link com.google.common.collect.Table} - *
    A new type, which is similar to {@link java.util.Map}, but which indexes its values by an - * ordered pair of keys, a row key and column key. - *
    {@link com.google.common.collect.ClassToInstanceMap} + *
    {@link ClassToInstanceMap} *
    An extension of {@link java.util.Map} that associates a raw type with an instance of that * type. *
    * - *

    Collection Implementations

    - * - *

    of {@link java.util.List}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableList} - *
    - * - *

    of {@link java.util.Set}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableSet} - *
    • {@link com.google.common.collect.ImmutableSortedSet} - *
    • {@link com.google.common.collect.ContiguousSet} (see {@code Range}) - *
    - * - *

    of {@link java.util.Map}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableMap} - *
    • {@link com.google.common.collect.ImmutableSortedMap} - *
    • {@link com.google.common.collect.MapMaker} - *
    - * - *

    of {@link com.google.common.collect.BiMap}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableBiMap} - *
    • {@link com.google.common.collect.HashBiMap} - *
    • {@link com.google.common.collect.EnumBiMap} - *
    • {@link com.google.common.collect.EnumHashBiMap} - *
    - * - *

    of {@link com.google.common.collect.Multiset}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableMultiset} - *
    • {@link com.google.common.collect.ImmutableSortedMultiset} - *
    • {@link com.google.common.collect.HashMultiset} - *
    • {@link com.google.common.collect.LinkedHashMultiset} - *
    • {@link com.google.common.collect.TreeMultiset} - *
    • {@link com.google.common.collect.EnumMultiset} - *
    • {@link com.google.common.collect.ConcurrentHashMultiset} - *
    - * - *

    of {@link com.google.common.collect.Multimap}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableMultimap} - *
    • {@link com.google.common.collect.ImmutableListMultimap} - *
    • {@link com.google.common.collect.ImmutableSetMultimap} - *
    • {@link com.google.common.collect.ArrayListMultimap} - *
    • {@link com.google.common.collect.HashMultimap} - *
    • {@link com.google.common.collect.TreeMultimap} - *
    • {@link com.google.common.collect.LinkedHashMultimap} - *
    • {@link com.google.common.collect.LinkedListMultimap} - *
    - * - *

    of {@link com.google.common.collect.Table}

    - * - *
      - *
    • {@link com.google.common.collect.ImmutableTable} - *
    • {@link com.google.common.collect.ArrayTable} - *
    • {@link com.google.common.collect.HashBasedTable} - *
    • {@link com.google.common.collect.TreeBasedTable} - *
    - * - *

    of {@link com.google.common.collect.ClassToInstanceMap}

    + *

    Ranges

    * *
      - *
    • {@link com.google.common.collect.ImmutableClassToInstanceMap} - *
    • {@link com.google.common.collect.MutableClassToInstanceMap} + *
    • {@link Range} + *
    • {@link RangeMap} + *
    • {@link RangeSet} + *
    • {@link DiscreteDomain} + *
    • {@link ContiguousSet} *
    * *

    Classes of static utility methods

    * *
      - *
    • {@link com.google.common.collect.Collections2} - *
    • {@link com.google.common.collect.Iterators} - *
    • {@link com.google.common.collect.Iterables} - *
    • {@link com.google.common.collect.Lists} - *
    • {@link com.google.common.collect.Maps} - *
    • {@link com.google.common.collect.Queues} - *
    • {@link com.google.common.collect.Sets} - *
    • {@link com.google.common.collect.Multisets} - *
    • {@link com.google.common.collect.Multimaps} - *
    • {@link com.google.common.collect.Tables} - *
    • {@link com.google.common.collect.ObjectArrays} - *
    - * - *

    Comparison

    - * - *
      - *
    • {@link com.google.common.collect.Ordering} - *
    • {@link com.google.common.collect.ComparisonChain} + *
    • {@link Collections2} + *
    • {@link Comparators} + *
    • {@link Iterables} + *
    • {@link Iterators} + *
    • {@link Lists} + *
    • {@link Maps} + *
    • {@link MoreCollectors} + *
    • {@link Multimaps} + *
    • {@link Multisets} + *
    • {@link ObjectArrays} + *
    • {@link Queues} + *
    • {@link Sets} + *
    • {@link Streams} + *
    • {@link Tables} *
    * *

    Abstract implementations

    * *
      - *
    • {@link com.google.common.collect.AbstractIterator} - *
    • {@link com.google.common.collect.AbstractSequentialIterator} - *
    • {@link com.google.common.collect.ImmutableCollection} - *
    • {@link com.google.common.collect.UnmodifiableIterator} - *
    • {@link com.google.common.collect.UnmodifiableListIterator} + *
    • {@link AbstractIterator} + *
    • {@link AbstractSequentialIterator} + *
    • {@link UnmodifiableIterator} + *
    • {@link UnmodifiableListIterator} *
    * - *

    Ranges

    + *

    Forwarding collections

    * - *
      - *
    • {@link com.google.common.collect.Range} - *
    • {@link com.google.common.collect.RangeMap} - *
    • {@link com.google.common.collect.DiscreteDomain} - *
    • {@link com.google.common.collect.ContiguousSet} - *
    + * We provide implementations of collections that forward all method calls to a delegate collection + * by default. Subclasses can override one or more methods to implement the decorator pattern. For + * an example, see {@link ForwardingCollection}. * *

    Other

    * *
      - *
    • {@link com.google.common.collect.Interner}, {@link com.google.common.collect.Interners} - *
    • {@link com.google.common.collect.MapDifference}, {@link - * com.google.common.collect.SortedMapDifference} - *
    • {@link com.google.common.collect.MinMaxPriorityQueue} - *
    • {@link com.google.common.collect.PeekingIterator} - *
    - * - *

    Forwarding collections

    - * - *
      - *
    • {@link com.google.common.collect.ForwardingCollection} - *
    • {@link com.google.common.collect.ForwardingConcurrentMap} - *
    • {@link com.google.common.collect.ForwardingIterator} - *
    • {@link com.google.common.collect.ForwardingList} - *
    • {@link com.google.common.collect.ForwardingListIterator} - *
    • {@link com.google.common.collect.ForwardingListMultimap} - *
    • {@link com.google.common.collect.ForwardingMap} - *
    • {@link com.google.common.collect.ForwardingMapEntry} - *
    • {@link com.google.common.collect.ForwardingMultimap} - *
    • {@link com.google.common.collect.ForwardingMultiset} - *
    • {@link com.google.common.collect.ForwardingNavigableMap} - *
    • {@link com.google.common.collect.ForwardingNavigableSet} - *
    • {@link com.google.common.collect.ForwardingObject} - *
    • {@link com.google.common.collect.ForwardingQueue} - *
    • {@link com.google.common.collect.ForwardingSet} - *
    • {@link com.google.common.collect.ForwardingSetMultimap} - *
    • {@link com.google.common.collect.ForwardingSortedMap} - *
    • {@link com.google.common.collect.ForwardingSortedMultiset} - *
    • {@link com.google.common.collect.ForwardingSortedSet} - *
    • {@link com.google.common.collect.ForwardingSortedSetMultimap} - *
    • {@link com.google.common.collect.ForwardingTable} + *
    • {@link EvictingQueue} + *
    • {@link Interner}, {@link Interners} + *
    • {@link MapMaker} + *
    • {@link MinMaxPriorityQueue} + *
    • {@link PeekingIterator} *
    */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.collect; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/escape/ArrayBasedCharEscaper.java b/android/guava/src/com/google/common/escape/ArrayBasedCharEscaper.java index 20e856348476..8f9dddabe0d3 100644 --- a/android/guava/src/com/google/common/escape/ArrayBasedCharEscaper.java +++ b/android/guava/src/com/google/common/escape/ArrayBasedCharEscaper.java @@ -16,10 +16,9 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link CharEscaper} that uses an array to quickly look up replacement characters for a given @@ -41,9 +40,7 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ArrayBasedCharEscaper extends CharEscaper { // The replacement array (see ArrayBasedEscaperMap). private final char[][] replacements; @@ -123,8 +120,7 @@ public final String escape(String s) { * @return the replacement characters, or {@code null} if no escaping was required */ @Override - @CheckForNull - protected final char[] escape(char c) { + protected final char @Nullable [] escape(char c) { if (c < replacementsLength) { char[] chars = replacements[c]; if (chars != null) { @@ -150,6 +146,5 @@ protected final char[] escape(char c) { * @return the replacement characters, or {@code null} if no escaping was required */ // TODO(dbeaumont,cpovirk): Rename this something better once refactoring done - @CheckForNull - protected abstract char[] escapeUnsafe(char c); + protected abstract char @Nullable [] escapeUnsafe(char c); } diff --git a/android/guava/src/com/google/common/escape/ArrayBasedEscaperMap.java b/android/guava/src/com/google/common/escape/ArrayBasedEscaperMap.java index a0883fea2d0f..68515dfa3512 100644 --- a/android/guava/src/com/google/common/escape/ArrayBasedEscaperMap.java +++ b/android/guava/src/com/google/common/escape/ArrayBasedEscaperMap.java @@ -15,11 +15,10 @@ package com.google.common.escape; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.max; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; -import java.util.Collections; import java.util.Map; /** @@ -36,9 +35,7 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class ArrayBasedEscaperMap { /** * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or @@ -72,7 +69,7 @@ static char[][] createReplacementArray(Map map) { if (map.isEmpty()) { return EMPTY_REPLACEMENT_ARRAY; } - char max = Collections.max(map.keySet()); + char max = max(map.keySet()); char[][] replacements = new char[max + 1][]; for (Character c : map.keySet()) { replacements[c] = map.get(c).toCharArray(); @@ -81,5 +78,6 @@ static char[][] createReplacementArray(Map map) { } // Immutable empty array for when there are no replacements. + @SuppressWarnings("ConstantCaseForConstants") // An empty array is a constant. private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; } diff --git a/android/guava/src/com/google/common/escape/ArrayBasedUnicodeEscaper.java b/android/guava/src/com/google/common/escape/ArrayBasedUnicodeEscaper.java index 5ea780712a06..020d952b0299 100644 --- a/android/guava/src/com/google/common/escape/ArrayBasedUnicodeEscaper.java +++ b/android/guava/src/com/google/common/escape/ArrayBasedUnicodeEscaper.java @@ -15,12 +15,11 @@ package com.google.common.escape; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.min; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link UnicodeEscaper} that uses an array to quickly look up replacement characters for a given @@ -41,9 +40,8 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("EscapedEntity") // We do mean for the user to see "&" etc. public abstract class ArrayBasedUnicodeEscaper extends UnicodeEscaper { // The replacement array (see ArrayBasedEscaperMap). private final char[][] replacements; @@ -130,10 +128,10 @@ protected ArrayBasedUnicodeEscaper( this.safeMinChar = Character.MAX_VALUE; this.safeMaxChar = 0; } else { - // The safe range is non empty and contains values below the surrogate + // The safe range is non-empty and contains values below the surrogate // range but may extend above it. We may need to clip the maximum value. this.safeMinChar = (char) safeMin; - this.safeMaxChar = (char) Math.min(safeMax, Character.MIN_HIGH_SURROGATE - 1); + this.safeMaxChar = (char) min(safeMax, Character.MIN_HIGH_SURROGATE - 1); } } @@ -163,8 +161,7 @@ public final String escape(String s) { * @return the replacement characters, or {@code null} if no escaping was required */ @Override - @CheckForNull - protected final char[] escape(int cp) { + protected final char @Nullable [] escape(int cp) { if (cp < replacementsLength) { char[] chars = replacements[cp]; if (chars != null) { @@ -204,6 +201,5 @@ protected final int nextEscapeIndex(CharSequence csq, int index, int end) { * @param cp the Unicode code point to escape * @return the replacement characters, or {@code null} if no escaping was required */ - @CheckForNull - protected abstract char[] escapeUnsafe(int cp); + protected abstract char @Nullable [] escapeUnsafe(int cp); } diff --git a/android/guava/src/com/google/common/escape/CharEscaper.java b/android/guava/src/com/google/common/escape/CharEscaper.java index 55090f69804c..0d4d3a196ce5 100644 --- a/android/guava/src/com/google/common/escape/CharEscaper.java +++ b/android/guava/src/com/google/common/escape/CharEscaper.java @@ -16,9 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An object that converts literal text into a format safe for inclusion in a particular context @@ -40,9 +39,8 @@ * @author Sven Mawson * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("EscapedEntity") // We do mean for the user to see "<" etc. public abstract class CharEscaper extends Escaper { /** Constructor for use by subclasses. */ protected CharEscaper() {} @@ -82,8 +80,7 @@ public String escape(String string) { * @param c the character to escape if necessary * @return the replacement characters, or {@code null} if no escaping was needed */ - @CheckForNull - protected abstract char[] escape(char c); + protected abstract char @Nullable [] escape(char c); /** * Returns the escaped form of a given literal string, starting at the given index. This method is diff --git a/android/guava/src/com/google/common/escape/CharEscaperBuilder.java b/android/guava/src/com/google/common/escape/CharEscaperBuilder.java index cbe6958f3b72..3496d91366f2 100644 --- a/android/guava/src/com/google/common/escape/CharEscaperBuilder.java +++ b/android/guava/src/com/google/common/escape/CharEscaperBuilder.java @@ -16,14 +16,12 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Simple helper class to build a "sparse" array of objects based on the indexes that were added to @@ -34,9 +32,7 @@ * @author Sven Mawson * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class CharEscaperBuilder { /** * Simple decorator that turns an array of replacement char[]s into a CharEscaper, this results in @@ -68,8 +64,7 @@ public String escape(String s) { } @Override - @CheckForNull - protected char[] escape(char c) { + protected char @Nullable [] escape(char c) { return c < replaceLength ? replacements[c] : null; } } diff --git a/android/guava/src/com/google/common/escape/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/escape/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 992c9a3e4d84..000000000000 --- a/android/guava/src/com/google/common/escape/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.escape; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/escape/Escaper.java b/android/guava/src/com/google/common/escape/Escaper.java index 31b7df0e64ab..924e3d34c8d0 100644 --- a/android/guava/src/com/google/common/escape/Escaper.java +++ b/android/guava/src/com/google/common/escape/Escaper.java @@ -56,7 +56,7 @@ */ @DoNotMock("Use Escapers.nullEscaper() or another methods from the *Escapers classes") @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("EscapedEntity") // We do mean for the user to see "<" etc. public abstract class Escaper { // TODO(dbeaumont): evaluate custom implementations, considering package private constructor. /** Constructor for use by subclasses. */ diff --git a/android/guava/src/com/google/common/escape/Escapers.java b/android/guava/src/com/google/common/escape/Escapers.java index dd44907f5cf2..c5f2bd026fdb 100644 --- a/android/guava/src/com/google/common/escape/Escapers.java +++ b/android/guava/src/com/google/common/escape/Escapers.java @@ -16,13 +16,11 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.HashMap; import java.util.Map; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@link Escaper} instances. @@ -31,9 +29,7 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Escapers { private Escapers() {} @@ -54,8 +50,7 @@ public String escape(String string) { } @Override - @CheckForNull - protected char[] escape(char c) { + protected char @Nullable [] escape(char c) { // TODO: Fix tests not to call this directly and make it throw an error. return null; } @@ -93,12 +88,11 @@ public static Builder builder() { * @author David Beaumont * @since 15.0 */ - @Beta public static final class Builder { private final Map replacementMap = new HashMap<>(); private char safeMin = Character.MIN_VALUE; private char safeMax = Character.MAX_VALUE; - @CheckForNull private String unsafeReplacement = null; + private @Nullable String unsafeReplacement = null; // The constructor is exposed via the builder() method above. private Builder() {} @@ -154,46 +148,17 @@ public Builder addEscape(char c, String replacement) { /** Returns a new escaper based on the current state of the builder. */ public Escaper build() { return new ArrayBasedCharEscaper(replacementMap, safeMin, safeMax) { - @CheckForNull - private final char[] replacementChars = + private final char @Nullable [] replacementChars = unsafeReplacement != null ? unsafeReplacement.toCharArray() : null; @Override - @CheckForNull - protected char[] escapeUnsafe(char c) { + protected char @Nullable [] escapeUnsafe(char c) { return replacementChars; } }; } } - /** - * Returns a {@link UnicodeEscaper} equivalent to the given escaper instance. If the escaper is - * already a UnicodeEscaper then it is simply returned, otherwise it is wrapped in a - * UnicodeEscaper. - * - *

    When a {@link CharEscaper} escaper is wrapped by this method it acquires extra behavior with - * respect to the well-formedness of Unicode character sequences and will throw {@link - * IllegalArgumentException} when given bad input. - * - * @param escaper the instance to be wrapped - * @return a UnicodeEscaper with the same behavior as the given instance - * @throws NullPointerException if escaper is null - * @throws IllegalArgumentException if escaper is not a UnicodeEscaper or a CharEscaper - */ - static UnicodeEscaper asUnicodeEscaper(Escaper escaper) { - checkNotNull(escaper); - if (escaper instanceof UnicodeEscaper) { - return (UnicodeEscaper) escaper; - } else if (escaper instanceof CharEscaper) { - return wrap((CharEscaper) escaper); - } - // In practice this shouldn't happen because it would be very odd not to - // extend either CharEscaper or UnicodeEscaper for non trivial cases. - throw new IllegalArgumentException( - "Cannot create a UnicodeEscaper from: " + escaper.getClass().getName()); - } - /** * Returns a string that would replace the given character in the specified escaper, or {@code * null} if no replacement should be made. This method is intended for use in tests through the @@ -203,8 +168,7 @@ static UnicodeEscaper asUnicodeEscaper(Escaper escaper) { * @param c the character to escape if necessary * @return the replacement string, or {@code null} if no escaping was needed */ - @CheckForNull - public static String computeReplacement(CharEscaper escaper, char c) { + public static @Nullable String computeReplacement(CharEscaper escaper, char c) { return stringOrNull(escaper.escape(c)); } @@ -217,64 +181,11 @@ public static String computeReplacement(CharEscaper escaper, char c) { * @param cp the Unicode code point to escape if necessary * @return the replacement string, or {@code null} if no escaping was needed */ - @CheckForNull - public static String computeReplacement(UnicodeEscaper escaper, int cp) { + public static @Nullable String computeReplacement(UnicodeEscaper escaper, int cp) { return stringOrNull(escaper.escape(cp)); } - @CheckForNull - private static String stringOrNull(@CheckForNull char[] in) { + private static @Nullable String stringOrNull(char @Nullable [] in) { return (in == null) ? null : new String(in); } - - /** Private helper to wrap a CharEscaper as a UnicodeEscaper. */ - private static UnicodeEscaper wrap(CharEscaper escaper) { - return new UnicodeEscaper() { - @Override - @CheckForNull - protected char[] escape(int cp) { - // If a code point maps to a single character, just escape that. - if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - return escaper.escape((char) cp); - } - // Convert the code point to a surrogate pair and escape them both. - // Note: This code path is horribly slow and typically allocates 4 new - // char[] each time it is invoked. However this avoids any - // synchronization issues and makes the escaper thread safe. - char[] surrogateChars = new char[2]; - Character.toChars(cp, surrogateChars, 0); - char[] hiChars = escaper.escape(surrogateChars[0]); - char[] loChars = escaper.escape(surrogateChars[1]); - - // If either hiChars or lowChars are non-null, the CharEscaper is trying - // to escape the characters of a surrogate pair separately. This is - // uncommon and applies only to escapers that assume UCS-2 rather than - // UTF-16. See: http://en.wikipedia.org/wiki/UTF-16/UCS-2 - if (hiChars == null && loChars == null) { - // We expect this to be the common code path for most escapers. - return null; - } - // Combine the characters and/or escaped sequences into a single array. - int hiCount = hiChars != null ? hiChars.length : 1; - int loCount = loChars != null ? loChars.length : 1; - char[] output = new char[hiCount + loCount]; - if (hiChars != null) { - // TODO: Is this faster than System.arraycopy() for small arrays? - for (int n = 0; n < hiChars.length; ++n) { - output[n] = hiChars[n]; - } - } else { - output[0] = surrogateChars[0]; - } - if (loChars != null) { - for (int n = 0; n < loChars.length; ++n) { - output[hiCount + n] = loChars[n]; - } - } else { - output[hiCount] = surrogateChars[1]; - } - return output; - } - }; - } } diff --git a/android/guava/src/com/google/common/escape/ParametricNullness.java b/android/guava/src/com/google/common/escape/ParametricNullness.java index d9412065ffd9..3ddd153ba04f 100644 --- a/android/guava/src/com/google/common/escape/ParametricNullness.java +++ b/android/guava/src/com/google/common/escape/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/escape/Platform.java b/android/guava/src/com/google/common/escape/Platform.java index dc6610c041bb..3480ee215e91 100644 --- a/android/guava/src/com/google/common/escape/Platform.java +++ b/android/guava/src/com/google/common/escape/Platform.java @@ -14,6 +14,8 @@ package com.google.common.escape; +import static java.util.Objects.requireNonNull; + import com.google.common.annotations.GwtCompatible; /** @@ -22,13 +24,13 @@ * @author Jesse Wilson */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class Platform { private Platform() {} /** Returns a thread-local 1024-char array. */ static char[] charBufferFromThreadLocal() { - return DEST_TL.get(); + // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get + return requireNonNull(DEST_TL.get()); } /** diff --git a/android/guava/src/com/google/common/escape/UnicodeEscaper.java b/android/guava/src/com/google/common/escape/UnicodeEscaper.java index c10ae34cd348..caad7da1fdc1 100644 --- a/android/guava/src/com/google/common/escape/UnicodeEscaper.java +++ b/android/guava/src/com/google/common/escape/UnicodeEscaper.java @@ -16,9 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An {@link Escaper} that converts literal text into a format safe for inclusion in a particular @@ -50,9 +49,8 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("EscapedEntity") // We do mean for the user to see "<" etc. public abstract class UnicodeEscaper extends Escaper { /** The amount of padding (chars) to use when growing the escape buffer. */ private static final int DEST_PAD = 32; @@ -79,8 +77,7 @@ protected UnicodeEscaper() {} * @param cp the Unicode code point to escape if necessary * @return the replacement characters, or {@code null} if no escaping was needed */ - @CheckForNull - protected abstract char[] escape(int cp); + protected abstract char @Nullable [] escape(int cp); /** * Returns the escaped form of a given literal string. diff --git a/android/guava/src/com/google/common/escape/package-info.java b/android/guava/src/com/google/common/escape/package-info.java index 8cd29e6f85fb..173f811a3aae 100644 --- a/android/guava/src/com/google/common/escape/package-info.java +++ b/android/guava/src/com/google/common/escape/package-info.java @@ -14,19 +14,19 @@ /** * Interfaces, utilities, and simple implementations of escapers and encoders. The primary type is - * {@link com.google.common.escape.Escaper}. + * {@link Escaper}. * *

    Additional escapers implementations are found in the applicable packages: {@link * com.google.common.html.HtmlEscapers} in {@code com.google.common.html}, {@link * com.google.common.xml.XmlEscapers} in {@code com.google.common.xml}, and {@link * com.google.common.net.UrlEscapers} in {@code com.google.common.net}. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.escape; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java b/android/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java index 28bc4b23e0c3..652e5e50bc6a 100644 --- a/android/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java +++ b/android/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java @@ -30,5 +30,4 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@ElementTypesAreNonnullByDefault public @interface AllowConcurrentEvents {} diff --git a/android/guava/src/com/google/common/eventbus/AsyncEventBus.java b/android/guava/src/com/google/common/eventbus/AsyncEventBus.java index 4f387a712841..a6dac17f2289 100644 --- a/android/guava/src/com/google/common/eventbus/AsyncEventBus.java +++ b/android/guava/src/com/google/common/eventbus/AsyncEventBus.java @@ -23,7 +23,6 @@ * @author Cliff Biffle * @since 10.0 */ -@ElementTypesAreNonnullByDefault public class AsyncEventBus extends EventBus { /** diff --git a/android/guava/src/com/google/common/eventbus/DeadEvent.java b/android/guava/src/com/google/common/eventbus/DeadEvent.java index 2cdb23f712d7..90910b9b0805 100644 --- a/android/guava/src/com/google/common/eventbus/DeadEvent.java +++ b/android/guava/src/com/google/common/eventbus/DeadEvent.java @@ -27,7 +27,6 @@ * @author Cliff Biffle * @since 10.0 */ -@ElementTypesAreNonnullByDefault public class DeadEvent { private final Object source; diff --git a/android/guava/src/com/google/common/eventbus/Dispatcher.java b/android/guava/src/com/google/common/eventbus/Dispatcher.java index ff1ae2a197a4..c941a04a11a6 100644 --- a/android/guava/src/com/google/common/eventbus/Dispatcher.java +++ b/android/guava/src/com/google/common/eventbus/Dispatcher.java @@ -15,6 +15,7 @@ package com.google.common.eventbus; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import com.google.common.collect.Queues; import java.util.Iterator; @@ -31,7 +32,6 @@ * * @author Colin Decker */ -@ElementTypesAreNonnullByDefault abstract class Dispatcher { /** @@ -97,7 +97,8 @@ protected Boolean initialValue() { void dispatch(Object event, Iterator subscribers) { checkNotNull(event); checkNotNull(subscribers); - Queue queueForThread = queue.get(); + // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get + Queue queueForThread = requireNonNull(queue.get()); queueForThread.offer(new Event(event, subscribers)); if (!dispatching.get()) { @@ -133,7 +134,7 @@ private static final class LegacyAsyncDispatcher extends Dispatcher { // This dispatcher matches the original dispatch behavior of AsyncEventBus. // // We can't really make any guarantees about the overall dispatch order for this dispatcher in - // a multithreaded environment for a couple reasons: + // a multithreaded environment for a couple of reasons: // // 1. Subscribers to events posted on different threads can be interleaved with each other // freely. (A event on one thread, B event on another could yield any of diff --git a/android/guava/src/com/google/common/eventbus/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/eventbus/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index e8542bba63ec..000000000000 --- a/android/guava/src/com/google/common/eventbus/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.eventbus; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/eventbus/EventBus.java b/android/guava/src/com/google/common/eventbus/EventBus.java index 4afc19775627..6adf3c70f833 100644 --- a/android/guava/src/com/google/common/eventbus/EventBus.java +++ b/android/guava/src/com/google/common/eventbus/EventBus.java @@ -15,9 +15,9 @@ package com.google.common.eventbus; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import com.google.common.base.MoreObjects; -import com.google.common.util.concurrent.MoreExecutors; import java.lang.reflect.Method; import java.util.Iterator; import java.util.Locale; @@ -147,7 +147,6 @@ * @author Cliff Biffle * @since 10.0 */ -@ElementTypesAreNonnullByDefault public class EventBus { private static final Logger logger = Logger.getLogger(EventBus.class.getName()); @@ -172,10 +171,7 @@ public EventBus() { */ public EventBus(String identifier) { this( - identifier, - MoreExecutors.directExecutor(), - Dispatcher.perThreadDispatchQueue(), - LoggingHandler.INSTANCE); + identifier, directExecutor(), Dispatcher.perThreadDispatchQueue(), LoggingHandler.INSTANCE); } /** @@ -185,11 +181,7 @@ public EventBus(String identifier) { * @since 16.0 */ public EventBus(SubscriberExceptionHandler exceptionHandler) { - this( - "default", - MoreExecutors.directExecutor(), - Dispatcher.perThreadDispatchQueue(), - exceptionHandler); + this("default", directExecutor(), Dispatcher.perThreadDispatchQueue(), exceptionHandler); } EventBus( diff --git a/android/guava/src/com/google/common/eventbus/ParametricNullness.java b/android/guava/src/com/google/common/eventbus/ParametricNullness.java index ac91392f75eb..06ab743cb700 100644 --- a/android/guava/src/com/google/common/eventbus/ParametricNullness.java +++ b/android/guava/src/com/google/common/eventbus/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/eventbus/Subscribe.java b/android/guava/src/com/google/common/eventbus/Subscribe.java index 88477f1bae75..0449efe2e113 100644 --- a/android/guava/src/com/google/common/eventbus/Subscribe.java +++ b/android/guava/src/com/google/common/eventbus/Subscribe.java @@ -35,5 +35,4 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) -@ElementTypesAreNonnullByDefault public @interface Subscribe {} diff --git a/android/guava/src/com/google/common/eventbus/Subscriber.java b/android/guava/src/com/google/common/eventbus/Subscriber.java index 71ee197c9b25..bd87333de9d8 100644 --- a/android/guava/src/com/google/common/eventbus/Subscriber.java +++ b/android/guava/src/com/google/common/eventbus/Subscriber.java @@ -21,7 +21,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.Executor; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A subscriber method on a specific object, plus the executor that should be used for dispatching @@ -32,7 +32,6 @@ * * @author Colin Decker */ -@ElementTypesAreNonnullByDefault class Subscriber { /** Creates a {@code Subscriber} for {@code method} on {@code listener}. */ @@ -106,7 +105,7 @@ public final int hashCode() { } @Override - public final boolean equals(@CheckForNull Object obj) { + public final boolean equals(@Nullable Object obj) { if (obj instanceof Subscriber) { Subscriber that = (Subscriber) obj; // Use == so that different equal instances will still receive events. diff --git a/android/guava/src/com/google/common/eventbus/SubscriberExceptionContext.java b/android/guava/src/com/google/common/eventbus/SubscriberExceptionContext.java index 63c7d557fec9..f6beaad511d7 100644 --- a/android/guava/src/com/google/common/eventbus/SubscriberExceptionContext.java +++ b/android/guava/src/com/google/common/eventbus/SubscriberExceptionContext.java @@ -23,7 +23,6 @@ * * @since 16.0 */ -@ElementTypesAreNonnullByDefault public class SubscriberExceptionContext { private final EventBus eventBus; private final Object event; @@ -53,17 +52,23 @@ public EventBus getEventBus() { return eventBus; } - /** @return The event object that caused the subscriber to throw. */ + /** + * @return The event object that caused the subscriber to throw. + */ public Object getEvent() { return event; } - /** @return The object context that the subscriber was called on. */ + /** + * @return The object context that the subscriber was called on. + */ public Object getSubscriber() { return subscriber; } - /** @return The subscribed method that threw the exception. */ + /** + * @return The subscribed method that threw the exception. + */ public Method getSubscriberMethod() { return subscriberMethod; } diff --git a/android/guava/src/com/google/common/eventbus/SubscriberExceptionHandler.java b/android/guava/src/com/google/common/eventbus/SubscriberExceptionHandler.java index 1c2fbb109adb..c239ad74fc87 100644 --- a/android/guava/src/com/google/common/eventbus/SubscriberExceptionHandler.java +++ b/android/guava/src/com/google/common/eventbus/SubscriberExceptionHandler.java @@ -14,13 +14,11 @@ package com.google.common.eventbus; - /** * Handler for exceptions thrown by event subscribers. * * @since 16.0 */ -@ElementTypesAreNonnullByDefault public interface SubscriberExceptionHandler { /** Handles exceptions thrown by subscribers. */ void handleException(Throwable exception, SubscriberExceptionContext context); diff --git a/android/guava/src/com/google/common/eventbus/SubscriberRegistry.java b/android/guava/src/com/google/common/eventbus/SubscriberRegistry.java index 46e982016c55..94bf471053f5 100644 --- a/android/guava/src/com/google/common/eventbus/SubscriberRegistry.java +++ b/android/guava/src/com/google/common/eventbus/SubscriberRegistry.java @@ -16,12 +16,10 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Throwables.throwIfUnchecked; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; -import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -46,14 +44,13 @@ import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Registry of subscribers to a single event bus. * * @author Colin Decker */ -@ElementTypesAreNonnullByDefault final class SubscriberRegistry { /** @@ -150,13 +147,7 @@ Iterator getSubscribers(Object event) { private static final LoadingCache, ImmutableList> subscriberMethodsCache = CacheBuilder.newBuilder() .weakKeys() - .build( - new CacheLoader, ImmutableList>() { - @Override - public ImmutableList load(Class concreteClass) throws Exception { - return getAnnotatedMethodsNotCached(concreteClass); - } - }); + .build(CacheLoader.from(SubscriberRegistry::getAnnotatedMethodsNotCached)); /** * Returns all subscribers for the given listener grouped by the type of event they subscribe to. @@ -176,7 +167,24 @@ private static ImmutableList getAnnotatedMethods(Class clazz) { try { return subscriberMethodsCache.getUnchecked(clazz); } catch (UncheckedExecutionException e) { - throwIfUnchecked(e.getCause()); + if (e.getCause() instanceof IllegalArgumentException) { + /* + * IllegalArgumentException is the one unchecked exception that we know is likely to happen + * (thanks to the checkArgument calls in getAnnotatedMethodsNotCached). If it happens, we'd + * prefer to propagate an IllegalArgumentException to the caller. However, we don't want to + * simply rethrow an exception (e.getCause()) that may in rare cases have come from another + * thread. To accomplish both goals, we wrap that IllegalArgumentException in a new + * instance. + */ + throw new IllegalArgumentException(e.getCause().getMessage(), e.getCause()); + } + /* + * If some other exception happened, we just propagate the wrapper + * UncheckedExecutionException, which has the stack trace from this thread and which has its + * cause set to the underlying exception (which may be from another thread). If we someday + * learn that some other exception besides IllegalArgumentException is common, then we could + * add another special case to throw an instance of it, too. + */ throw e; } } @@ -220,15 +228,9 @@ private static ImmutableList getAnnotatedMethodsNotCached(Class clazz CacheBuilder.newBuilder() .weakKeys() .build( - new CacheLoader, ImmutableSet>>() { - // > is actually needed to compile - @SuppressWarnings("RedundantTypeArguments") - @Override - public ImmutableSet> load(Class concreteClass) { - return ImmutableSet.>copyOf( - TypeToken.of(concreteClass).getTypes().rawTypes()); - } - }); + CacheLoader.from( + concreteClass -> + ImmutableSet.copyOf(TypeToken.of(concreteClass).getTypes().rawTypes()))); /** * Flattens a class's type hierarchy into a set of {@code Class} objects including all @@ -236,11 +238,7 @@ public ImmutableSet> load(Class concreteClass) { */ @VisibleForTesting static ImmutableSet> flattenHierarchy(Class concreteClass) { - try { - return flattenHierarchyCache.getUnchecked(concreteClass); - } catch (UncheckedExecutionException e) { - throw Throwables.propagate(e.getCause()); - } + return flattenHierarchyCache.getUnchecked(concreteClass); } private static final class MethodIdentifier { @@ -259,7 +257,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof MethodIdentifier) { MethodIdentifier ident = (MethodIdentifier) o; return name.equals(ident.name) && parameterTypes.equals(ident.parameterTypes); diff --git a/android/guava/src/com/google/common/eventbus/package-info.java b/android/guava/src/com/google/common/eventbus/package-info.java index fa7faa4ab4b0..2b467c08e805 100644 --- a/android/guava/src/com/google/common/eventbus/package-info.java +++ b/android/guava/src/com/google/common/eventbus/package-info.java @@ -13,245 +13,15 @@ */ /** - * The EventBus allows publish-subscribe-style communication between components without requiring - * the components to explicitly register with one another (and thus be aware of each other). It is - * designed exclusively to replace traditional Java in-process event distribution using explicit - * registration. It is not a general-purpose publish-subscribe system, nor is it intended - * for interprocess communication. + * {@linkplain EventBus Discouraged} in favor of dependency injection and concurrency frameworks, + * EventBus allows publish-subscribe-style communication. * *

    See the Guava User Guide article on {@code EventBus}. - * - *

    One-Minute Guide

    - * - *

    Converting an existing EventListener-based system to use the EventBus is easy. - * - *

    For Listeners

    - * - *

    To listen for a specific flavor of event (say, a CustomerChangeEvent)... - * - *

      - *
    • ...in traditional Java events: implement an interface defined with the - * event — such as CustomerChangeEventListener. - *
    • ...with EventBus: create a method that accepts CustomerChangeEvent as its - * sole argument, and mark it with the {@link com.google.common.eventbus.Subscribe} - * annotation. - *
    - * - *

    To register your listener methods with the event producers... - * - *

      - *
    • ...in traditional Java events: pass your object to each producer's {@code - * registerCustomerChangeEventListener} method. These methods are rarely defined in common - * interfaces, so in addition to knowing every possible producer, you must also know its type. - *
    • ...with EventBus: pass your object to the {@link - * com.google.common.eventbus.EventBus#register(Object)} method on an EventBus. You'll need to - * make sure that your object shares an EventBus instance with the event producers. - *
    - * - *

    To listen for a common event supertype (such as EventObject or Object)... - * - *

      - *
    • ...in traditional Java events: not easy. - *
    • ...with EventBus: events are automatically dispatched to listeners of any - * supertype, allowing listeners for interface types or "wildcard listeners" for Object. - *
    - * - *

    To listen for and detect events that were dispatched without listeners... - * - *

      - *
    • ...in traditional Java events: add code to each event-dispatching method - * (perhaps using AOP). - *
    • ...with EventBus: subscribe to {@link - * com.google.common.eventbus.DeadEvent}. The EventBus will notify you of any events that were - * posted but not delivered. (Handy for debugging.) - *
    - * - *

    For Producers

    - * - *

    To keep track of listeners to your events... - * - *

      - *
    • ...in traditional Java events: write code to manage a list of listeners to - * your object, including synchronization, or use a utility class like EventListenerList. - *
    • ...with EventBus: EventBus does this for you. - *
    - * - *

    To dispatch an event to listeners... - * - *

      - *
    • ...in traditional Java events: write a method to dispatch events to each - * event listener, including error isolation and (if desired) asynchronicity. - *
    • ...with EventBus: pass the event object to an EventBus's {@link - * com.google.common.eventbus.EventBus#post(Object)} method. - *
    - * - *

    Glossary

    - * - *

    The EventBus system and code use the following terms to discuss event distribution: - * - *

    - *
    Event - *
    Any object that may be posted to a bus. - *
    Subscribing - *
    The act of registering a listener with an EventBus, so that its subscriber - * methods will receive events. - *
    Listener - *
    An object that wishes to receive events, by exposing subscriber methods. - *
    Subscriber method - *
    A public method that the EventBus should use to deliver posted events. Subscriber - * methods are marked by the {@link com.google.common.eventbus.Subscribe} annotation. - *
    Posting an event - *
    Making the event available to any listeners through the EventBus. - *
    - * - *

    FAQ

    - * - *

    Why must I create my own Event Bus, rather than using a singleton?

    - * - *

    The Event Bus doesn't specify how you use it; there's nothing stopping your application from - * having separate EventBus instances for each component, or using separate instances to separate - * events by context or topic. This also makes it trivial to set up and tear down EventBus objects - * in your tests. - * - *

    Of course, if you'd like to have a process-wide EventBus singleton, there's nothing stopping - * you from doing it that way. Simply have your container (such as Guice) create the EventBus as a - * singleton at global scope (or stash it in a static field, if you're into that sort of thing). - * - *

    In short, the EventBus is not a singleton because we'd rather not make that decision for you. - * Use it how you like. - * - *

    Why use an annotation to mark subscriber methods, rather than requiring the listener to - * implement an interface?

    - * - *

    We feel that the Event Bus's {@code @Subscribe} annotation conveys your intentions just as - * explicitly as implementing an interface (or perhaps more so), while leaving you free to place - * event subscriber methods wherever you wish and give them intention-revealing names. - * - *

    Traditional Java Events use a listener interface which typically sports only a handful of - * methods -- typically one. This has a number of disadvantages: - * - *

      - *
    • Any one class can only implement a single response to a given event. - *
    • Listener interface methods may conflict. - *
    • The method must be named after the event (e.g. {@code handleChangeEvent}), rather than its - * purpose (e.g. {@code recordChangeInJournal}). - *
    • Each event usually has its own interface, without a common parent interface for a family of - * events (e.g. all UI events). - *
    - * - *

    The difficulties in implementing this cleanly has given rise to a pattern, particularly common - * in Swing apps, of using tiny anonymous classes to implement event listener interfaces. - * - *

    Compare these two cases: - * - *

    {@code
    - * class ChangeRecorder {
    - *   void setCustomer(Customer cust) {
    - *     cust.addChangeListener(new ChangeListener() {
    - *       void customerChanged(ChangeEvent e) {
    - *         recordChange(e.getChange());
    - *       }
    - *     };
    - *   }
    - * }
    - *
    - * // Class is typically registered by the container.
    - * class EventBusChangeRecorder {
    - *  }{@code @Subscribe void recordCustomerChange(ChangeEvent e) {
    - *     recordChange(e.getChange());
    - *   }
    - * }
    - * }
    - * - *

    The intent is actually clearer in the second case: there's less noise code, and the event - * subscriber has a clear and meaningful name. - * - *

    What about a generic {@code Subscriber} interface?

    - * - *

    Some have proposed a generic {@code Subscriber} interface for EventBus listeners. This runs - * into issues with Java's use of type erasure, not to mention problems in usability. - * - *

    Let's say the interface looked something like the following: - * - *

    {@code
    - * interface Subscriber {
    - *   void handleEvent(T event);
    - * }
    - * }
    - * - *

    Due to erasure, no single class can implement a generic interface more than once with - * different type parameters. This is a giant step backwards from traditional Java Events, where - * even if {@code actionPerformed} and {@code keyPressed} aren't very meaningful names, at least you - * can implement both methods! - * - *

    Doesn't EventBus destroy static typing and eliminate automated refactoring support?

    - * - *

    Some have freaked out about EventBus's {@code register(Object)} and {@code post(Object)} - * methods' use of the {@code Object} type. - * - *

    {@code Object} is used here for a good reason: the Event Bus library places no restrictions on - * the types of either your event listeners (as in {@code register(Object)}) or the events - * themselves (in {@code post(Object)}). - * - *

    Event subscriber methods, on the other hand, must explicitly declare their argument type -- - * the type of event desired (or one of its supertypes). Thus, searching for references to an event - * class will instantly find all subscriber methods for that event, and renaming the type will - * affect all subscriber methods within view of your IDE (and any code that creates the event). - * - *

    It's true that you can rename your {@code @Subscribed} event subscriber methods at will; Event - * Bus will not stop this or do anything to propagate the rename because, to Event Bus, the names of - * your subscriber methods are irrelevant. Test code that calls the methods directly, of course, - * will be affected by your renaming -- but that's what your refactoring tools are for. - * - *

    What happens if I {@code register} a listener without any subscriber methods?

    - * - *

    Nothing at all. - * - *

    The Event Bus was designed to integrate with containers and module systems, with Guice as the - * prototypical example. In these cases, it's convenient to have the container/factory/environment - * pass every created object to an EventBus's {@code register(Object)} method. - * - *

    This way, any object created by the container/factory/environment can hook into the system's - * event model simply by exposing subscriber methods. - * - *

    What Event Bus problems can be detected at compile time?

    - * - *

    Any problem that can be unambiguously detected by Java's type system. For example, defining a - * subscriber method for a nonexistent event type. - * - *

    What Event Bus problems can be detected immediately at registration?

    - * - *

    Immediately upon invoking {@code register(Object)}, the listener being registered is checked - * for the well-formedness of its subscriber methods. Specifically, any methods marked with - * {@code @Subscribe} must take only a single argument. - * - *

    Any violations of this rule will cause an {@code IllegalArgumentException} to be thrown. - * - *

    (This check could be moved to compile-time using APT, a solution we're researching.) - * - *

    What Event Bus problems may only be detected later, at runtime?

    - * - *

    If a component posts events with no registered listeners, it may indicate an error - * (typically an indication that you missed a {@code @Subscribe} annotation, or that the listening - * component is not loaded). - * - *

    (Note that this is not necessarily indicative of a problem. There are many cases where - * an application will deliberately ignore a posted event, particularly if the event is coming from - * code you don't control.) - * - *

    To handle such events, register a subscriber method for the {@code DeadEvent} class. Whenever - * EventBus receives an event with no registered subscribers, it will turn it into a {@code - * DeadEvent} and pass it your way -- allowing you to log it or otherwise recover. - * - *

    How do I test event listeners and their subscriber methods?

    - * - *

    Because subscriber methods on your listener classes are normal methods, you can simply call - * them from your test code to simulate the EventBus. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.eventbus; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/graph/AbstractBaseGraph.java b/android/guava/src/com/google/common/graph/AbstractBaseGraph.java index 797468b7aa92..9ce3838947b8 100644 --- a/android/guava/src/com/google/common/graph/AbstractBaseGraph.java +++ b/android/guava/src/com/google/common/graph/AbstractBaseGraph.java @@ -20,6 +20,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; +import static com.google.common.graph.GraphConstants.NODE_PAIR_REMOVED_FROM_GRAPH; +import static com.google.common.graph.GraphConstants.NODE_REMOVED_FROM_GRAPH; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; @@ -29,7 +31,7 @@ import com.google.common.primitives.Ints; import java.util.AbstractSet; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of {@link BaseGraph}. @@ -40,13 +42,12 @@ * @author James Sexton * @param Node parameter type */ -@ElementTypesAreNonnullByDefault abstract class AbstractBaseGraph implements BaseGraph { /** - * Returns the number of edges in this graph; used to calculate the size of {@link #edges()}. This - * implementation requires O(|N|) time. Classes extending this one may manually keep track of the - * number of edges as the graph is updated, and override this method for better performance. + * Returns the number of edges in this graph; used to calculate the size of {@link Graph#edges()}. + * This implementation requires O(|N|) time. Classes extending this one may manually keep track of + * the number of edges as the graph is updated, and override this method for better performance. */ protected long edgeCount() { long degreeSum = 0L; @@ -59,8 +60,8 @@ protected long edgeCount() { } /** - * An implementation of {@link BaseGraph#edges()} defined in terms of {@link #nodes()} and {@link - * #successors(Object)}. + * An implementation of {@link BaseGraph#edges()} defined in terms of {@link Graph#nodes()} and + * {@link #successors(Object)}. */ @Override public Set> edges() { @@ -76,7 +77,7 @@ public int size() { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { throw new UnsupportedOperationException(); } @@ -85,7 +86,7 @@ public boolean remove(@CheckForNull Object o) { // Graph. @SuppressWarnings("unchecked") @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (!(obj instanceof EndpointPair)) { return false; } @@ -106,27 +107,30 @@ public ElementOrder incidentEdgeOrder() { public Set> incidentEdges(N node) { checkNotNull(node); checkArgument(nodes().contains(node), "Node %s is not an element of this graph.", node); - return new IncidentEdgeSet(this, node) { - @Override - public UnmodifiableIterator> iterator() { - if (graph.isDirected()) { - return Iterators.unmodifiableIterator( - Iterators.concat( - Iterators.transform( - graph.predecessors(node).iterator(), - (N predecessor) -> EndpointPair.ordered(predecessor, node)), + IncidentEdgeSet incident = + new IncidentEdgeSet(this, node) { + @Override + public UnmodifiableIterator> iterator() { + if (graph.isDirected()) { + return Iterators.unmodifiableIterator( + Iterators.concat( + Iterators.transform( + graph.predecessors(node).iterator(), + (N predecessor) -> EndpointPair.ordered(predecessor, node)), + Iterators.transform( + // filter out 'node' from successors (already covered by predecessors, + // above) + Sets.difference(graph.successors(node), ImmutableSet.of(node)).iterator(), + (N successor) -> EndpointPair.ordered(node, successor)))); + } else { + return Iterators.unmodifiableIterator( Iterators.transform( - // filter out 'node' from successors (already covered by predecessors, above) - Sets.difference(graph.successors(node), ImmutableSet.of(node)).iterator(), - (N successor) -> EndpointPair.ordered(node, successor)))); - } else { - return Iterators.unmodifiableIterator( - Iterators.transform( - graph.adjacentNodes(node).iterator(), - (N adjacentNode) -> EndpointPair.unordered(node, adjacentNode))); - } - } - }; + graph.adjacentNodes(node).iterator(), + (N adjacentNode) -> EndpointPair.unordered(node, adjacentNode))); + } + } + }; + return nodeInvalidatableSet(incident, node); } @Override @@ -177,7 +181,23 @@ protected final void validateEndpoints(EndpointPair endpoints) { checkArgument(isOrderingCompatible(endpoints), ENDPOINTS_MISMATCH); } + /** + * Returns {@code true} iff {@code endpoints}' ordering is compatible with the directionality of + * this graph. + */ protected final boolean isOrderingCompatible(EndpointPair endpoints) { - return endpoints.isOrdered() || !this.isDirected(); + return endpoints.isOrdered() == this.isDirected(); + } + + protected final Set nodeInvalidatableSet(Set set, N node) { + return InvalidatableSet.of( + set, () -> nodes().contains(node), () -> String.format(NODE_REMOVED_FROM_GRAPH, node)); + } + + protected final Set nodePairInvalidatableSet(Set set, N nodeU, N nodeV) { + return InvalidatableSet.of( + set, + () -> nodes().contains(nodeU) && nodes().contains(nodeV), + () -> String.format(NODE_PAIR_REMOVED_FROM_GRAPH, nodeU, nodeV)); } } diff --git a/android/guava/src/com/google/common/graph/AbstractDirectedNetworkConnections.java b/android/guava/src/com/google/common/graph/AbstractDirectedNetworkConnections.java index ac452d4d610a..92b203872e5f 100644 --- a/android/guava/src/com/google/common/graph/AbstractDirectedNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/AbstractDirectedNetworkConnections.java @@ -31,7 +31,7 @@ import java.util.Collections; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A base implementation of {@link NetworkConnections} for directed networks. @@ -40,7 +40,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault abstract class AbstractDirectedNetworkConnections implements NetworkConnections { /** Keys are edges incoming to the origin node, values are the source node. */ final Map inEdgeMap; @@ -80,7 +79,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return inEdgeMap.containsKey(obj) || outEdgeMap.containsKey(obj); } }; diff --git a/android/guava/src/com/google/common/graph/AbstractGraph.java b/android/guava/src/com/google/common/graph/AbstractGraph.java index 9402be5d9360..968c627bc5cc 100644 --- a/android/guava/src/com/google/common/graph/AbstractGraph.java +++ b/android/guava/src/com/google/common/graph/AbstractGraph.java @@ -17,7 +17,7 @@ package com.google.common.graph; import com.google.common.annotations.Beta; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of {@link Graph}. It is recommended to extend this @@ -28,11 +28,12 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public abstract class AbstractGraph extends AbstractBaseGraph implements Graph { + /** Constructor for use by subclasses. */ + public AbstractGraph() {} @Override - public final boolean equals(@CheckForNull Object obj) { + public final boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/android/guava/src/com/google/common/graph/AbstractGraphBuilder.java b/android/guava/src/com/google/common/graph/AbstractGraphBuilder.java index a4e1543f1d63..84e461a46c37 100644 --- a/android/guava/src/com/google/common/graph/AbstractGraphBuilder.java +++ b/android/guava/src/com/google/common/graph/AbstractGraphBuilder.java @@ -23,7 +23,6 @@ * * @author James Sexton */ -@ElementTypesAreNonnullByDefault abstract class AbstractGraphBuilder { final boolean directed; boolean allowsSelfLoops = false; diff --git a/android/guava/src/com/google/common/graph/AbstractNetwork.java b/android/guava/src/com/google/common/graph/AbstractNetwork.java index 9afbb0377b81..65eb8aeed842 100644 --- a/android/guava/src/com/google/common/graph/AbstractNetwork.java +++ b/android/guava/src/com/google/common/graph/AbstractNetwork.java @@ -18,12 +18,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.graph.GraphConstants.EDGE_REMOVED_FROM_GRAPH; import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; import static com.google.common.graph.GraphConstants.MULTIPLE_EDGES_CONNECTING; +import static com.google.common.graph.GraphConstants.NODE_PAIR_REMOVED_FROM_GRAPH; +import static com.google.common.graph.GraphConstants.NODE_REMOVED_FROM_GRAPH; import static java.util.Collections.unmodifiableSet; import com.google.common.annotations.Beta; -import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; @@ -34,7 +36,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of {@link Network}. It is recommended to extend @@ -49,8 +51,9 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public abstract class AbstractNetwork implements Network { + /** Constructor for use by subclasses. */ + public AbstractNetwork() {} @Override public Graph asGraph() { @@ -71,13 +74,7 @@ public Set> edges() { @Override public Iterator> iterator() { return Iterators.transform( - AbstractNetwork.this.edges().iterator(), - new Function>() { - @Override - public EndpointPair apply(E edge) { - return incidentNodes(edge); - } - }); + AbstractNetwork.this.edges().iterator(), edge -> incidentNodes(edge)); } @Override @@ -90,7 +87,7 @@ public int size() { // Network. @SuppressWarnings("unchecked") @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (!(obj instanceof EndpointPair)) { return false; } @@ -167,16 +164,20 @@ public Set adjacentEdges(E edge) { EndpointPair endpointPair = incidentNodes(edge); // Verifies that edge is in this network. Set endpointPairIncidentEdges = Sets.union(incidentEdges(endpointPair.nodeU()), incidentEdges(endpointPair.nodeV())); - return Sets.difference(endpointPairIncidentEdges, ImmutableSet.of(edge)); + return edgeInvalidatableSet( + Sets.difference(endpointPairIncidentEdges, ImmutableSet.of(edge)), edge); } @Override public Set edgesConnecting(N nodeU, N nodeV) { Set outEdgesU = outEdges(nodeU); Set inEdgesV = inEdges(nodeV); - return outEdgesU.size() <= inEdgesV.size() - ? unmodifiableSet(Sets.filter(outEdgesU, connectedPredicate(nodeU, nodeV))) - : unmodifiableSet(Sets.filter(inEdgesV, connectedPredicate(nodeV, nodeU))); + return nodePairInvalidatableSet( + outEdgesU.size() <= inEdgesV.size() + ? unmodifiableSet(Sets.filter(outEdgesU, connectedPredicate(nodeU, nodeV))) + : unmodifiableSet(Sets.filter(inEdgesV, connectedPredicate(nodeV, nodeU))), + nodeU, + nodeV); } @Override @@ -185,18 +186,12 @@ public Set edgesConnecting(EndpointPair endpoints) { return edgesConnecting(endpoints.nodeU(), endpoints.nodeV()); } - private Predicate connectedPredicate(final N nodePresent, final N nodeToCheck) { - return new Predicate() { - @Override - public boolean apply(E edge) { - return incidentNodes(edge).adjacentNode(nodePresent).equals(nodeToCheck); - } - }; + private Predicate connectedPredicate(N nodePresent, N nodeToCheck) { + return edge -> incidentNodes(edge).adjacentNode(nodePresent).equals(nodeToCheck); } @Override - @CheckForNull - public E edgeConnectingOrNull(N nodeU, N nodeV) { + public @Nullable E edgeConnectingOrNull(N nodeU, N nodeV) { Set edgesConnecting = edgesConnecting(nodeU, nodeV); switch (edgesConnecting.size()) { case 0: @@ -209,8 +204,7 @@ public E edgeConnectingOrNull(N nodeU, N nodeV) { } @Override - @CheckForNull - public E edgeConnectingOrNull(EndpointPair endpoints) { + public @Nullable E edgeConnectingOrNull(EndpointPair endpoints) { validateEndpoints(endpoints); return edgeConnectingOrNull(endpoints.nodeU(), endpoints.nodeV()); } @@ -241,11 +235,11 @@ protected final void validateEndpoints(EndpointPair endpoints) { } protected final boolean isOrderingCompatible(EndpointPair endpoints) { - return endpoints.isOrdered() || !this.isDirected(); + return endpoints.isOrdered() == this.isDirected(); } @Override - public final boolean equals(@CheckForNull Object obj) { + public final boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -279,14 +273,42 @@ public String toString() { + edgeIncidentNodesMap(this); } - private static Map> edgeIncidentNodesMap(final Network network) { - Function> edgeToIncidentNodesFn = - new Function>() { - @Override - public EndpointPair apply(E edge) { - return network.incidentNodes(edge); - } - }; - return Maps.asMap(network.edges(), edgeToIncidentNodesFn); + /** + * Returns a {@link Set} whose methods throw {@link IllegalStateException} when the given edge is + * not present in this network. + * + * @since 33.1.0 + */ + protected final Set edgeInvalidatableSet(Set set, E edge) { + return InvalidatableSet.of( + set, () -> edges().contains(edge), () -> String.format(EDGE_REMOVED_FROM_GRAPH, edge)); + } + + /** + * Returns a {@link Set} whose methods throw {@link IllegalStateException} when the given node is + * not present in this network. + * + * @since 33.1.0 + */ + protected final Set nodeInvalidatableSet(Set set, N node) { + return InvalidatableSet.of( + set, () -> nodes().contains(node), () -> String.format(NODE_REMOVED_FROM_GRAPH, node)); + } + + /** + * Returns a {@link Set} whose methods throw {@link IllegalStateException} when either of the + * given nodes is not present in this network. + * + * @since 33.1.0 + */ + protected final Set nodePairInvalidatableSet(Set set, N nodeU, N nodeV) { + return InvalidatableSet.of( + set, + () -> nodes().contains(nodeU) && nodes().contains(nodeV), + () -> String.format(NODE_PAIR_REMOVED_FROM_GRAPH, nodeU, nodeV)); + } + + private static Map> edgeIncidentNodesMap(Network network) { + return Maps.asMap(network.edges(), network::incidentNodes); } } diff --git a/android/guava/src/com/google/common/graph/AbstractUndirectedNetworkConnections.java b/android/guava/src/com/google/common/graph/AbstractUndirectedNetworkConnections.java index 8f736d7baa23..dc4ab842f5b2 100644 --- a/android/guava/src/com/google/common/graph/AbstractUndirectedNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/AbstractUndirectedNetworkConnections.java @@ -23,7 +23,7 @@ import java.util.Collections; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A base implementation of {@link NetworkConnections} for undirected networks. @@ -32,7 +32,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault abstract class AbstractUndirectedNetworkConnections implements NetworkConnections { /** Keys are edges incident to the origin node, values are the node at the other end. */ final Map incidentEdgeMap; @@ -73,8 +72,7 @@ public N adjacentNode(E edge) { } @Override - @CheckForNull - public N removeInEdge(E edge, boolean isSelfLoop) { + public @Nullable N removeInEdge(E edge, boolean isSelfLoop) { if (!isSelfLoop) { return removeOutEdge(edge); } diff --git a/android/guava/src/com/google/common/graph/AbstractValueGraph.java b/android/guava/src/com/google/common/graph/AbstractValueGraph.java index 940aeffc525d..167ffdcc2f5e 100644 --- a/android/guava/src/com/google/common/graph/AbstractValueGraph.java +++ b/android/guava/src/com/google/common/graph/AbstractValueGraph.java @@ -19,11 +19,10 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; -import com.google.common.base.Function; import com.google.common.collect.Maps; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * This class provides a skeletal implementation of {@link ValueGraph}. It is recommended to extend @@ -38,9 +37,10 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public abstract class AbstractValueGraph extends AbstractBaseGraph implements ValueGraph { + /** Constructor for use by subclasses. */ + public AbstractValueGraph() {} @Override public Graph asGraph() { @@ -108,7 +108,7 @@ public int outDegree(N node) { } @Override - public final boolean equals(@CheckForNull Object obj) { + public final boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -140,15 +140,11 @@ public String toString() { + edgeValueMap(this); } - private static Map, V> edgeValueMap(final ValueGraph graph) { - Function, V> edgeToValueFn = - new Function, V>() { - @Override - public V apply(EndpointPair edge) { + private static Map, V> edgeValueMap(ValueGraph graph) { + return Maps.asMap( + graph.edges(), + edge -> // requireNonNull is safe because the endpoint pair comes from the graph. - return requireNonNull(graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null)); - } - }; - return Maps.asMap(graph.edges(), edgeToValueFn); + requireNonNull(graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null))); } } diff --git a/android/guava/src/com/google/common/graph/BaseGraph.java b/android/guava/src/com/google/common/graph/BaseGraph.java index 68813e18a76d..a451989e712b 100644 --- a/android/guava/src/com/google/common/graph/BaseGraph.java +++ b/android/guava/src/com/google/common/graph/BaseGraph.java @@ -24,7 +24,6 @@ * @author James Sexton * @param Node parameter type */ -@ElementTypesAreNonnullByDefault interface BaseGraph extends SuccessorsFunction, PredecessorsFunction { // // Graph-level accessors @@ -71,44 +70,93 @@ interface BaseGraph extends SuccessorsFunction, PredecessorsFunction { // /** - * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * Returns a live view of the nodes which have an incident edge in common with {@code node} in + * this graph. * *

    This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other `equals()` expression + * involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ Set adjacentNodes(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s incoming edges against the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s incoming edges against the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other `equals()` expression + * involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set predecessors(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s outgoing edges in the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s outgoing edges in the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * *

    This is not the same as "all nodes reachable from {@code node} by following outgoing * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other `equals()` expression + * involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set successors(N node); /** - * Returns the edges in this graph whose endpoints include {@code node}. + * Returns a live view of the edges in this graph whose endpoints include {@code node}. * *

    This is equal to the union of incoming and outgoing edges. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other `equals()` expression + * involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph * @since 24.0 */ diff --git a/android/guava/src/com/google/common/graph/DirectedGraphConnections.java b/android/guava/src/com/google/common/graph/DirectedGraphConnections.java index 0feb973f3f79..5864835cb476 100644 --- a/android/guava/src/com/google/common/graph/DirectedGraphConnections.java +++ b/android/guava/src/com/google/common/graph/DirectedGraphConnections.java @@ -40,7 +40,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link GraphConnections} for directed graphs. @@ -50,7 +50,6 @@ * @param Node parameter type * @param Value parameter type */ -@ElementTypesAreNonnullByDefault final class DirectedGraphConnections implements GraphConnections { /** * A wrapper class to indicate a node is both a predecessor and successor while still providing @@ -83,7 +82,7 @@ static final class Pred extends NodeConnection { } @Override - public boolean equals(@CheckForNull Object that) { + public boolean equals(@Nullable Object that) { if (that instanceof Pred) { return this.node.equals(((Pred) that).node); } else { @@ -104,7 +103,7 @@ static final class Succ extends NodeConnection { } @Override - public boolean equals(@CheckForNull Object that) { + public boolean equals(@Nullable Object that) { if (that instanceof Succ) { return this.node.equals(((Succ) that).node); } else { @@ -134,14 +133,14 @@ public int hashCode() { * LinkedHashMap combines two such edges into a single node-value pair, even though the edges may * not have been inserted consecutively. */ - @CheckForNull private final List> orderedNodeConnections; + private final @Nullable List> orderedNodeConnections; private int predecessorCount; private int successorCount; private DirectedGraphConnections( Map adjacentNodeValues, - @CheckForNull List> orderedNodeConnections, + @Nullable List> orderedNodeConnections, int predecessorCount, int successorCount) { this.adjacentNodeValues = checkNotNull(adjacentNodeValues); @@ -170,10 +169,10 @@ static DirectedGraphConnections of(ElementOrder incidentEdgeOrde } return new DirectedGraphConnections<>( - /* adjacentNodeValues = */ new HashMap(initialCapacity, INNER_LOAD_FACTOR), + /* adjacentNodeValues= */ new HashMap(initialCapacity, INNER_LOAD_FACTOR), orderedNodeConnections, - /* predecessorCount = */ 0, - /* successorCount = */ 0); + /* predecessorCount= */ 0, + /* successorCount= */ 0); } static DirectedGraphConnections ofImmutable( @@ -243,8 +242,7 @@ public UnmodifiableIterator iterator() { Set seenNodes = new HashSet<>(); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { while (nodeConnections.hasNext()) { NodeConnection nodeConnection = nodeConnections.next(); boolean added = seenNodes.add(nodeConnection.node); @@ -263,7 +261,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return adjacentNodeValues.containsKey(obj); } }; @@ -279,8 +277,7 @@ public UnmodifiableIterator iterator() { Iterator> entries = adjacentNodeValues.entrySet().iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { while (entries.hasNext()) { Entry entry = entries.next(); if (isPredecessor(entry.getValue())) { @@ -294,8 +291,7 @@ protected N computeNext() { Iterator> nodeConnections = orderedNodeConnections.iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { while (nodeConnections.hasNext()) { NodeConnection nodeConnection = nodeConnections.next(); if (nodeConnection instanceof NodeConnection.Pred) { @@ -314,7 +310,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return isPredecessor(adjacentNodeValues.get(obj)); } }; @@ -329,8 +325,7 @@ public UnmodifiableIterator iterator() { Iterator> entries = adjacentNodeValues.entrySet().iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { while (entries.hasNext()) { Entry entry = entries.next(); if (isSuccessor(entry.getValue())) { @@ -344,8 +339,7 @@ protected N computeNext() { Iterator> nodeConnections = orderedNodeConnections.iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { while (nodeConnections.hasNext()) { NodeConnection nodeConnection = nodeConnections.next(); if (nodeConnection instanceof NodeConnection.Succ) { @@ -364,7 +358,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { return isSuccessor(adjacentNodeValues.get(obj)); } }; @@ -400,8 +394,7 @@ public Iterator> incidentEdgeIterator(N thisNode) { AtomicBoolean alreadySeenSelfLoop = new AtomicBoolean(false); return new AbstractIterator>() { @Override - @CheckForNull - protected EndpointPair computeNext() { + protected @Nullable EndpointPair computeNext() { while (resultWithDoubleSelfLoop.hasNext()) { EndpointPair edge = resultWithDoubleSelfLoop.next(); if (edge.nodeU().equals(edge.nodeV())) { @@ -419,8 +412,7 @@ protected EndpointPair computeNext() { @SuppressWarnings("unchecked") @Override - @CheckForNull - public V value(N node) { + public @Nullable V value(N node) { checkNotNull(node); Object value = adjacentNodeValues.get(node); if (value == PRED) { @@ -432,7 +424,6 @@ public V value(N node) { return (V) value; } - @SuppressWarnings("unchecked") @Override public void removePredecessor(N node) { checkNotNull(node); @@ -461,8 +452,7 @@ public void removePredecessor(N node) { @SuppressWarnings("unchecked") @Override - @CheckForNull - public V removeSuccessor(Object node) { + public @Nullable V removeSuccessor(Object node) { checkNotNull(node); Object previousValue = adjacentNodeValues.get(node); Object removedValue; @@ -525,8 +515,7 @@ public void addPredecessor(N node, V unused) { @SuppressWarnings("unchecked") @Override - @CheckForNull - public V addSuccessor(N node, V value) { + public @Nullable V addSuccessor(N node, V value) { Object previousValue = adjacentNodeValues.put(node, value); Object previousSuccessor; @@ -554,11 +543,11 @@ public V addSuccessor(N node, V value) { return previousSuccessor == null ? null : (V) previousSuccessor; } - private static boolean isPredecessor(@CheckForNull Object value) { + private static boolean isPredecessor(@Nullable Object value) { return (value == PRED) || (value instanceof PredAndSucc); } - private static boolean isSuccessor(@CheckForNull Object value) { + private static boolean isSuccessor(@Nullable Object value) { return (value != PRED) && (value != null); } } diff --git a/android/guava/src/com/google/common/graph/DirectedMultiNetworkConnections.java b/android/guava/src/com/google/common/graph/DirectedMultiNetworkConnections.java index bd0e546f26e3..b21a63a48d99 100644 --- a/android/guava/src/com/google/common/graph/DirectedMultiNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/DirectedMultiNetworkConnections.java @@ -30,7 +30,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link NetworkConnections} for directed networks with parallel edges. @@ -39,7 +39,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class DirectedMultiNetworkConnections extends AbstractDirectedNetworkConnections { private DirectedMultiNetworkConnections( @@ -60,7 +59,7 @@ static DirectedMultiNetworkConnections ofImmutable( ImmutableMap.copyOf(inEdges), ImmutableMap.copyOf(outEdges), selfLoopCount); } - @CheckForNull @LazyInit private transient Reference> predecessorsReference; + @LazyInit private transient @Nullable Reference> predecessorsReference; @Override public Set predecessors() { @@ -76,7 +75,7 @@ private Multiset predecessorsMultiset() { return predecessors; } - @CheckForNull @LazyInit private transient Reference> successorsReference; + @LazyInit private transient @Nullable Reference> successorsReference; @Override public Set successors() { @@ -140,8 +139,7 @@ public void addOutEdge(E edge, N node) { } } - @CheckForNull - private static T getReference(@CheckForNull Reference reference) { + private static @Nullable T getReference(@Nullable Reference reference) { return (reference == null) ? null : reference.get(); } } diff --git a/android/guava/src/com/google/common/graph/DirectedNetworkConnections.java b/android/guava/src/com/google/common/graph/DirectedNetworkConnections.java index e1db65708eb0..c866a252c254 100644 --- a/android/guava/src/com/google/common/graph/DirectedNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/DirectedNetworkConnections.java @@ -32,7 +32,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class DirectedNetworkConnections extends AbstractDirectedNetworkConnections { DirectedNetworkConnections(Map inEdgeMap, Map outEdgeMap, int selfLoopCount) { diff --git a/android/guava/src/com/google/common/graph/EdgesConnecting.java b/android/guava/src/com/google/common/graph/EdgesConnecting.java index 797970ba29c1..234c9bb3ee6c 100644 --- a/android/guava/src/com/google/common/graph/EdgesConnecting.java +++ b/android/guava/src/com/google/common/graph/EdgesConnecting.java @@ -23,7 +23,7 @@ import com.google.common.collect.UnmodifiableIterator; import java.util.AbstractSet; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A class to represent the set of edges connecting an (implicit) origin node to a target node. @@ -34,7 +34,6 @@ * @author James Sexton * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class EdgesConnecting extends AbstractSet { private final Map nodeToOutEdge; @@ -59,13 +58,12 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object edge) { + public boolean contains(@Nullable Object edge) { E connectingEdge = getConnectingEdge(); return (connectingEdge != null && connectingEdge.equals(edge)); } - @CheckForNull - private E getConnectingEdge() { + private @Nullable E getConnectingEdge() { return nodeToOutEdge.get(targetNode); } } diff --git a/android/guava/src/com/google/common/graph/ElementOrder.java b/android/guava/src/com/google/common/graph/ElementOrder.java index b5985a280b86..88562a3bda3f 100644 --- a/android/guava/src/com/google/common/graph/ElementOrder.java +++ b/android/guava/src/com/google/common/graph/ElementOrder.java @@ -28,7 +28,7 @@ import com.google.errorprone.annotations.Immutable; import java.util.Comparator; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Used to represent the order of elements in a data structure that supports different options for @@ -46,13 +46,11 @@ */ @Beta @Immutable -@ElementTypesAreNonnullByDefault public final class ElementOrder { private final Type type; @SuppressWarnings("Immutable") // Hopefully the comparator provided is immutable! - @CheckForNull - private final Comparator comparator; + private final @Nullable Comparator comparator; /** * The type of ordering that this object specifies. @@ -72,7 +70,7 @@ public enum Type { SORTED } - private ElementOrder(Type type, @CheckForNull Comparator comparator) { + private ElementOrder(Type type, @Nullable Comparator comparator) { this.type = checkNotNull(type); this.comparator = comparator; checkState((type == Type.SORTED) == (comparator != null)); @@ -161,7 +159,7 @@ public Comparator comparator() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -197,9 +195,8 @@ Map createMap(int expectedSize) { return Maps.newLinkedHashMapWithExpectedSize(expectedSize); case SORTED: return Maps.newTreeMap(comparator()); - default: - throw new AssertionError(); } + throw new AssertionError(); } @SuppressWarnings("unchecked") diff --git a/android/guava/src/com/google/common/graph/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/graph/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 0798de0c9883..000000000000 --- a/android/guava/src/com/google/common/graph/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.graph; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/graph/EndpointPair.java b/android/guava/src/com/google/common/graph/EndpointPair.java index fe2397c6b97b..3bf575186525 100644 --- a/android/guava/src/com/google/common/graph/EndpointPair.java +++ b/android/guava/src/com/google/common/graph/EndpointPair.java @@ -24,7 +24,7 @@ import com.google.common.collect.Iterators; import com.google.common.collect.UnmodifiableIterator; import com.google.errorprone.annotations.Immutable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable pair representing the two endpoints of an edge in a graph. The {@link EndpointPair} @@ -39,7 +39,6 @@ */ @Beta @Immutable(containerOf = {"N"}) -@ElementTypesAreNonnullByDefault public abstract class EndpointPair implements Iterable { private final N nodeU; private final N nodeV; @@ -134,7 +133,7 @@ public final UnmodifiableIterator iterator() { * ordered {@link EndpointPair} is never equal to an unordered {@link EndpointPair}. */ @Override - public abstract boolean equals(@CheckForNull Object obj); + public abstract boolean equals(@Nullable Object obj); /** * The hashcode of an ordered {@link EndpointPair} is equal to {@code Objects.hashCode(source(), @@ -165,7 +164,7 @@ public boolean isOrdered() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } @@ -213,7 +212,7 @@ public boolean isOrdered() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } diff --git a/android/guava/src/com/google/common/graph/EndpointPairIterator.java b/android/guava/src/com/google/common/graph/EndpointPairIterator.java index 7096dbe3da2a..f9b563989bb9 100644 --- a/android/guava/src/com/google/common/graph/EndpointPairIterator.java +++ b/android/guava/src/com/google/common/graph/EndpointPairIterator.java @@ -24,21 +24,18 @@ import com.google.common.collect.Sets; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A class to facilitate the set returned by {@link Graph#edges()}. * * @author James Sexton */ -@ElementTypesAreNonnullByDefault abstract class EndpointPairIterator extends AbstractIterator> { private final BaseGraph graph; private final Iterator nodeIterator; - @CheckForNull - N node = null; // null is safe as an initial value because graphs don't allow null nodes + @Nullable N node = null; // null is safe as an initial value because graphs don't allow null nodes Iterator successorIterator = ImmutableSet.of().iterator(); @@ -75,8 +72,7 @@ private Directed(BaseGraph graph) { } @Override - @CheckForNull - protected EndpointPair computeNext() { + protected @Nullable EndpointPair computeNext() { while (true) { if (successorIterator.hasNext()) { // requireNonNull is safe because successorIterator is empty until we set this.node. @@ -117,7 +113,7 @@ protected EndpointPair computeNext() { */ private static final class Undirected extends EndpointPairIterator { // It's a little weird that we add `null` to this set, but it makes for slightly simpler code. - @CheckForNull private Set<@Nullable N> visitedNodes; + private @Nullable Set<@Nullable N> visitedNodes; private Undirected(BaseGraph graph) { super(graph); @@ -125,8 +121,7 @@ private Undirected(BaseGraph graph) { } @Override - @CheckForNull - protected EndpointPair computeNext() { + protected @Nullable EndpointPair computeNext() { while (true) { /* * requireNonNull is safe because visitedNodes isn't cleared until this method calls diff --git a/android/guava/src/com/google/common/graph/ForwardingGraph.java b/android/guava/src/com/google/common/graph/ForwardingGraph.java index f4ddf425db29..99faf4c999aa 100644 --- a/android/guava/src/com/google/common/graph/ForwardingGraph.java +++ b/android/guava/src/com/google/common/graph/ForwardingGraph.java @@ -24,7 +24,6 @@ * * @author James Sexton */ -@ElementTypesAreNonnullByDefault abstract class ForwardingGraph extends AbstractGraph { abstract BaseGraph delegate(); diff --git a/android/guava/src/com/google/common/graph/ForwardingNetwork.java b/android/guava/src/com/google/common/graph/ForwardingNetwork.java index fd211524efb0..4a2f62c17579 100644 --- a/android/guava/src/com/google/common/graph/ForwardingNetwork.java +++ b/android/guava/src/com/google/common/graph/ForwardingNetwork.java @@ -17,7 +17,7 @@ package com.google.common.graph; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A class to allow {@link Network} implementations to be backed by a provided delegate. This is not @@ -26,7 +26,6 @@ * @author James Sexton * @author Joshua O'Madadhain */ -@ElementTypesAreNonnullByDefault abstract class ForwardingNetwork extends AbstractNetwork { abstract Network delegate(); @@ -132,14 +131,12 @@ public Set edgesConnecting(EndpointPair endpoints) { } @Override - @CheckForNull - public E edgeConnectingOrNull(N nodeU, N nodeV) { + public @Nullable E edgeConnectingOrNull(N nodeU, N nodeV) { return delegate().edgeConnectingOrNull(nodeU, nodeV); } @Override - @CheckForNull - public E edgeConnectingOrNull(EndpointPair endpoints) { + public @Nullable E edgeConnectingOrNull(EndpointPair endpoints) { return delegate().edgeConnectingOrNull(endpoints); } diff --git a/android/guava/src/com/google/common/graph/ForwardingValueGraph.java b/android/guava/src/com/google/common/graph/ForwardingValueGraph.java index 70ed6e5595f3..04a4a39e25a8 100644 --- a/android/guava/src/com/google/common/graph/ForwardingValueGraph.java +++ b/android/guava/src/com/google/common/graph/ForwardingValueGraph.java @@ -17,7 +17,7 @@ package com.google.common.graph; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A class to allow {@link ValueGraph} implementations to be backed by a provided delegate. This is @@ -26,7 +26,6 @@ * @author James Sexton * @author Joshua O'Madadhain */ -@ElementTypesAreNonnullByDefault abstract class ForwardingValueGraph extends AbstractValueGraph { abstract ValueGraph delegate(); @@ -106,14 +105,12 @@ public boolean hasEdgeConnecting(EndpointPair endpoints) { } @Override - @CheckForNull - public V edgeValueOrDefault(N nodeU, N nodeV, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue) { return delegate().edgeValueOrDefault(nodeU, nodeV, defaultValue); } @Override - @CheckForNull - public V edgeValueOrDefault(EndpointPair endpoints, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(EndpointPair endpoints, @Nullable V defaultValue) { return delegate().edgeValueOrDefault(endpoints, defaultValue); } } diff --git a/android/guava/src/com/google/common/graph/Graph.java b/android/guava/src/com/google/common/graph/Graph.java index 5dc0e71faf6d..b9ebd5cd1a0c 100644 --- a/android/guava/src/com/google/common/graph/Graph.java +++ b/android/guava/src/com/google/common/graph/Graph.java @@ -20,7 +20,7 @@ import com.google.errorprone.annotations.DoNotMock; import java.util.Collection; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An interface for extends BaseGraph { // // Graph-level accessors @@ -156,45 +155,94 @@ public interface Graph extends BaseGraph { // /** - * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * Returns a live view of the nodes which have an incident edge in common with {@code node} in + * this graph. * *

    This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set adjacentNodes(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s incoming edges against the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s incoming edges against the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set predecessors(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s outgoing edges in the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s outgoing edges in the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * *

    This is not the same as "all nodes reachable from {@code node} by following outgoing * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set successors(N node); /** - * Returns the edges in this graph whose endpoints include {@code node}. + * Returns a live view of the edges in this graph whose endpoints include {@code node}. * *

    This is equal to the union of incoming and outgoing edges. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph * @since 24.0 */ @@ -290,7 +338,7 @@ public interface Graph extends BaseGraph { *

    A reference implementation of this is provided by {@link AbstractGraph#equals(Object)}. */ @Override - boolean equals(@CheckForNull Object object); + boolean equals(@Nullable Object object); /** * Returns the hash code for this graph. The hash code of a graph is defined as the hash code of diff --git a/android/guava/src/com/google/common/graph/GraphBuilder.java b/android/guava/src/com/google/common/graph/GraphBuilder.java index 8c0871b467d1..8d25db4ad646 100644 --- a/android/guava/src/com/google/common/graph/GraphBuilder.java +++ b/android/guava/src/com/google/common/graph/GraphBuilder.java @@ -22,19 +22,26 @@ import com.google.common.annotations.Beta; import com.google.common.base.Optional; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; /** * A builder for constructing instances of {@link MutableGraph} or {@link ImmutableGraph} with * user-defined properties. * - *

    A graph built by this class will have the following properties by default: + *

    A {@code Graph} built by this class has the following default properties: * *

      *
    • does not allow self-loops - *
    • orders {@link Graph#nodes()} in the order in which the elements were added + *
    • orders {@link Graph#nodes()} in the order in which the elements were added (insertion + * order) *
    * + *

    {@code Graph}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * *

    Examples of use: * *

    {@code
    @@ -64,7 +71,6 @@
      */
     @Beta
     @DoNotMock
    -@ElementTypesAreNonnullByDefault
     public final class GraphBuilder extends AbstractGraphBuilder {
     
       /** Creates a new instance with the specified edge directionality. */
    @@ -118,6 +124,7 @@ public  ImmutableGraph.Builder immutable() {
        *
        * 

    The default value is {@code false}. */ + @CanIgnoreReturnValue public GraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { this.allowsSelfLoops = allowsSelfLoops; return this; @@ -128,6 +135,7 @@ public GraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { * * @throws IllegalArgumentException if {@code expectedNodeCount} is negative */ + @CanIgnoreReturnValue public GraphBuilder expectedNodeCount(int expectedNodeCount) { this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); return this; diff --git a/android/guava/src/com/google/common/graph/GraphConnections.java b/android/guava/src/com/google/common/graph/GraphConnections.java index 7d4f8f0cf02b..61e36e9b1e43 100644 --- a/android/guava/src/com/google/common/graph/GraphConnections.java +++ b/android/guava/src/com/google/common/graph/GraphConnections.java @@ -19,7 +19,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An interface for representing and manipulating an origin node's adjacent nodes and edge values in @@ -29,7 +29,6 @@ * @param Node parameter type * @param Value parameter type */ -@ElementTypesAreNonnullByDefault interface GraphConnections { Set adjacentNodes(); @@ -49,8 +48,7 @@ interface GraphConnections { * Returns the value associated with the edge connecting the origin node to {@code node}, or null * if there is no such edge. */ - @CheckForNull - V value(N node); + @Nullable V value(N node); /** Remove {@code node} from the set of predecessors. */ void removePredecessor(N node); @@ -60,8 +58,7 @@ interface GraphConnections { * the edge connecting the two nodes. */ @CanIgnoreReturnValue - @CheckForNull - V removeSuccessor(N node); + @Nullable V removeSuccessor(N node); /** * Add {@code node} as a predecessor to the origin node. In the case of an undirected graph, it @@ -75,6 +72,5 @@ interface GraphConnections { * the value previously associated with the edge connecting the two nodes. */ @CanIgnoreReturnValue - @CheckForNull - V addSuccessor(N node, V value); + @Nullable V addSuccessor(N node, V value); } diff --git a/android/guava/src/com/google/common/graph/GraphConstants.java b/android/guava/src/com/google/common/graph/GraphConstants.java index ae224fdd2252..8ede199e5a29 100644 --- a/android/guava/src/com/google/common/graph/GraphConstants.java +++ b/android/guava/src/com/google/common/graph/GraphConstants.java @@ -16,9 +16,7 @@ package com.google.common.graph; - /** A utility class to hold various constants used by the Guava Graph library. */ -@ElementTypesAreNonnullByDefault final class GraphConstants { private GraphConstants() {} @@ -35,6 +33,12 @@ private GraphConstants() {} // Error messages static final String NODE_NOT_IN_GRAPH = "Node %s is not an element of this graph."; static final String EDGE_NOT_IN_GRAPH = "Edge %s is not an element of this graph."; + static final String NODE_REMOVED_FROM_GRAPH = + "Node %s that was used to generate this set is no longer in the graph."; + static final String NODE_PAIR_REMOVED_FROM_GRAPH = + "Node %s or node %s that were used to generate this set are no longer in the graph."; + static final String EDGE_REMOVED_FROM_GRAPH = + "Edge %s that was used to generate this set is no longer in the graph."; static final String REUSING_EDGE = "Edge %s already exists between the following nodes: %s, " + "so it cannot be reused to connect the following nodes: %s."; @@ -52,7 +56,7 @@ private GraphConstants() {} + "adjacentNode(node) if you already have a node, or nodeU()/nodeV() if you don't."; static final String EDGE_ALREADY_EXISTS = "Edge %s already exists in the graph."; static final String ENDPOINTS_MISMATCH = - "Mismatch: unordered endpoints cannot be used with directed graphs"; + "Mismatch: endpoints' ordering is not compatible with directionality of the graph"; /** Singleton edge value for {@link Graph} implementations backed by {@link ValueGraph}s. */ enum Presence { diff --git a/android/guava/src/com/google/common/graph/Graphs.java b/android/guava/src/com/google/common/graph/Graphs.java index bad8580ac859..25ef8ebadd54 100644 --- a/android/guava/src/com/google/common/graph/Graphs.java +++ b/android/guava/src/com/google/common/graph/Graphs.java @@ -21,19 +21,21 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; -import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.util.ArrayDeque; import java.util.Collection; +import java.util.Deque; import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import java.util.Queue; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods for {@link Graph}, {@link ValueGraph}, and {@link Network} instances. @@ -43,8 +45,7 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault -public final class Graphs { +public final class Graphs extends GraphsBridgeMethods { private Graphs() {} @@ -69,7 +70,7 @@ public static boolean hasCycle(Graph graph) { Map visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { - if (subgraphHasCycle(graph, visitedNodes, node, null)) { + if (subgraphHasCycle(graph, visitedNodes, node)) { return true; } } @@ -95,34 +96,67 @@ public static boolean hasCycle(Network network) { } /** - * Performs a traversal of the nodes reachable from {@code node}. If we ever reach a node we've - * already visited (following only outgoing edges and without reusing edges), we know there's a - * cycle in the graph. + * Performs a traversal of the nodes reachable from {@code startNode}. If we ever reach a node + * we've already visited (following only outgoing edges and without reusing edges), we know + * there's a cycle in the graph. */ private static boolean subgraphHasCycle( - Graph graph, - Map visitedNodes, - N node, - @CheckForNull N previousNode) { - NodeVisitState state = visitedNodes.get(node); - if (state == NodeVisitState.COMPLETE) { - return false; - } - if (state == NodeVisitState.PENDING) { - return true; - } + Graph graph, Map visitedNodes, N startNode) { + Deque> stack = new ArrayDeque<>(); + stack.addLast(new NodeAndRemainingSuccessors<>(startNode)); + + while (!stack.isEmpty()) { + // To peek at the top two items, we need to temporarily remove one. + NodeAndRemainingSuccessors top = stack.removeLast(); + NodeAndRemainingSuccessors prev = stack.peekLast(); + stack.addLast(top); + + N node = top.node; + N previousNode = prev == null ? null : prev.node; + if (top.remainingSuccessors == null) { + NodeVisitState state = visitedNodes.get(node); + if (state == NodeVisitState.COMPLETE) { + stack.removeLast(); + continue; + } + if (state == NodeVisitState.PENDING) { + return true; + } - visitedNodes.put(node, NodeVisitState.PENDING); - for (N nextNode : graph.successors(node)) { - if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode) - && subgraphHasCycle(graph, visitedNodes, nextNode, node)) { - return true; + visitedNodes.put(node, NodeVisitState.PENDING); + top.remainingSuccessors = new ArrayDeque<>(graph.successors(node)); + } + + if (!top.remainingSuccessors.isEmpty()) { + N nextNode = top.remainingSuccessors.remove(); + if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode)) { + stack.addLast(new NodeAndRemainingSuccessors<>(nextNode)); + continue; + } } + + stack.removeLast(); + visitedNodes.put(node, NodeVisitState.COMPLETE); } - visitedNodes.put(node, NodeVisitState.COMPLETE); return false; } + private static final class NodeAndRemainingSuccessors { + final N node; + + /** + * The successors left to be visited, or {@code null} if we just added this {@code + * NodeAndRemainingSuccessors} instance to the stack. In the latter case, we'll compute the + * successors if we determine that we need them after we've performed the initial processing of + * the node. + */ + @Nullable Queue remainingSuccessors; + + NodeAndRemainingSuccessors(N node) { + this.node = node; + } + } + /** * Determines whether an edge has already been used during traversal. In the directed case a cycle * is always detected before reusing an edge, so no special logic is required. In the undirected @@ -130,7 +164,7 @@ && subgraphHasCycle(graph, visitedNodes, nextNode, node)) { * from B to A). */ private static boolean canTraverseWithoutReusingEdge( - Graph graph, Object nextNode, @CheckForNull Object previousNode) { + Graph graph, Object nextNode, @Nullable Object previousNode) { if (graph.isDirected() || !Objects.equal(previousNode, nextNode)) { return true; } @@ -147,10 +181,13 @@ private static boolean canTraverseWithoutReusingEdge( *

    This is a "snapshot" based on the current topology of {@code graph}, rather than a live view * of the transitive closure of {@code graph}. In other words, the returned {@link Graph} will not * be updated after modifications to {@code graph}. + * + * @since 33.1.0 (present with return type {@code Graph} since 20.0) */ // TODO(b/31438252): Consider potential optimizations for this algorithm. - public static Graph transitiveClosure(Graph graph) { - MutableGraph transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build(); + public static ImmutableGraph transitiveClosure(Graph graph) { + ImmutableGraph.Builder transitiveClosure = + GraphBuilder.from(graph).allowsSelfLoops(true).immutable(); // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it. @@ -164,7 +201,7 @@ public static Graph transitiveClosure(Graph graph) { } else { // An optimization for the undirected case: for every node B reachable from node A, // node A and node B have the same reachability set. - Set visitedNodes = new HashSet(); + Set visitedNodes = new HashSet<>(); for (N node : graph.nodes()) { if (!visitedNodes.contains(node)) { Set reachableNodes = reachableNodes(graph, node); @@ -179,7 +216,7 @@ public static Graph transitiveClosure(Graph graph) { } } - return transitiveClosure; + return transitiveClosure.build(); } /** @@ -192,8 +229,9 @@ public static Graph transitiveClosure(Graph graph) { * not be updated after modifications to {@code graph}. * * @throws IllegalArgumentException if {@code node} is not present in {@code graph} + * @since 33.1.0 (present with return type {@code Set} since 20.0) */ - public static Set reachableNodes(Graph graph, N node) { + public static ImmutableSet reachableNodes(Graph graph, N node) { checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); return ImmutableSet.copyOf(Traverser.forGraph(graph).breadthFirst(node)); } @@ -215,7 +253,7 @@ public static Graph transpose(Graph graph) { return ((TransposedGraph) graph).graph; } - return new TransposedGraph(graph); + return new TransposedGraph<>(graph); } /** @@ -288,12 +326,7 @@ public Set> incidentEdges(N node) { public Iterator> iterator() { return Iterators.transform( delegate().incidentEdges(node).iterator(), - new Function, EndpointPair>() { - @Override - public EndpointPair apply(EndpointPair edge) { - return EndpointPair.of(delegate(), edge.nodeV(), edge.nodeU()); - } - }); + edge -> EndpointPair.of(delegate(), edge.nodeV(), edge.nodeU())); } }; } @@ -364,14 +397,12 @@ public boolean hasEdgeConnecting(EndpointPair endpoints) { } @Override - @CheckForNull - public V edgeValueOrDefault(N nodeU, N nodeV, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue) { return delegate().edgeValueOrDefault(nodeV, nodeU, defaultValue); // transpose } @Override - @CheckForNull - public V edgeValueOrDefault(EndpointPair endpoints, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(EndpointPair endpoints, @Nullable V defaultValue) { return delegate().edgeValueOrDefault(transpose(endpoints), defaultValue); } } @@ -435,14 +466,12 @@ public Set edgesConnecting(EndpointPair endpoints) { } @Override - @CheckForNull - public E edgeConnectingOrNull(N nodeU, N nodeV) { + public @Nullable E edgeConnectingOrNull(N nodeU, N nodeV) { return delegate().edgeConnectingOrNull(nodeV, nodeU); // transpose } @Override - @CheckForNull - public E edgeConnectingOrNull(EndpointPair endpoints) { + public @Nullable E edgeConnectingOrNull(EndpointPair endpoints) { return delegate().edgeConnectingOrNull(transpose(endpoints)); } diff --git a/android/guava/src/com/google/common/graph/GraphsBridgeMethods.java b/android/guava/src/com/google/common/graph/GraphsBridgeMethods.java new file mode 100644 index 000000000000..5cbb790bfabf --- /dev/null +++ b/android/guava/src/com/google/common/graph/GraphsBridgeMethods.java @@ -0,0 +1,22 @@ +package com.google.common.graph; + +import com.google.common.annotations.Beta; +import java.util.Set; + +/** + * Supertype for {@link Graphs}, containing the old signatures of methods whose signatures we've + * changed. This provides binary compatibility for users who compiled against the old signatures. + */ +@Beta +abstract class GraphsBridgeMethods { + + @SuppressWarnings("PreferredInterfaceType") + public static Graph transitiveClosure(Graph graph) { + return Graphs.transitiveClosure(graph); + } + + @SuppressWarnings("PreferredInterfaceType") + public static Set reachableNodes(Graph graph, N node) { + return Graphs.reachableNodes(graph, node); + } +} diff --git a/android/guava/src/com/google/common/graph/ImmutableGraph.java b/android/guava/src/com/google/common/graph/ImmutableGraph.java index afad211a10bf..56e018ad6f52 100644 --- a/android/guava/src/com/google/common/graph/ImmutableGraph.java +++ b/android/guava/src/com/google/common/graph/ImmutableGraph.java @@ -26,6 +26,7 @@ import com.google.common.graph.GraphConstants.Presence; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.InlineMe; /** * A {@link Graph} whose elements and structural relationships will never change. Instances of this @@ -45,7 +46,6 @@ */ @Beta @Immutable(containerOf = {"N"}) -@ElementTypesAreNonnullByDefault public class ImmutableGraph extends ForwardingGraph { @SuppressWarnings("Immutable") // The backing graph must be immutable. private final BaseGraph backingGraph; @@ -68,6 +68,9 @@ public static ImmutableGraph copyOf(Graph graph) { * * @deprecated no need to use this */ + @InlineMe( + replacement = "checkNotNull(graph)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static ImmutableGraph copyOf(ImmutableGraph graph) { return checkNotNull(graph); @@ -87,7 +90,7 @@ private static ImmutableMap> getNodeConnect for (N node : graph.nodes()) { nodeConnections.put(node, connectionsOf(graph, node)); } - return nodeConnections.build(); + return nodeConnections.buildOrThrow(); } @SuppressWarnings("unchecked") diff --git a/android/guava/src/com/google/common/graph/ImmutableNetwork.java b/android/guava/src/com/google/common/graph/ImmutableNetwork.java index 3c858018b664..3589759c4620 100644 --- a/android/guava/src/com/google/common/graph/ImmutableNetwork.java +++ b/android/guava/src/com/google/common/graph/ImmutableNetwork.java @@ -24,6 +24,7 @@ import com.google.common.collect.Maps; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.InlineMe; import java.util.Map; /** @@ -46,7 +47,6 @@ @Beta @Immutable(containerOf = {"N", "E"}) @SuppressWarnings("Immutable") // Extends StandardNetwork but uses ImmutableMaps. -@ElementTypesAreNonnullByDefault public final class ImmutableNetwork extends StandardNetwork { private ImmutableNetwork(Network network) { @@ -66,6 +66,9 @@ public static ImmutableNetwork copyOf(Network network) { * * @deprecated no need to use this */ + @InlineMe( + replacement = "checkNotNull(network)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static ImmutableNetwork copyOf(ImmutableNetwork network) { return checkNotNull(network); @@ -84,7 +87,7 @@ private static Map> getNodeConnections(Networ for (N node : network.nodes()) { nodeConnections.put(node, connectionsOf(network, node)); } - return nodeConnections.build(); + return nodeConnections.buildOrThrow(); } private static Map getEdgeToReferenceNode(Network network) { @@ -95,7 +98,7 @@ private static Map getEdgeToReferenceNode(Network network) { for (E edge : network.edges()) { edgeToReferenceNode.put(edge, network.incidentNodes(edge).nodeU()); } - return edgeToReferenceNode.build(); + return edgeToReferenceNode.buildOrThrow(); } private static NetworkConnections connectionsOf(Network network, N node) { diff --git a/android/guava/src/com/google/common/graph/ImmutableValueGraph.java b/android/guava/src/com/google/common/graph/ImmutableValueGraph.java index 4ca2234bdaa2..25e23c22015c 100644 --- a/android/guava/src/com/google/common/graph/ImmutableValueGraph.java +++ b/android/guava/src/com/google/common/graph/ImmutableValueGraph.java @@ -25,6 +25,7 @@ import com.google.common.collect.Maps; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.InlineMe; /** * A {@link ValueGraph} whose elements and structural relationships will never change. Instances of @@ -44,7 +45,6 @@ @Beta @Immutable(containerOf = {"N", "V"}) @SuppressWarnings("Immutable") // Extends StandardValueGraph but uses ImmutableMaps. -@ElementTypesAreNonnullByDefault public final class ImmutableValueGraph extends StandardValueGraph { private ImmutableValueGraph(ValueGraph graph) { @@ -63,6 +63,9 @@ public static ImmutableValueGraph copyOf(ValueGraph graph) { * * @deprecated no need to use this */ + @InlineMe( + replacement = "checkNotNull(graph)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static ImmutableValueGraph copyOf(ImmutableValueGraph graph) { return checkNotNull(graph); @@ -87,7 +90,7 @@ private static ImmutableMap> getNodeConnections for (N node : graph.nodes()) { nodeConnections.put(node, connectionsOf(graph, node)); } - return nodeConnections.build(); + return nodeConnections.buildOrThrow(); } private static GraphConnections connectionsOf(ValueGraph graph, N node) { diff --git a/android/guava/src/com/google/common/graph/IncidentEdgeSet.java b/android/guava/src/com/google/common/graph/IncidentEdgeSet.java index ac94b65779d4..19e882e0e84b 100644 --- a/android/guava/src/com/google/common/graph/IncidentEdgeSet.java +++ b/android/guava/src/com/google/common/graph/IncidentEdgeSet.java @@ -18,13 +18,12 @@ import java.util.AbstractSet; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Abstract base class for an incident edges set that allows different implementations of {@link * AbstractSet#iterator()}. */ -@ElementTypesAreNonnullByDefault abstract class IncidentEdgeSet extends AbstractSet> { final N node; final BaseGraph graph; @@ -35,7 +34,7 @@ abstract class IncidentEdgeSet extends AbstractSet> { } @Override - public boolean remove(@CheckForNull Object o) { + public boolean remove(@Nullable Object o) { throw new UnsupportedOperationException(); } @@ -51,7 +50,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object obj) { + public boolean contains(@Nullable Object obj) { if (!(obj instanceof EndpointPair)) { return false; } diff --git a/android/guava/src/com/google/common/graph/InvalidatableSet.java b/android/guava/src/com/google/common/graph/InvalidatableSet.java new file mode 100644 index 000000000000..80e4a943b9f5 --- /dev/null +++ b/android/guava/src/com/google/common/graph/InvalidatableSet.java @@ -0,0 +1,53 @@ +package com.google.common.graph; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Supplier; +import com.google.common.collect.ForwardingSet; +import java.util.Set; + +/** + * A subclass of `ForwardingSet` that throws `IllegalStateException` on invocation of any method + * (except `hashCode` and `equals`) if the provided `Supplier` returns false. + */ +final class InvalidatableSet extends ForwardingSet { + private final Supplier validator; + private final Set delegate; + private final Supplier errorMessage; + + static InvalidatableSet of( + Set delegate, Supplier validator, Supplier errorMessage) { + return new InvalidatableSet<>( + checkNotNull(delegate), checkNotNull(validator), checkNotNull(errorMessage)); + } + + @Override + protected Set delegate() { + validate(); + return delegate; + } + + private InvalidatableSet( + Set delegate, Supplier validator, Supplier errorMessage) { + this.delegate = delegate; + this.validator = validator; + this.errorMessage = errorMessage; + } + + // Override hashCode() to access delegate directly (so that it doesn't trigger the validate() call + // via delegate()); it seems inappropriate to throw ISE on this method. + @Override + public int hashCode() { + return delegate.hashCode(); + } + + private void validate() { + // Don't use checkState(), because we don't want the overhead of generating the error message + // unless it's actually going to be used; validate() is called for all set method calls, so it + // needs to be fast. + // (We could instead generate the message once, when the set is created, but zero is better.) + if (!validator.get()) { + throw new IllegalStateException(errorMessage.get()); + } + } +} diff --git a/android/guava/src/com/google/common/graph/MapIteratorCache.java b/android/guava/src/com/google/common/graph/MapIteratorCache.java index 05cdde8c3f12..b376ef92b4b4 100644 --- a/android/guava/src/com/google/common/graph/MapIteratorCache.java +++ b/android/guava/src/com/google/common/graph/MapIteratorCache.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A map-like data structure that wraps a backing map and caches values while iterating through @@ -41,7 +41,6 @@ * * @author James Sexton */ -@ElementTypesAreNonnullByDefault class MapIteratorCache { private final Map backingMap; @@ -54,15 +53,14 @@ class MapIteratorCache { * while writing to it in another. All it does is help with _reading_ from multiple threads * concurrently. For more information, see AbstractNetworkTest.concurrentIteration. */ - @CheckForNull private transient volatile Entry cacheEntry; + private transient volatile @Nullable Entry cacheEntry; MapIteratorCache(Map backingMap) { this.backingMap = checkNotNull(backingMap); } @CanIgnoreReturnValue - @CheckForNull - final V put(K key, V value) { + final @Nullable V put(K key, V value) { checkNotNull(key); checkNotNull(value); clearCache(); @@ -70,8 +68,7 @@ final V put(K key, V value) { } @CanIgnoreReturnValue - @CheckForNull - final V remove(Object key) { + final @Nullable V remove(Object key) { checkNotNull(key); clearCache(); return backingMap.remove(key); @@ -82,8 +79,7 @@ final void clear() { backingMap.clear(); } - @CheckForNull - V get(Object key) { + @Nullable V get(Object key) { checkNotNull(key); V value = getIfCached(key); // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. @@ -94,13 +90,12 @@ V get(Object key) { } } - @CheckForNull - final V getWithoutCaching(Object key) { + final @Nullable V getWithoutCaching(Object key) { checkNotNull(key); return backingMap.get(key); } - final boolean containsKey(@CheckForNull Object key) { + final boolean containsKey(@Nullable Object key) { return getIfCached(key) != null || backingMap.containsKey(key); } @@ -131,7 +126,7 @@ public int size() { } @Override - public boolean contains(@CheckForNull Object key) { + public boolean contains(@Nullable Object key) { return containsKey(key); } }; @@ -139,8 +134,7 @@ public boolean contains(@CheckForNull Object key) { // Internal methods (package-visible, but treat as only subclass-visible) - @CheckForNull - V getIfCached(@CheckForNull Object key) { + @Nullable V getIfCached(@Nullable Object key) { Entry entry = cacheEntry; // store local reference for thread-safety // Check cache. We use == on purpose because it's cheaper and a cache miss is ok. diff --git a/android/guava/src/com/google/common/graph/MapRetrievalCache.java b/android/guava/src/com/google/common/graph/MapRetrievalCache.java index ada78f28414d..6e3b8abf4188 100644 --- a/android/guava/src/com/google/common/graph/MapRetrievalCache.java +++ b/android/guava/src/com/google/common/graph/MapRetrievalCache.java @@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link MapIteratorCache} that adds additional caching. In addition to the caching provided by @@ -27,11 +27,10 @@ * * @author James Sexton */ -@ElementTypesAreNonnullByDefault final class MapRetrievalCache extends MapIteratorCache { // See the note about volatile in the superclass. - @CheckForNull private transient volatile CacheEntry cacheEntry1; - @CheckForNull private transient volatile CacheEntry cacheEntry2; + private transient volatile @Nullable CacheEntry cacheEntry1; + private transient volatile @Nullable CacheEntry cacheEntry2; MapRetrievalCache(Map backingMap) { super(backingMap); @@ -39,8 +38,7 @@ final class MapRetrievalCache extends MapIteratorCache { @SuppressWarnings("unchecked") // Safe because we only cast if key is found in map. @Override - @CheckForNull - V get(Object key) { + @Nullable V get(Object key) { checkNotNull(key); V value = getIfCached(key); if (value != null) { @@ -57,8 +55,7 @@ V get(Object key) { // Internal methods (package-visible, but treat as only subclass-visible) @Override - @CheckForNull - V getIfCached(@CheckForNull Object key) { + @Nullable V getIfCached(@Nullable Object key) { V value = super.getIfCached(key); if (value != null) { return value; diff --git a/android/guava/src/com/google/common/graph/MultiEdgesConnecting.java b/android/guava/src/com/google/common/graph/MultiEdgesConnecting.java index 620f986a555a..2d24508dfb00 100644 --- a/android/guava/src/com/google/common/graph/MultiEdgesConnecting.java +++ b/android/guava/src/com/google/common/graph/MultiEdgesConnecting.java @@ -24,7 +24,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A class to represent the set of edges connecting an (implicit) origin node to a target node. @@ -35,7 +35,6 @@ * @author James Sexton * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault abstract class MultiEdgesConnecting extends AbstractSet { private final Map outEdgeToNode; @@ -51,8 +50,7 @@ public UnmodifiableIterator iterator() { Iterator> entries = outEdgeToNode.entrySet().iterator(); return new AbstractIterator() { @Override - @CheckForNull - protected E computeNext() { + protected @Nullable E computeNext() { while (entries.hasNext()) { Entry entry = entries.next(); if (targetNode.equals(entry.getValue())) { @@ -65,7 +63,7 @@ protected E computeNext() { } @Override - public boolean contains(@CheckForNull Object edge) { + public boolean contains(@Nullable Object edge) { return targetNode.equals(outEdgeToNode.get(edge)); } } diff --git a/android/guava/src/com/google/common/graph/MutableGraph.java b/android/guava/src/com/google/common/graph/MutableGraph.java index b1f3359cab58..8324079f6c08 100644 --- a/android/guava/src/com/google/common/graph/MutableGraph.java +++ b/android/guava/src/com/google/common/graph/MutableGraph.java @@ -29,7 +29,6 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public interface MutableGraph extends Graph { /** diff --git a/android/guava/src/com/google/common/graph/MutableNetwork.java b/android/guava/src/com/google/common/graph/MutableNetwork.java index 64acb13162af..d702903604cf 100644 --- a/android/guava/src/com/google/common/graph/MutableNetwork.java +++ b/android/guava/src/com/google/common/graph/MutableNetwork.java @@ -30,7 +30,6 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public interface MutableNetwork extends Network { /** diff --git a/android/guava/src/com/google/common/graph/MutableValueGraph.java b/android/guava/src/com/google/common/graph/MutableValueGraph.java index f32d2ee7a38c..829f774ae0ae 100644 --- a/android/guava/src/com/google/common/graph/MutableValueGraph.java +++ b/android/guava/src/com/google/common/graph/MutableValueGraph.java @@ -18,7 +18,7 @@ import com.google.common.annotations.Beta; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A subinterface of {@link ValueGraph} which adds mutation methods. When mutation is not required, @@ -30,7 +30,6 @@ * @since 20.0 */ @Beta -@ElementTypesAreNonnullByDefault public interface MutableValueGraph extends ValueGraph { /** @@ -61,8 +60,7 @@ public interface MutableValueGraph extends ValueGraph { * #allowsSelfLoops()} */ @CanIgnoreReturnValue - @CheckForNull - V putEdgeValue(N nodeU, N nodeV, V value); + @Nullable V putEdgeValue(N nodeU, N nodeV, V value); /** * Adds an edge connecting {@code endpoints} if one is not already present, and sets a value for @@ -86,8 +84,7 @@ public interface MutableValueGraph extends ValueGraph { * @since 27.1 */ @CanIgnoreReturnValue - @CheckForNull - V putEdgeValue(EndpointPair endpoints, V value); + @Nullable V putEdgeValue(EndpointPair endpoints, V value); /** * Removes {@code node} if it is present; all edges incident to {@code node} will also be removed. @@ -104,8 +101,7 @@ public interface MutableValueGraph extends ValueGraph { * nodeV}, or null if there was no such edge. */ @CanIgnoreReturnValue - @CheckForNull - V removeEdge(N nodeU, N nodeV); + @Nullable V removeEdge(N nodeU, N nodeV); /** * Removes the edge connecting {@code endpoints}, if it is present. @@ -117,6 +113,5 @@ public interface MutableValueGraph extends ValueGraph { * @since 27.1 */ @CanIgnoreReturnValue - @CheckForNull - V removeEdge(EndpointPair endpoints); + @Nullable V removeEdge(EndpointPair endpoints); } diff --git a/android/guava/src/com/google/common/graph/Network.java b/android/guava/src/com/google/common/graph/Network.java index 70e39cdc083c..9f05b5c66509 100644 --- a/android/guava/src/com/google/common/graph/Network.java +++ b/android/guava/src/com/google/common/graph/Network.java @@ -19,7 +19,7 @@ import com.google.common.annotations.Beta; import com.google.errorprone.annotations.DoNotMock; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An interface for {@code - * MutableNetwork graph = NetworkBuilder.directed().build(); + * MutableNetwork network = NetworkBuilder.directed().build(); * }

    * *

    {@link NetworkBuilder#build()} returns an instance of {@link MutableNetwork}, which is a * subtype of {@code Network} that provides methods for adding and removing nodes and edges. If you - * do not need to mutate a graph (e.g. if you write a method than runs a read-only algorithm on the - * graph), you should use the non-mutating {@link Network} interface, or an {@link + * do not need to mutate a network (e.g. if you write a method than runs a read-only algorithm on + * the network), you should use the non-mutating {@link Network} interface, or an {@link * ImmutableNetwork}. * *

    You can create an immutable copy of an existing {@code Network} using {@link * ImmutableNetwork#copyOf(Network)}: * *

    {@code
    - * ImmutableNetwork immutableGraph = ImmutableNetwork.copyOf(graph);
    + * ImmutableNetwork immutableGraph = ImmutableNetwork.copyOf(network);
      * }
    * *

    Instances of {@link ImmutableNetwork} do not implement {@link MutableNetwork} (obviously!) and @@ -103,7 +103,6 @@ */ @Beta @DoNotMock("Use NetworkBuilder to create a real instance") -@ElementTypesAreNonnullByDefault public interface Network extends SuccessorsFunction, PredecessorsFunction { // // Network-level accessors @@ -160,69 +159,135 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti // /** - * Returns the nodes which have an incident edge in common with {@code node} in this network. + * Returns a live view of the nodes which have an incident edge in common with {@code node} in + * this network. * *

    This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this network */ Set adjacentNodes(N node); /** - * Returns all nodes in this network adjacent to {@code node} which can be reached by traversing - * {@code node}'s incoming edges against the direction (if any) of the edge. + * Returns a live view of all nodes in this network adjacent to {@code node} which can be reached + * by traversing {@code node}'s incoming edges against the direction (if any) of the edge. * *

    In an undirected network, this is equivalent to {@link #adjacentNodes(Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * returned by this method will be invalidated, and will throw {@code IllegalStateException} if it + * is accessed in any way. + * * @throws IllegalArgumentException if {@code node} is not an element of this network */ @Override Set predecessors(N node); /** - * Returns all nodes in this network adjacent to {@code node} which can be reached by traversing - * {@code node}'s outgoing edges in the direction (if any) of the edge. + * Returns a live view of all nodes in this network adjacent to {@code node} which can be reached + * by traversing {@code node}'s outgoing edges in the direction (if any) of the edge. * *

    In an undirected network, this is equivalent to {@link #adjacentNodes(Object)}. * *

    This is not the same as "all nodes reachable from {@code node} by following outgoing * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this network */ @Override Set successors(N node); /** - * Returns the edges whose {@link #incidentNodes(Object) incident nodes} in this network include - * {@code node}. + * Returns a live view of the edges whose {@link #incidentNodes(Object) incident nodes} in this + * network include {@code node}. * *

    This is equal to the union of {@link #inEdges(Object)} and {@link #outEdges(Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this network + * @since 24.0 */ Set incidentEdges(N node); /** - * Returns all edges in this network which can be traversed in the direction (if any) of the edge - * to end at {@code node}. + * Returns a live view of all edges in this network which can be traversed in the direction (if + * any) of the edge to end at {@code node}. * *

    In a directed network, an incoming edge's {@link EndpointPair#target()} equals {@code node}. * *

    In an undirected network, this is equivalent to {@link #incidentEdges(Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this network */ Set inEdges(N node); /** - * Returns all edges in this network which can be traversed in the direction (if any) of the edge - * starting from {@code node}. + * Returns a live view of all edges in this network which can be traversed in the direction (if + * any) of the edge starting from {@code node}. * *

    In a directed network, an outgoing edge's {@link EndpointPair#source()} equals {@code node}. * *

    In an undirected network, this is equivalent to {@link #incidentEdges(Object)}. * + *

    If {@code node} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this network */ Set outEdges(N node); @@ -270,21 +335,48 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti EndpointPair incidentNodes(E edge); /** - * Returns the edges which have an {@link #incidentNodes(Object) incident node} in common with - * {@code edge}. An edge is not considered adjacent to itself. + * Returns a live view of the edges which have an {@link #incidentNodes(Object) incident node} in + * common with {@code edge}. An edge is not considered adjacent to itself. + * + *

    If {@code edge} is removed from the network after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code edge} is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    * * @throws IllegalArgumentException if {@code edge} is not an element of this network */ Set adjacentEdges(E edge); /** - * Returns the set of edges that each directly connect {@code nodeU} to {@code nodeV}. + * Returns a live view of the set of edges that each directly connect {@code nodeU} to {@code + * nodeV}. * *

    In an undirected network, this is equal to {@code edgesConnecting(nodeV, nodeU)}. * - *

    The resulting set of edges will be parallel (i.e. have equal {@link #incidentNodes(Object)}. - * If this network does not {@link #allowsParallelEdges() allow parallel edges}, the resulting set - * will contain at most one edge (equivalent to {@code edgeConnecting(nodeU, nodeV).asSet()}). + *

    The resulting set of edges will be parallel (i.e. have equal {@link + * #incidentNodes(Object)}). If this network does not {@link #allowsParallelEdges() allow parallel + * edges}, the resulting set will contain at most one edge (equivalent to {@code + * edgeConnecting(nodeU, nodeV).asSet()}). + * + *

    If either {@code nodeU} or {@code nodeV} are removed from the network after this method is + * called, the {@code Set} {@code view} returned by this method will be invalidated, and will + * throw {@code IllegalStateException} if it is accessed in any way, with the following + * exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code nodeU} or {@code nodeV} are re-added to the network after having been removed, + * {@code view}'s behavior is undefined + *
    * * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this * network @@ -292,17 +384,31 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti Set edgesConnecting(N nodeU, N nodeV); /** - * Returns the set of edges that each directly connect {@code endpoints} (in the order, if any, - * specified by {@code endpoints}). + * Returns a live view of the set of edges that each directly connect {@code endpoints} (in the + * order, if any, specified by {@code endpoints}). * - *

    The resulting set of edges will be parallel (i.e. have equal {@link #incidentNodes(Object)}. - * If this network does not {@link #allowsParallelEdges() allow parallel edges}, the resulting set - * will contain at most one edge (equivalent to {@code edgeConnecting(endpoints).asSet()}). + *

    The resulting set of edges will be parallel (i.e. have equal {@link + * #incidentNodes(Object)}). If this network does not {@link #allowsParallelEdges() allow parallel + * edges}, the resulting set will contain at most one edge (equivalent to {@code + * edgeConnecting(endpoints).asSet()}). * *

    If this network is directed, {@code endpoints} must be ordered. * + *

    If either element of {@code endpoints} is removed from the network after this method is + * called, the {@code Set} {@code view} returned by this method will be invalidated, and will + * throw {@code IllegalStateException} if it is accessed in any way, with the following + * exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if either endpoint is re-added to the network after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if either endpoint is not an element of this network - * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @throws IllegalArgumentException if the endpoints are unordered and the network is directed * @since 27.1 */ Set edgesConnecting(EndpointPair endpoints); @@ -319,30 +425,28 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti * network * @since 23.0 */ - @CheckForNull - E edgeConnectingOrNull(N nodeU, N nodeV); + @Nullable E edgeConnectingOrNull(N nodeU, N nodeV); /** * Returns the single edge that directly connects {@code endpoints} (in the order, if any, * specified by {@code endpoints}), if one is present, or {@code null} if no such edge exists. * - *

    If this graph is directed, the endpoints must be ordered. + *

    If this network is directed, the endpoints must be ordered. * * @throws IllegalArgumentException if there are multiple parallel edges connecting {@code nodeU} * to {@code nodeV} * @throws IllegalArgumentException if either endpoint is not an element of this network - * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @throws IllegalArgumentException if the endpoints are unordered and the network is directed * @since 27.1 */ - @CheckForNull - E edgeConnectingOrNull(EndpointPair endpoints); + @Nullable E edgeConnectingOrNull(EndpointPair endpoints); /** * Returns true if there is an edge that directly connects {@code nodeU} to {@code nodeV}. This is * equivalent to {@code nodes().contains(nodeU) && successors(nodeU).contains(nodeV)}, and to * {@code edgeConnectingOrNull(nodeU, nodeV) != null}. * - *

    In an undirected graph, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. + *

    In an undirected network, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. * * @since 23.0 */ @@ -353,8 +457,8 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti * any, specified by {@code endpoints}). * *

    Unlike the other {@code EndpointPair}-accepting methods, this method does not throw if the - * endpoints are unordered and the graph is directed; it simply returns {@code false}. This is for - * consistency with {@link Graph#hasEdgeConnecting(EndpointPair)} and {@link + * endpoints are unordered and the network is directed; it simply returns {@code false}. This is + * for consistency with {@link Graph#hasEdgeConnecting(EndpointPair)} and {@link * ValueGraph#hasEdgeConnecting(EndpointPair)}. * * @since 27.1 @@ -386,7 +490,7 @@ public interface Network extends SuccessorsFunction, PredecessorsFuncti *

    A reference implementation of this is provided by {@link AbstractNetwork#equals(Object)}. */ @Override - boolean equals(@CheckForNull Object object); + boolean equals(@Nullable Object object); /** * Returns the hash code for this network. The hash code of a network is defined as the hash code diff --git a/android/guava/src/com/google/common/graph/NetworkBuilder.java b/android/guava/src/com/google/common/graph/NetworkBuilder.java index 968944493c7b..9af9acc74e3f 100644 --- a/android/guava/src/com/google/common/graph/NetworkBuilder.java +++ b/android/guava/src/com/google/common/graph/NetworkBuilder.java @@ -21,20 +21,26 @@ import com.google.common.annotations.Beta; import com.google.common.base.Optional; +import com.google.errorprone.annotations.CanIgnoreReturnValue; /** * A builder for constructing instances of {@link MutableNetwork} or {@link ImmutableNetwork} with * user-defined properties. * - *

    A network built by this class will have the following properties by default: + *

    A {@code Network} built by this class has the following default properties: * *

      *
    • does not allow parallel edges *
    • does not allow self-loops *
    • orders {@link Network#nodes()} and {@link Network#edges()} in the order in which the - * elements were added + * elements were added (insertion order) *
    * + *

    {@code Network}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * *

    Examples of use: * *

    {@code
    @@ -67,7 +73,6 @@
      * @since 20.0
      */
     @Beta
    -@ElementTypesAreNonnullByDefault
     public final class NetworkBuilder extends AbstractGraphBuilder {
       boolean allowsParallelEdges = false;
       ElementOrder edgeOrder = ElementOrder.insertion();
    @@ -122,6 +127,7 @@ public  ImmutableNetwork.Builder immutable()
        *
        * 

    The default value is {@code false}. */ + @CanIgnoreReturnValue public NetworkBuilder allowsParallelEdges(boolean allowsParallelEdges) { this.allowsParallelEdges = allowsParallelEdges; return this; @@ -134,6 +140,7 @@ public NetworkBuilder allowsParallelEdges(boolean allowsParallelEdges) { * *

    The default value is {@code false}. */ + @CanIgnoreReturnValue public NetworkBuilder allowsSelfLoops(boolean allowsSelfLoops) { this.allowsSelfLoops = allowsSelfLoops; return this; @@ -144,6 +151,7 @@ public NetworkBuilder allowsSelfLoops(boolean allowsSelfLoops) { * * @throws IllegalArgumentException if {@code expectedNodeCount} is negative */ + @CanIgnoreReturnValue public NetworkBuilder expectedNodeCount(int expectedNodeCount) { this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); return this; @@ -154,6 +162,7 @@ public NetworkBuilder expectedNodeCount(int expectedNodeCount) { * * @throws IllegalArgumentException if {@code expectedEdgeCount} is negative */ + @CanIgnoreReturnValue public NetworkBuilder expectedEdgeCount(int expectedEdgeCount) { this.expectedEdgeCount = Optional.of(checkNonNegative(expectedEdgeCount)); return this; diff --git a/android/guava/src/com/google/common/graph/NetworkConnections.java b/android/guava/src/com/google/common/graph/NetworkConnections.java index 94d1780d3135..940d6c2074b5 100644 --- a/android/guava/src/com/google/common/graph/NetworkConnections.java +++ b/android/guava/src/com/google/common/graph/NetworkConnections.java @@ -18,7 +18,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An interface for representing and manipulating an origin node's adjacent nodes and incident edges @@ -28,7 +28,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault interface NetworkConnections { Set adjacentNodes(); @@ -62,8 +61,7 @@ interface NetworkConnections { *

    In the undirected case, returns {@code null} if {@code isSelfLoop} is true. */ @CanIgnoreReturnValue - @CheckForNull - N removeInEdge(E edge, boolean isSelfLoop); + @Nullable N removeInEdge(E edge, boolean isSelfLoop); /** Remove {@code edge} from the set of outgoing edges. Returns the former successor node. */ @CanIgnoreReturnValue diff --git a/android/guava/src/com/google/common/graph/ParametricNullness.java b/android/guava/src/com/google/common/graph/ParametricNullness.java index 87ff930a4aac..67db8773c3d0 100644 --- a/android/guava/src/com/google/common/graph/ParametricNullness.java +++ b/android/guava/src/com/google/common/graph/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/graph/PredecessorsFunction.java b/android/guava/src/com/google/common/graph/PredecessorsFunction.java index 750a8acd603b..f9ca48ae77b9 100644 --- a/android/guava/src/com/google/common/graph/PredecessorsFunction.java +++ b/android/guava/src/com/google/common/graph/PredecessorsFunction.java @@ -80,7 +80,6 @@ */ @Beta @DoNotMock("Implement with a lambda, or use GraphBuilder to build a Graph with the desired edges") -@ElementTypesAreNonnullByDefault public interface PredecessorsFunction { /** diff --git a/android/guava/src/com/google/common/graph/StandardMutableGraph.java b/android/guava/src/com/google/common/graph/StandardMutableGraph.java index 1be9640f419e..840dd5e095a5 100644 --- a/android/guava/src/com/google/common/graph/StandardMutableGraph.java +++ b/android/guava/src/com/google/common/graph/StandardMutableGraph.java @@ -28,7 +28,6 @@ * @author James Sexton * @param Node parameter type */ -@ElementTypesAreNonnullByDefault final class StandardMutableGraph extends ForwardingGraph implements MutableGraph { private final MutableValueGraph backingValueGraph; diff --git a/android/guava/src/com/google/common/graph/StandardMutableNetwork.java b/android/guava/src/com/google/common/graph/StandardMutableNetwork.java index c58b6d39093e..23512b6f97d5 100644 --- a/android/guava/src/com/google/common/graph/StandardMutableNetwork.java +++ b/android/guava/src/com/google/common/graph/StandardMutableNetwork.java @@ -40,7 +40,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class StandardMutableNetwork extends StandardNetwork implements MutableNetwork { diff --git a/android/guava/src/com/google/common/graph/StandardMutableValueGraph.java b/android/guava/src/com/google/common/graph/StandardMutableValueGraph.java index 0ea641a5b1cd..bad4eb7626bd 100644 --- a/android/guava/src/com/google/common/graph/StandardMutableValueGraph.java +++ b/android/guava/src/com/google/common/graph/StandardMutableValueGraph.java @@ -24,8 +24,9 @@ import static com.google.common.graph.Graphs.checkPositive; import static java.util.Objects.requireNonNull; +import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Standard implementation of {@link MutableValueGraph} that supports both directed and undirected @@ -40,7 +41,6 @@ * @param Node parameter type * @param Value parameter type */ -@ElementTypesAreNonnullByDefault final class StandardMutableValueGraph extends StandardValueGraph implements MutableValueGraph { @@ -84,8 +84,7 @@ private GraphConnections addNodeInternal(N node) { @Override @CanIgnoreReturnValue - @CheckForNull - public V putEdgeValue(N nodeU, N nodeV, V value) { + public @Nullable V putEdgeValue(N nodeU, N nodeV, V value) { checkNotNull(nodeU, "nodeU"); checkNotNull(nodeV, "nodeV"); checkNotNull(value, "value"); @@ -112,8 +111,7 @@ public V putEdgeValue(N nodeU, N nodeV, V value) { @Override @CanIgnoreReturnValue - @CheckForNull - public V putEdgeValue(EndpointPair endpoints, V value) { + public @Nullable V putEdgeValue(EndpointPair endpoints, V value) { validateEndpoints(endpoints); return putEdgeValue(endpoints.nodeU(), endpoints.nodeV(), value); } @@ -136,17 +134,21 @@ public boolean removeNode(N node) { } } - for (N successor : connections.successors()) { + for (N successor : ImmutableList.copyOf(connections.successors())) { // requireNonNull is safe because the node is a successor. requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node); + requireNonNull(connections.removeSuccessor(successor)); --edgeCount; } if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal. - for (N predecessor : connections.predecessors()) { + // Since views are returned, we need to copy the predecessors that will be removed. + // Thus we avoid modifying the underlying view while iterating over it. + for (N predecessor : ImmutableList.copyOf(connections.predecessors())) { // requireNonNull is safe because the node is a predecessor. checkState( requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node) != null); + connections.removePredecessor(predecessor); --edgeCount; } } @@ -157,8 +159,7 @@ public boolean removeNode(N node) { @Override @CanIgnoreReturnValue - @CheckForNull - public V removeEdge(N nodeU, N nodeV) { + public @Nullable V removeEdge(N nodeU, N nodeV) { checkNotNull(nodeU, "nodeU"); checkNotNull(nodeV, "nodeV"); @@ -178,8 +179,7 @@ public V removeEdge(N nodeU, N nodeV) { @Override @CanIgnoreReturnValue - @CheckForNull - public V removeEdge(EndpointPair endpoints) { + public @Nullable V removeEdge(EndpointPair endpoints) { validateEndpoints(endpoints); return removeEdge(endpoints.nodeU(), endpoints.nodeV()); } diff --git a/android/guava/src/com/google/common/graph/StandardNetwork.java b/android/guava/src/com/google/common/graph/StandardNetwork.java index 2aa103f99683..19f9e47887ff 100644 --- a/android/guava/src/com/google/common/graph/StandardNetwork.java +++ b/android/guava/src/com/google/common/graph/StandardNetwork.java @@ -48,7 +48,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault class StandardNetwork extends AbstractNetwork { private final boolean isDirected; private final boolean allowsParallelEdges; @@ -130,7 +129,7 @@ public ElementOrder edgeOrder() { @Override public Set incidentEdges(N node) { - return checkedConnections(node).incidentEdges(); + return nodeInvalidatableSet(checkedConnections(node).incidentEdges(), node); } @Override @@ -143,7 +142,7 @@ public EndpointPair incidentNodes(E edge) { @Override public Set adjacentNodes(N node) { - return checkedConnections(node).adjacentNodes(); + return nodeInvalidatableSet(checkedConnections(node).adjacentNodes(), node); } @Override @@ -153,27 +152,27 @@ public Set edgesConnecting(N nodeU, N nodeV) { return ImmutableSet.of(); } checkArgument(containsNode(nodeV), NODE_NOT_IN_GRAPH, nodeV); - return connectionsU.edgesConnecting(nodeV); + return nodePairInvalidatableSet(connectionsU.edgesConnecting(nodeV), nodeU, nodeV); } @Override public Set inEdges(N node) { - return checkedConnections(node).inEdges(); + return nodeInvalidatableSet(checkedConnections(node).inEdges(), node); } @Override public Set outEdges(N node) { - return checkedConnections(node).outEdges(); + return nodeInvalidatableSet(checkedConnections(node).outEdges(), node); } @Override public Set predecessors(N node) { - return checkedConnections(node).predecessors(); + return nodeInvalidatableSet(checkedConnections(node).predecessors(), node); } @Override public Set successors(N node) { - return checkedConnections(node).successors(); + return nodeInvalidatableSet(checkedConnections(node).successors(), node); } final NetworkConnections checkedConnections(N node) { diff --git a/android/guava/src/com/google/common/graph/StandardValueGraph.java b/android/guava/src/com/google/common/graph/StandardValueGraph.java index ab3ae582b55e..568f0f63274d 100644 --- a/android/guava/src/com/google/common/graph/StandardValueGraph.java +++ b/android/guava/src/com/google/common/graph/StandardValueGraph.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Standard implementation of {@link ValueGraph} that supports the options supplied by {@link @@ -43,7 +43,6 @@ * @param Node parameter type * @param Value parameter type */ -@ElementTypesAreNonnullByDefault class StandardValueGraph extends AbstractValueGraph { private final boolean isDirected; private final boolean allowsSelfLoops; @@ -103,29 +102,30 @@ public ElementOrder nodeOrder() { @Override public Set adjacentNodes(N node) { - return checkedConnections(node).adjacentNodes(); + return nodeInvalidatableSet(checkedConnections(node).adjacentNodes(), node); } @Override public Set predecessors(N node) { - return checkedConnections(node).predecessors(); + return nodeInvalidatableSet(checkedConnections(node).predecessors(), node); } @Override public Set successors(N node) { - return checkedConnections(node).successors(); + return nodeInvalidatableSet(checkedConnections(node).successors(), node); } @Override public Set> incidentEdges(N node) { GraphConnections connections = checkedConnections(node); - - return new IncidentEdgeSet(this, node) { - @Override - public Iterator> iterator() { - return connections.incidentEdgeIterator(node); - } - }; + IncidentEdgeSet incident = + new IncidentEdgeSet(this, node) { + @Override + public Iterator> iterator() { + return connections.incidentEdgeIterator(node); + } + }; + return nodeInvalidatableSet(incident, node); } @Override @@ -141,14 +141,12 @@ public boolean hasEdgeConnecting(EndpointPair endpoints) { } @Override - @CheckForNull - public V edgeValueOrDefault(N nodeU, N nodeV, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue) { return edgeValueOrDefaultInternal(checkNotNull(nodeU), checkNotNull(nodeV), defaultValue); } @Override - @CheckForNull - public V edgeValueOrDefault(EndpointPair endpoints, @CheckForNull V defaultValue) { + public @Nullable V edgeValueOrDefault(EndpointPair endpoints, @Nullable V defaultValue) { validateEndpoints(endpoints); return edgeValueOrDefaultInternal(endpoints.nodeU(), endpoints.nodeV(), defaultValue); } @@ -167,7 +165,7 @@ private final GraphConnections checkedConnections(N node) { return connections; } - final boolean containsNode(@CheckForNull N node) { + final boolean containsNode(@Nullable N node) { return nodeConnections.containsKey(node); } @@ -176,8 +174,7 @@ private final boolean hasEdgeConnectingInternal(N nodeU, N nodeV) { return (connectionsU != null) && connectionsU.successors().contains(nodeV); } - @CheckForNull - private final V edgeValueOrDefaultInternal(N nodeU, N nodeV, @CheckForNull V defaultValue) { + private final @Nullable V edgeValueOrDefaultInternal(N nodeU, N nodeV, @Nullable V defaultValue) { GraphConnections connectionsU = nodeConnections.get(nodeU); V value = (connectionsU == null) ? null : connectionsU.value(nodeV); // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. diff --git a/android/guava/src/com/google/common/graph/SuccessorsFunction.java b/android/guava/src/com/google/common/graph/SuccessorsFunction.java index c29bb4b94360..f74f437c2937 100644 --- a/android/guava/src/com/google/common/graph/SuccessorsFunction.java +++ b/android/guava/src/com/google/common/graph/SuccessorsFunction.java @@ -80,7 +80,6 @@ */ @Beta @DoNotMock("Implement with a lambda, or use GraphBuilder to build a Graph with the desired edges") -@ElementTypesAreNonnullByDefault public interface SuccessorsFunction { /** diff --git a/android/guava/src/com/google/common/graph/Traverser.java b/android/guava/src/com/google/common/graph/Traverser.java index fb594b440365..b4ddda01cb55 100644 --- a/android/guava/src/com/google/common/graph/Traverser.java +++ b/android/guava/src/com/google/common/graph/Traverser.java @@ -29,7 +29,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An object that can traverse the nodes that are reachable from a specified (set of) start node(s) @@ -64,7 +64,6 @@ @DoNotMock( "Call forGraph or forTree, passing a lambda or a Graph with the desired edges (built with" + " GraphBuilder)") -@ElementTypesAreNonnullByDefault public abstract class Traverser { private final SuccessorsFunction successorFunction; @@ -114,8 +113,8 @@ Traversal newTraversal() { * structure being traversed is, in addition to being a tree/forest, also defined recursively. * This is because the {@code forTree()}-based implementations don't keep track of visited nodes, - * and therefore don't need to call `equals()` or `hashCode()` on the node objects; this saves - * both time and space versus traversing the same graph using {@code forGraph()}. + * and therefore don't need to call {@code equals()} or {@code hashCode()} on the node objects; + * this saves both time and space versus traversing the same graph using {@code forGraph()}. * *

    Providing a graph to be traversed for which there is more than one path from the start * node(s) to any node may lead to: @@ -240,12 +239,7 @@ public final Iterable breadthFirst(N startNode) { */ public final Iterable breadthFirst(Iterable startNodes) { ImmutableSet validated = validate(startNodes); - return new Iterable() { - @Override - public Iterator iterator() { - return newTraversal().breadthFirst(validated.iterator()); - } - }; + return () -> newTraversal().breadthFirst(validated.iterator()); } /** @@ -295,12 +289,7 @@ public final Iterable depthFirstPreOrder(N startNode) { */ public final Iterable depthFirstPreOrder(Iterable startNodes) { ImmutableSet validated = validate(startNodes); - return new Iterable() { - @Override - public Iterator iterator() { - return newTraversal().preOrder(validated.iterator()); - } - }; + return () -> newTraversal().preOrder(validated.iterator()); } /** @@ -350,12 +339,7 @@ public final Iterable depthFirstPostOrder(N startNode) { */ public final Iterable depthFirstPostOrder(Iterable startNodes) { ImmutableSet validated = validate(startNodes); - return new Iterable() { - @Override - public Iterator iterator() { - return newTraversal().postOrder(validated.iterator()); - } - }; + return () -> newTraversal().postOrder(validated.iterator()); } abstract Traversal newTraversal(); @@ -385,8 +369,7 @@ static Traversal inGraph(SuccessorsFunction graph) { Set visited = new HashSet<>(); return new Traversal(graph) { @Override - @CheckForNull - N visitNext(Deque> horizon) { + @Nullable N visitNext(Deque> horizon) { Iterator top = horizon.getFirst(); while (top.hasNext()) { N element = top.next(); @@ -396,7 +379,7 @@ N visitNext(Deque> horizon) { * requireNonNull(top.next())`) once our checker supports it. * * (The problem is likely - * https://github.com/jspecify/nullness-checker-for-checker-framework/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecAnnotatedTypeFactory.java#L896) + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecAnnotatedTypeFactory.java#L896) */ requireNonNull(element); if (visited.add(element)) { @@ -411,9 +394,8 @@ N visitNext(Deque> horizon) { static Traversal inTree(SuccessorsFunction tree) { return new Traversal(tree) { - @CheckForNull @Override - N visitNext(Deque> horizon) { + @Nullable N visitNext(Deque> horizon) { Iterator top = horizon.getFirst(); if (top.hasNext()) { return checkNotNull(top.next()); @@ -443,8 +425,7 @@ private Iterator topDown(Iterator startNodes, InsertionOrder ord horizon.add(startNodes); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { do { N next = visitNext(horizon); if (next != null) { @@ -468,8 +449,7 @@ final Iterator postOrder(Iterator startNodes) { horizon.add(startNodes); return new AbstractIterator() { @Override - @CheckForNull - protected N computeNext() { + protected @Nullable N computeNext() { for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) { Iterator successors = successorFunction.successors(next).iterator(); if (!successors.hasNext()) { @@ -497,8 +477,7 @@ protected N computeNext() { * into {@code horizon} between calls to {@code visitNext()}. This causes them to receive * additional values interleaved with those shown above.) */ - @CheckForNull - abstract N visitNext(Deque> horizon); + abstract @Nullable N visitNext(Deque> horizon); } /** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */ diff --git a/android/guava/src/com/google/common/graph/UndirectedGraphConnections.java b/android/guava/src/com/google/common/graph/UndirectedGraphConnections.java index 4eeb2328fe0e..ca3e880ea194 100644 --- a/android/guava/src/com/google/common/graph/UndirectedGraphConnections.java +++ b/android/guava/src/com/google/common/graph/UndirectedGraphConnections.java @@ -28,7 +28,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link GraphConnections} for undirected graphs. @@ -37,7 +37,6 @@ * @param Node parameter type * @param Value parameter type */ -@ElementTypesAreNonnullByDefault final class UndirectedGraphConnections implements GraphConnections { private final Map adjacentNodeValues; @@ -85,8 +84,7 @@ public Iterator> incidentEdgeIterator(N thisNode) { } @Override - @CheckForNull - public V value(N node) { + public @Nullable V value(N node) { return adjacentNodeValues.get(node); } @@ -97,8 +95,7 @@ public void removePredecessor(N node) { } @Override - @CheckForNull - public V removeSuccessor(N node) { + public @Nullable V removeSuccessor(N node) { return adjacentNodeValues.remove(node); } @@ -109,8 +106,7 @@ public void addPredecessor(N node, V value) { } @Override - @CheckForNull - public V addSuccessor(N node, V value) { + public @Nullable V addSuccessor(N node, V value) { return adjacentNodeValues.put(node, value); } } diff --git a/android/guava/src/com/google/common/graph/UndirectedMultiNetworkConnections.java b/android/guava/src/com/google/common/graph/UndirectedMultiNetworkConnections.java index 6caac3b715dd..a93d04d8faab 100644 --- a/android/guava/src/com/google/common/graph/UndirectedMultiNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/UndirectedMultiNetworkConnections.java @@ -30,7 +30,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An implementation of {@link NetworkConnections} for undirected networks with parallel edges. @@ -39,7 +39,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class UndirectedMultiNetworkConnections extends AbstractUndirectedNetworkConnections { @@ -56,7 +55,7 @@ static UndirectedMultiNetworkConnections ofImmutable(Map inci return new UndirectedMultiNetworkConnections<>(ImmutableMap.copyOf(incidentEdges)); } - @CheckForNull @LazyInit private transient Reference> adjacentNodesReference; + @LazyInit private transient @Nullable Reference> adjacentNodesReference; @Override public Set adjacentNodes() { @@ -83,8 +82,7 @@ public int size() { } @Override - @CheckForNull - public N removeInEdge(E edge, boolean isSelfLoop) { + public @Nullable N removeInEdge(E edge, boolean isSelfLoop) { if (!isSelfLoop) { return removeOutEdge(edge); } @@ -117,8 +115,7 @@ public void addOutEdge(E edge, N node) { } } - @CheckForNull - private static T getReference(@CheckForNull Reference reference) { + private static @Nullable T getReference(@Nullable Reference reference) { return (reference == null) ? null : reference.get(); } } diff --git a/android/guava/src/com/google/common/graph/UndirectedNetworkConnections.java b/android/guava/src/com/google/common/graph/UndirectedNetworkConnections.java index 190897f8825d..5d3473c20ece 100644 --- a/android/guava/src/com/google/common/graph/UndirectedNetworkConnections.java +++ b/android/guava/src/com/google/common/graph/UndirectedNetworkConnections.java @@ -32,7 +32,6 @@ * @param Node parameter type * @param Edge parameter type */ -@ElementTypesAreNonnullByDefault final class UndirectedNetworkConnections extends AbstractUndirectedNetworkConnections { UndirectedNetworkConnections(Map incidentEdgeMap) { diff --git a/android/guava/src/com/google/common/graph/ValueGraph.java b/android/guava/src/com/google/common/graph/ValueGraph.java index bd3cf36401e3..9d9bd8d6b90b 100644 --- a/android/guava/src/com/google/common/graph/ValueGraph.java +++ b/android/guava/src/com/google/common/graph/ValueGraph.java @@ -19,7 +19,7 @@ import com.google.common.annotations.Beta; import java.util.Collection; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An interface for extends BaseGraph { // // ValueGraph-level accessors @@ -166,45 +165,86 @@ public interface ValueGraph extends BaseGraph { // /** - * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * Returns a live view of the nodes which have an incident edge in common with {@code node} in + * this graph. * *

    This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set adjacentNodes(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s incoming edges against the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s incoming edges against the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * returned by this method will be invalidated, and will throw {@code IllegalStateException} if it + * is accessed in any way. + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set predecessors(N node); /** - * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing - * {@code node}'s outgoing edges in the direction (if any) of the edge. + * Returns a live view of all nodes in this graph adjacent to {@code node} which can be reached by + * traversing {@code node}'s outgoing edges in the direction (if any) of the edge. * *

    In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. * *

    This is not the same as "all nodes reachable from {@code node} by following outgoing * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph */ @Override Set successors(N node); /** - * Returns the edges in this graph whose endpoints include {@code node}. + * Returns a live view of the edges in this graph whose endpoints include {@code node}. * *

    This is equal to the union of incoming and outgoing edges. * + *

    If {@code node} is removed from the graph after this method is called, the {@code Set} + * {@code view} returned by this method will be invalidated, and will throw {@code + * IllegalStateException} if it is accessed in any way, with the following exceptions: + * + *

      + *
    • {@code view.equals(view)} evaluates to {@code true} (but any other {@code equals()} + * expression involving {@code view} will throw) + *
    • {@code hashCode()} does not throw + *
    • if {@code node} is re-added to the graph after having been removed, {@code view}'s + * behavior is undefined + *
    + * * @throws IllegalArgumentException if {@code node} is not an element of this graph * @since 24.0 */ @@ -286,8 +326,7 @@ public interface ValueGraph extends BaseGraph { * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this * graph */ - @CheckForNull - V edgeValueOrDefault(N nodeU, N nodeV, @CheckForNull V defaultValue); + @Nullable V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue); /** * Returns the value of the edge that connects {@code endpoints} (in the order, if any, specified @@ -299,8 +338,7 @@ public interface ValueGraph extends BaseGraph { * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed * @since 27.1 */ - @CheckForNull - V edgeValueOrDefault(EndpointPair endpoints, @CheckForNull V defaultValue); + @Nullable V edgeValueOrDefault(EndpointPair endpoints, @Nullable V defaultValue); // // ValueGraph identity @@ -316,7 +354,8 @@ public interface ValueGraph extends BaseGraph { *
  • A and B have equal {@link #isDirected() directedness}. *
  • A and B have equal {@link #nodes() node sets}. *
  • A and B have equal {@link #edges() edge sets}. - *
  • The {@link #edgeValue(Object, Object) value} of a given edge is the same in both A and B. + *
  • The {@link #edgeValueOrDefault(N, N, V) value} of a given edge is the same in both A and + * B. * * *

    Graph properties besides {@link #isDirected() directedness} do not affect equality. @@ -327,12 +366,12 @@ public interface ValueGraph extends BaseGraph { *

    A reference implementation of this is provided by {@link AbstractValueGraph#equals(Object)}. */ @Override - boolean equals(@CheckForNull Object object); + boolean equals(@Nullable Object object); /** * Returns the hash code for this graph. The hash code of a graph is defined as the hash code of a - * map from each of its {@link #edges() edges} to the associated {@link #edgeValue(Object, Object) - * edge value}. + * map from each of its {@link #edges() edges} to the associated {@link #edgeValueOrDefault(N, N, + * V) edge value}. * *

    A reference implementation of this is provided by {@link AbstractValueGraph#hashCode()}. */ diff --git a/android/guava/src/com/google/common/graph/ValueGraphBuilder.java b/android/guava/src/com/google/common/graph/ValueGraphBuilder.java index ce146c325504..0d32004b2707 100644 --- a/android/guava/src/com/google/common/graph/ValueGraphBuilder.java +++ b/android/guava/src/com/google/common/graph/ValueGraphBuilder.java @@ -22,18 +22,25 @@ import com.google.common.annotations.Beta; import com.google.common.base.Optional; +import com.google.errorprone.annotations.CanIgnoreReturnValue; /** * A builder for constructing instances of {@link MutableValueGraph} or {@link ImmutableValueGraph} * with user-defined properties. * - *

    A graph built by this class will have the following properties by default: + *

    A {@code ValueGraph} built by this class has the following default properties: * *

      *
    • does not allow self-loops - *
    • orders {@link Graph#nodes()} in the order in which the elements were added + *
    • orders {@link ValueGraph#nodes()} in the order in which the elements were added (insertion + * order) *
    * + *

    {@code ValueGraph}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * *

    Examples of use: * *

    {@code
    @@ -66,7 +73,6 @@
      * @since 20.0
      */
     @Beta
    -@ElementTypesAreNonnullByDefault
     public final class ValueGraphBuilder extends AbstractGraphBuilder {
     
       /** Creates a new instance with the specified edge directionality. */
    @@ -122,6 +128,7 @@ public  ImmutableValueGraph.Builder immutabl
        *
        * 

    The default value is {@code false}. */ + @CanIgnoreReturnValue public ValueGraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { this.allowsSelfLoops = allowsSelfLoops; return this; @@ -132,6 +139,7 @@ public ValueGraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { * * @throws IllegalArgumentException if {@code expectedNodeCount} is negative */ + @CanIgnoreReturnValue public ValueGraphBuilder expectedNodeCount(int expectedNodeCount) { this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); return this; @@ -173,6 +181,7 @@ public ValueGraphBuilder incidentEdgeOrder( newBuilder.incidentEdgeOrder = checkNotNull(incidentEdgeOrder); return newBuilder; } + /** * Returns an empty {@link MutableValueGraph} with the properties of this {@link * ValueGraphBuilder}. diff --git a/android/guava/src/com/google/common/graph/package-info.java b/android/guava/src/com/google/common/graph/package-info.java index 32d8b0157bb3..7e97756afabf 100644 --- a/android/guava/src/com/google/common/graph/package-info.java +++ b/android/guava/src/com/google/common/graph/package-info.java @@ -22,8 +22,8 @@ * library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.graph; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/hash/AbstractByteHasher.java b/android/guava/src/com/google/common/hash/AbstractByteHasher.java index 9f7e041909f5..bd96e8aead3f 100644 --- a/android/guava/src/com/google/common/hash/AbstractByteHasher.java +++ b/android/guava/src/com/google/common/hash/AbstractByteHasher.java @@ -31,8 +31,6 @@ * * @author Colin Decker */ -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault abstract class AbstractByteHasher extends AbstractHasher { private final ByteBuffer scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); @@ -64,6 +62,7 @@ protected void update(ByteBuffer b) { } /** Updates the sink with the given number of bytes from the buffer. */ + @CanIgnoreReturnValue private Hasher update(int bytes) { try { update(scratch.array(), 0, bytes); @@ -74,12 +73,14 @@ private Hasher update(int bytes) { } @Override + @CanIgnoreReturnValue public Hasher putByte(byte b) { update(b); return this; } @Override + @CanIgnoreReturnValue public Hasher putBytes(byte[] bytes) { checkNotNull(bytes); update(bytes); @@ -87,6 +88,7 @@ public Hasher putBytes(byte[] bytes) { } @Override + @CanIgnoreReturnValue public Hasher putBytes(byte[] bytes, int off, int len) { checkPositionIndexes(off, off + len, bytes.length); update(bytes, off, len); @@ -94,30 +96,35 @@ public Hasher putBytes(byte[] bytes, int off, int len) { } @Override + @CanIgnoreReturnValue public Hasher putBytes(ByteBuffer bytes) { update(bytes); return this; } @Override + @CanIgnoreReturnValue public Hasher putShort(short s) { scratch.putShort(s); return update(Shorts.BYTES); } @Override + @CanIgnoreReturnValue public Hasher putInt(int i) { scratch.putInt(i); return update(Ints.BYTES); } @Override + @CanIgnoreReturnValue public Hasher putLong(long l) { scratch.putLong(l); return update(Longs.BYTES); } @Override + @CanIgnoreReturnValue public Hasher putChar(char c) { scratch.putChar(c); return update(Chars.BYTES); diff --git a/android/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java b/android/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java index 820fe963bf84..15ce417441df 100644 --- a/android/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java +++ b/android/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java @@ -20,7 +20,7 @@ import com.google.errorprone.annotations.Immutable; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An abstract composition of multiple hash functions. {@linkplain #newHasher()} delegates to the @@ -30,7 +30,6 @@ * @author Dimitris Andreou */ @Immutable -@ElementTypesAreNonnullByDefault abstract class AbstractCompositeHashFunction extends AbstractHashFunction { @SuppressWarnings("Immutable") // array not modified after creation diff --git a/android/guava/src/com/google/common/hash/AbstractHashFunction.java b/android/guava/src/com/google/common/hash/AbstractHashFunction.java index 73085560024f..2479b29a502a 100644 --- a/android/guava/src/com/google/common/hash/AbstractHashFunction.java +++ b/android/guava/src/com/google/common/hash/AbstractHashFunction.java @@ -20,7 +20,7 @@ import com.google.errorprone.annotations.Immutable; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Skeleton implementation of {@link HashFunction} in terms of {@link #newHasher()}. @@ -28,7 +28,6 @@ *

    TODO(lowasser): make public */ @Immutable -@ElementTypesAreNonnullByDefault abstract class AbstractHashFunction implements HashFunction { @Override public HashCode hashObject( diff --git a/android/guava/src/com/google/common/hash/AbstractHasher.java b/android/guava/src/com/google/common/hash/AbstractHasher.java index c72e05be05e3..4136b231b99d 100644 --- a/android/guava/src/com/google/common/hash/AbstractHasher.java +++ b/android/guava/src/com/google/common/hash/AbstractHasher.java @@ -18,7 +18,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An abstract implementation of {@link Hasher}, which only requires subtypes to implement {@link @@ -26,25 +26,27 @@ * * @author Dimitris Andreou */ -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault abstract class AbstractHasher implements Hasher { @Override + @CanIgnoreReturnValue public final Hasher putBoolean(boolean b) { return putByte(b ? (byte) 1 : (byte) 0); } @Override + @CanIgnoreReturnValue public final Hasher putDouble(double d) { return putLong(Double.doubleToRawLongBits(d)); } @Override + @CanIgnoreReturnValue public final Hasher putFloat(float f) { return putInt(Float.floatToRawIntBits(f)); } @Override + @CanIgnoreReturnValue public Hasher putUnencodedChars(CharSequence charSequence) { for (int i = 0, len = charSequence.length(); i < len; i++) { putChar(charSequence.charAt(i)); @@ -53,16 +55,19 @@ public Hasher putUnencodedChars(CharSequence charSequence) { } @Override + @CanIgnoreReturnValue public Hasher putString(CharSequence charSequence, Charset charset) { return putBytes(charSequence.toString().getBytes(charset)); } @Override + @CanIgnoreReturnValue public Hasher putBytes(byte[] bytes) { return putBytes(bytes, 0, bytes.length); } @Override + @CanIgnoreReturnValue public Hasher putBytes(byte[] bytes, int off, int len) { Preconditions.checkPositionIndexes(off, off + len, bytes.length); for (int i = 0; i < len; i++) { @@ -72,6 +77,7 @@ public Hasher putBytes(byte[] bytes, int off, int len) { } @Override + @CanIgnoreReturnValue public Hasher putBytes(ByteBuffer b) { if (b.hasArray()) { putBytes(b.array(), b.arrayOffset() + b.position(), b.remaining()); @@ -85,6 +91,7 @@ public Hasher putBytes(ByteBuffer b) { } @Override + @CanIgnoreReturnValue public Hasher putShort(short s) { putByte((byte) s); putByte((byte) (s >>> 8)); @@ -92,6 +99,7 @@ public Hasher putShort(short s) { } @Override + @CanIgnoreReturnValue public Hasher putInt(int i) { putByte((byte) i); putByte((byte) (i >>> 8)); @@ -101,6 +109,7 @@ public Hasher putInt(int i) { } @Override + @CanIgnoreReturnValue public Hasher putLong(long l) { for (int i = 0; i < 64; i += 8) { putByte((byte) (l >>> i)); @@ -109,6 +118,7 @@ public Hasher putLong(long l) { } @Override + @CanIgnoreReturnValue public Hasher putChar(char c) { putByte((byte) c); putByte((byte) (c >>> 8)); @@ -116,6 +126,7 @@ public Hasher putChar(char c) { } @Override + @CanIgnoreReturnValue public Hasher putObject( @ParametricNullness T instance, Funnel funnel) { funnel.funnel(instance, this); diff --git a/android/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java b/android/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java index 4969e35b22ba..54c76de19564 100644 --- a/android/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java +++ b/android/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java @@ -30,7 +30,6 @@ * @author Dimitris Andreou */ @Immutable -@ElementTypesAreNonnullByDefault abstract class AbstractNonStreamingHashFunction extends AbstractHashFunction { @Override public Hasher newHasher() { diff --git a/android/guava/src/com/google/common/hash/AbstractStreamingHasher.java b/android/guava/src/com/google/common/hash/AbstractStreamingHasher.java index a987b48c35f7..e28520d12701 100644 --- a/android/guava/src/com/google/common/hash/AbstractStreamingHasher.java +++ b/android/guava/src/com/google/common/hash/AbstractStreamingHasher.java @@ -28,8 +28,6 @@ * @author Dimitris Andreou */ // TODO(kevinb): this class still needs some design-and-document-for-inheritance love -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault abstract class AbstractStreamingHasher extends AbstractHasher { /** Buffer via which we pass data to the hash algorithm (the implementor) */ private final ByteBuffer buffer; @@ -92,11 +90,13 @@ protected void processRemaining(ByteBuffer bb) { } @Override + @CanIgnoreReturnValue public final Hasher putBytes(byte[] bytes, int off, int len) { return putBytesInternal(ByteBuffer.wrap(bytes, off, len).order(ByteOrder.LITTLE_ENDIAN)); } @Override + @CanIgnoreReturnValue public final Hasher putBytes(ByteBuffer readBuffer) { ByteOrder order = readBuffer.order(); try { @@ -107,6 +107,7 @@ public final Hasher putBytes(ByteBuffer readBuffer) { } } + @CanIgnoreReturnValue private Hasher putBytesInternal(ByteBuffer readBuffer) { // If we have room for all of it, this is easy if (readBuffer.remaining() <= buffer.remaining()) { @@ -143,6 +144,7 @@ private Hasher putBytesInternal(ByteBuffer readBuffer) { */ @Override + @CanIgnoreReturnValue public final Hasher putByte(byte b) { buffer.put(b); munchIfFull(); @@ -150,6 +152,7 @@ public final Hasher putByte(byte b) { } @Override + @CanIgnoreReturnValue public final Hasher putShort(short s) { buffer.putShort(s); munchIfFull(); @@ -157,6 +160,7 @@ public final Hasher putShort(short s) { } @Override + @CanIgnoreReturnValue public final Hasher putChar(char c) { buffer.putChar(c); munchIfFull(); @@ -164,6 +168,7 @@ public final Hasher putChar(char c) { } @Override + @CanIgnoreReturnValue public final Hasher putInt(int i) { buffer.putInt(i); munchIfFull(); @@ -171,6 +176,7 @@ public final Hasher putInt(int i) { } @Override + @CanIgnoreReturnValue public final Hasher putLong(long l) { buffer.putLong(l); munchIfFull(); diff --git a/android/guava/src/com/google/common/hash/BloomFilter.java b/android/guava/src/com/google/common/hash/BloomFilter.java index 6ffe583f8346..22d20dd0e1db 100644 --- a/android/guava/src/com/google/common/hash/BloomFilter.java +++ b/android/guava/src/com/google/common/hash/BloomFilter.java @@ -16,6 +16,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.max; import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; @@ -23,18 +24,22 @@ import com.google.common.base.Predicate; import com.google.common.hash.BloomFilterStrategies.LockFreeBitArray; import com.google.common.math.DoubleMath; +import com.google.common.math.LongMath; import com.google.common.primitives.SignedBytes; import com.google.common.primitives.UnsignedBytes; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.Serializable; import java.math.RoundingMode; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.stream.Collector; +import org.jspecify.annotations.Nullable; /** * A Bloom filter for instances of {@code T}. A Bloom filter offers an approximate containment test @@ -64,7 +69,6 @@ * @since 11.0 (thread-safe since 23.0) */ @Beta -@ElementTypesAreNonnullByDefault public final class BloomFilter implements Predicate, Serializable { /** * A strategy to translate T instances, to {@code numHashFunctions} bit indexes. @@ -116,6 +120,12 @@ interface Strategy extends java.io.Serializable { /** The strategy we employ to map an element T to {@code numHashFunctions} bit indexes. */ private final Strategy strategy; + /** Natural logarithm of 2, used to optimize calculations in Bloom filter sizing. */ + private static final double LOG_TWO = Math.log(2); + + /** Square of the natural logarithm of 2, reused to optimize the bit size calculation. */ + private static final double SQUARED_LOG_TWO = LOG_TWO * LOG_TWO; + /** Creates a BloomFilter. */ private BloomFilter( LockFreeBitArray bits, int numHashFunctions, Funnel funnel, Strategy strategy) { @@ -135,7 +145,7 @@ private BloomFilter( * @since 12.0 */ public BloomFilter copy() { - return new BloomFilter(bits.copy(), numHashFunctions, funnel, strategy); + return new BloomFilter<>(bits.copy(), numHashFunctions, funnel, strategy); } /** @@ -150,6 +160,7 @@ public boolean mightContain(@ParametricNullness T object) { * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #mightContain} * instead. */ + @InlineMe(replacement = "this.mightContain(input)") @Deprecated @Override public boolean apply(@ParametricNullness T input) { @@ -198,7 +209,7 @@ public long approximateElementCount() { long bitSize = bits.bitSize(); long bitCount = bits.bitCount(); - /** + /* * Each insertion is expected to reduce the # of clear bits by a factor of * `numHashFunctions/bitSize`. So, after n insertions, expected bitCount is `bitSize * (1 - (1 - * numHashFunctions/bitSize)^n)`. Solving that for n, and approximating `ln x` as `x - 1` when x @@ -275,7 +286,7 @@ public void putAll(BloomFilter that) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -294,6 +305,76 @@ public int hashCode() { return Objects.hashCode(numHashFunctions, funnel, strategy, bits); } + /** + * Returns a {@code Collector} expecting the specified number of insertions, and yielding a {@link + * BloomFilter} with false positive probability 3%. + * + *

    Note that if the {@code Collector} receives significantly more elements than specified, the + * resulting {@code BloomFilter} will suffer a sharp deterioration of its false positive + * probability. + * + *

    The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

    It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @return a {@code Collector} generating a {@code BloomFilter} of the received elements + * @since 33.4.0 (but since 23.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toBloomFilter( + Funnel funnel, long expectedInsertions) { + return toBloomFilter(funnel, expectedInsertions, 0.03); + } + + /** + * Returns a {@code Collector} expecting the specified number of insertions, and yielding a {@link + * BloomFilter} with the specified expected false positive probability. + * + *

    Note that if the {@code Collector} receives significantly more elements than specified, the + * resulting {@code BloomFilter} will suffer a sharp deterioration of its false positive + * probability. + * + *

    The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

    It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @param fpp the desired false positive probability (must be positive and less than 1.0) + * @return a {@code Collector} generating a {@code BloomFilter} of the received elements + * @since 33.4.0 (but since 23.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector> toBloomFilter( + Funnel funnel, long expectedInsertions, double fpp) { + checkNotNull(funnel); + checkArgument( + expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions); + checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp); + checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp); + return Collector.of( + () -> BloomFilter.create(funnel, expectedInsertions, fpp), + BloomFilter::put, + (bf1, bf2) -> { + bf1.putAll(bf2); + return bf1; + }, + Collector.Characteristics.UNORDERED, + Collector.Characteristics.CONCURRENT); + } + /** * Creates a {@link BloomFilter} with the expected number of insertions and expected false * positive probability. @@ -364,9 +445,9 @@ public int hashCode() { * optimalM(1000, 0.0000000000000001) = 76680 which is less than 10kb. Who cares! */ long numBits = optimalNumOfBits(expectedInsertions, fpp); - int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits); + int numHashFunctions = optimalNumOfHashFunctions(fpp); try { - return new BloomFilter(new LockFreeBitArray(numBits), numHashFunctions, funnel, strategy); + return new BloomFilter<>(new LockFreeBitArray(numBits), numHashFunctions, funnel, strategy); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e); } @@ -434,18 +515,16 @@ public int hashCode() { // 4) For optimal k: m = -nlnp / ((ln2) ^ 2) /** - * Computes the optimal k (number of hashes per element inserted in Bloom filter), given the - * expected insertions and total number of bits in the Bloom filter. + * Computes the optimal number of hash functions (k) for a given false positive probability (p). * *

    See http://en.wikipedia.org/wiki/File:Bloom_filter_fp_probability.svg for the formula. * - * @param n expected insertions (must be positive) - * @param m total number of bits in Bloom filter (must be positive) + * @param p desired false positive probability (must be between 0 and 1, exclusive) */ @VisibleForTesting - static int optimalNumOfHashFunctions(long n, long m) { - // (m / n) * log(2), but avoid truncation due to division! - return Math.max(1, (int) Math.round((double) m / n * Math.log(2))); + static int optimalNumOfHashFunctions(double p) { + // -log(p) / log(2), ensuring the result is rounded to avoid truncation. + return max(1, (int) Math.round(-Math.log(p) / LOG_TWO)); } /** @@ -463,13 +542,17 @@ static long optimalNumOfBits(long n, double p) { if (p == 0) { p = Double.MIN_VALUE; } - return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); + return (long) (-n * Math.log(p) / SQUARED_LOG_TWO); } private Object writeReplace() { return new SerialForm(this); } + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + private static class SerialForm implements Serializable { final long[] data; final int numHashFunctions; @@ -523,6 +606,7 @@ public void writeTo(OutputStream out) throws IOException { * @throws IOException if the InputStream throws an {@code IOException}, or if its data does not * appear to be a BloomFilter serialized using the {@linkplain #writeTo(OutputStream)} method. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public static BloomFilter readFrom( InputStream in, Funnel funnel) throws IOException { checkNotNull(in, "InputStream"); @@ -539,13 +623,22 @@ public void writeTo(OutputStream out) throws IOException { numHashFunctions = UnsignedBytes.toInt(din.readByte()); dataLength = din.readInt(); + /* + * We document in BloomFilterStrategies that we must not change the ordering, and we have a + * test that verifies that we don't do so. + */ + @SuppressWarnings("EnumOrdinal") Strategy strategy = BloomFilterStrategies.values()[strategyOrdinal]; - long[] data = new long[dataLength]; - for (int i = 0; i < data.length; i++) { - data[i] = din.readLong(); + + LockFreeBitArray dataArray = new LockFreeBitArray(LongMath.checkedMultiply(dataLength, 64L)); + for (int i = 0; i < dataLength; i++) { + dataArray.putData(i, din.readLong()); } - return new BloomFilter(new LockFreeBitArray(data), numHashFunctions, funnel, strategy); - } catch (RuntimeException e) { + + return new BloomFilter<>(dataArray, numHashFunctions, funnel, strategy); + } catch (IOException e) { + throw e; + } catch (Exception e) { // sneaky checked exception String message = "Unable to deserialize BloomFilter from InputStream." + " strategyOrdinal: " @@ -557,4 +650,6 @@ public void writeTo(OutputStream out) throws IOException { throw new IOException(message, e); } } + + private static final long serialVersionUID = 0xdecaf; } diff --git a/android/guava/src/com/google/common/hash/BloomFilterStrategies.java b/android/guava/src/com/google/common/hash/BloomFilterStrategies.java index 3a012f35885d..a2aa6b51df9d 100644 --- a/android/guava/src/com/google/common/hash/BloomFilterStrategies.java +++ b/android/guava/src/com/google/common/hash/BloomFilterStrategies.java @@ -22,8 +22,7 @@ import java.math.RoundingMode; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLongArray; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Collections of strategies of generating the k * log(M) bits required for an element to be mapped @@ -37,7 +36,6 @@ * @author Dimitris Andreou * @author Kurt Alfred Kluever */ -@ElementTypesAreNonnullByDefault enum BloomFilterStrategies implements BloomFilter.Strategy { /** * See "Less Hashing, Same Performance: Building a Better Bloom Filter" by Adam Kirsch and Michael @@ -94,7 +92,7 @@ enum BloomFilterStrategies implements BloomFilter.Strategy { }, /** * This strategy uses all 128 bits of {@link Hashing#murmur3_128} when hashing. It looks different - * than the implementation in MURMUR128_MITZ_32 because we're avoiding the multiplication in the + * from the implementation in MURMUR128_MITZ_32 because we're avoiding the multiplication in the * loop and doing a (much simpler) += hash2. We're also changing the index to a positive number by * AND'ing with Long.MAX_VALUE instead of flipping the bits. */ @@ -263,29 +261,40 @@ void putAll(LockFreeBitArray other) { data.length(), other.data.length()); for (int i = 0; i < data.length(); i++) { - long otherLong = other.data.get(i); - - long ourLongOld; - long ourLongNew; - boolean changedAnyBits = true; - do { - ourLongOld = data.get(i); - ourLongNew = ourLongOld | otherLong; - if (ourLongOld == ourLongNew) { - changedAnyBits = false; - break; - } - } while (!data.compareAndSet(i, ourLongOld, ourLongNew)); + putData(i, other.data.get(i)); + } + } - if (changedAnyBits) { - int bitsAdded = Long.bitCount(ourLongNew) - Long.bitCount(ourLongOld); - bitCount.add(bitsAdded); + /** + * ORs the bits encoded in the {@code i}th {@code long} in the underlying {@link + * AtomicLongArray} with the given value. + */ + void putData(int i, long longValue) { + long ourLongOld; + long ourLongNew; + boolean changedAnyBits = true; + do { + ourLongOld = data.get(i); + ourLongNew = ourLongOld | longValue; + if (ourLongOld == ourLongNew) { + changedAnyBits = false; + break; } + } while (!data.compareAndSet(i, ourLongOld, ourLongNew)); + + if (changedAnyBits) { + int bitsAdded = Long.bitCount(ourLongNew) - Long.bitCount(ourLongOld); + bitCount.add(bitsAdded); } } + /** Returns the number of {@code long}s in the underlying {@link AtomicLongArray}. */ + int dataLength() { + return data.length(); + } + @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof LockFreeBitArray) { LockFreeBitArray lockFreeBitArray = (LockFreeBitArray) o; // TODO(lowasser): avoid allocation here diff --git a/android/guava/src/com/google/common/hash/ChecksumHashFunction.java b/android/guava/src/com/google/common/hash/ChecksumHashFunction.java index 159adbb8194b..380c3a39ab6e 100644 --- a/android/guava/src/com/google/common/hash/ChecksumHashFunction.java +++ b/android/guava/src/com/google/common/hash/ChecksumHashFunction.java @@ -27,7 +27,6 @@ * @author Colin Decker */ @Immutable -@ElementTypesAreNonnullByDefault final class ChecksumHashFunction extends AbstractHashFunction implements Serializable { private final ImmutableSupplier checksumSupplier; private final int bits; diff --git a/android/guava/src/com/google/common/hash/Crc32cHashFunction.java b/android/guava/src/com/google/common/hash/Crc32cHashFunction.java index 8e17e6538c21..679c47852062 100644 --- a/android/guava/src/com/google/common/hash/Crc32cHashFunction.java +++ b/android/guava/src/com/google/common/hash/Crc32cHashFunction.java @@ -24,7 +24,6 @@ * @author Kurt Alfred Kluever */ @Immutable -@ElementTypesAreNonnullByDefault final class Crc32cHashFunction extends AbstractHashFunction { static final HashFunction CRC_32_C = new Crc32cHashFunction(); @@ -50,8 +49,8 @@ static final class Crc32cHasher extends AbstractStreamingHasher { * CRC(x ^ y) == CRC(x) ^ CRC(y). The approach we take is to break the message as follows, * with each letter representing a 4-byte word: ABCDABCDABCDABCD... and to calculate * CRC(A000A000A000...), CRC(0B000B000B...), CRC(00C000C000C...), CRC(000D000D000D...) - * and then to XOR them together. The STRIDE_TABLE enables us to hash an int followed by 12 - * zero bytes (3 ints), while the BYTE_TABLE is for advancing one byte at a time. + * and then to XOR them together. The strideTable enables us to hash an int followed by 12 + * zero bytes (3 ints), while the byteTable is for advancing one byte at a time. * This algorithm is due to the paper "Everything we know about CRC but [are] afraid to forget" * by Kadatch and Jenkins, 2010. */ @@ -107,7 +106,7 @@ protected void processRemaining(ByteBuffer bb) { crc0 = combine(crc0, crc2); crc0 = combine(crc0, crc3); while (bb.hasRemaining()) { - crc0 = (crc0 >>> 8) ^ BYTE_TABLE[(bb.get() ^ crc0) & 0xFF]; + crc0 = (crc0 >>> 8) ^ byteTable[(bb.get() ^ crc0) & 0xFF]; } finished = true; } @@ -122,7 +121,7 @@ protected HashCode makeHash() { return HashCode.fromInt(~crc0); } - static final int[] BYTE_TABLE = { + static final int[] byteTable = { 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, @@ -168,7 +167,7 @@ protected HashCode makeHash() { 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 }; - static final int[][] STRIDE_TABLE = { + static final int[][] strideTable = { { 0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1, 0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76, @@ -355,16 +354,16 @@ protected HashCode makeHash() { static final int INVERSE_COMPUTE_FOR_WORD_OF_ALL_1S = 0xeee3ddcd; static int computeForWord(int word) { - return STRIDE_TABLE[3][word & 0xFF] - ^ STRIDE_TABLE[2][(word >>> 8) & 0xFF] - ^ STRIDE_TABLE[1][(word >>> 16) & 0xFF] - ^ STRIDE_TABLE[0][word >>> 24]; + return strideTable[3][word & 0xFF] + ^ strideTable[2][(word >>> 8) & 0xFF] + ^ strideTable[1][(word >>> 16) & 0xFF] + ^ strideTable[0][word >>> 24]; } static int combine(int csum, int crc) { csum ^= crc; for (int i = 0; i < 4; i++) { - csum = (csum >>> 8) ^ BYTE_TABLE[csum & 0xFF]; + csum = (csum >>> 8) ^ byteTable[csum & 0xFF]; } return csum; } diff --git a/android/guava/src/com/google/common/hash/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/hash/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index a2382b3514cc..000000000000 --- a/android/guava/src/com/google/common/hash/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.hash; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/hash/FarmHashFingerprint64.java b/android/guava/src/com/google/common/hash/FarmHashFingerprint64.java index 3437b00a2167..3785093f0924 100644 --- a/android/guava/src/com/google/common/hash/FarmHashFingerprint64.java +++ b/android/guava/src/com/google/common/hash/FarmHashFingerprint64.java @@ -38,7 +38,6 @@ * @author Kyle Maddison * @author Geoff Pike */ -@ElementTypesAreNonnullByDefault final class FarmHashFingerprint64 extends AbstractNonStreamingHashFunction { static final HashFunction FARMHASH_FINGERPRINT_64 = new FarmHashFingerprint64(); @@ -117,7 +116,7 @@ private static void weakHashLength32WithSeeds( private static long hashLength0to16(byte[] bytes, int offset, int length) { if (length >= 8) { - long mul = K2 + length * 2; + long mul = K2 + length * 2L; long a = load64(bytes, offset) + K2; long b = load64(bytes, offset + length - 8); long c = rotateRight(b, 37) * mul + a; @@ -141,7 +140,7 @@ private static long hashLength0to16(byte[] bytes, int offset, int length) { } private static long hashLength17to32(byte[] bytes, int offset, int length) { - long mul = K2 + length * 2; + long mul = K2 + length * 2L; long a = load64(bytes, offset) * K1; long b = load64(bytes, offset + 8); long c = load64(bytes, offset + length - 8) * mul; @@ -151,7 +150,7 @@ private static long hashLength17to32(byte[] bytes, int offset, int length) { } private static long hashLength33To64(byte[] bytes, int offset, int length) { - long mul = K2 + length * 2; + long mul = K2 + length * 2L; long a = load64(bytes, offset) * K2; long b = load64(bytes, offset + 8); long c = load64(bytes, offset + length - 8) * mul; diff --git a/android/guava/src/com/google/common/hash/Fingerprint2011.java b/android/guava/src/com/google/common/hash/Fingerprint2011.java new file mode 100644 index 000000000000..f54232bc482f --- /dev/null +++ b/android/guava/src/com/google/common/hash/Fingerprint2011.java @@ -0,0 +1,197 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package com.google.common.hash; + +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static com.google.common.hash.LittleEndianByteArray.load64; +import static com.google.common.hash.LittleEndianByteArray.load64Safely; +import static java.lang.Long.rotateRight; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Implementation of Geoff Pike's fingerprint2011 hash function. See {@link Hashing#fingerprint2011} + * for information on the behaviour of the algorithm. + * + *

    On Intel Core2 2.66, on 1000 bytes, fingerprint2011 takes 0.9 microseconds compared to + * fingerprint at 4.0 microseconds and md5 at 4.5 microseconds. + * + *

    Note to maintainers: This implementation relies on signed arithmetic being bit-wise equivalent + * to unsigned arithmetic in all cases except: + * + *

      + *
    • comparisons (signed values can be negative) + *
    • division (avoided here) + *
    • shifting (right shift must be unsigned) + *
    + * + * @author kylemaddison@google.com (Kyle Maddison) + * @author gpike@google.com (Geoff Pike) + */ +final class Fingerprint2011 extends AbstractNonStreamingHashFunction { + static final HashFunction FINGERPRINT_2011 = new Fingerprint2011(); + + // Some primes between 2^63 and 2^64 for various uses. + private static final long K0 = 0xa5b85c5e198ed849L; + private static final long K1 = 0x8d58ac26afe12e47L; + private static final long K2 = 0xc47b6e9e3a970ed3L; + private static final long K3 = 0xc6a4a7935bd1e995L; + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + checkPositionIndexes(off, off + len, input.length); + return HashCode.fromLong(fingerprint(input, off, len)); + } + + @Override + public int bits() { + return 64; + } + + @Override + public String toString() { + return "Hashing.fingerprint2011()"; + } + + // End of public functions. + + @VisibleForTesting + static long fingerprint(byte[] bytes, int offset, int length) { + long result; + + if (length <= 32) { + result = murmurHash64WithSeed(bytes, offset, length, K0 ^ K1 ^ K2); + } else if (length <= 64) { + result = hashLength33To64(bytes, offset, length); + } else { + result = fullFingerprint(bytes, offset, length); + } + + long u = length >= 8 ? load64(bytes, offset) : K0; + long v = length >= 9 ? load64(bytes, offset + length - 8) : K0; + result = hash128to64(result + v, u); + return result == 0 || result == 1 ? result + ~1 : result; + } + + private static long shiftMix(long val) { + return val ^ (val >>> 47); + } + + /** Implementation of Hash128to64 from util/hash/hash128to64.h */ + @VisibleForTesting + static long hash128to64(long high, long low) { + long a = (low ^ high) * K3; + a ^= (a >>> 47); + long b = (high ^ a) * K3; + b ^= (b >>> 47); + b *= K3; + return b; + } + + /** + * Computes intermediate hash of 32 bytes of byte array from the given offset. Results are + * returned in the output array - this is 12% faster than allocating new arrays every time. + */ + private static void weakHashLength32WithSeeds( + byte[] bytes, int offset, long seedA, long seedB, long[] output) { + long part1 = load64(bytes, offset); + long part2 = load64(bytes, offset + 8); + long part3 = load64(bytes, offset + 16); + long part4 = load64(bytes, offset + 24); + + seedA += part1; + seedB = rotateRight(seedB + seedA + part4, 51); + long c = seedA; + seedA += part2; + seedA += part3; + seedB += rotateRight(seedA, 23); + output[0] = seedA + part4; + output[1] = seedB + c; + } + + /* + * Compute an 8-byte hash of a byte array of length greater than 64 bytes. + */ + private static long fullFingerprint(byte[] bytes, int offset, int length) { + // For lengths over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + long x = load64(bytes, offset); + long y = load64(bytes, offset + length - 16) ^ K1; + long z = load64(bytes, offset + length - 56) ^ K0; + long[] v = new long[2]; + long[] w = new long[2]; + weakHashLength32WithSeeds(bytes, offset + length - 64, length, y, v); + weakHashLength32WithSeeds(bytes, offset + length - 32, length * K1, K0, w); + z += shiftMix(v[1]) * K1; + x = rotateRight(z + x, 39) * K1; + y = rotateRight(y, 33) * K1; + + // Decrease length to the nearest multiple of 64, and operate on 64-byte chunks. + length = (length - 1) & ~63; + do { + x = rotateRight(x + y + v[0] + load64(bytes, offset + 16), 37) * K1; + y = rotateRight(y + v[1] + load64(bytes, offset + 48), 42) * K1; + x ^= w[1]; + y ^= v[0]; + z = rotateRight(z ^ w[0], 33); + weakHashLength32WithSeeds(bytes, offset, v[1] * K1, x + w[0], v); + weakHashLength32WithSeeds(bytes, offset + 32, z + w[1], y, w); + long tmp = z; + z = x; + x = tmp; + offset += 64; + length -= 64; + } while (length != 0); + return hash128to64(hash128to64(v[0], w[0]) + shiftMix(y) * K1 + z, hash128to64(v[1], w[1]) + x); + } + + private static long hashLength33To64(byte[] bytes, int offset, int length) { + long z = load64(bytes, offset + 24); + long a = load64(bytes, offset) + (length + load64(bytes, offset + length - 16)) * K0; + long b = rotateRight(a + z, 52); + long c = rotateRight(a, 37); + a += load64(bytes, offset + 8); + c += rotateRight(a, 7); + a += load64(bytes, offset + 16); + long vf = a + z; + long vs = b + rotateRight(a, 31) + c; + a = load64(bytes, offset + 16) + load64(bytes, offset + length - 32); + z = load64(bytes, offset + length - 8); + b = rotateRight(a + z, 52); + c = rotateRight(a, 37); + a += load64(bytes, offset + length - 24); + c += rotateRight(a, 7); + a += load64(bytes, offset + length - 16); + long wf = a + z; + long ws = b + rotateRight(a, 31) + c; + long r = shiftMix((vf + ws) * K2 + (wf + vs) * K0); + return shiftMix(r * K0 + vs) * K2; + } + + @VisibleForTesting + static long murmurHash64WithSeed(byte[] bytes, int offset, int length, long seed) { + long mul = K3; + int topBit = 0x7; + + int lengthAligned = length & ~topBit; + int lengthRemainder = length & topBit; + long hash = seed ^ (length * mul); + + for (int i = 0; i < lengthAligned; i += 8) { + long loaded = load64(bytes, offset + i); + long data = shiftMix(loaded * mul) * mul; + hash ^= data; + hash *= mul; + } + + if (lengthRemainder != 0) { + long data = load64Safely(bytes, offset + lengthAligned, lengthRemainder); + hash ^= data; + hash *= mul; + } + + hash = shiftMix(hash) * mul; + hash = shiftMix(hash); + return hash; + } +} diff --git a/android/guava/src/com/google/common/hash/Funnel.java b/android/guava/src/com/google/common/hash/Funnel.java index 9d80dabcf6ed..d5e7482b7cba 100644 --- a/android/guava/src/com/google/common/hash/Funnel.java +++ b/android/guava/src/com/google/common/hash/Funnel.java @@ -17,7 +17,7 @@ import com.google.common.annotations.Beta; import com.google.errorprone.annotations.DoNotMock; import java.io.Serializable; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An object which can send data from an object of type {@code T} into a {@code PrimitiveSink}. @@ -44,7 +44,6 @@ */ @Beta @DoNotMock("Implement with a lambda") -@ElementTypesAreNonnullByDefault public interface Funnel extends Serializable { /** diff --git a/android/guava/src/com/google/common/hash/Funnels.java b/android/guava/src/com/google/common/hash/Funnels.java index b8e63d504ddb..2557e9c2bb80 100644 --- a/android/guava/src/com/google/common/hash/Funnels.java +++ b/android/guava/src/com/google/common/hash/Funnels.java @@ -16,11 +16,12 @@ import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.Serializable; import java.nio.charset.Charset; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Funnels for common types. All implementations are serializable. @@ -29,7 +30,6 @@ * @since 11.0 */ @Beta -@ElementTypesAreNonnullByDefault public final class Funnels { private Funnels() {} @@ -87,7 +87,7 @@ public static Funnel stringFunnel(Charset charset) { return new StringCharsetFunnel(charset); } - private static class StringCharsetFunnel implements Funnel, Serializable { + private static class StringCharsetFunnel implements Funnel { private final Charset charset; StringCharsetFunnel(Charset charset) { @@ -105,7 +105,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof StringCharsetFunnel) { StringCharsetFunnel funnel = (StringCharsetFunnel) o; return this.charset.equals(funnel.charset); @@ -122,6 +122,10 @@ Object writeReplace() { return new SerializedForm(charset); } + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + private static class SerializedForm implements Serializable { private final String charsetCanonicalName; @@ -172,7 +176,7 @@ public String toString() { } private static class SequentialFunnel - implements Funnel>, Serializable { + implements Funnel> { private final Funnel elementFunnel; SequentialFunnel(Funnel elementFunnel) { @@ -192,7 +196,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof SequentialFunnel) { SequentialFunnel funnel = (SequentialFunnel) o; return elementFunnel.equals(funnel.elementFunnel); diff --git a/android/guava/src/com/google/common/hash/HashCode.java b/android/guava/src/com/google/common/hash/HashCode.java index fde2a86d6466..6d5d9adc5565 100644 --- a/android/guava/src/com/google/common/hash/HashCode.java +++ b/android/guava/src/com/google/common/hash/HashCode.java @@ -17,13 +17,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.lang.Math.min; import com.google.common.base.Preconditions; -import com.google.common.primitives.Ints; import com.google.common.primitives.UnsignedInts; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable hash code of arbitrary bit length. @@ -32,7 +32,6 @@ * @author Kurt Alfred Kluever * @since 11.0 */ -@ElementTypesAreNonnullByDefault public abstract class HashCode { HashCode() {} @@ -83,7 +82,7 @@ public abstract class HashCode { */ @CanIgnoreReturnValue public int writeBytesTo(byte[] dest, int offset, int maxLength) { - maxLength = Ints.min(maxLength, bits() / 8); + maxLength = min(maxLength, bits() / 8); Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); writeBytesToImpl(dest, offset, maxLength); return maxLength; @@ -289,7 +288,7 @@ public long asLong() { @Override public long padToLong() { long retVal = (bytes[0] & 0xFF); - for (int i = 1; i < Math.min(bytes.length, 8); i++) { + for (int i = 1; i < min(bytes.length, 8); i++) { retVal |= (bytes[i] & 0xFFL) << (i * 8); } return retVal; @@ -368,7 +367,7 @@ private static int decode(char ch) { * to protect against timing attacks. */ @Override - public final boolean equals(@CheckForNull Object object) { + public final boolean equals(@Nullable Object object) { if (object instanceof HashCode) { HashCode that = (HashCode) object; return bits() == that.bits() && equalsSameBits(that); diff --git a/android/guava/src/com/google/common/hash/HashFunction.java b/android/guava/src/com/google/common/hash/HashFunction.java index d4b7f8a01fcc..0da69abd4026 100644 --- a/android/guava/src/com/google/common/hash/HashFunction.java +++ b/android/guava/src/com/google/common/hash/HashFunction.java @@ -18,7 +18,7 @@ import com.google.errorprone.annotations.Immutable; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A hash function is a collision-averse pure function that maps an arbitrary block of data to a @@ -116,7 +116,6 @@ * @since 11.0 */ @Immutable -@ElementTypesAreNonnullByDefault public interface HashFunction { /** * Begins a new hash code computation by returning an initialized, stateful {@code Hasher} diff --git a/android/guava/src/com/google/common/hash/Hasher.java b/android/guava/src/com/google/common/hash/Hasher.java index b3f24fa282b9..218310449c2b 100644 --- a/android/guava/src/com/google/common/hash/Hasher.java +++ b/android/guava/src/com/google/common/hash/Hasher.java @@ -18,7 +18,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link PrimitiveSink} that can compute a hash code after reading the input. Each hasher should @@ -54,42 +54,51 @@ * @since 11.0 */ @Beta -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault public interface Hasher extends PrimitiveSink { + @CanIgnoreReturnValue @Override Hasher putByte(byte b); + @CanIgnoreReturnValue @Override Hasher putBytes(byte[] bytes); + @CanIgnoreReturnValue @Override Hasher putBytes(byte[] bytes, int off, int len); + @CanIgnoreReturnValue @Override Hasher putBytes(ByteBuffer bytes); + @CanIgnoreReturnValue @Override Hasher putShort(short s); + @CanIgnoreReturnValue @Override Hasher putInt(int i); + @CanIgnoreReturnValue @Override Hasher putLong(long l); /** Equivalent to {@code putInt(Float.floatToRawIntBits(f))}. */ + @CanIgnoreReturnValue @Override Hasher putFloat(float f); /** Equivalent to {@code putLong(Double.doubleToRawLongBits(d))}. */ + @CanIgnoreReturnValue @Override Hasher putDouble(double d); /** Equivalent to {@code putByte(b ? (byte) 1 : (byte) 0)}. */ + @CanIgnoreReturnValue @Override Hasher putBoolean(boolean b); + @CanIgnoreReturnValue @Override Hasher putChar(char c); @@ -106,6 +115,7 @@ public interface Hasher extends PrimitiveSink { * * @since 15.0 (since 11.0 as putString(CharSequence)). */ + @CanIgnoreReturnValue @Override Hasher putUnencodedChars(CharSequence charSequence); @@ -117,10 +127,12 @@ public interface Hasher extends PrimitiveSink { * faster, produces the same output across Java releases, and hashes every {@code char} in the * input, even if some are invalid. */ + @CanIgnoreReturnValue @Override Hasher putString(CharSequence charSequence, Charset charset); /** A simple convenience for {@code funnel.funnel(object, this)}. */ + @CanIgnoreReturnValue Hasher putObject( @ParametricNullness T instance, Funnel funnel); diff --git a/android/guava/src/com/google/common/hash/Hashing.java b/android/guava/src/com/google/common/hash/Hashing.java index dd6536773aa0..84396d227071 100644 --- a/android/guava/src/com/google/common/hash/Hashing.java +++ b/android/guava/src/com/google/common/hash/Hashing.java @@ -17,33 +17,31 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.errorprone.annotations.Immutable; import java.security.Key; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.zip.Adler32; import java.util.zip.CRC32; import java.util.zip.Checksum; -import javax.annotation.CheckForNull; import javax.crypto.spec.SecretKeySpec; +import org.jspecify.annotations.Nullable; /** * Static methods to obtain {@link HashFunction} instances, and other static hashing-related * utilities. * *

    A comparison of the various hash functions can be found here. + * href="https://docs.google.com/spreadsheets/d/1_q2EVcxA2HjcrlVMbaqXwMj31h9M5-Bqj_m8vITOwwk/">here. * * @author Kevin Bourrillion * @author Dimitris Andreou * @author Kurt Alfred Kluever * @since 11.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class Hashing { /** * Returns a general-purpose, temporary-use, non-cryptographic hash function. The algorithm @@ -58,7 +56,8 @@ public final class Hashing { *

    Repeated calls to this method on the same loaded {@code Hashing} class, using the same value * for {@code minimumBits}, will return identically-behaving {@link HashFunction} instances. * - * @param minimumBits a positive integer (can be arbitrarily large) + * @param minimumBits a positive integer. This can be arbitrarily large. The returned {@link + * HashFunction} instance may use memory proportional to this integer. * @return a hash function, described above, that produces hash codes of length {@code * minimumBits} or greater */ @@ -105,6 +104,7 @@ public static HashFunction goodFastHash(int minimumBits) { * #murmur3_32_fixed(int)} instead. */ @Deprecated + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_32(int seed) { return new Murmur3_32HashFunction(seed, /* supplementaryPlaneFix= */ false); } @@ -123,6 +123,7 @@ public static HashFunction murmur3_32(int seed) { * #murmur3_32_fixed()} instead. */ @Deprecated + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_32() { return Murmur3_32HashFunction.MURMUR3_32; } @@ -139,6 +140,7 @@ public static HashFunction murmur3_32() { * * @since 31.0 */ + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_32_fixed(int seed) { return new Murmur3_32HashFunction(seed, /* supplementaryPlaneFix= */ true); } @@ -155,6 +157,7 @@ public static HashFunction murmur3_32_fixed(int seed) { * * @since 31.0 */ + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_32_fixed() { return Murmur3_32HashFunction.MURMUR3_32_FIXED; } @@ -166,6 +169,7 @@ public static HashFunction murmur3_32_fixed() { * *

    The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). */ + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_128(int seed) { return new Murmur3_128HashFunction(seed); } @@ -177,6 +181,7 @@ public static HashFunction murmur3_128(int seed) { * *

    The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). */ + @SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks public static HashFunction murmur3_128() { return Murmur3_128HashFunction.MURMUR3_128; } @@ -281,6 +286,10 @@ private static class Sha512Holder { * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the * MD5 (128 hash bits) hash function and the given secret key. * + *

    If you are designing a new system that needs HMAC, prefer {@link #hmacSha256} or other + * future-proof algorithms over {@code hmacMd5}. + * * @param key the secret key * @throws IllegalArgumentException if the given key is inappropriate for initializing this MAC * @since 20.0 @@ -294,6 +303,10 @@ public static HashFunction hmacMd5(Key key) { * MD5 (128 hash bits) hash function and a {@link SecretKeySpec} created from the given byte array * and the MD5 algorithm. * + *

    If you are designing a new system that needs HMAC, prefer {@link #hmacSha256} or other + * future-proof algorithms over {@code hmacMd5}. + * * @param key the key material of the secret key * @since 20.0 */ @@ -374,9 +387,13 @@ public static HashFunction hmacSha512(byte[] key) { } private static String hmacToString(String methodName, Key key) { - return String.format( - "Hashing.%s(Key[algorithm=%s, format=%s])", - methodName, key.getAlgorithm(), key.getFormat()); + return "Hashing." + + methodName + + "(Key[algorithm=" + + key.getAlgorithm() + + ", format=" + + key.getFormat() + + "])"; } /** @@ -469,6 +486,30 @@ public static HashFunction farmHashFingerprint64() { return FarmHashFingerprint64.FARMHASH_FINGERPRINT_64; } + /** + * Returns a hash function implementing the Fingerprint2011 hashing function (64 hash bits). + * + *

    This is designed for generating persistent fingerprints of strings. It isn't + * cryptographically secure, but it produces a high-quality hash with few collisions. Fingerprints + * generated using this are byte-wise identical to those created using the C++ version, but note + * that this uses unsigned integers (see {@link com.google.common.primitives.UnsignedInts}). + * Comparisons between the two should take this into account. + * + *

    Fingerprint2011() is a form of Murmur2 on strings up to 32 bytes and a form of CityHash for + * longer strings. It could have been one or the other throughout. The main advantage of the + * combination is that CityHash has a bunch of special cases for short strings that don't need to + * be replicated here. The result will never be 0 or 1. + * + *

    This function is best understood as a fingerprint rather than a true + * hash function. + * + * @since 31.1 + */ + public static HashFunction fingerprint2011() { + return Fingerprint2011.FINGERPRINT_2011; + } + /** * Assigns to {@code hashCode} a "bucket" in the range {@code [0, buckets)}, in a uniform manner * that minimizes the need for remapping as {@code buckets} grows. That is, {@code @@ -621,7 +662,7 @@ public static HashFunction concatenating( List list = new ArrayList<>(); list.add(first); list.add(second); - list.addAll(Arrays.asList(rest)); + Collections.addAll(list, rest); return new ConcatenatedHashFunction(list.toArray(new HashFunction[0])); } @@ -642,7 +683,7 @@ public static HashFunction concatenating(Iterable hashFunctions) { for (HashFunction hashFunction : hashFunctions) { list.add(hashFunction); } - checkArgument(list.size() > 0, "number of hash functions (%s) must be > 0", list.size()); + checkArgument(!list.isEmpty(), "number of hash functions (%s) must be > 0", list.size()); return new ConcatenatedHashFunction(list.toArray(new HashFunction[0])); } @@ -680,7 +721,7 @@ public int bits() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof ConcatenatedHashFunction) { ConcatenatedHashFunction other = (ConcatenatedHashFunction) object; return Arrays.equals(functions, other.functions); diff --git a/android/guava/src/com/google/common/hash/HashingInputStream.java b/android/guava/src/com/google/common/hash/HashingInputStream.java index bf9464ce5573..f49dfd62daa5 100644 --- a/android/guava/src/com/google/common/hash/HashingInputStream.java +++ b/android/guava/src/com/google/common/hash/HashingInputStream.java @@ -29,7 +29,6 @@ * @since 16.0 */ @Beta -@ElementTypesAreNonnullByDefault public final class HashingInputStream extends FilterInputStream { private final Hasher hasher; diff --git a/android/guava/src/com/google/common/hash/HashingOutputStream.java b/android/guava/src/com/google/common/hash/HashingOutputStream.java index f138bba15050..20f1316a558c 100644 --- a/android/guava/src/com/google/common/hash/HashingOutputStream.java +++ b/android/guava/src/com/google/common/hash/HashingOutputStream.java @@ -24,11 +24,10 @@ /** * An {@link OutputStream} that maintains a hash of the data written to it. * - * @author Nick Piepmeier + * @author Zoe Piepmeier * @since 16.0 */ @Beta -@ElementTypesAreNonnullByDefault public final class HashingOutputStream extends FilterOutputStream { private final Hasher hasher; diff --git a/android/guava/src/com/google/common/hash/IgnoreJRERequirement.java b/android/guava/src/com/google/common/hash/IgnoreJRERequirement.java new file mode 100644 index 000000000000..8799c56dd850 --- /dev/null +++ b/android/guava/src/com/google/common/hash/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.hash; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/hash/ImmutableSupplier.java b/android/guava/src/com/google/common/hash/ImmutableSupplier.java index 24f711a313fa..b6a74a77b719 100644 --- a/android/guava/src/com/google/common/hash/ImmutableSupplier.java +++ b/android/guava/src/com/google/common/hash/ImmutableSupplier.java @@ -23,5 +23,4 @@ */ // TODO(cpovirk): Should we just use ChecksumType directly instead of defining this type? @Immutable -@ElementTypesAreNonnullByDefault interface ImmutableSupplier extends Supplier {} diff --git a/android/guava/src/com/google/common/hash/Java8Compatibility.java b/android/guava/src/com/google/common/hash/Java8Compatibility.java index c15f2b3cc575..52f71e788558 100644 --- a/android/guava/src/com/google/common/hash/Java8Compatibility.java +++ b/android/guava/src/com/google/common/hash/Java8Compatibility.java @@ -22,7 +22,6 @@ * https://github.com/google/guava/issues/3990 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault final class Java8Compatibility { static void clear(Buffer b) { b.clear(); diff --git a/android/guava/src/com/google/common/hash/LittleEndianByteArray.java b/android/guava/src/com/google/common/hash/LittleEndianByteArray.java index 625201547d1c..e0e83f22eae2 100644 --- a/android/guava/src/com/google/common/hash/LittleEndianByteArray.java +++ b/android/guava/src/com/google/common/hash/LittleEndianByteArray.java @@ -14,8 +14,16 @@ package com.google.common.hash; +import static java.lang.Math.min; + +import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Longs; +import java.lang.reflect.Field; import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Objects; import sun.misc.Unsafe; /** @@ -24,11 +32,13 @@ * @author Kevin Damm * @author Kyle Maddison */ -@ElementTypesAreNonnullByDefault final class LittleEndianByteArray { - /** The instance that actually does the work; delegates to Unsafe or a pure-Java fallback. */ - private static final LittleEndianBytes byteArray; + /** + * The instance that actually does the work; delegates to VarHandle, Unsafe, or a Java-8 + * compatible pure-Java fallback. + */ + private static final LittleEndianBytes byteArray = makeGetter(); /** * Load 8 bytes into long in a little endian manner, from the substring between position and @@ -61,7 +71,7 @@ static long load64Safely(byte[] input, int offset, int length) { // of the result already being filled with zeros. // This loop is critical to performance, so please check HashBenchmark if altering it. - int limit = Math.min(length, 8); + int limit = min(length, 8); for (int i = 0; i < limit; i++) { // Shift value left while iterating logically through the array. result |= (input[offset + i] & 0xFFL) << (i * 8); @@ -99,12 +109,12 @@ static int load32(byte[] source, int offset) { } /** - * Indicates that the loading of Unsafe was successful and the load and store operations will be - * very efficient. May be useful for calling code to fall back on an alternative implementation - * that is slower than Unsafe.get/store but faster than the pure-Java mask-and-shift. + * Indicates that the load and store operations will be very efficient because of use of VarHandle + * or Unsafe. May be useful for calling code to fall back on an alternative implementation that is + * slower than those implementations but faster than the pure-Java mask-and-shift. */ - static boolean usingUnsafe() { - return (byteArray instanceof UnsafeByteArray); + static boolean usingFastPath() { + return byteArray.usesFastPath(); } /** @@ -117,6 +127,8 @@ private interface LittleEndianBytes { long getLongLittleEndian(byte[] array, int offset); void putLongLittleEndian(byte[] array, int offset, long value); + + boolean usesFastPath(); } /** @@ -124,7 +136,9 @@ private interface LittleEndianBytes { * Unsafe.theUnsafe is inaccessible, the attempt to load the nested class fails, and the outer * class's static initializer can fall back on a non-Unsafe version. */ - private enum UnsafeByteArray implements LittleEndianBytes { + @SuppressWarnings("SunApi") // b/345822163 + @VisibleForTesting + enum UnsafeByteArray implements LittleEndianBytes { // Do *not* change the order of these constants! UNSAFE_LITTLE_ENDIAN { @Override @@ -153,6 +167,11 @@ public void putLongLittleEndian(byte[] array, int offset, long value) { } }; + @Override + public boolean usesFastPath() { + return true; + } + // Provides load and store operations that use native instructions to get better performance. private static final Unsafe theUnsafe; @@ -160,34 +179,32 @@ public void putLongLittleEndian(byte[] array, int offset, long value) { private static final int BYTE_ARRAY_BASE_OFFSET; /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple - * call to Unsafe.getUnsafe when integrating into a jdk. + * Returns an Unsafe. Suitable for use in a 3rd party package. Replace with a simple call to + * Unsafe.getUnsafe when integrating into a JDK. * - * @return a sun.misc.Unsafe instance if successful + * @return an Unsafe instance if successful */ - private static sun.misc.Unsafe getUnsafe() { + private static Unsafe getUnsafe() { try { - return sun.misc.Unsafe.getUnsafe(); + return Unsafe.getUnsafe(); } catch (SecurityException tryReflectionInstead) { // We'll try reflection instead. } try { - return java.security.AccessController.doPrivileged( - new java.security.PrivilegedExceptionAction() { - @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) { - return k.cast(x); + return AccessController.doPrivileged( + (PrivilegedExceptionAction) + () -> { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } } - } - throw new NoSuchFieldError("the Unsafe"); - } - }); - } catch (java.security.PrivilegedActionException e) { + throw new NoSuchFieldError("the Unsafe"); + }); + } catch (PrivilegedActionException e) { throw new RuntimeException("Could not initialize intrinsics", e.getCause()); } } @@ -203,7 +220,10 @@ public sun.misc.Unsafe run() throws Exception { } } - /** Fallback implementation for when Unsafe is not available in our current environment. */ + /** + * Fallback implementation for when VarHandle and Unsafe are not available in our current + * environment. + */ private enum JavaLittleEndianBytes implements LittleEndianBytes { INSTANCE { @Override @@ -226,11 +246,15 @@ public void putLongLittleEndian(byte[] sink, int offset, long value) { sink[offset + i] = (byte) ((value & mask) >> (i * 8)); } } - }; + + @Override + public boolean usesFastPath() { + return false; + } + } } - static { - LittleEndianBytes theGetter = JavaLittleEndianBytes.INSTANCE; + static LittleEndianBytes makeGetter() { try { /* * UnsafeByteArray uses Unsafe.getLong() in an unsupported way, which is known to cause @@ -244,16 +268,16 @@ public void putLongLittleEndian(byte[] sink, int offset, long value) { * */ String arch = System.getProperty("os.arch"); - if ("amd64".equals(arch)) { - theGetter = - ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN) - ? UnsafeByteArray.UNSAFE_LITTLE_ENDIAN - : UnsafeByteArray.UNSAFE_BIG_ENDIAN; + if (Objects.equals(arch, "amd64")) { + return ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN) + ? UnsafeByteArray.UNSAFE_LITTLE_ENDIAN + : UnsafeByteArray.UNSAFE_BIG_ENDIAN; } } catch (Throwable t) { // ensure we really catch *everything* } - byteArray = theGetter; + + return JavaLittleEndianBytes.INSTANCE; } /** Deter instantiation of this class. */ diff --git a/android/guava/src/com/google/common/hash/LongAddable.java b/android/guava/src/com/google/common/hash/LongAddable.java index 5c6a7f0c7db8..a95eece2e1ff 100644 --- a/android/guava/src/com/google/common/hash/LongAddable.java +++ b/android/guava/src/com/google/common/hash/LongAddable.java @@ -14,13 +14,11 @@ package com.google.common.hash; - /** * Abstract interface for objects that can concurrently add longs. * * @author Louis Wasserman */ -@ElementTypesAreNonnullByDefault interface LongAddable { void increment(); diff --git a/android/guava/src/com/google/common/hash/LongAddables.java b/android/guava/src/com/google/common/hash/LongAddables.java index 370030dab5d8..5ae9ba0b138b 100644 --- a/android/guava/src/com/google/common/hash/LongAddables.java +++ b/android/guava/src/com/google/common/hash/LongAddables.java @@ -22,14 +22,14 @@ * * @author Louis Wasserman */ -@ElementTypesAreNonnullByDefault final class LongAddables { private static final Supplier SUPPLIER; static { Supplier supplier; try { - new LongAdder(); // trigger static initialization of the LongAdder class, which may fail + // trigger static initialization of the LongAdder class, which may fail + LongAdder unused = new LongAdder(); supplier = new Supplier() { @Override diff --git a/android/guava/src/com/google/common/hash/LongAdder.java b/android/guava/src/com/google/common/hash/LongAdder.java index dc864aa9a417..70ef21bc4e09 100644 --- a/android/guava/src/com/google/common/hash/LongAdder.java +++ b/android/guava/src/com/google/common/hash/LongAdder.java @@ -11,6 +11,8 @@ package com.google.common.hash; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -38,8 +40,8 @@ * @since 1.8 * @author Doug Lea */ -@ElementTypesAreNonnullByDefault final class LongAdder extends Striped64 implements Serializable, LongAddable { + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 7249069246863182397L; /** Version of plus for use in retryUpdate */ diff --git a/android/guava/src/com/google/common/hash/MacHashFunction.java b/android/guava/src/com/google/common/hash/MacHashFunction.java index 031b1c017b60..390b49c30234 100644 --- a/android/guava/src/com/google/common/hash/MacHashFunction.java +++ b/android/guava/src/com/google/common/hash/MacHashFunction.java @@ -30,7 +30,6 @@ * @author Kurt Alfred Kluever */ @Immutable -@ElementTypesAreNonnullByDefault final class MacHashFunction extends AbstractHashFunction { @SuppressWarnings("Immutable") // cloned before each use @@ -58,7 +57,7 @@ public int bits() { private static boolean supportsClone(Mac mac) { try { - mac.clone(); + Object unused = mac.clone(); return true; } catch (CloneNotSupportedException e) { return false; diff --git a/android/guava/src/com/google/common/hash/MessageDigestHashFunction.java b/android/guava/src/com/google/common/hash/MessageDigestHashFunction.java index 48b47b0f0bc1..9a435b9edc13 100644 --- a/android/guava/src/com/google/common/hash/MessageDigestHashFunction.java +++ b/android/guava/src/com/google/common/hash/MessageDigestHashFunction.java @@ -19,6 +19,8 @@ import static com.google.common.base.Preconditions.checkState; import com.google.errorprone.annotations.Immutable; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.security.MessageDigest; @@ -32,7 +34,6 @@ * @author Dimitris Andreou */ @Immutable -@ElementTypesAreNonnullByDefault final class MessageDigestHashFunction extends AbstractHashFunction implements Serializable { @SuppressWarnings("Immutable") // cloned before each use @@ -61,7 +62,7 @@ final class MessageDigestHashFunction extends AbstractHashFunction implements Se private static boolean supportsClone(MessageDigest digest) { try { - digest.clone(); + Object unused = digest.clone(); return true; } catch (CloneNotSupportedException e) { return false; @@ -120,6 +121,10 @@ Object writeReplace() { return new SerializedForm(prototype.getAlgorithm(), bytes, toString); } + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + /** Hasher that updates a message digest. */ private static final class MessageDigestHasher extends AbstractByteHasher { private final MessageDigest digest; diff --git a/android/guava/src/com/google/common/hash/Murmur3_128HashFunction.java b/android/guava/src/com/google/common/hash/Murmur3_128HashFunction.java index d1304f8273c5..3ce78e8614c6 100644 --- a/android/guava/src/com/google/common/hash/Murmur3_128HashFunction.java +++ b/android/guava/src/com/google/common/hash/Murmur3_128HashFunction.java @@ -31,7 +31,7 @@ import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * See MurmurHash3_x64_128 in the @@ -41,7 +41,7 @@ * @author Dimitris Andreou */ @Immutable -@ElementTypesAreNonnullByDefault +@SuppressWarnings("IdentifierName") // the best we could do for adjacent digit blocks final class Murmur3_128HashFunction extends AbstractHashFunction implements Serializable { static final HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0); @@ -71,7 +71,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof Murmur3_128HashFunction) { Murmur3_128HashFunction other = (Murmur3_128HashFunction) object; return seed == other.seed; diff --git a/android/guava/src/com/google/common/hash/Murmur3_32HashFunction.java b/android/guava/src/com/google/common/hash/Murmur3_32HashFunction.java index a47184be58b9..b04dd22f0b07 100644 --- a/android/guava/src/com/google/common/hash/Murmur3_32HashFunction.java +++ b/android/guava/src/com/google/common/hash/Murmur3_32HashFunction.java @@ -28,8 +28,8 @@ import static com.google.common.base.Preconditions.checkPositionIndexes; import static com.google.common.base.Preconditions.checkState; import static com.google.common.primitives.UnsignedBytes.toInt; +import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Charsets; import com.google.common.primitives.Chars; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; @@ -39,7 +39,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * See MurmurHash3_x86_32 in {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/hash/PrimitiveSink.java b/android/guava/src/com/google/common/hash/PrimitiveSink.java index a29ba4e13609..71c5eceb7bff 100644 --- a/android/guava/src/com/google/common/hash/PrimitiveSink.java +++ b/android/guava/src/com/google/common/hash/PrimitiveSink.java @@ -26,8 +26,6 @@ * @since 12.0 (in 11.0 as {@code Sink}) */ @Beta -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault public interface PrimitiveSink { /** * Puts a byte into this sink. @@ -35,6 +33,7 @@ public interface PrimitiveSink { * @param b a byte * @return this instance */ + @CanIgnoreReturnValue PrimitiveSink putByte(byte b); /** @@ -43,6 +42,7 @@ public interface PrimitiveSink { * @param bytes a byte array * @return this instance */ + @CanIgnoreReturnValue PrimitiveSink putBytes(byte[] bytes); /** @@ -56,6 +56,7 @@ public interface PrimitiveSink { * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > bytes.length} or * {@code len < 0} */ + @CanIgnoreReturnValue PrimitiveSink putBytes(byte[] bytes, int off, int len); /** @@ -67,27 +68,35 @@ public interface PrimitiveSink { * @return this instance * @since 23.0 */ + @CanIgnoreReturnValue PrimitiveSink putBytes(ByteBuffer bytes); /** Puts a short into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putShort(short s); /** Puts an int into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putInt(int i); /** Puts a long into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putLong(long l); /** Puts a float into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putFloat(float f); /** Puts a double into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putDouble(double d); /** Puts a boolean into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putBoolean(boolean b); /** Puts a character into this sink. */ + @CanIgnoreReturnValue PrimitiveSink putChar(char c); /** @@ -99,6 +108,7 @@ public interface PrimitiveSink { * * @since 15.0 (since 11.0 as putString(CharSequence)) */ + @CanIgnoreReturnValue PrimitiveSink putUnencodedChars(CharSequence charSequence); /** @@ -109,5 +119,6 @@ public interface PrimitiveSink { * is faster, produces the same output across Java releases, and processes every {@code char} in * the input, even if some are invalid. */ + @CanIgnoreReturnValue PrimitiveSink putString(CharSequence charSequence, Charset charset); } diff --git a/android/guava/src/com/google/common/hash/SipHashFunction.java b/android/guava/src/com/google/common/hash/SipHashFunction.java index a226b61a50b9..a5f328c3f3fc 100644 --- a/android/guava/src/com/google/common/hash/SipHashFunction.java +++ b/android/guava/src/com/google/common/hash/SipHashFunction.java @@ -24,7 +24,7 @@ import com.google.errorprone.annotations.Immutable; import java.io.Serializable; import java.nio.ByteBuffer; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * {@link HashFunction} implementation of SipHash-c-d. @@ -34,7 +34,6 @@ * @author Daniel J. Bernstein */ @Immutable -@ElementTypesAreNonnullByDefault final class SipHashFunction extends AbstractHashFunction implements Serializable { static final HashFunction SIP_HASH_24 = new SipHashFunction(2, 4, 0x0706050403020100L, 0x0f0e0d0c0b0a0908L); @@ -82,7 +81,7 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof SipHashFunction) { SipHashFunction other = (SipHashFunction) object; return (c == other.c) && (d == other.d) && (k0 == other.k0) && (k1 == other.k1); diff --git a/android/guava/src/com/google/common/hash/SneakyThrows.java b/android/guava/src/com/google/common/hash/SneakyThrows.java new file mode 100644 index 000000000000..ead0cd086adc --- /dev/null +++ b/android/guava/src/com/google/common/hash/SneakyThrows.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.common.hash; + +import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +/** Static utility method for unchecked throwing of any {@link Throwable}. */ +@GwtCompatible +final class SneakyThrows { + /** + * Throws {@code t} as if it were an unchecked {@link Throwable}. + * + *

    This method is useful primarily when we make a reflective call to a method with no {@code + * throws} clause: Java forces us to handle an arbitrary {@link Throwable} from that method, + * rather than just the {@link RuntimeException} or {@link Error} that should be possible. (And in + * fact the static type of {@link Throwable} is occasionally justified even for a method with no + * {@code throws} clause: Some such methods can in fact throw a checked exception (e.g., by + * calling code written in Kotlin).) Typically, we want to let a {@link Throwable} from such a + * method propagate untouched, just as we'd typically let it do for a non-reflective call. + * However, we can't usually write {@code throw t;} when {@code t} has a static type of {@link + * Throwable}. But we can write {@code sneakyThrow(t);}. + * + *

    We sometimes also use {@code sneakyThrow} for testing how our code responds to + * sneaky checked exception. + * + * @return never; this method declares a return type of {@link Error} only so that callers can + * write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always + * throw. + */ + @CanIgnoreReturnValue + static Error sneakyThrow(Throwable t) { + throw new SneakyThrows().throwIt(t); + } + + @SuppressWarnings("unchecked") // not really safe, but that's the point + private Error throwIt(Throwable t) throws T { + throw (T) t; + } + + private SneakyThrows() {} +} diff --git a/android/guava/src/com/google/common/hash/Striped64.java b/android/guava/src/com/google/common/hash/Striped64.java index 1a0671c9d7c5..aa7185509f13 100644 --- a/android/guava/src/com/google/common/hash/Striped64.java +++ b/android/guava/src/com/google/common/hash/Striped64.java @@ -12,9 +12,13 @@ package com.google.common.hash; import com.google.common.annotations.GwtIncompatible; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Random; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; +import sun.misc.Unsafe; /** * A package-local class holding common representation and mechanics for classes supporting dynamic @@ -22,7 +26,7 @@ * so. */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings("SunApi") // b/345822163 abstract class Striped64 extends Number { /* * This class maintains a lazily-initialized table of atomically @@ -104,18 +108,18 @@ static final class Cell { } final boolean cas(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, cmp, val); } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long valueOffset; + private static final Unsafe UNSAFE; + private static final long VALUE_OFFSET; static { try { UNSAFE = getUnsafe(); Class ak = Cell.class; - valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); + VALUE_OFFSET = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); } catch (Exception e) { throw new Error(e); } @@ -136,7 +140,7 @@ final boolean cas(long cmp, long val) { static final int NCPU = Runtime.getRuntime().availableProcessors(); /** Table of cells. When non-null, size is a power of 2. */ - @CheckForNull transient volatile Cell[] cells; + transient volatile Cell @Nullable [] cells; /** * Base value, used mainly when there is no contention, but also as a fallback during table @@ -152,12 +156,12 @@ final boolean cas(long cmp, long val) { /** CASes the base field. */ final boolean casBase(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + return UNSAFE.compareAndSwapLong(this, BASE_OFFSET, cmp, val); } /** CASes the busy field from 0 to 1 to acquire lock. */ final boolean casBusy() { - return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + return UNSAFE.compareAndSwapInt(this, BUSY_OFFSET, 0, 1); } /** @@ -179,7 +183,7 @@ final boolean casBusy() { * @param hc the hash code holder * @param wasUncontended false if CAS failed before call */ - final void retryUpdate(long x, @CheckForNull int[] hc, boolean wasUncontended) { + final void retryUpdate(long x, int @Nullable [] hc, boolean wasUncontended) { int h; if (hc == null) { threadHashCode.set(hc = new int[1]); // Initialize randomly @@ -266,16 +270,16 @@ final void internalReset(long initialValue) { } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long baseOffset; - private static final long busyOffset; + private static final Unsafe UNSAFE; + private static final long BASE_OFFSET; + private static final long BUSY_OFFSET; static { try { UNSAFE = getUnsafe(); Class sk = Striped64.class; - baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); - busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + BASE_OFFSET = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + BUSY_OFFSET = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); } catch (Exception e) { throw new Error(e); } @@ -287,18 +291,18 @@ final void internalReset(long initialValue) { * * @return a sun.misc.Unsafe */ - private static sun.misc.Unsafe getUnsafe() { + private static Unsafe getUnsafe() { try { - return sun.misc.Unsafe.getUnsafe(); + return Unsafe.getUnsafe(); } catch (SecurityException tryReflectionInstead) { } try { - return java.security.AccessController.doPrivileged( - new java.security.PrivilegedExceptionAction() { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { + public Unsafe run() throws Exception { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { f.setAccessible(true); Object x = f.get(null); if (k.isInstance(x)) return k.cast(x); @@ -306,7 +310,7 @@ public sun.misc.Unsafe run() throws Exception { throw new NoSuchFieldError("the Unsafe"); } }); - } catch (java.security.PrivilegedActionException e) { + } catch (PrivilegedActionException e) { throw new RuntimeException("Could not initialize intrinsics", e.getCause()); } } diff --git a/android/guava/src/com/google/common/hash/package-info.java b/android/guava/src/com/google/common/hash/package-info.java index d210f7ef7b46..b5405d48129c 100644 --- a/android/guava/src/com/google/common/hash/package-info.java +++ b/android/guava/src/com/google/common/hash/package-info.java @@ -20,8 +20,8 @@ * href="https://github.com/google/guava/wiki/HashingExplained">hashing. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.hash; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/html/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/html/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index a28b716632d4..000000000000 --- a/android/guava/src/com/google/common/html/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.html; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/html/HtmlEscapers.java b/android/guava/src/com/google/common/html/HtmlEscapers.java index c42638871507..6da547570bdc 100644 --- a/android/guava/src/com/google/common/html/HtmlEscapers.java +++ b/android/guava/src/com/google/common/html/HtmlEscapers.java @@ -25,17 +25,17 @@ * One Google-authored templating system available for external use is Closure Templates. * - *

    HTML escaping is particularly tricky: For example, some - * elements' text contents must not be HTML escaped. As a result, it is impossible to escape an - * HTML document correctly without domain-specific knowledge beyond what {@code HtmlEscapers} - * provides. We strongly encourage the use of HTML templating systems. + *

    HTML escaping is particularly tricky: For example, some elements' text contents must not be HTML + * escaped. As a result, it is impossible to escape an HTML document correctly without + * domain-specific knowledge beyond what {@code HtmlEscapers} provides. We strongly encourage the + * use of HTML templating systems. * * @author Sven Mawson * @author David Beaumont * @since 15.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class HtmlEscapers { /** * Returns an {@link Escaper} instance that escapes HTML metacharacters as specified by {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/html/package-info.java b/android/guava/src/com/google/common/html/package-info.java index f84d7f23d0ca..ccfd5b5693f9 100644 --- a/android/guava/src/com/google/common/html/package-info.java +++ b/android/guava/src/com/google/common/html/package-info.java @@ -17,12 +17,12 @@ * for * HTML. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.html; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/io/AppendableWriter.java b/android/guava/src/com/google/common/io/AppendableWriter.java index d9aab342f29e..ae180d781565 100644 --- a/android/guava/src/com/google/common/io/AppendableWriter.java +++ b/android/guava/src/com/google/common/io/AppendableWriter.java @@ -17,11 +17,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.Writer; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Writer that places all output on an {@link Appendable} target. If the target is {@link Flushable} @@ -31,8 +32,8 @@ * @author Sebastian Kanthak * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault class AppendableWriter extends Writer { private final Appendable target; private boolean closed; @@ -107,14 +108,14 @@ public Writer append(char c) throws IOException { } @Override - public Writer append(@CheckForNull CharSequence charSeq) throws IOException { + public Writer append(@Nullable CharSequence charSeq) throws IOException { checkNotClosed(); target.append(charSeq); return this; } @Override - public Writer append(@CheckForNull CharSequence charSeq, int start, int end) throws IOException { + public Writer append(@Nullable CharSequence charSeq, int start, int end) throws IOException { checkNotClosed(); target.append(charSeq, start, end); return this; diff --git a/android/guava/src/com/google/common/io/BaseEncoding.java b/android/guava/src/com/google/common/io/BaseEncoding.java index 0d6f2e068068..663638c22b92 100644 --- a/android/guava/src/com/google/common/io/BaseEncoding.java +++ b/android/guava/src/com/google/common/io/BaseEncoding.java @@ -20,14 +20,16 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.math.IntMath.divide; import static com.google.common.math.IntMath.log2; +import static java.lang.Math.max; +import static java.lang.Math.min; import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.FLOOR; import static java.math.RoundingMode.UNNECESSARY; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ascii; -import com.google.common.base.Objects; import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.IOException; import java.io.InputStream; @@ -35,7 +37,8 @@ import java.io.Reader; import java.io.Writer; import java.util.Arrays; -import javax.annotation.CheckForNull; +import java.util.Objects; +import org.jspecify.annotations.Nullable; /** * A binary encoding scheme for reversibly translating between byte sequences and printable ASCII @@ -43,7 +46,7 @@ * href="http://tools.ietf.org/html/rfc4648">RFC 4648. For example, the expression: * *

    {@code
    - * BaseEncoding.base32().encode("foo".getBytes(Charsets.US_ASCII))
    + * BaseEncoding.base32().encode("foo".getBytes(US_ASCII))
      * }
    * *

    returns the string {@code "MZXW6==="}, and @@ -122,7 +125,6 @@ * @since 14.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class BaseEncoding { // TODO(lowasser): consider making encodeTo(Appendable, byte[], int, int) public. @@ -135,13 +137,9 @@ public abstract class BaseEncoding { * @since 15.0 */ public static final class DecodingException extends IOException { - DecodingException(String message) { + DecodingException(@Nullable String message) { super(message); } - - DecodingException(Throwable cause) { - super(cause); - } } /** Encodes the specified byte array, and returns the encoded {@code String}. */ @@ -169,12 +167,14 @@ public final String encode(byte[] bytes, int off, int len) { * {@code Writer}. When the returned {@code OutputStream} is closed, so is the backing {@code * Writer}. */ + @J2ktIncompatible @GwtIncompatible // Writer,OutputStream public abstract OutputStream encodingStream(Writer writer); /** * Returns a {@code ByteSink} that writes base-encoded bytes to the specified {@code CharSink}. */ + @J2ktIncompatible @GwtIncompatible // ByteSink,CharSink public final ByteSink encodingSink(CharSink encodedSink) { checkNotNull(encodedSink); @@ -239,6 +239,7 @@ final byte[] decodeChecked(CharSequence chars) * Returns an {@code InputStream} that decodes base-encoded input from the specified {@code * Reader}. The returned stream throws a {@link DecodingException} upon decoding-specific errors. */ + @J2ktIncompatible @GwtIncompatible // Reader,InputStream public abstract InputStream decodingStream(Reader reader); @@ -246,6 +247,7 @@ final byte[] decodeChecked(CharSequence chars) * Returns a {@code ByteSource} that reads base-encoded bytes from the specified {@code * CharSource}. */ + @J2ktIncompatible @GwtIncompatible // ByteSource,CharSource public final ByteSource decodingSource(CharSource encodedSource) { checkNotNull(encodedSource); @@ -318,6 +320,16 @@ CharSequence trimTrailingPadding(CharSequence chars) { */ public abstract BaseEncoding lowerCase(); + /** + * Returns an encoding that behaves equivalently to this encoding, but decodes letters without + * regard to case. + * + * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and + * lower-case characters + * @since 32.0.0 + */ + public abstract BaseEncoding ignoreCase(); + private static final BaseEncoding BASE64 = new Base64Encoding( "base64()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", '='); @@ -418,7 +430,7 @@ public static BaseEncoding base16() { return BASE16; } - private static final class Alphabet { + static final class Alphabet { private final String name; // this is meant to be immutable -- don't modify it! private final char[] chars; @@ -428,8 +440,13 @@ private static final class Alphabet { final int bytesPerChunk; private final byte[] decodabet; private final boolean[] validPadding; + private final boolean ignoreCase; Alphabet(String name, char[] chars) { + this(name, chars, decodabetFor(chars), /* ignoreCase= */ false); + } + + private Alphabet(String name, char[] chars, byte[] decodabet, boolean ignoreCase) { this.name = checkNotNull(name); this.chars = checkNotNull(chars); try { @@ -438,20 +455,30 @@ private static final class Alphabet { throw new IllegalArgumentException("Illegal alphabet length " + chars.length, e); } - /* - * e.g. for base64, bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk == 3. This makes - * for the smallest chunk size that still has charsPerChunk * bitsPerChar be a multiple of 8. - */ - int gcd = Math.min(8, Integer.lowestOneBit(bitsPerChar)); - try { - this.charsPerChunk = 8 / gcd; - this.bytesPerChunk = bitsPerChar / gcd; - } catch (ArithmeticException e) { - throw new IllegalArgumentException("Illegal alphabet " + new String(chars), e); - } + // Compute how input bytes are chunked. For example, with base64 we chunk every 3 bytes into + // 4 characters. We have bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk == 3. + // We're looking for the smallest charsPerChunk such that bitsPerChar * charsPerChunk is a + // multiple of 8. A multiple of 8 has 3 low zero bits, so we just need to figure out how many + // extra zero bits we need to add to the end of bitsPerChar to get 3 in total. + // The logic here would be wrong for bitsPerChar > 8, but since we require distinct ASCII + // characters that can't happen. + int zeroesInBitsPerChar = Integer.numberOfTrailingZeros(bitsPerChar); + this.charsPerChunk = 1 << (3 - zeroesInBitsPerChar); + this.bytesPerChunk = bitsPerChar >> zeroesInBitsPerChar; this.mask = chars.length - 1; + this.decodabet = decodabet; + + boolean[] validPadding = new boolean[charsPerChunk]; + for (int i = 0; i < bytesPerChunk; i++) { + validPadding[divide(i * 8, bitsPerChar, CEILING)] = true; + } + this.validPadding = validPadding; + this.ignoreCase = ignoreCase; + } + + private static byte[] decodabetFor(char[] chars) { byte[] decodabet = new byte[Ascii.MAX + 1]; Arrays.fill(decodabet, (byte) -1); for (int i = 0; i < chars.length; i++) { @@ -460,13 +487,33 @@ private static final class Alphabet { checkArgument(decodabet[c] == -1, "Duplicate character: %s", c); decodabet[c] = (byte) i; } - this.decodabet = decodabet; + return decodabet; + } - boolean[] validPadding = new boolean[charsPerChunk]; - for (int i = 0; i < bytesPerChunk; i++) { - validPadding[divide(i * 8, bitsPerChar, CEILING)] = true; + /** Returns an equivalent {@code Alphabet} except it ignores case. */ + Alphabet ignoreCase() { + if (ignoreCase) { + return this; } - this.validPadding = validPadding; + + // We can't use .clone() because of GWT. + byte[] newDecodabet = Arrays.copyOf(decodabet, decodabet.length); + for (int upper = 'A'; upper <= 'Z'; upper++) { + int lower = upper | 0x20; + byte decodeUpper = decodabet[upper]; + byte decodeLower = decodabet[lower]; + if (decodeUpper == -1) { + newDecodabet[upper] = decodeLower; + } else { + checkState( + decodeLower == -1, + "Can't ignoreCase() since '%s' and '%s' encode different values", + (char) upper, + (char) lower); + newDecodabet[lower] = decodeUpper; + } + } + return new Alphabet(name + ".ignoreCase()", chars, newDecodabet, /* ignoreCase= */ true); } char encode(int bits) { @@ -523,7 +570,8 @@ Alphabet upperCase() { for (int i = 0; i < chars.length; i++) { upperCased[i] = Ascii.toUpperCase(chars[i]); } - return new Alphabet(name + ".upperCase()", upperCased); + Alphabet upperCase = new Alphabet(name + ".upperCase()", upperCased); + return ignoreCase ? upperCase.ignoreCase() : upperCase; } Alphabet lowerCase() { @@ -535,7 +583,8 @@ Alphabet lowerCase() { for (int i = 0; i < chars.length; i++) { lowerCased[i] = Ascii.toLowerCase(chars[i]); } - return new Alphabet(name + ".lowerCase()", lowerCased); + Alphabet lowerCase = new Alphabet(name + ".lowerCase()", lowerCased); + return ignoreCase ? lowerCase.ignoreCase() : lowerCase; } public boolean matches(char c) { @@ -548,31 +597,30 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof Alphabet) { Alphabet that = (Alphabet) other; - return Arrays.equals(this.chars, that.chars); + return this.ignoreCase == that.ignoreCase && Arrays.equals(this.chars, that.chars); } return false; } @Override public int hashCode() { - return Arrays.hashCode(chars); + return Arrays.hashCode(chars) + (ignoreCase ? 1231 : 1237); } } - static class StandardBaseEncoding extends BaseEncoding { - // TODO(lowasser): provide a useful toString + private static class StandardBaseEncoding extends BaseEncoding { final Alphabet alphabet; - @CheckForNull final Character paddingChar; + final @Nullable Character paddingChar; - StandardBaseEncoding(String name, String alphabetChars, @CheckForNull Character paddingChar) { + StandardBaseEncoding(String name, String alphabetChars, @Nullable Character paddingChar) { this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); } - StandardBaseEncoding(Alphabet alphabet, @CheckForNull Character paddingChar) { + StandardBaseEncoding(Alphabet alphabet, @Nullable Character paddingChar) { this.alphabet = checkNotNull(alphabet); checkArgument( paddingChar == null || !alphabet.matches(paddingChar), @@ -586,6 +634,7 @@ int maxEncodedSize(int bytes) { return alphabet.charsPerChunk * divide(bytes, alphabet.bytesPerChunk, CEILING); } + @J2ktIncompatible @GwtIncompatible // Writer,OutputStream @Override public OutputStream encodingStream(Writer out) { @@ -636,7 +685,7 @@ void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOExcept checkNotNull(target); checkPositionIndexes(off, off + len, bytes.length); for (int i = 0; i < len; i += alphabet.bytesPerChunk) { - encodeChunkTo(target, bytes, off + i, Math.min(alphabet.bytesPerChunk, len - i)); + encodeChunkTo(target, bytes, off + i, min(alphabet.bytesPerChunk, len - i)); } } @@ -727,6 +776,7 @@ int decodeTo(byte[] target, CharSequence chars) throws DecodingException { } @Override + @J2ktIncompatible @GwtIncompatible // Reader,InputStream public InputStream decodingStream(Reader reader) { checkNotNull(reader); @@ -830,8 +880,9 @@ public BaseEncoding withSeparator(String separator, int afterEveryChars) { return new SeparatedBaseEncoding(this, separator, afterEveryChars); } - @LazyInit @CheckForNull private transient BaseEncoding upperCase; - @LazyInit @CheckForNull private transient BaseEncoding lowerCase; + @LazyInit private volatile @Nullable BaseEncoding upperCase; + @LazyInit private volatile @Nullable BaseEncoding lowerCase; + @LazyInit private volatile @Nullable BaseEncoding ignoreCase; @Override public BaseEncoding upperCase() { @@ -853,14 +904,24 @@ public BaseEncoding lowerCase() { return result; } - BaseEncoding newInstance(Alphabet alphabet, @CheckForNull Character paddingChar) { + @Override + public BaseEncoding ignoreCase() { + BaseEncoding result = ignoreCase; + if (result == null) { + Alphabet ignore = alphabet.ignoreCase(); + result = ignoreCase = (ignore == alphabet) ? this : newInstance(ignore, paddingChar); + } + return result; + } + + BaseEncoding newInstance(Alphabet alphabet, @Nullable Character paddingChar) { return new StandardBaseEncoding(alphabet, paddingChar); } @Override public String toString() { StringBuilder builder = new StringBuilder("BaseEncoding."); - builder.append(alphabet.toString()); + builder.append(alphabet); if (8 % alphabet.bitsPerChar != 0) { if (paddingChar == null) { builder.append(".omitPadding()"); @@ -872,11 +933,11 @@ public String toString() { } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof StandardBaseEncoding) { StandardBaseEncoding that = (StandardBaseEncoding) other; return this.alphabet.equals(that.alphabet) - && Objects.equal(this.paddingChar, that.paddingChar); + && Objects.equals(this.paddingChar, that.paddingChar); } return false; } @@ -887,7 +948,7 @@ public int hashCode() { } } - static final class Base16Encoding extends StandardBaseEncoding { + private static final class Base16Encoding extends StandardBaseEncoding { final char[] encoding = new char[512]; Base16Encoding(String name, String alphabetChars) { @@ -929,17 +990,17 @@ int decodeTo(byte[] target, CharSequence chars) throws DecodingException { } @Override - BaseEncoding newInstance(Alphabet alphabet, @CheckForNull Character paddingChar) { + BaseEncoding newInstance(Alphabet alphabet, @Nullable Character paddingChar) { return new Base16Encoding(alphabet); } } - static final class Base64Encoding extends StandardBaseEncoding { - Base64Encoding(String name, String alphabetChars, @CheckForNull Character paddingChar) { + private static final class Base64Encoding extends StandardBaseEncoding { + Base64Encoding(String name, String alphabetChars, @Nullable Character paddingChar) { this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); } - private Base64Encoding(Alphabet alphabet, @CheckForNull Character paddingChar) { + private Base64Encoding(Alphabet alphabet, @Nullable Character paddingChar) { super(alphabet, paddingChar); checkArgument(alphabet.chars.length == 64); } @@ -986,11 +1047,12 @@ int decodeTo(byte[] target, CharSequence chars) throws DecodingException { } @Override - BaseEncoding newInstance(Alphabet alphabet, @CheckForNull Character paddingChar) { + BaseEncoding newInstance(Alphabet alphabet, @Nullable Character paddingChar) { return new Base64Encoding(alphabet, paddingChar); } } + @J2ktIncompatible @GwtIncompatible static Reader ignoringReader(Reader delegate, String toIgnore) { checkNotNull(delegate); @@ -1037,17 +1099,18 @@ public Appendable append(char c) throws IOException { } @Override - public Appendable append(@CheckForNull CharSequence chars, int off, int len) { + public Appendable append(@Nullable CharSequence chars, int off, int len) { throw new UnsupportedOperationException(); } @Override - public Appendable append(@CheckForNull CharSequence chars) { + public Appendable append(@Nullable CharSequence chars) { throw new UnsupportedOperationException(); } }; } + @J2ktIncompatible @GwtIncompatible // Writer static Writer separatingWriter(Writer delegate, String separator, int afterEveryChars) { Appendable separatingAppendable = separatingAppendable(delegate, separator, afterEveryChars); @@ -1096,9 +1159,10 @@ CharSequence trimTrailingPadding(CharSequence chars) { int maxEncodedSize(int bytes) { int unseparatedSize = delegate.maxEncodedSize(bytes); return unseparatedSize - + separator.length() * divide(Math.max(0, unseparatedSize - 1), afterEveryChars, FLOOR); + + separator.length() * divide(max(0, unseparatedSize - 1), afterEveryChars, FLOOR); } + @J2ktIncompatible @GwtIncompatible // Writer,OutputStream @Override public OutputStream encodingStream(Writer output) { @@ -1140,6 +1204,7 @@ int decodeTo(byte[] target, CharSequence chars) throws DecodingException { } @Override + @J2ktIncompatible @GwtIncompatible // Reader,InputStream public InputStream decodingStream(Reader reader) { return delegate.decodingStream(ignoringReader(reader, separator)); @@ -1170,6 +1235,11 @@ public BaseEncoding lowerCase() { return delegate.lowerCase().withSeparator(separator, afterEveryChars); } + @Override + public BaseEncoding ignoreCase() { + return delegate.ignoreCase().withSeparator(separator, afterEveryChars); + } + @Override public String toString() { return delegate + ".withSeparator(\"" + separator + "\", " + afterEveryChars + ")"; diff --git a/android/guava/src/com/google/common/io/ByteArrayDataInput.java b/android/guava/src/com/google/common/io/ByteArrayDataInput.java index cf84fcc136b7..375f07cd67f4 100644 --- a/android/guava/src/com/google/common/io/ByteArrayDataInput.java +++ b/android/guava/src/com/google/common/io/ByteArrayDataInput.java @@ -15,10 +15,11 @@ package com.google.common.io; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.DataInput; import java.io.IOException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An extension of {@code DataInput} for reading from in-memory byte arrays; its methods offer @@ -32,14 +33,14 @@ * @author Kevin Bourrillion * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface ByteArrayDataInput extends DataInput { @Override - void readFully(byte b[]); + void readFully(byte[] b); @Override - void readFully(byte b[], int off, int len); + void readFully(byte[] b, int off, int len); // not guaranteed to skip n bytes so result should NOT be ignored // use ByteStreams.skipFully or one of the read methods instead @@ -88,8 +89,7 @@ public interface ByteArrayDataInput extends DataInput { @CanIgnoreReturnValue // to skip a line @Override - @CheckForNull - String readLine(); + @Nullable String readLine(); @CanIgnoreReturnValue // to skip a field @Override diff --git a/android/guava/src/com/google/common/io/ByteArrayDataOutput.java b/android/guava/src/com/google/common/io/ByteArrayDataOutput.java index 373907361d8c..32c9e2fca9bc 100644 --- a/android/guava/src/com/google/common/io/ByteArrayDataOutput.java +++ b/android/guava/src/com/google/common/io/ByteArrayDataOutput.java @@ -15,6 +15,7 @@ package com.google.common.io; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.DataOutput; import java.io.IOException; @@ -25,17 +26,17 @@ * @author Jayaprabhakar Kadarkarai * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface ByteArrayDataOutput extends DataOutput { @Override void write(int b); @Override - void write(byte b[]); + void write(byte[] b); @Override - void write(byte b[], int off, int len); + void write(byte[] b, int off, int len); @Override void writeBoolean(boolean v); diff --git a/android/guava/src/com/google/common/io/ByteProcessor.java b/android/guava/src/com/google/common/io/ByteProcessor.java index 98ea3ffbb27d..5a2a667650ba 100644 --- a/android/guava/src/com/google/common/io/ByteProcessor.java +++ b/android/guava/src/com/google/common/io/ByteProcessor.java @@ -14,12 +14,12 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; import java.io.IOException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A callback interface to process bytes from a stream. @@ -30,10 +30,9 @@ * @author Chris Nokleberg * @since 1.0 */ -@Beta @DoNotMock("Implement it normally") +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface ByteProcessor { /** * This method will be called for each chunk of bytes in an input stream. The implementation diff --git a/android/guava/src/com/google/common/io/ByteSink.java b/android/guava/src/com/google/common/io/ByteSink.java index 7a6af6fc588b..d3013cb1ffb3 100644 --- a/android/guava/src/com/google/common/io/ByteSink.java +++ b/android/guava/src/com/google/common/io/ByteSink.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.BufferedOutputStream; import java.io.IOException; @@ -45,8 +46,8 @@ * @since 14.0 * @author Colin Decker */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ByteSink { /** Constructor for use by subclasses. */ @@ -97,15 +98,8 @@ public OutputStream openBufferedStream() throws IOException { public void write(byte[] bytes) throws IOException { checkNotNull(bytes); - Closer closer = Closer.create(); - try { - OutputStream out = closer.register(openStream()); + try (OutputStream out = openStream()) { out.write(bytes); - out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); } } @@ -120,16 +114,8 @@ public void write(byte[] bytes) throws IOException { public long writeFrom(InputStream input) throws IOException { checkNotNull(input); - Closer closer = Closer.create(); - try { - OutputStream out = closer.register(openStream()); - long written = ByteStreams.copy(input, out); - out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 - return written; - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); + try (OutputStream out = openStream()) { + return ByteStreams.copy(input, out); } } diff --git a/android/guava/src/com/google/common/io/ByteSource.java b/android/guava/src/com/google/common/io/ByteSource.java index c8da9678b812..d938fdf6bb4e 100644 --- a/android/guava/src/com/google/common/io/ByteSource.java +++ b/android/guava/src/com/google/common/io/ByteSource.java @@ -18,9 +18,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.io.ByteStreams.createBuffer; import static com.google.common.io.ByteStreams.skipUpTo; +import static java.lang.Math.min; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; @@ -40,7 +41,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Iterator; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A readable source of bytes, such as a file. Unlike an {@link InputStream}, a {@code ByteSource} @@ -73,8 +74,8 @@ * @since 14.0 * @author Colin Decker */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ByteSource { /** Constructor for use by subclasses. */ @@ -178,7 +179,6 @@ public boolean isEmpty() throws IOException { * * @since 19.0 */ - @Beta public Optional sizeIfKnown() { return Optional.absent(); } @@ -314,8 +314,8 @@ public byte[] read() throws IOException { * processor} throws an {@code IOException} * @since 16.0 */ - @Beta @CanIgnoreReturnValue // some processors won't return a useful result + @ParametricNullness public T read(ByteProcessor processor) throws IOException { checkNotNull(processor); @@ -546,7 +546,7 @@ public ByteSource slice(long offset, long length) { long maxLength = this.length - offset; return maxLength <= 0 ? ByteSource.empty() - : ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength)); + : ByteSource.this.slice(this.offset + offset, min(length, maxLength)); } @Override @@ -559,8 +559,8 @@ public Optional sizeIfKnown() { Optional optionalUnslicedSize = ByteSource.this.sizeIfKnown(); if (optionalUnslicedSize.isPresent()) { long unslicedSize = optionalUnslicedSize.get(); - long off = Math.min(offset, unslicedSize); - return Optional.of(Math.min(length, unslicedSize - off)); + long off = min(offset, unslicedSize); + return Optional.of(min(length, unslicedSize - off)); } return Optional.absent(); } @@ -571,7 +571,9 @@ public String toString() { } } - private static class ByteArrayByteSource extends ByteSource { + private static class ByteArrayByteSource extends + ByteSource + { final byte[] bytes; final int offset; @@ -594,7 +596,7 @@ public InputStream openStream() { } @Override - public InputStream openBufferedStream() throws IOException { + public InputStream openBufferedStream() { return openStream(); } @@ -642,8 +644,8 @@ public ByteSource slice(long offset, long length) { checkArgument(offset >= 0, "offset (%s) may not be negative", offset); checkArgument(length >= 0, "length (%s) may not be negative", length); - offset = Math.min(offset, this.length); - length = Math.min(length, this.length - offset); + offset = min(offset, this.length); + length = min(length, this.length - offset); int newOffset = this.offset + (int) offset; return new ByteArrayByteSource(bytes, newOffset, (int) length); } diff --git a/android/guava/src/com/google/common/io/ByteStreams.java b/android/guava/src/com/google/common/io/ByteStreams.java index 388188030861..17e8b9c92638 100644 --- a/android/guava/src/com/google/common/io/ByteStreams.java +++ b/android/guava/src/com/google/common/io/ByteStreams.java @@ -18,9 +18,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndex; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.max; +import static java.lang.Math.min; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.math.IntMath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayInputStream; @@ -41,8 +43,7 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides utility methods for working with byte arrays and I/O streams. @@ -51,8 +52,8 @@ * @author Colin Decker * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ByteStreams { private static final int BUFFER_SIZE = 8192; @@ -97,6 +98,9 @@ private ByteStreams() {} * Copies all bytes from the input stream to the output stream. Does not close or flush either * stream. * + *

    Java 9 users and later: this method should be treated as deprecated; use the + * equivalent {@link InputStream#transferTo} method instead. + * * @param from the input stream to read from * @param to the output stream to write to * @return the number of bytes copied @@ -170,13 +174,18 @@ public static long copy(ReadableByteChannel from, WritableByteChannel to) throws */ private static byte[] toByteArrayInternal(InputStream in, Queue bufs, int totalLen) throws IOException { - // Starting with an 8k buffer, double the size of each successive buffer. Buffers are retained - // in a deque so that there's no copying between buffers while reading and so all of the bytes - // in each new allocated buffer are available for reading from the stream. - for (int bufSize = BUFFER_SIZE; + // Roughly size to match what has been read already. Some file systems, such as procfs, return 0 + // as their length. These files are very small, so it's wasteful to allocate an 8KB buffer. + int initialBufferSize = min(BUFFER_SIZE, max(128, Integer.highestOneBit(totalLen) * 2)); + // Starting with an 8k buffer, double the size of each successive buffer. Smaller buffers + // quadruple in size until they reach 8k, to minimize the number of small reads for longer + // streams. Buffers are retained in a deque so that there's no copying between buffers while + // reading and so all of the bytes in each new allocated buffer are available for reading from + // the stream. + for (int bufSize = initialBufferSize; totalLen < MAX_ARRAY_LEN; - bufSize = IntMath.saturatedMultiply(bufSize, 2)) { - byte[] buf = new byte[Math.min(bufSize, MAX_ARRAY_LEN - totalLen)]; + bufSize = IntMath.saturatedMultiply(bufSize, bufSize < 4096 ? 4 : 2)) { + byte[] buf = new byte[min(bufSize, MAX_ARRAY_LEN - totalLen)]; bufs.add(buf); int off = 0; while (off < buf.length) { @@ -200,11 +209,18 @@ private static byte[] toByteArrayInternal(InputStream in, Queue bufs, in } private static byte[] combineBuffers(Queue bufs, int totalLen) { - byte[] result = new byte[totalLen]; - int remaining = totalLen; + if (bufs.isEmpty()) { + return new byte[0]; + } + byte[] result = bufs.remove(); + if (result.length == totalLen) { + return result; + } + int remaining = totalLen - result.length; + result = Arrays.copyOf(result, totalLen); while (remaining > 0) { byte[] buf = bufs.remove(); - int bytesToCopy = Math.min(remaining, buf.length); + int bytesToCopy = min(remaining, buf.length); int resultOffset = totalLen - remaining; System.arraycopy(buf, 0, result, resultOffset, bytesToCopy); remaining -= bytesToCopy; @@ -215,6 +231,8 @@ private static byte[] combineBuffers(Queue bufs, int totalLen) { /** * Reads all bytes from an input stream into a byte array. Does not close the stream. * + *

    Java 9+ users: use {@code in#readAllBytes()} instead. + * * @param in the input stream to read from * @return a byte array containing all the bytes from the stream * @throws IOException if an I/O error occurs @@ -269,7 +287,6 @@ static byte[] toByteArray(InputStream in, long expectedSize) throws IOException * @since 20.0 */ @CanIgnoreReturnValue - @Beta public static long exhaust(InputStream in) throws IOException { long total = 0; long read; @@ -284,7 +301,6 @@ public static long exhaust(InputStream in) throws IOException { * Returns a new {@link ByteArrayDataInput} instance to read from the {@code bytes} array from the * beginning. */ - @Beta public static ByteArrayDataInput newDataInput(byte[] bytes) { return newDataInput(new ByteArrayInputStream(bytes)); } @@ -296,7 +312,6 @@ public static ByteArrayDataInput newDataInput(byte[] bytes) { * @throws IndexOutOfBoundsException if {@code start} is negative or greater than the length of * the array */ - @Beta public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { checkPositionIndex(start, bytes.length); return newDataInput(new ByteArrayInputStream(bytes, start, bytes.length - start)); @@ -309,7 +324,6 @@ public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { * * @since 17.0 */ - @Beta public static ByteArrayDataInput newDataInput(ByteArrayInputStream byteArrayInputStream) { return new ByteArrayDataInputStream(checkNotNull(byteArrayInputStream)); } @@ -322,7 +336,7 @@ private static class ByteArrayDataInputStream implements ByteArrayDataInput { } @Override - public void readFully(byte b[]) { + public void readFully(byte[] b) { try { input.readFully(b); } catch (IOException e) { @@ -331,7 +345,7 @@ public void readFully(byte b[]) { } @Override - public void readFully(byte b[], int off, int len) { + public void readFully(byte[] b, int off, int len) { try { input.readFully(b, off, len); } catch (IOException e) { @@ -441,8 +455,7 @@ public double readDouble() { } @Override - @CheckForNull - public String readLine() { + public @Nullable String readLine() { try { return input.readLine(); } catch (IOException e) { @@ -461,7 +474,6 @@ public String readUTF() { } /** Returns a new {@link ByteArrayDataOutput} instance with a default size. */ - @Beta public static ByteArrayDataOutput newDataOutput() { return newDataOutput(new ByteArrayOutputStream()); } @@ -472,7 +484,6 @@ public static ByteArrayDataOutput newDataOutput() { * * @throws IllegalArgumentException if {@code size} is negative */ - @Beta public static ByteArrayDataOutput newDataOutput(int size) { // When called at high frequency, boxing size generates too much garbage, // so avoid doing that if we can. @@ -494,7 +505,6 @@ public static ByteArrayDataOutput newDataOutput(int size) { * * @since 17.0 */ - @Beta public static ByteArrayDataOutput newDataOutput(ByteArrayOutputStream byteArrayOutputStream) { return new ByteArrayDataOutputStream(checkNotNull(byteArrayOutputStream)); } @@ -657,6 +667,7 @@ public void write(byte[] b) { @Override public void write(byte[] b, int off, int len) { checkNotNull(b); + checkPositionIndexes(off, off + len, b.length); } @Override @@ -670,7 +681,6 @@ public String toString() { * * @since 14.0 (since 1.0 as com.google.common.io.NullOutputStream) */ - @Beta public static OutputStream nullOutputStream() { return NULL_OUTPUT_STREAM; } @@ -683,7 +693,6 @@ public static OutputStream nullOutputStream() { * @return a length-limited {@link InputStream} * @since 14.0 (since 1.0 as com.google.common.io.LimitInputStream) */ - @Beta public static InputStream limit(InputStream in, long limit) { return new LimitedInputStream(in, limit); } @@ -702,7 +711,7 @@ private static final class LimitedInputStream extends FilterInputStream { @Override public int available() throws IOException { - return (int) Math.min(in.available(), left); + return (int) min(in.available(), left); } // it's okay to mark even if mark isn't supported, as reset won't work @@ -731,7 +740,7 @@ public int read(byte[] b, int off, int len) throws IOException { return -1; } - len = (int) Math.min(len, left); + len = (int) min(len, left); int result = in.read(b, off, len); if (result != -1) { left -= result; @@ -754,7 +763,7 @@ public synchronized void reset() throws IOException { @Override public long skip(long n) throws IOException { - n = Math.min(n, left); + n = min(n, left); long skipped = in.skip(n); left -= skipped; return skipped; @@ -770,7 +779,6 @@ public long skip(long n) throws IOException { * @throws EOFException if this stream reaches the end before reading all the bytes. * @throws IOException if an I/O error occurs. */ - @Beta public static void readFully(InputStream in, byte[] b) throws IOException { readFully(in, b, 0, b.length); } @@ -787,7 +795,6 @@ public static void readFully(InputStream in, byte[] b) throws IOException { * @throws EOFException if this stream reaches the end before reading all the bytes. * @throws IOException if an I/O error occurs. */ - @Beta public static void readFully(InputStream in, byte[] b, int off, int len) throws IOException { int read = read(in, b, off, len); if (read != len) { @@ -805,7 +812,6 @@ public static void readFully(InputStream in, byte[] b, int off, int len) throws * @throws EOFException if this stream reaches the end before skipping all the bytes * @throws IOException if an I/O error occurs, or the stream does not support skipping */ - @Beta public static void skipFully(InputStream in, long n) throws IOException { long skipped = skipUpTo(in, n); if (skipped < n) { @@ -831,7 +837,7 @@ static long skipUpTo(InputStream in, long n) throws IOException { if (skipped == 0) { // Do a buffered read since skipSafely could return 0 repeatedly, for example if // in.available() always returns 0 (the default). - int skip = (int) Math.min(remaining, BUFFER_SIZE); + int skip = (int) min(remaining, BUFFER_SIZE); if (buf == null) { // Allocate a buffer bounded by the maximum size that can be requested, for // example an array of BUFFER_SIZE is unnecessary when the value of remaining @@ -859,7 +865,7 @@ static long skipUpTo(InputStream in, long n) throws IOException { */ private static long skipSafely(InputStream in, long n) throws IOException { int available = in.available(); - return available == 0 ? 0 : in.skip(Math.min(available, n)); + return available == 0 ? 0 : in.skip(min(available, n)); } /** @@ -871,7 +877,6 @@ private static long skipSafely(InputStream in, long n) throws IOException { * @throws IOException if an I/O error occurs * @since 14.0 */ - @Beta @CanIgnoreReturnValue // some processors won't return a useful result @ParametricNullness public static T readBytes( @@ -911,7 +916,6 @@ private static long skipSafely(InputStream in, long n) throws IOException { * @throws IndexOutOfBoundsException if {@code off} is negative, if {@code len} is negative, or if * {@code off + len} is greater than {@code b.length} */ - @Beta @CanIgnoreReturnValue // Sometimes you don't care how many bytes you actually read, I guess. // (You know that it's either going to read len bytes or stop at EOF.) diff --git a/android/guava/src/com/google/common/io/CharSequenceReader.java b/android/guava/src/com/google/common/io/CharSequenceReader.java index 790e26623625..819abd1134f5 100644 --- a/android/guava/src/com/google/common/io/CharSequenceReader.java +++ b/android/guava/src/com/google/common/io/CharSequenceReader.java @@ -17,13 +17,15 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.Reader; import java.nio.CharBuffer; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link Reader} that reads the characters in a {@link CharSequence}. Like {@code StringReader}, @@ -32,11 +34,11 @@ * @author Colin Decker */ // TODO(cgdecker): make this public? as a type, or a method in CharStreams? +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class CharSequenceReader extends Reader { - @CheckForNull private CharSequence seq; + private @Nullable CharSequence seq; private int pos; private int mark; @@ -80,7 +82,7 @@ public synchronized int read(CharBuffer target) throws IOException { if (!hasRemaining()) { return -1; } - int charsToRead = Math.min(target.remaining(), remaining()); + int charsToRead = min(target.remaining(), remaining()); for (int i = 0; i < charsToRead; i++) { target.put(seq.charAt(pos++)); } @@ -102,7 +104,7 @@ public synchronized int read(char[] cbuf, int off, int len) throws IOException { if (!hasRemaining()) { return -1; } - int charsToRead = Math.min(len, remaining()); + int charsToRead = min(len, remaining()); for (int i = 0; i < charsToRead; i++) { cbuf[off + i] = seq.charAt(pos++); } @@ -113,7 +115,7 @@ public synchronized int read(char[] cbuf, int off, int len) throws IOException { public synchronized long skip(long n) throws IOException { checkArgument(n >= 0, "n (%s) may not be negative", n); checkOpen(); - int charsToSkip = (int) Math.min(remaining(), n); // safe because remaining is an int + int charsToSkip = (int) min(remaining(), n); // safe because remaining is an int pos += charsToSkip; return charsToSkip; } diff --git a/android/guava/src/com/google/common/io/CharSink.java b/android/guava/src/com/google/common/io/CharSink.java index bdf5f38c3c32..a013d519a442 100644 --- a/android/guava/src/com/google/common/io/CharSink.java +++ b/android/guava/src/com/google/common/io/CharSink.java @@ -15,14 +15,18 @@ package com.google.common.io; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.BufferedWriter; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.stream.Stream; /** * A destination to which characters can be written, such as a text file. Unlike a {@link Writer}, a @@ -47,8 +51,8 @@ * @since 14.0 * @author Colin Decker */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class CharSink { /** Constructor for use by subclasses. */ @@ -90,15 +94,8 @@ public Writer openBufferedStream() throws IOException { public void write(CharSequence charSequence) throws IOException { checkNotNull(charSequence); - Closer closer = Closer.create(); - try { - Writer out = closer.register(openStream()); + try (Writer out = openStream()) { out.append(charSequence); - out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); } } @@ -121,20 +118,45 @@ public void writeLines(Iterable lines) throws IOExceptio */ public void writeLines(Iterable lines, String lineSeparator) throws IOException { - checkNotNull(lines); + writeLines(lines.iterator(), lineSeparator); + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the operating system's default line separator. This method is equivalent to {@code + * writeLines(lines, System.getProperty("line.separator"))}. + * + * @throws IOException if an I/O error occurs while writing to this sink + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Stream. + public void writeLines(Stream lines) throws IOException { + writeLines(lines, LINE_SEPARATOR.value()); + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the given line separator. + * + * @throws IOException if an I/O error occurs while writing to this sink + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Stream. + public void writeLines(Stream lines, String lineSeparator) + throws IOException { + writeLines(lines.iterator(), lineSeparator); + } + + private void writeLines(Iterator lines, String lineSeparator) + throws IOException { checkNotNull(lineSeparator); - Closer closer = Closer.create(); - try { - Writer out = closer.register(openBufferedStream()); - for (CharSequence line : lines) { - out.append(line).append(lineSeparator); + try (Writer out = openBufferedStream()) { + while (lines.hasNext()) { + out.append(lines.next()).append(lineSeparator); } - out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); } } @@ -150,16 +172,8 @@ public void writeLines(Iterable lines, String lineSepara public long writeFrom(Readable readable) throws IOException { checkNotNull(readable); - Closer closer = Closer.create(); - try { - Writer out = closer.register(openStream()); - long written = CharStreams.copy(readable, out); - out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 - return written; - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); + try (Writer out = openStream()) { + return CharStreams.copy(readable, out); } } } diff --git a/android/guava/src/com/google/common/io/CharSource.java b/android/guava/src/com/google/common/io/CharSource.java index b7623ff62176..aba363c4b86e 100644 --- a/android/guava/src/com/google/common/io/CharSource.java +++ b/android/guava/src/com/google/common/io/CharSource.java @@ -15,9 +15,10 @@ package com.google.common.io; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Streams.stream; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Optional; import com.google.common.base.Splitter; @@ -25,17 +26,21 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.MustBeClosed; import java.io.BufferedReader; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; +import java.io.UncheckedIOException; import java.io.Writer; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.function.Consumer; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; /** * A readable source of characters, such as a text file. Unlike a {@link Reader}, a {@code @@ -76,8 +81,8 @@ * @since 14.0 * @author Colin Decker */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class CharSource { /** Constructor for use by subclasses. */ @@ -94,7 +99,6 @@ protected CharSource() {} * * @since 20.0 */ - @Beta public ByteSource asByteSource(Charset charset) { return new AsByteSource(charset); } @@ -124,6 +128,55 @@ public BufferedReader openBufferedStream() throws IOException { : new BufferedReader(reader); } + /** + * Opens a new {@link Stream} for reading text one line at a time from this source. This method + * returns a new, independent stream each time it is called. + * + *

    The returned stream is lazy and only reads from the source in the terminal operation. If an + * I/O error occurs while the stream is reading from the source or when the stream is closed, an + * {@link UncheckedIOException} is thrown. + * + *

    Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + *

    The caller is responsible for ensuring that the returned stream is closed. For example: + * + *

    {@code
    +   * try (Stream lines = source.lines()) {
    +   *   lines.map(...)
    +   *      .filter(...)
    +   *      .forEach(...);
    +   * }
    +   * }
    + * + * @throws IOException if an I/O error occurs while opening the stream + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @MustBeClosed + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Stream calls. + @IgnoreJRERequirement + public Stream lines() throws IOException { + BufferedReader reader = openBufferedStream(); + return reader.lines().onClose(() -> closeUnchecked(reader)); + } + + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // helper for lines() + /* + * If we make these calls inline inside the lambda inside lines(), we get an Animal Sniffer error, + * despite the @IgnoreJRERequirement annotation there. For details, see ImmutableSortedMultiset. + */ + private static void closeUnchecked(Closeable closeable) { + try { + closeable.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + /** * Returns the size of this source in chars, if the size can be easily determined without actually * opening the data stream. @@ -138,7 +191,6 @@ public BufferedReader openBufferedStream() throws IOException { * * @since 19.0 */ - @Beta public Optional lengthIfKnown() { return Optional.absent(); } @@ -162,7 +214,6 @@ public Optional lengthIfKnown() { * @throws IOException if an I/O error occurs while reading the length of this source * @since 19.0 */ - @Beta public long length() throws IOException { Optional lengthIfKnown = lengthIfKnown(); if (lengthIfKnown.isPresent()) { @@ -262,8 +313,7 @@ public String read() throws IOException { * * @throws IOException if an I/O error occurs while reading from this source */ - @CheckForNull - public String readFirstLine() throws IOException { + public @Nullable String readFirstLine() throws IOException { Closer closer = Closer.create(); try { BufferedReader reader = closer.register(openBufferedStream()); @@ -317,7 +367,6 @@ public ImmutableList readLines() throws IOException { * processor} throws an {@code IOException} * @since 16.0 */ - @Beta @CanIgnoreReturnValue // some processors won't return a useful result @ParametricNullness public T readLines(LineProcessor processor) throws IOException { @@ -334,6 +383,34 @@ public ImmutableList readLines() throws IOException { } } + /** + * Reads all lines of text from this source, running the given {@code action} for each line as it + * is read. + * + *

    Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + * @throws IOException if an I/O error occurs while reading from this source or if {@code action} + * throws an {@code UncheckedIOException} + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + /* + * We have to rely on users not to call this without library desugaring, as NewApi won't flag + * Consumer creation. + */ + @IgnoreJRERequirement + public void forEachLine(Consumer action) throws IOException { + try (Stream lines = lines()) { + // The lines should be ordered regardless in most cases, but use forEachOrdered to be sure + lines.forEachOrdered(action); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + /** * Returns whether the source has zero chars. The default implementation first checks {@link * #lengthIfKnown}, returning true if it's known to be zero and false if it's known to be @@ -509,8 +586,7 @@ private Iterator linesIterator() { Iterator lines = LINE_SPLITTER.split(seq).iterator(); @Override - @CheckForNull - protected String computeNext() { + protected @Nullable String computeNext() { if (lines.hasNext()) { String next = lines.next(); // skip last line if it's empty @@ -524,8 +600,15 @@ protected String computeNext() { } @Override - @CheckForNull - public String readFirstLine() { + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Stream calls + @IgnoreJRERequirement + public Stream lines() { + return stream(linesIterator()); + } + + @Override + public @Nullable String readFirstLine() { Iterator lines = linesIterator(); return lines.hasNext() ? lines.next() : null; } diff --git a/android/guava/src/com/google/common/io/CharStreams.java b/android/guava/src/com/google/common/io/CharStreams.java index d36f9a3c5cd6..5fcb7be50495 100644 --- a/android/guava/src/com/google/common/io/CharStreams.java +++ b/android/guava/src/com/google/common/io/CharStreams.java @@ -17,8 +17,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Closeable; import java.io.EOFException; @@ -28,8 +28,7 @@ import java.nio.CharBuffer; import java.util.ArrayList; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides utility methods for working with character streams. @@ -43,8 +42,8 @@ * @author Colin Decker * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class CharStreams { // 2K chars (4K bytes) @@ -193,7 +192,6 @@ private static StringBuilder toStringBuilder(Readable r) throws IOException { * @return a mutable {@link List} containing all the lines * @throws IOException if an I/O error occurs */ - @Beta public static List readLines(Readable r) throws IOException { List result = new ArrayList<>(); LineReader lineReader = new LineReader(r); @@ -213,7 +211,6 @@ public static List readLines(Readable r) throws IOException { * @throws IOException if an I/O error occurs * @since 14.0 */ - @Beta @CanIgnoreReturnValue // some processors won't return a useful result @ParametricNullness public static T readLines( @@ -237,7 +234,6 @@ public static List readLines(Readable r) throws IOException { * * @since 20.0 */ - @Beta @CanIgnoreReturnValue public static long exhaust(Readable readable) throws IOException { long total = 0; @@ -259,7 +255,6 @@ public static long exhaust(Readable readable) throws IOException { * @throws EOFException if this stream reaches the end before skipping all the characters * @throws IOException if an I/O error occurs */ - @Beta public static void skipFully(Reader reader, long n) throws IOException { checkNotNull(reader); while (n > 0) { @@ -276,7 +271,6 @@ public static void skipFully(Reader reader, long n) throws IOException { * * @since 15.0 */ - @Beta public static Writer nullWriter() { return NullWriter.INSTANCE; } @@ -309,12 +303,12 @@ public void write(String str, int off, int len) { } @Override - public Writer append(@CheckForNull CharSequence csq) { + public Writer append(@Nullable CharSequence csq) { return this; } @Override - public Writer append(@CheckForNull CharSequence csq, int start, int end) { + public Writer append(@Nullable CharSequence csq, int start, int end) { checkPositionIndexes(start, end, csq == null ? "null".length() : csq.length()); return this; } @@ -344,7 +338,6 @@ public String toString() { * @param target the object to which output will be sent * @return a new Writer object, unless target is a Writer, in which case the target is returned */ - @Beta public static Writer asWriter(Appendable target) { if (target instanceof Writer) { return (Writer) target; diff --git a/android/guava/src/com/google/common/io/Closeables.java b/android/guava/src/com/google/common/io/Closeables.java index b45f5f0fde65..e9c580af36a6 100644 --- a/android/guava/src/com/google/common/io/Closeables.java +++ b/android/guava/src/com/google/common/io/Closeables.java @@ -14,8 +14,8 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.io.Closeable; import java.io.IOException; @@ -23,7 +23,7 @@ import java.io.Reader; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Utility methods for working with {@link Closeable} objects. @@ -31,9 +31,8 @@ * @author Michael Lancaster * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Closeables { @VisibleForTesting static final Logger logger = Logger.getLogger(Closeables.class.getName()); @@ -70,7 +69,17 @@ private Closeables() {} * @throws IOException if {@code swallowIOException} is false and {@code close} throws an {@code * IOException}. */ - public static void close(@CheckForNull Closeable closeable, boolean swallowIOException) + /* + * The proper capitalization would be "swallowIoException." However: + * + * - It might be preferable to be consistent with the JDK precedent (which they stuck with even + * for "UncheckedIOException"). + * + * - If we change the name, some of our callers break because our Android Lint ParameterName check + * doesn't make the exception for com.google.common that internal Error Prone does: b/386402967. + */ + @SuppressWarnings("IdentifierName") + public static void close(@Nullable Closeable closeable, boolean swallowIOException) throws IOException { if (closeable == null) { return; @@ -100,7 +109,7 @@ public static void close(@CheckForNull Closeable closeable, boolean swallowIOExc * does nothing * @since 17.0 */ - public static void closeQuietly(@CheckForNull InputStream inputStream) { + public static void closeQuietly(@Nullable InputStream inputStream) { try { close(inputStream, true); } catch (IOException impossible) { @@ -121,7 +130,7 @@ public static void closeQuietly(@CheckForNull InputStream inputStream) { * @param reader the reader to be closed, or {@code null} in which case this method does nothing * @since 17.0 */ - public static void closeQuietly(@CheckForNull Reader reader) { + public static void closeQuietly(@Nullable Reader reader) { try { close(reader, true); } catch (IOException impossible) { diff --git a/android/guava/src/com/google/common/io/Closer.java b/android/guava/src/com/google/common/io/Closer.java index 12998eff95bb..b6bf2cbef42c 100644 --- a/android/guava/src/com/google/common/io/Closer.java +++ b/android/guava/src/com/google/common/io/Closer.java @@ -15,29 +15,26 @@ package com.google.common.io; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.throwIfInstanceOf; +import static com.google.common.base.Throwables.throwIfUnchecked; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.Closeable; import java.io.IOException; -import java.lang.reflect.Method; import java.util.ArrayDeque; import java.util.Deque; import java.util.logging.Level; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link Closeable} that collects {@code Closeable} resources and closes them all when it is - * {@linkplain #close closed}. This is intended to approximately emulate the behavior of Java 7's try-with-resources statement in JDK6-compatible code. Running on Java 7, code using this - * should be approximately equivalent in behavior to the same code written with try-with-resources. - * Running on Java 6, exceptions that cannot be thrown must be logged rather than being added to the - * thrown exception as a suppressed exception. + * {@linkplain #close closed}. This was intended to approximately emulate the behavior of Java 7's + * try-with-resources statement in JDK6-compatible code. Code using this should be + * approximately equivalent in behavior to the same code written with try-with-resources. * *

    This class is intended to be used in the following pattern: * @@ -74,42 +71,26 @@ * another exception is already being thrown) is suppressed. * * - *

    An exception that is suppressed is not thrown. The method of suppression used depends on the - * version of Java the code is running on: - * - *

      - *
    • Java 7+: Exceptions are suppressed by adding them to the exception that will - * be thrown using {@code Throwable.addSuppressed(Throwable)}. - *
    • Java 6: Exceptions are suppressed by logging them instead. - *
    + *

    An exception that is suppressed is added to the exception that will be thrown using + * {@code Throwable.addSuppressed(Throwable)}. * * @author Colin Decker * @since 14.0 */ // Coffee's for {@link Closer closers} only. -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Closer implements Closeable { - - /** The suppressor implementation to use for the current Java version. */ - private static final Suppressor SUPPRESSOR; - - static { - SuppressingSuppressor suppressingSuppressor = SuppressingSuppressor.tryCreate(); - SUPPRESSOR = suppressingSuppressor == null ? LoggingSuppressor.INSTANCE : suppressingSuppressor; - } - /** Creates a new {@link Closer}. */ public static Closer create() { - return new Closer(SUPPRESSOR); + return new Closer(SUPPRESSING_SUPPRESSOR); } @VisibleForTesting final Suppressor suppressor; // only need space for 2 elements in most cases, so try to use the smallest array possible private final Deque stack = new ArrayDeque<>(4); - @CheckForNull private Throwable thrown; + private @Nullable Throwable thrown; @VisibleForTesting Closer(Suppressor suppressor) { @@ -149,7 +130,8 @@ public static Closer create() { public RuntimeException rethrow(Throwable e) throws IOException { checkNotNull(e); thrown = e; - Throwables.propagateIfPossible(e, IOException.class); + throwIfInstanceOf(e, IOException.class); + throwIfUnchecked(e); throw new RuntimeException(e); } @@ -171,8 +153,9 @@ public RuntimeException rethrow(Throwable e, Class decl throws IOException, X { checkNotNull(e); thrown = e; - Throwables.propagateIfPossible(e, IOException.class); - Throwables.propagateIfPossible(e, declaredType); + throwIfInstanceOf(e, IOException.class); + throwIfInstanceOf(e, declaredType); + throwIfUnchecked(e); throw new RuntimeException(e); } @@ -195,8 +178,10 @@ public RuntimeException rethrow( Throwable e, Class declaredType1, Class declaredType2) throws IOException, X1, X2 { checkNotNull(e); thrown = e; - Throwables.propagateIfPossible(e, IOException.class); - Throwables.propagateIfPossible(e, declaredType1, declaredType2); + throwIfInstanceOf(e, IOException.class); + throwIfInstanceOf(e, declaredType1); + throwIfInstanceOf(e, declaredType2); + throwIfUnchecked(e); throw new RuntimeException(e); } @@ -226,7 +211,8 @@ public void close() throws IOException { } if (thrown == null && throwable != null) { - Throwables.propagateIfPossible(throwable, IOException.class); + throwIfInstanceOf(throwable, IOException.class); + throwIfUnchecked(throwable); throw new AssertionError(throwable); // not possible } } @@ -242,55 +228,27 @@ interface Suppressor { void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); } - /** Suppresses exceptions by logging them. */ - @VisibleForTesting - static final class LoggingSuppressor implements Suppressor { - - static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); - - @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { - // log to the same place as Closeables - Closeables.logger.log( - Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); - } - } - /** - * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's + * Suppresses exceptions by adding them to the exception that will be thrown using the * addSuppressed(Throwable) mechanism. */ - @VisibleForTesting - static final class SuppressingSuppressor implements Suppressor { - @CheckForNull - static SuppressingSuppressor tryCreate() { - Method addSuppressed; - try { - addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); - } catch (Throwable e) { - return null; - } - return new SuppressingSuppressor(addSuppressed); - } - - private final Method addSuppressed; - - private SuppressingSuppressor(Method addSuppressed) { - this.addSuppressed = addSuppressed; - } - - @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { - // ensure no exceptions from addSuppressed - if (thrown == suppressed) { - return; - } - try { - addSuppressed.invoke(thrown, suppressed); - } catch (Throwable e) { - // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging - LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); - } - } - } + private static final Suppressor SUPPRESSING_SUPPRESSOR = + (closeable, thrown, suppressed) -> { + // ensure no exceptions from addSuppressed + if (thrown == suppressed) { + return; + } + try { + thrown.addSuppressed(suppressed); + } catch (Throwable e) { + /* + * A Throwable is very unlikely, but we really don't want to throw from a Suppressor, so + * we catch everything. (Any Exception is either a RuntimeException or + * sneaky checked exception.) With no better options, we log anything to the same + * place as Closeables logs. + */ + Closeables.logger.log( + Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); + } + }; } diff --git a/android/guava/src/com/google/common/io/CountingInputStream.java b/android/guava/src/com/google/common/io/CountingInputStream.java index a37807ae7341..c2f73f5ff114 100644 --- a/android/guava/src/com/google/common/io/CountingInputStream.java +++ b/android/guava/src/com/google/common/io/CountingInputStream.java @@ -16,8 +16,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -28,9 +28,8 @@ * @author Chris Nokleberg * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class CountingInputStream extends FilterInputStream { private long count; diff --git a/android/guava/src/com/google/common/io/CountingOutputStream.java b/android/guava/src/com/google/common/io/CountingOutputStream.java index cf62b9c377a4..c2273f8c5e3f 100644 --- a/android/guava/src/com/google/common/io/CountingOutputStream.java +++ b/android/guava/src/com/google/common/io/CountingOutputStream.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -27,8 +28,8 @@ * @author Chris Nokleberg * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class CountingOutputStream extends FilterOutputStream { private long count; diff --git a/android/guava/src/com/google/common/io/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/io/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 48bc10f192a1..000000000000 --- a/android/guava/src/com/google/common/io/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.io; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/io/FileBackedOutputStream.java b/android/guava/src/com/google/common/io/FileBackedOutputStream.java index 9912e2fcd19c..69aabd8448a9 100644 --- a/android/guava/src/com/google/common/io/FileBackedOutputStream.java +++ b/android/guava/src/com/google/common/io/FileBackedOutputStream.java @@ -14,12 +14,15 @@ package com.google.common.io; +import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.concurrent.GuardedBy; +import com.google.j2objc.annotations.J2ObjCIncompatible; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -28,12 +31,20 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An {@link OutputStream} that starts buffering to a byte array, but switches to file buffering * once the data reaches a configurable size. * + *

    When this stream creates a temporary file, it restricts the file's permissions to the current + * user or, in the case of Android, the current app. If that is not possible (as is the case under + * the very old Android Ice Cream Sandwich release), then this stream throws an exception instead of + * creating a file that would be more accessible. (This behavior is new in Guava 32.0.0. Previous + * versions would create a file that is more accessible, as discussed in Guava issue 2575. TODO: b/283778848 - Fill + * in CVE number once it's available.) + * *

    Temporary files created by this stream may live in the local filesystem until either: * *

      @@ -51,24 +62,22 @@ * @since 1.0 */ @Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ObjCIncompatible public final class FileBackedOutputStream extends OutputStream { private final int fileThreshold; private final boolean resetOnFinalize; private final ByteSource source; - @CheckForNull private final File parentDirectory; @GuardedBy("this") private OutputStream out; @GuardedBy("this") - @CheckForNull - private MemoryOutput memory; + private @Nullable MemoryOutput memory; @GuardedBy("this") - @CheckForNull - private File file; + private @Nullable File file; /** ByteArrayOutputStream that exposes its internals. */ private static class MemoryOutput extends ByteArrayOutputStream { @@ -83,8 +92,7 @@ int getCount() { /** Returns the file holding the data (possibly null). */ @VisibleForTesting - @CheckForNull - synchronized File getFile() { + synchronized @Nullable File getFile() { return file; } @@ -93,6 +101,7 @@ synchronized File getFile() { * {@link ByteSource} returned by {@link #asByteSource} is finalized. * * @param fileThreshold the number of bytes before the stream should switch to buffering to a file + * @throws IllegalArgumentException if {@code fileThreshold} is negative */ public FileBackedOutputStream(int fileThreshold) { this(fileThreshold, false); @@ -105,16 +114,13 @@ public FileBackedOutputStream(int fileThreshold) { * @param fileThreshold the number of bytes before the stream should switch to buffering to a file * @param resetOnFinalize if true, the {@link #reset} method will be called when the {@link * ByteSource} returned by {@link #asByteSource} is finalized. + * @throws IllegalArgumentException if {@code fileThreshold} is negative */ public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) { - this(fileThreshold, resetOnFinalize, null); - } - - private FileBackedOutputStream( - int fileThreshold, boolean resetOnFinalize, @CheckForNull File parentDirectory) { + checkArgument( + fileThreshold >= 0, "fileThreshold must be non-negative, but was %s", fileThreshold); this.fileThreshold = fileThreshold; this.resetOnFinalize = resetOnFinalize; - this.parentDirectory = parentDirectory; memory = new MemoryOutput(); out = memory; @@ -126,6 +132,7 @@ public InputStream openStream() throws IOException { return openInputStream(); } + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { try { @@ -225,7 +232,7 @@ public synchronized void flush() throws IOException { @GuardedBy("this") private void update(int len) throws IOException { if (memory != null && (memory.getCount() + len > fileThreshold)) { - File temp = File.createTempFile("FileBackedOutputStream", null, parentDirectory); + File temp = TempFileCreator.INSTANCE.createTempFile("FileBackedOutputStream"); if (resetOnFinalize) { // Finalizers are not guaranteed to be called on system shutdown; // this is insurance. diff --git a/android/guava/src/com/google/common/io/FileWriteMode.java b/android/guava/src/com/google/common/io/FileWriteMode.java index 86872d1badb2..c253b00afe1f 100644 --- a/android/guava/src/com/google/common/io/FileWriteMode.java +++ b/android/guava/src/com/google/common/io/FileWriteMode.java @@ -15,6 +15,7 @@ package com.google.common.io; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; /** * Modes for opening a file for writing. The default when mode when none is specified is to truncate @@ -22,8 +23,8 @@ * * @author Colin Decker */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public enum FileWriteMode { /** Specifies that writes to the opened file should append to the end of the file. */ APPEND diff --git a/android/guava/src/com/google/common/io/Files.java b/android/guava/src/com/google/common/io/Files.java index bf6289e90f25..5f056368a600 100644 --- a/android/guava/src/com/google/common/io/Files.java +++ b/android/guava/src/com/google/common/io/Files.java @@ -17,9 +17,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.io.FileWriteMode.APPEND; +import static java.util.Collections.unmodifiableList; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; @@ -32,6 +34,8 @@ import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; +import com.google.j2objc.annotations.J2ObjCIncompatible; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -50,10 +54,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides utility methods for working with {@linkplain File files}. @@ -65,13 +67,10 @@ * @author Colin Decker * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Files { - /** Maximum loop count when creating temp directories. */ - private static final int TEMP_DIR_ATTEMPTS = 10000; - private Files() {} /** @@ -85,7 +84,6 @@ private Files() {} * helpful predefined constants * @return the buffered reader */ - @Beta public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException { checkNotNull(file); checkNotNull(charset); @@ -104,7 +102,6 @@ public static BufferedReader newReader(File file, Charset charset) throws FileNo * helpful predefined constants * @return the buffered writer */ - @Beta public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException { checkNotNull(file); checkNotNull(charset); @@ -120,7 +117,9 @@ public static ByteSource asByteSource(File file) { return new FileByteSource(file); } - private static final class FileByteSource extends ByteSource { + private static final class FileByteSource extends + ByteSource + { private final File file; @@ -235,7 +234,6 @@ public static CharSink asCharSink(File file, Charset charset, FileWriteMode... m * (2^31 - 1) * @throws IOException if an I/O error occurs */ - @Beta public static byte[] toByteArray(File file) throws IOException { return asByteSource(file).read(); } @@ -248,11 +246,12 @@ public static byte[] toByteArray(File file) throws IOException { * helpful predefined constants * @return a string containing all the characters from the file * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asCharSource(file, charset).read()}. This method is scheduled to be - * removed in October 2019. + * @deprecated Prefer {@code asCharSource(file, charset).read()}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asCharSource(file, charset).read()", + imports = "com.google.common.io.Files") public static String toString(File file, Charset charset) throws IOException { return asCharSource(file, charset).read(); } @@ -267,7 +266,6 @@ public static String toString(File file, Charset charset) throws IOException { * @param to the destination file * @throws IOException if an I/O error occurs */ - @Beta public static void write(byte[] from, File to) throws IOException { asByteSink(to).write(from); } @@ -280,11 +278,12 @@ public static void write(byte[] from, File to) throws IOException { * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for * helpful predefined constants * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asCharSink(to, charset).write(from)}. This method is scheduled to be - * removed in October 2019. + * @deprecated Prefer {@code asCharSink(to, charset).write(from)}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asCharSink(to, charset).write(from)", + imports = "com.google.common.io.Files") public static void write(CharSequence from, File to, Charset charset) throws IOException { asCharSink(to, charset).write(from); } @@ -299,7 +298,6 @@ public static void write(CharSequence from, File to, Charset charset) throws IOE * @param to the output stream * @throws IOException if an I/O error occurs */ - @Beta public static void copy(File from, OutputStream to) throws IOException { asByteSource(from).copyTo(to); } @@ -323,7 +321,6 @@ public static void copy(File from, OutputStream to) throws IOException { * @throws IOException if an I/O error occurs * @throws IllegalArgumentException if {@code from.equals(to)} */ - @Beta public static void copy(File from, File to) throws IOException { checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); asByteSource(from).copyTo(asByteSink(to)); @@ -337,11 +334,12 @@ public static void copy(File from, File to) throws IOException { * helpful predefined constants * @param to the appendable object * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asCharSource(from, charset).copyTo(to)}. This method is scheduled to - * be removed in October 2019. + * @deprecated Prefer {@code asCharSource(from, charset).copyTo(to)}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asCharSource(from, charset).copyTo(to)", + imports = "com.google.common.io.Files") public static void copy(File from, Charset charset, Appendable to) throws IOException { asCharSource(from, charset).copyTo(to); @@ -358,8 +356,10 @@ static void copy(File from, Charset charset, Appendable to) throws IOException { * @deprecated Prefer {@code asCharSink(to, charset, FileWriteMode.APPEND).write(from)}. This * method is scheduled to be removed in October 2019. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asCharSink(to, charset, FileWriteMode.APPEND).write(from)", + imports = {"com.google.common.io.FileWriteMode", "com.google.common.io.Files"}) public static void append(CharSequence from, File to, Charset charset) throws IOException { asCharSink(to, charset, FileWriteMode.APPEND).write(from); @@ -370,7 +370,6 @@ static void append(CharSequence from, File to, Charset charset) throws IOExcepti * * @throws IOException if an I/O error occurs */ - @Beta public static boolean equal(File file1, File file2) throws IOException { checkNotNull(file1); checkNotNull(file2); @@ -395,17 +394,19 @@ public static boolean equal(File file1, File file2) throws IOException { * Atomically creates a new directory somewhere beneath the system's temporary directory (as * defined by the {@code java.io.tmpdir} system property), and returns its name. * + *

      The temporary directory is created with permissions restricted to the current user or, in + * the case of Android, the current app. If that is not possible (as is the case under the very + * old Android Ice Cream Sandwich release), then this method throws an exception instead of + * creating a directory that would be more accessible. (This behavior is new in Guava 32.0.0. + * Previous versions would create a directory that is more accessible, as discussed in CVE-2020-8908.) + * *

      Use this method instead of {@link File#createTempFile(String, String)} when you wish to * create a directory, not a regular file. A common pitfall is to call {@code createTempFile}, * delete the file and create a directory in its place, but this leads a race condition which can * be exploited to create security vulnerabilities, especially when executable files are to be * written into the directory. * - *

      Depending on the environmment that this code is run in, the system temporary directory (and - * thus the directory this method creates) may be more visible that a program would like - files - * written to this directory may be read or overwritten by hostile programs running on the same - * machine. - * *

      This method assumes that the temporary volume is writable, has free inodes and free blocks, * and that it will not be called thousands of times per second. * @@ -413,36 +414,26 @@ public static boolean equal(File file1, File file2) throws IOException { * java.nio.file.Files#createTempDirectory}. * * @return the newly-created directory - * @throws IllegalStateException if the directory could not be created + * @throws IllegalStateException if the directory could not be created, such as if the system does + * not support creating temporary directories securely * @deprecated For Android users, see the Data and File * Storage overview to select an appropriate temporary directory (perhaps {@code - * context.getCacheDir()}). For developers on Java 7 or later, use {@link + * context.getCacheDir()}), and create your own directory under that. (For example, you might + * use {@code new File(context.getCacheDir(), "directoryname").mkdir()}, or, if you need an + * arbitrary number of temporary directories, you might have to generate multiple directory + * names in a loop until {@code mkdir()} returns {@code true}.) For JRE users, prefer {@link * java.nio.file.Files#createTempDirectory}, transforming it to a {@link File} using {@link - * java.nio.file.Path#toFile() toFile()} if needed. + * java.nio.file.Path#toFile() toFile()} if needed. To restrict permissions as this method + * does, pass {@code + * PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))} to your + * call to {@code createTempDirectory}. */ @Beta @Deprecated + @J2ObjCIncompatible public static File createTempDir() { - File baseDir = new File(System.getProperty("java.io.tmpdir")); - @SuppressWarnings("GoodTime") // reading system time without TimeSource - String baseName = System.currentTimeMillis() + "-"; - - for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { - File tempDir = new File(baseDir, baseName + counter); - if (tempDir.mkdir()) { - return tempDir; - } - } - throw new IllegalStateException( - "Failed to create directory within " - + TEMP_DIR_ATTEMPTS - + " attempts (tried " - + baseName - + "0 to " - + baseName - + (TEMP_DIR_ATTEMPTS - 1) - + ')'); + return TempFileCreator.INSTANCE.createTempDir(); } /** @@ -452,7 +443,6 @@ public static File createTempDir() { * @param file the file to create or update * @throws IOException if an I/O error occurs */ - @Beta @SuppressWarnings("GoodTime") // reading system time without TimeSource public static void touch(File file) throws IOException { checkNotNull(file); @@ -470,7 +460,6 @@ public static void touch(File file) throws IOException { * directories of the specified file could not be created. * @since 4.0 */ - @Beta public static void createParentDirs(File file) throws IOException { checkNotNull(file); File parent = file.getCanonicalFile().getParentFile(); @@ -501,7 +490,6 @@ public static void createParentDirs(File file) throws IOException { * @throws IOException if an I/O error occurs * @throws IllegalArgumentException if {@code from.equals(to)} */ - @Beta public static void move(File from, File to) throws IOException { checkNotNull(from); checkNotNull(to); @@ -527,14 +515,14 @@ public static void move(File from, File to) throws IOException { * helpful predefined constants * @return the first line, or null if the file is empty * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asCharSource(file, charset).readFirstLine()}. This method is - * scheduled to be removed in October 2019. + * @deprecated Prefer {@code asCharSource(file, charset).readFirstLine()}. */ - @Beta @Deprecated - @CheckForNull + @InlineMe( + replacement = "Files.asCharSource(file, charset).readFirstLine()", + imports = "com.google.common.io.Files") public - static String readFirstLine(File file, Charset charset) throws IOException { + static @Nullable String readFirstLine(File file, Charset charset) throws IOException { return asCharSource(file, charset).readFirstLine(); } @@ -554,7 +542,6 @@ static String readFirstLine(File file, Charset charset) throws IOException { * @return a mutable {@link List} containing all the lines * @throws IOException if an I/O error occurs */ - @Beta public static List readLines(File file, Charset charset) throws IOException { // don't use asCharSource(file, charset).readLines() because that returns // an immutable list, which would change the behavior of this method @@ -586,11 +573,12 @@ public List getResult() { * @param callback the {@link LineProcessor} to use to handle the lines * @return the output of processing the lines * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asCharSource(file, charset).readLines(callback)}. This method is - * scheduled to be removed in October 2019. + * @deprecated Prefer {@code asCharSource(file, charset).readLines(callback)}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asCharSource(file, charset).readLines(callback)", + imports = "com.google.common.io.Files") @CanIgnoreReturnValue // some processors won't return a useful result @ParametricNullness public @@ -608,11 +596,12 @@ public List getResult() { * @param processor the object to which the bytes of the file are passed. * @return the result of the byte processor * @throws IOException if an I/O error occurs - * @deprecated Prefer {@code asByteSource(file).read(processor)}. This method is scheduled to be - * removed in October 2019. + * @deprecated Prefer {@code asByteSource(file).read(processor)}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asByteSource(file).read(processor)", + imports = "com.google.common.io.Files") @CanIgnoreReturnValue // some processors won't return a useful result @ParametricNullness public @@ -629,11 +618,12 @@ public List getResult() { * @return the {@link HashCode} of all of the bytes in the file * @throws IOException if an I/O error occurs * @since 12.0 - * @deprecated Prefer {@code asByteSource(file).hash(hashFunction)}. This method is scheduled to - * be removed in October 2019. + * @deprecated Prefer {@code asByteSource(file).hash(hashFunction)}. */ - @Beta @Deprecated + @InlineMe( + replacement = "Files.asByteSource(file).hash(hashFunction)", + imports = "com.google.common.io.Files") public static HashCode hash(File file, HashFunction hashFunction) throws IOException { return asByteSource(file).hash(hashFunction); @@ -654,7 +644,6 @@ static HashCode hash(File file, HashFunction hashFunction) throws IOException { * @see FileChannel#map(MapMode, long, long) * @since 2.0 */ - @Beta public static MappedByteBuffer map(File file) throws IOException { checkNotNull(file); return map(file, MapMode.READ_ONLY); @@ -677,7 +666,6 @@ public static MappedByteBuffer map(File file) throws IOException { * @see FileChannel#map(MapMode, long, long) * @since 2.0 */ - @Beta public static MappedByteBuffer map(File file, MapMode mode) throws IOException { return mapInternal(file, mode, -1); } @@ -701,7 +689,6 @@ public static MappedByteBuffer map(File file, MapMode mode) throws IOException { * @see FileChannel#map(MapMode, long, long) * @since 2.0 */ - @Beta public static MappedByteBuffer map(File file, MapMode mode, long size) throws IOException { checkArgument(size >= 0, "size (%s) may not be negative", size); return mapInternal(file, mode, size); @@ -745,7 +732,6 @@ private static MappedByteBuffer mapInternal(File file, MapMode mode, long size) * * @since 11.0 */ - @Beta public static String simplifyPath(String pathname) { checkNotNull(pathname); if (pathname.length() == 0) { @@ -785,7 +771,7 @@ public static String simplifyPath(String pathname) { } if (result.equals("/..")) { result = "/"; - } else if ("".equals(result)) { + } else if (result.isEmpty()) { result = "."; } @@ -802,11 +788,12 @@ public static String simplifyPath(String pathname) { * behavior that the {@link File} API does not already account for. For example, on NTFS it will * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS * will drop the {@code ":.txt"} part of the name when the file is actually created on the - * filesystem due to NTFS's Alternate Data Streams. + * filesystem due to NTFS's Alternate + * Data Streams. * * @since 11.0 */ - @Beta public static String getFileExtension(String fullName) { checkNotNull(fullName); String fileName = new File(fullName).getName(); @@ -824,7 +811,6 @@ public static String getFileExtension(String fullName) { * @return The file name without its path or extension. * @since 14.0 */ - @Beta public static String getNameWithoutExtension(String file) { checkNotNull(file); String fileName = new File(file).getName(); @@ -854,25 +840,21 @@ public static String getNameWithoutExtension(String file) { * * @since 23.5 */ - @Beta public static Traverser fileTraverser() { return Traverser.forTree(FILE_TREE); } private static final SuccessorsFunction FILE_TREE = - new SuccessorsFunction() { - @Override - public Iterable successors(File file) { - // check isDirectory() just because it may be faster than listFiles() on a non-directory - if (file.isDirectory()) { - File[] files = file.listFiles(); - if (files != null) { - return Collections.unmodifiableList(Arrays.asList(files)); - } + file -> { + // check isDirectory() just because it may be faster than listFiles() on a non-directory + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + return unmodifiableList(Arrays.asList(files)); } - - return ImmutableList.of(); } + + return ImmutableList.of(); }; /** @@ -880,7 +862,6 @@ public Iterable successors(File file) { * * @since 15.0 */ - @Beta public static Predicate isDirectory() { return FilePredicate.IS_DIRECTORY; } @@ -890,7 +871,6 @@ public static Predicate isDirectory() { * * @since 15.0 */ - @Beta public static Predicate isFile() { return FilePredicate.IS_FILE; } diff --git a/android/guava/src/com/google/common/io/Flushables.java b/android/guava/src/com/google/common/io/Flushables.java index 1f795a24ab24..7e5e11275c0a 100644 --- a/android/guava/src/com/google/common/io/Flushables.java +++ b/android/guava/src/com/google/common/io/Flushables.java @@ -16,6 +16,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Flushable; import java.io.IOException; import java.util.logging.Level; @@ -27,9 +28,8 @@ * @author Michael Lancaster * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Flushables { private static final Logger logger = Logger.getLogger(Flushables.class.getName()); @@ -48,6 +48,7 @@ private Flushables() {} * an {@code IOException}. * @see Closeables#close */ + @SuppressWarnings("IdentifierName") // See Closeables.close public static void flush(Flushable flushable, boolean swallowIOException) throws IOException { try { flushable.flush(); @@ -66,6 +67,7 @@ public static void flush(Flushable flushable, boolean swallowIOException) throws * * @param flushable the {@code Flushable} object to be flushed. */ + @Beta public static void flushQuietly(Flushable flushable) { try { flush(flushable, true); diff --git a/android/guava/src/com/google/common/io/IgnoreJRERequirement.java b/android/guava/src/com/google/common/io/IgnoreJRERequirement.java new file mode 100644 index 000000000000..383163d4977c --- /dev/null +++ b/android/guava/src/com/google/common/io/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.io; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

      Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/io/InsecureRecursiveDeleteException.java b/android/guava/src/com/google/common/io/InsecureRecursiveDeleteException.java new file mode 100644 index 000000000000..87c132580f1f --- /dev/null +++ b/android/guava/src/com/google/common/io/InsecureRecursiveDeleteException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.j2objc.annotations.J2ObjCIncompatible; +import java.nio.file.FileSystemException; +import java.nio.file.SecureDirectoryStream; +import org.jspecify.annotations.Nullable; + +/** + * Exception indicating that a recursive delete can't be performed because the file system does not + * have the support necessary to guarantee that it is not vulnerable to race conditions that would + * allow it to delete files and directories outside of the directory being deleted (i.e., {@link + * SecureDirectoryStream} is not supported). + * + *

      {@link RecursiveDeleteOption#ALLOW_INSECURE} can be used to force the recursive delete method + * to proceed anyway. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@J2ObjCIncompatible // java.nio.file +// Users are unlikely to use this unless they're already interacting with MoreFiles and Path. +@IgnoreJRERequirement +public final class InsecureRecursiveDeleteException extends FileSystemException { + + public InsecureRecursiveDeleteException(@Nullable String file) { + super(file, null, "unable to guarantee security of recursive delete"); + } +} diff --git a/android/guava/src/com/google/common/io/Java8Compatibility.java b/android/guava/src/com/google/common/io/Java8Compatibility.java index 705d97b4dfa5..f1cd446ed330 100644 --- a/android/guava/src/com/google/common/io/Java8Compatibility.java +++ b/android/guava/src/com/google/common/io/Java8Compatibility.java @@ -15,14 +15,15 @@ package com.google.common.io; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.nio.Buffer; /** * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See * https://github.com/google/guava/issues/3990 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class Java8Compatibility { static void clear(Buffer b) { b.clear(); diff --git a/android/guava/src/com/google/common/io/LineBuffer.java b/android/guava/src/com/google/common/io/LineBuffer.java index f944abc49099..ab376ee570a0 100644 --- a/android/guava/src/com/google/common/io/LineBuffer.java +++ b/android/guava/src/com/google/common/io/LineBuffer.java @@ -15,6 +15,7 @@ package com.google.common.io; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -29,11 +30,12 @@ * @author Chris Nokleberg * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class LineBuffer { /** Holds partial line contents. */ private StringBuilder line = new StringBuilder(); + /** Whether a line ending with a CR is pending processing. */ private boolean sawReturn; diff --git a/android/guava/src/com/google/common/io/LineProcessor.java b/android/guava/src/com/google/common/io/LineProcessor.java index e28bebcc5f2b..aab83ad16e1c 100644 --- a/android/guava/src/com/google/common/io/LineProcessor.java +++ b/android/guava/src/com/google/common/io/LineProcessor.java @@ -14,11 +14,11 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A callback to be used with the streaming {@code readLines} methods. @@ -29,9 +29,8 @@ * @author Miles Barr * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface LineProcessor { /** diff --git a/android/guava/src/com/google/common/io/LineReader.java b/android/guava/src/com/google/common/io/LineReader.java index 2c57ac4546b7..b313a8c9880e 100644 --- a/android/guava/src/com/google/common/io/LineReader.java +++ b/android/guava/src/com/google/common/io/LineReader.java @@ -17,15 +17,15 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.io.CharStreams.createBuffer; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.io.Reader; import java.nio.CharBuffer; import java.util.ArrayDeque; import java.util.Queue; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A class for reading lines of text. Provides the same functionality as {@link @@ -35,12 +35,11 @@ * @author Chris Nokleberg * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class LineReader { private final Readable readable; - @CheckForNull private final Reader reader; + private final @Nullable Reader reader; private final CharBuffer cbuf = createBuffer(); private final char[] buf = cbuf.array(); @@ -69,8 +68,7 @@ public LineReader(Readable readable) { * @throws IOException if an I/O error occurs */ @CanIgnoreReturnValue // to skip a line - @CheckForNull - public String readLine() throws IOException { + public @Nullable String readLine() throws IOException { while (lines.peek() == null) { Java8Compatibility.clear(cbuf); // The default implementation of Reader#read(CharBuffer) allocates a diff --git a/android/guava/src/com/google/common/io/LittleEndianDataInputStream.java b/android/guava/src/com/google/common/io/LittleEndianDataInputStream.java index 88c8e27bc3f9..9e2f05e7413c 100644 --- a/android/guava/src/com/google/common/io/LittleEndianDataInputStream.java +++ b/android/guava/src/com/google/common/io/LittleEndianDataInputStream.java @@ -14,8 +14,8 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; @@ -39,9 +39,8 @@ * @author Keith Bottner * @since 8.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class LittleEndianDataInputStream extends FilterInputStream implements DataInput { /** @@ -80,7 +79,7 @@ public int skipBytes(int n) throws IOException { @Override public int readUnsignedByte() throws IOException { int b1 = in.read(); - if (0 > b1) { + if (b1 < 0) { throw new EOFException(); } @@ -231,7 +230,7 @@ public boolean readBoolean() throws IOException { private byte readAndCheckByte() throws IOException, EOFException { int b1 = in.read(); - if (-1 == b1) { + if (b1 == -1) { throw new EOFException(); } diff --git a/android/guava/src/com/google/common/io/LittleEndianDataOutputStream.java b/android/guava/src/com/google/common/io/LittleEndianDataOutputStream.java index 6e51aff49d92..dd3746cc6781 100644 --- a/android/guava/src/com/google/common/io/LittleEndianDataOutputStream.java +++ b/android/guava/src/com/google/common/io/LittleEndianDataOutputStream.java @@ -14,10 +14,9 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; -import com.google.common.primitives.Longs; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.FilterOutputStream; @@ -35,9 +34,8 @@ * @author Keith Bottner * @since 8.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput { /** @@ -143,8 +141,7 @@ public void writeInt(int v) throws IOException { */ @Override public void writeLong(long v) throws IOException { - byte[] bytes = Longs.toByteArray(Long.reverseBytes(v)); - write(bytes, 0, bytes.length); + ((DataOutputStream) out).writeLong(Long.reverseBytes(v)); } /** diff --git a/android/guava/src/com/google/common/io/MoreFiles.java b/android/guava/src/com/google/common/io/MoreFiles.java new file mode 100644 index 000000000000..30983e16fbac --- /dev/null +++ b/android/guava/src/com/google/common/io/MoreFiles.java @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.nio.file.LinkOption.NOFOLLOW_LINKS; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.graph.Traverser; +import com.google.j2objc.annotations.J2ObjCIncompatible; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.Charset; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.SecureDirectoryStream; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; + +/** + * Static utilities for use with {@link Path} instances, intended to complement {@link Files}. + * + *

      Many methods provided by Guava's {@code Files} class for {@link java.io.File} instances are + * now available via the JDK's {@link java.nio.file.Files} class for {@code Path} - check the JDK's + * class if a sibling method from {@code Files} appears to be missing from this class. + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@J2ObjCIncompatible // java.nio.file +@IgnoreJRERequirement // Users will use this only if they're already using Path. +public final class MoreFiles { + + private MoreFiles() {} + + /** + * Returns a view of the given {@code path} as a {@link ByteSource}. + * + *

      Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned source and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#READ READ} option. + */ + public static ByteSource asByteSource(Path path, OpenOption... options) { + return new PathByteSource(path, options); + } + + @IgnoreJRERequirement // *should* be redundant with the one on MoreFiles itself + private static final class PathByteSource extends + ByteSource + { + + private static final LinkOption[] FOLLOW_LINKS = {}; + + private final Path path; + private final OpenOption[] options; + private final boolean followLinks; + + private PathByteSource(Path path, OpenOption... options) { + this.path = checkNotNull(path); + this.options = options.clone(); + this.followLinks = followLinks(this.options); + // TODO(cgdecker): validate the provided options... for example, just WRITE seems wrong + } + + private static boolean followLinks(OpenOption[] options) { + for (OpenOption option : options) { + if (option == NOFOLLOW_LINKS) { + return false; + } + } + return true; + } + + @Override + public InputStream openStream() throws IOException { + return Files.newInputStream(path, options); + } + + private BasicFileAttributes readAttributes() throws IOException { + return Files.readAttributes( + path, + BasicFileAttributes.class, + followLinks ? FOLLOW_LINKS : new LinkOption[] {NOFOLLOW_LINKS}); + } + + @Override + public Optional sizeIfKnown() { + BasicFileAttributes attrs; + try { + attrs = readAttributes(); + } catch (IOException e) { + // Failed to get attributes; we don't know the size. + return Optional.absent(); + } + + // Don't return a size for directories or symbolic links; their sizes are implementation + // specific and they can't be read as bytes using the read methods anyway. + if (attrs.isDirectory() || attrs.isSymbolicLink()) { + return Optional.absent(); + } + + return Optional.of(attrs.size()); + } + + @Override + public long size() throws IOException { + BasicFileAttributes attrs = readAttributes(); + + // Don't return a size for directories or symbolic links; their sizes are implementation + // specific and they can't be read as bytes using the read methods anyway. + if (attrs.isDirectory()) { + throw new IOException("can't read: is a directory"); + } else if (attrs.isSymbolicLink()) { + throw new IOException("can't read: is a symbolic link"); + } + + return attrs.size(); + } + + @Override + public byte[] read() throws IOException { + try (SeekableByteChannel channel = Files.newByteChannel(path, options)) { + return ByteStreams.toByteArray(Channels.newInputStream(channel), channel.size()); + } + } + + @Override + public CharSource asCharSource(Charset charset) { + if (options.length == 0) { + // If no OpenOptions were passed, delegate to Files.lines, which could have performance + // advantages. (If OpenOptions were passed we can't, because Files.lines doesn't have an + // overload taking OpenOptions, meaning we can't guarantee the same behavior w.r.t. things + // like following/not following symlinks.) + return new AsCharSource(charset) { + @SuppressWarnings({ + "FilesLinesLeak", // the user needs to close it in this case + /* + * If users use this when they shouldn't, we hope that NewApi will catch subsequent + * Stream calls. + * + * Anyway, this is just an override that is no more dangerous than the supermethod. + */ + "Java7ApiChecker", + }) + @Override + public Stream lines() throws IOException { + return Files.lines(path, charset); + } + }; + } + + return super.asCharSource(charset); + } + + @Override + public String toString() { + return "MoreFiles.asByteSource(" + path + ", " + Arrays.toString(options) + ")"; + } + } + + /** + * Returns a view of the given {@code path} as a {@link ByteSink}. + * + *

      Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned sink and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#CREATE CREATE}, {@link + * StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} and {@link StandardOpenOption#WRITE + * WRITE} options. + */ + public static ByteSink asByteSink(Path path, OpenOption... options) { + return new PathByteSink(path, options); + } + + @IgnoreJRERequirement // *should* be redundant with the one on MoreFiles itself + private static final class PathByteSink extends ByteSink { + + private final Path path; + private final OpenOption[] options; + + private PathByteSink(Path path, OpenOption... options) { + this.path = checkNotNull(path); + this.options = options.clone(); + // TODO(cgdecker): validate the provided options... for example, just READ seems wrong + } + + @Override + public OutputStream openStream() throws IOException { + return Files.newOutputStream(path, options); + } + + @Override + public String toString() { + return "MoreFiles.asByteSink(" + path + ", " + Arrays.toString(options) + ")"; + } + } + + /** + * Returns a view of the given {@code path} as a {@link CharSource} using the given {@code + * charset}. + * + *

      Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned source and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#READ READ} option. + */ + public static CharSource asCharSource(Path path, Charset charset, OpenOption... options) { + return asByteSource(path, options).asCharSource(charset); + } + + /** + * Returns a view of the given {@code path} as a {@link CharSink} using the given {@code charset}. + * + *

      Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned sink and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#CREATE CREATE}, {@link + * StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} and {@link StandardOpenOption#WRITE + * WRITE} options. + */ + public static CharSink asCharSink(Path path, Charset charset, OpenOption... options) { + return asByteSink(path, options).asCharSink(charset); + } + + /** + * Returns an immutable list of paths to the files contained in the given directory. + * + * @throws NoSuchFileException if the file does not exist (optional specific exception) + * @throws NotDirectoryException if the file could not be opened because it is not a directory + * (optional specific exception) + * @throws IOException if an I/O error occurs + */ + public static ImmutableList listFiles(Path dir) throws IOException { + try (DirectoryStream stream = Files.newDirectoryStream(dir)) { + return ImmutableList.copyOf(stream); + } catch (DirectoryIteratorException e) { + throw e.getCause(); + } + } + + /** + * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser + * starts from a {@link Path} and will return all files and directories it encounters. + * + *

      The returned traverser attempts to avoid following symbolic links to directories. However, + * the traverser cannot guarantee that it will not follow symbolic links to directories as it is + * possible for a directory to be replaced with a symbolic link between checking if the file is a + * directory and actually reading the contents of that directory. + * + *

      If the {@link Path} passed to one of the traversal methods does not exist or is not a + * directory, no exception will be thrown and the returned {@link Iterable} will contain a single + * element: that path. + * + *

      {@link DirectoryIteratorException} may be thrown when iterating {@link Iterable} instances + * created by this traverser if an {@link IOException} is thrown by a call to {@link + * #listFiles(Path)}. + * + *

      Example: {@code MoreFiles.fileTraverser().depthFirstPreOrder(Paths.get("/"))} may return the + * following paths: {@code ["/", "/etc", "/etc/config.txt", "/etc/fonts", "/home", "/home/alice", + * ...]} + * + * @since 23.5 + */ + public static Traverser fileTraverser() { + return Traverser.forTree(MoreFiles::fileTreeChildren); + } + + private static Iterable fileTreeChildren(Path dir) { + if (Files.isDirectory(dir, NOFOLLOW_LINKS)) { + try { + return listFiles(dir); + } catch (IOException e) { + // the exception thrown when iterating a DirectoryStream if an I/O exception occurs + throw new DirectoryIteratorException(e); + } + } + return ImmutableList.of(); + } + + /** + * Returns a predicate that returns the result of {@link java.nio.file.Files#isDirectory(Path, + * LinkOption...)} on input paths with the given link options. + */ + public static Predicate isDirectory(LinkOption... options) { + LinkOption[] optionsCopy = options.clone(); + return new Predicate() { + @Override + public boolean apply(Path input) { + return Files.isDirectory(input, optionsCopy); + } + + @Override + public String toString() { + return "MoreFiles.isDirectory(" + Arrays.toString(optionsCopy) + ")"; + } + }; + } + + /** Returns whether or not the file with the given name in the given dir is a directory. */ + private static boolean isDirectory( + SecureDirectoryStream dir, Path name, LinkOption... options) throws IOException { + return dir.getFileAttributeView(name, BasicFileAttributeView.class, options) + .readAttributes() + .isDirectory(); + } + + /** + * Returns a predicate that returns the result of {@link java.nio.file.Files#isRegularFile(Path, + * LinkOption...)} on input paths with the given link options. + */ + public static Predicate isRegularFile(LinkOption... options) { + LinkOption[] optionsCopy = options.clone(); + return new Predicate() { + @Override + public boolean apply(Path input) { + return Files.isRegularFile(input, optionsCopy); + } + + @Override + public String toString() { + return "MoreFiles.isRegularFile(" + Arrays.toString(optionsCopy) + ")"; + } + }; + } + + /** + * Returns true if the files located by the given paths exist, are not directories, and contain + * the same bytes. + * + * @throws IOException if an I/O error occurs + * @since 22.0 + */ + public static boolean equal(Path path1, Path path2) throws IOException { + checkNotNull(path1); + checkNotNull(path2); + if (Files.isSameFile(path1, path2)) { + return true; + } + + /* + * Some operating systems may return zero as the length for files denoting system-dependent + * entities such as devices or pipes, in which case we must fall back on comparing the bytes + * directly. + */ + ByteSource source1 = asByteSource(path1); + ByteSource source2 = asByteSource(path2); + long len1 = source1.sizeIfKnown().or(0L); + long len2 = source2.sizeIfKnown().or(0L); + if (len1 != 0 && len2 != 0 && len1 != len2) { + return false; + } + return source1.contentEquals(source2); + } + + /** + * Like the unix command of the same name, creates an empty file or updates the last modified + * timestamp of the existing file at the given path to the current system time. + */ + @SuppressWarnings("GoodTime") // reading system time without TimeSource + public static void touch(Path path) throws IOException { + checkNotNull(path); + + try { + Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis())); + } catch (NoSuchFileException e) { + try { + Files.createFile(path); + } catch (FileAlreadyExistsException ignore) { + // The file didn't exist when we called setLastModifiedTime, but it did when we called + // createFile, so something else created the file in between. The end result is + // what we wanted: a new file that probably has its last modified time set to approximately + // now. Or it could have an arbitrary last modified time set by the creator, but that's no + // different than if another process set its last modified time to something else after we + // created it here. + } + } + } + + /** + * Creates any necessary but nonexistent parent directories of the specified path. Note that if + * this operation fails, it may have succeeded in creating some (but not all) of the necessary + * parent directories. The parent directory is created with the given {@code attrs}. + * + * @throws IOException if an I/O error occurs, or if any necessary but nonexistent parent + * directories of the specified file could not be created. + */ + public static void createParentDirectories(Path path, FileAttribute... attrs) + throws IOException { + // Interestingly, unlike File.getCanonicalFile(), Path/Files provides no way of getting the + // canonical (absolute, normalized, symlinks resolved, etc.) form of a path to a nonexistent + // file. getCanonicalFile() can at least get the canonical form of the part of the path which + // actually exists and then append the normalized remainder of the path to that. + Path normalizedAbsolutePath = path.toAbsolutePath().normalize(); + Path parent = normalizedAbsolutePath.getParent(); + if (parent == null) { + // The given directory is a filesystem root. All zero of its ancestors exist. This doesn't + // mean that the root itself exists -- consider x:\ on a Windows machine without such a + // drive -- or even that the caller can create it, but this method makes no such guarantees + // even for non-root files. + return; + } + + // Check if the parent is a directory first because createDirectories will fail if the parent + // exists and is a symlink to a directory... we'd like for this to succeed in that case. + // (I'm kind of surprised that createDirectories would fail in that case; doesn't seem like + // what you'd want to happen.) + if (!Files.isDirectory(parent)) { + Files.createDirectories(parent, attrs); + if (!Files.isDirectory(parent)) { + throw new IOException("Unable to create parent directories of " + path); + } + } + } + + /** + * Returns the file extension for + * the file at the given path, or the empty string if the file has no extension. The result does + * not include the '{@code .}'. + * + *

      Note: This method simply returns everything after the last '{@code .}' in the file's + * name as determined by {@link Path#getFileName}. It does not account for any filesystem-specific + * behavior that the {@link Path} API does not already account for. For example, on NTFS it will + * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS + * will drop the {@code ":.txt"} part of the name when the file is actually created on the + * filesystem due to NTFS's Alternate + * Data Streams. + */ + public static String getFileExtension(Path path) { + Path name = path.getFileName(); + + // null for empty paths and root-only paths + if (name == null) { + return ""; + } + + String fileName = name.toString(); + int dotIndex = fileName.lastIndexOf('.'); + return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1); + } + + /** + * Returns the file name without its file extension or path. This is + * similar to the {@code basename} unix command. The result does not include the '{@code .}'. + */ + public static String getNameWithoutExtension(Path path) { + Path name = path.getFileName(); + + // null for empty paths and root-only paths + if (name == null) { + return ""; + } + + String fileName = name.toString(); + int dotIndex = fileName.lastIndexOf('.'); + return dotIndex == -1 ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Deletes the file or directory at the given {@code path} recursively. Deletes symbolic links, + * not their targets (subject to the caveat below). + * + *

      If an I/O exception occurs attempting to read, open or delete any file under the given + * directory, this method skips that file and continues. All such exceptions are collected and, + * after attempting to delete all files, an {@code IOException} is thrown containing those + * exceptions as {@linkplain Throwable#getSuppressed() suppressed exceptions}. + * + *

      Warning: Security of recursive deletes

      + * + *

      On a file system that supports symbolic links and does not support {@link + * SecureDirectoryStream}, it is possible for a recursive delete to delete files and directories + * that are outside the directory being deleted. This can happen if, after checking that a + * file is a directory (and not a symbolic link), that directory is replaced by a symbolic link to + * an outside directory before the call that opens the directory to read its entries. + * + *

      By default, this method throws {@link InsecureRecursiveDeleteException} if it can't + * guarantee the security of recursive deletes. If you wish to allow the recursive deletes anyway, + * pass {@link RecursiveDeleteOption#ALLOW_INSECURE} to this method to override that behavior. + * + * @throws NoSuchFileException if {@code path} does not exist (optional specific exception) + * @throws InsecureRecursiveDeleteException if the security of recursive deletes can't be + * guaranteed for the file system and {@link RecursiveDeleteOption#ALLOW_INSECURE} was not + * specified + * @throws IOException if {@code path} or any file in the subtree rooted at it can't be deleted + * for any reason + */ + public static void deleteRecursively(Path path, RecursiveDeleteOption... options) + throws IOException { + Path parentPath = getParentPath(path); + if (parentPath == null) { + throw new FileSystemException(path.toString(), null, "can't delete recursively"); + } + + Collection exceptions = null; // created lazily if needed + try { + boolean sdsSupported = false; + try (DirectoryStream parent = Files.newDirectoryStream(parentPath)) { + if (parent instanceof SecureDirectoryStream) { + sdsSupported = true; + exceptions = + deleteRecursivelySecure( + (SecureDirectoryStream) parent, + /* + * requireNonNull is safe because paths have file names when they have parents, + * and we checked for a parent at the beginning of the method. + */ + requireNonNull(path.getFileName())); + } + } + + if (!sdsSupported) { + checkAllowsInsecure(path, options); + exceptions = deleteRecursivelyInsecure(path); + } + } catch (IOException e) { + if (exceptions == null) { + throw e; + } else { + exceptions.add(e); + } + } + + if (exceptions != null) { + throwDeleteFailed(path, exceptions); + } + } + + /** + * Deletes all files within the directory at the given {@code path} {@linkplain #deleteRecursively + * recursively}. Does not delete the directory itself. Deletes symbolic links, not their targets + * (subject to the caveat below). If {@code path} itself is a symbolic link to a directory, that + * link is followed and the contents of the directory it targets are deleted. + * + *

      If an I/O exception occurs attempting to read, open or delete any file under the given + * directory, this method skips that file and continues. All such exceptions are collected and, + * after attempting to delete all files, an {@code IOException} is thrown containing those + * exceptions as {@linkplain Throwable#getSuppressed() suppressed exceptions}. + * + *

      Warning: Security of recursive deletes

      + * + *

      On a file system that supports symbolic links and does not support {@link + * SecureDirectoryStream}, it is possible for a recursive delete to delete files and directories + * that are outside the directory being deleted. This can happen if, after checking that a + * file is a directory (and not a symbolic link), that directory is replaced by a symbolic link to + * an outside directory before the call that opens the directory to read its entries. + * + *

      By default, this method throws {@link InsecureRecursiveDeleteException} if it can't + * guarantee the security of recursive deletes. If you wish to allow the recursive deletes anyway, + * pass {@link RecursiveDeleteOption#ALLOW_INSECURE} to this method to override that behavior. + * + * @throws NoSuchFileException if {@code path} does not exist (optional specific exception) + * @throws NotDirectoryException if the file at {@code path} is not a directory (optional + * specific exception) + * @throws InsecureRecursiveDeleteException if the security of recursive deletes can't be + * guaranteed for the file system and {@link RecursiveDeleteOption#ALLOW_INSECURE} was not + * specified + * @throws IOException if one or more files can't be deleted for any reason + */ + public static void deleteDirectoryContents(Path path, RecursiveDeleteOption... options) + throws IOException { + Collection exceptions = null; // created lazily if needed + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + if (stream instanceof SecureDirectoryStream) { + SecureDirectoryStream sds = (SecureDirectoryStream) stream; + exceptions = deleteDirectoryContentsSecure(sds); + } else { + checkAllowsInsecure(path, options); + exceptions = deleteDirectoryContentsInsecure(stream); + } + } catch (IOException e) { + if (exceptions == null) { + throw e; + } else { + exceptions.add(e); + } + } + + if (exceptions != null) { + throwDeleteFailed(path, exceptions); + } + } + + /** + * Secure recursive delete using {@code SecureDirectoryStream}. Returns a collection of exceptions + * that occurred or null if no exceptions were thrown. + */ + private static @Nullable Collection deleteRecursivelySecure( + SecureDirectoryStream dir, Path path) { + Collection exceptions = null; + try { + if (isDirectory(dir, path, NOFOLLOW_LINKS)) { + try (SecureDirectoryStream childDir = dir.newDirectoryStream(path, NOFOLLOW_LINKS)) { + exceptions = deleteDirectoryContentsSecure(childDir); + } + + // If exceptions is not null, something went wrong trying to delete the contents of the + // directory, so we shouldn't try to delete the directory as it will probably fail. + if (exceptions == null) { + dir.deleteDirectory(path); + } + } else { + dir.deleteFile(path); + } + + return exceptions; + } catch (IOException e) { + return addException(exceptions, e); + } + } + + /** + * Secure method for deleting the contents of a directory using {@code SecureDirectoryStream}. + * Returns a collection of exceptions that occurred or null if no exceptions were thrown. + */ + private static @Nullable Collection deleteDirectoryContentsSecure( + SecureDirectoryStream dir) { + Collection exceptions = null; + try { + for (Path path : dir) { + exceptions = concat(exceptions, deleteRecursivelySecure(dir, path.getFileName())); + } + + return exceptions; + } catch (DirectoryIteratorException e) { + return addException(exceptions, e.getCause()); + } + } + + /** + * Insecure recursive delete for file systems that don't support {@code SecureDirectoryStream}. + * Returns a collection of exceptions that occurred or null if no exceptions were thrown. + */ + private static @Nullable Collection deleteRecursivelyInsecure(Path path) { + Collection exceptions = null; + try { + if (Files.isDirectory(path, NOFOLLOW_LINKS)) { + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + exceptions = deleteDirectoryContentsInsecure(stream); + } + } + + // If exceptions is not null, something went wrong trying to delete the contents of the + // directory, so we shouldn't try to delete the directory as it will probably fail. + if (exceptions == null) { + Files.delete(path); + } + + return exceptions; + } catch (IOException e) { + return addException(exceptions, e); + } + } + + /** + * Simple, insecure method for deleting the contents of a directory for file systems that don't + * support {@code SecureDirectoryStream}. Returns a collection of exceptions that occurred or null + * if no exceptions were thrown. + */ + private static @Nullable Collection deleteDirectoryContentsInsecure( + DirectoryStream dir) { + Collection exceptions = null; + try { + for (Path entry : dir) { + exceptions = concat(exceptions, deleteRecursivelyInsecure(entry)); + } + + return exceptions; + } catch (DirectoryIteratorException e) { + return addException(exceptions, e.getCause()); + } + } + + /** + * Returns a path to the parent directory of the given path. If the path actually has a parent + * path, this is simple. Otherwise, we need to do some trickier things. Returns null if the path + * is a root or is the empty path. + */ + private static @Nullable Path getParentPath(Path path) { + Path parent = path.getParent(); + + // Paths that have a parent: + if (parent != null) { + // "/foo" ("/") + // "foo/bar" ("foo") + // "C:\foo" ("C:\") + // "\foo" ("\" - current drive for process on Windows) + // "C:foo" ("C:" - working dir of drive C on Windows) + return parent; + } + + // Paths that don't have a parent: + if (path.getNameCount() == 0) { + // "/", "C:\", "\" (no parent) + // "" (undefined, though typically parent of working dir) + // "C:" (parent of working dir of drive C on Windows) + // + // For working dir paths ("" and "C:"), return null because: + // A) it's not specified that "" is the path to the working directory. + // B) if we're getting this path for recursive delete, it's typically not possible to + // delete the working dir with a relative path anyway, so it's ok to fail. + // C) if we're getting it for opening a new SecureDirectoryStream, there's no need to get + // the parent path anyway since we can safely open a DirectoryStream to the path without + // worrying about a symlink. + return null; + } else { + // "foo" (working dir) + return path.getFileSystem().getPath("."); + } + } + + /** Checks that the given options allow an insecure delete, throwing an exception if not. */ + private static void checkAllowsInsecure(Path path, RecursiveDeleteOption[] options) + throws InsecureRecursiveDeleteException { + if (!Arrays.asList(options).contains(RecursiveDeleteOption.ALLOW_INSECURE)) { + throw new InsecureRecursiveDeleteException(path.toString()); + } + } + + /** + * Adds the given exception to the given collection, creating the collection if it's null. Returns + * the collection. + */ + private static Collection addException( + @Nullable Collection exceptions, IOException e) { + if (exceptions == null) { + exceptions = new ArrayList<>(); // don't need Set semantics + } + exceptions.add(e); + return exceptions; + } + + /** + * Concatenates the contents of the two given collections of exceptions. If either collection is + * null, the other collection is returned. Otherwise, the elements of {@code other} are added to + * {@code exceptions} and {@code exceptions} is returned. + */ + private static @Nullable Collection concat( + @Nullable Collection exceptions, @Nullable Collection other) { + if (exceptions == null) { + return other; + } else if (other != null) { + exceptions.addAll(other); + } + return exceptions; + } + + /** + * Throws an exception indicating that one or more files couldn't be deleted when deleting {@code + * path} or its contents. + * + *

      If there is only one exception in the collection, and it is a {@link NoSuchFileException} + * thrown because {@code path} itself didn't exist, then throws that exception. Otherwise, the + * thrown exception contains all the exceptions in the given collection as suppressed exceptions. + */ + private static void throwDeleteFailed(Path path, Collection exceptions) + throws FileSystemException { + NoSuchFileException pathNotFound = pathNotFound(path, exceptions); + if (pathNotFound != null) { + throw pathNotFound; + } + // TODO(cgdecker): Should there be a custom exception type for this? + // Also, should we try to include the Path of each file we may have failed to delete rather + // than just the exceptions that occurred? + FileSystemException deleteFailed = + new FileSystemException( + path.toString(), + null, + "failed to delete one or more files; see suppressed exceptions for details"); + for (IOException e : exceptions) { + deleteFailed.addSuppressed(e); + } + throw deleteFailed; + } + + private static @Nullable NoSuchFileException pathNotFound( + Path path, Collection exceptions) { + if (exceptions.size() != 1) { + return null; + } + IOException exception = getOnlyElement(exceptions); + if (!(exception instanceof NoSuchFileException)) { + return null; + } + NoSuchFileException noSuchFileException = (NoSuchFileException) exception; + String exceptionFile = noSuchFileException.getFile(); + if (exceptionFile == null) { + /* + * It's not clear whether this happens in practice, especially with the filesystem + * implementations that are built into java.nio. + */ + return null; + } + Path parentPath = getParentPath(path); + if (parentPath == null) { + /* + * This is probably impossible: + * + * - In deleteRecursively, we require the path argument to have a parent. + * + * - In deleteDirectoryContents, the path argument may have no parent. Fortunately, all the + * *other* paths we process will be descendants of that. That leaves only the original path + * argument for us to consider. And the only place we call pathNotFound is from + * throwDeleteFailed, and the other place that we call throwDeleteFailed inside + * deleteDirectoryContents is when an exception is thrown during the recursive steps. Any + * failure during the initial lookup of the path argument itself is rethrown directly. So + * any exception that we're seeing here is from a descendant, which naturally has a parent. + * I think. + * + * Still, if this can happen somehow (a weird filesystem implementation that lets callers + * change its working directly concurrently with a call to deleteDirectoryContents?), it makes + * more sense for us to fall back to a generic FileSystemException (by returning null here) + * than to dereference parentPath and end up producing NullPointerException. + */ + return null; + } + // requireNonNull is safe because paths have file names when they have parents. + Path pathResolvedFromParent = parentPath.resolve(requireNonNull(path.getFileName())); + if (exceptionFile.equals(pathResolvedFromParent.toString())) { + return noSuchFileException; + } + return null; + } +} diff --git a/android/guava/src/com/google/common/io/MultiInputStream.java b/android/guava/src/com/google/common/io/MultiInputStream.java index 9a7e0fd1c0e7..7f404b5da4aa 100644 --- a/android/guava/src/com/google/common/io/MultiInputStream.java +++ b/android/guava/src/com/google/common/io/MultiInputStream.java @@ -17,10 +17,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An {@link InputStream} that concatenates multiple substreams. At most one stream will be open at @@ -29,12 +30,12 @@ * @author Chris Nokleberg * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class MultiInputStream extends InputStream { private Iterator it; - @CheckForNull private InputStream in; + private @Nullable InputStream in; /** * Creates a new instance. diff --git a/android/guava/src/com/google/common/io/MultiReader.java b/android/guava/src/com/google/common/io/MultiReader.java index cc36e527b620..0e7bab9065bd 100644 --- a/android/guava/src/com/google/common/io/MultiReader.java +++ b/android/guava/src/com/google/common/io/MultiReader.java @@ -17,11 +17,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import java.io.IOException; import java.io.Reader; import java.util.Iterator; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link Reader} that concatenates multiple readers. @@ -29,11 +30,11 @@ * @author Bin Zhu * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault class MultiReader extends Reader { private final Iterator it; - @CheckForNull private Reader current; + private @Nullable Reader current; MultiReader(Iterator readers) throws IOException { this.it = readers; diff --git a/android/guava/src/com/google/common/io/ParametricNullness.java b/android/guava/src/com/google/common/io/ParametricNullness.java index 98da765852b6..48773c8718a1 100644 --- a/android/guava/src/com/google/common/io/ParametricNullness.java +++ b/android/guava/src/com/google/common/io/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

        + *
      • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
      • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
      + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
        + *
      • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
      • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
      * *

      Consumers of this annotation include: * *

        - *
      • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
      • J2ObjC - *
      • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
      • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
      • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
      * + *

      This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/io/PatternFilenameFilter.java b/android/guava/src/com/google/common/io/PatternFilenameFilter.java index 3cb2371e2fd4..e92eb66528c6 100644 --- a/android/guava/src/com/google/common/io/PatternFilenameFilter.java +++ b/android/guava/src/com/google/common/io/PatternFilenameFilter.java @@ -14,8 +14,8 @@ package com.google.common.io; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import java.io.File; import java.io.FilenameFilter; @@ -29,9 +29,8 @@ * @author Apple Chow * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class PatternFilenameFilter implements FilenameFilter { private final Pattern pattern; diff --git a/android/guava/src/com/google/common/io/ReaderInputStream.java b/android/guava/src/com/google/common/io/ReaderInputStream.java index 75f8a3e1e39b..386bad6f3397 100644 --- a/android/guava/src/com/google/common/io/ReaderInputStream.java +++ b/android/guava/src/com/google/common/io/ReaderInputStream.java @@ -17,8 +17,10 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.UnsignedBytes; import java.io.IOException; import java.io.InputStream; @@ -43,8 +45,8 @@ * * @author Chris Nokleberg */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class ReaderInputStream extends InputStream { private final Reader reader; private final CharsetEncoder encoder; @@ -65,8 +67,10 @@ final class ReaderInputStream extends InputStream { /** Whether we've finished reading the reader. */ private boolean endOfInput; + /** Whether we're copying encoded bytes to the caller's buffer. */ private boolean draining; + /** Whether we've successfully flushed the encoder. */ private boolean doneFlushing; @@ -198,8 +202,8 @@ private static CharBuffer grow(CharBuffer buf) { /** Handle the case of underflow caused by needing more input characters. */ private void readMoreChars() throws IOException { // Possibilities: - // 1) array has space available on right hand side (between limit and capacity) - // 2) array has space available on left hand side (before position) + // 1) array has space available on right-hand side (between limit and capacity) + // 2) array has space available on left-hand side (before position) // 3) array has no space available // // In case 2 we shift the existing chars to the left, and in case 3 we create a bigger @@ -249,7 +253,7 @@ private void startDraining(boolean overflow) { * number of characters copied. */ private int drain(byte[] b, int off, int len) { - int remaining = Math.min(len, byteBuffer.remaining()); + int remaining = min(len, byteBuffer.remaining()); byteBuffer.get(b, off, remaining); return remaining; } diff --git a/android/guava/src/com/google/common/io/RecursiveDeleteOption.java b/android/guava/src/com/google/common/io/RecursiveDeleteOption.java new file mode 100644 index 000000000000..c7cb270c8d44 --- /dev/null +++ b/android/guava/src/com/google/common/io/RecursiveDeleteOption.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.io; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.j2objc.annotations.J2ObjCIncompatible; +import java.nio.file.SecureDirectoryStream; + +/** + * Options for use with recursive delete methods ({@link MoreFiles#deleteRecursively} and {@link + * MoreFiles#deleteDirectoryContents}). + * + * @since 33.4.0 (but since 21.0 in the JRE flavor) + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@J2ObjCIncompatible // java.nio.file +public enum RecursiveDeleteOption { + /** + * Specifies that the recursive delete should not throw an exception when it can't be guaranteed + * that it can be done securely, without vulnerability to race conditions (i.e. when the file + * system does not support {@link SecureDirectoryStream}). + * + *

      Warning: On a file system that supports symbolic links, it is possible for an + * insecure recursive delete to delete files and directories that are outside the directory + * being deleted. This can happen if, after checking that a file is a directory (and not a + * symbolic link), that directory is deleted and replaced by a symbolic link to an outside + * directory before the call that opens the directory to read its entries. File systems that + * support {@code SecureDirectoryStream} do not have this vulnerability. + */ + ALLOW_INSECURE +} diff --git a/android/guava/src/com/google/common/io/Resources.java b/android/guava/src/com/google/common/io/Resources.java index ae3233408b35..6a19e8ea264a 100644 --- a/android/guava/src/com/google/common/io/Resources.java +++ b/android/guava/src/com/google/common/io/Resources.java @@ -17,9 +17,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Charsets; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -28,8 +27,9 @@ import java.io.OutputStream; import java.net.URL; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.List; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Provides utility methods for working with resources in the classpath. Note that even though these @@ -41,9 +41,8 @@ * @author Colin Decker * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Resources { private Resources() {} @@ -100,8 +99,8 @@ public static byte[] toByteArray(URL url) throws IOException { * Reads all characters from a URL into a {@link String}, using the given character set. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful - * predefined constants + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants * @return a string containing all the characters from the URL * @throws IOException if an I/O error occurs. */ @@ -114,8 +113,8 @@ public static String toString(URL url, Charset charset) throws IOException { * lines. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful - * predefined constants + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants * @param callback the LineProcessor to use to handle the lines * @return the output of processing the lines * @throws IOException if an I/O error occurs @@ -135,8 +134,8 @@ public static String toString(URL url, Charset charset) throws IOException { * Resources.asCharSource(url, charset).readLines()}. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful - * predefined constants + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants * @return a mutable {@link List} containing all the lines * @throws IOException if an I/O error occurs */ diff --git a/android/guava/src/com/google/common/io/TempFileCreator.java b/android/guava/src/com/google/common/io/TempFileCreator.java new file mode 100644 index 000000000000..6a65e39d2572 --- /dev/null +++ b/android/guava/src/com/google/common/io/TempFileCreator.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.io; + +import static com.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; +import static com.google.common.base.StandardSystemProperty.USER_NAME; +import static com.google.common.base.Throwables.throwIfUnchecked; +import static java.nio.file.attribute.AclEntryFlag.DIRECTORY_INHERIT; +import static java.nio.file.attribute.AclEntryFlag.FILE_INHERIT; +import static java.nio.file.attribute.AclEntryType.ALLOW; +import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.j2objc.annotations.J2ObjCIncompatible; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.FileSystems; +import java.nio.file.Paths; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.attribute.UserPrincipal; +import java.util.EnumSet; +import java.util.Set; + +/** + * Creates temporary files and directories whose permissions are restricted to the current user or, + * in the case of Android, the current app. If that is not possible (as is the case under the very + * old Android Ice Cream Sandwich release), then this class throws an exception instead of creating + * a file or directory that would be more accessible. + */ +@J2ktIncompatible +@GwtIncompatible +@J2ObjCIncompatible +abstract class TempFileCreator { + static final TempFileCreator INSTANCE = pickSecureCreator(); + + /** + * @throws IllegalStateException if the directory could not be created (to implement the contract + * of {@link Files#createTempDir()}, such as if the system does not support creating temporary + * directories securely + */ + abstract File createTempDir(); + + abstract File createTempFile(String prefix) throws IOException; + + private static TempFileCreator pickSecureCreator() { + try { + Class.forName("java.nio.file.Path"); + return new JavaNioCreator(); + } catch (ClassNotFoundException runningUnderAndroid) { + // Try another way. + } + + try { + int version = (int) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null); + int jellyBean = + (int) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null); + /* + * I assume that this check can't fail because JELLY_BEAN will be present only if we're + * running under Jelly Bean or higher. But it seems safest to check. + */ + if (version < jellyBean) { + return new ThrowingCreator(); + } + + // Don't merge these catch() blocks, let alone use ReflectiveOperationException directly: + // b/65343391 + } catch (NoSuchFieldException e) { + // The JELLY_BEAN field doesn't exist because we're running on a version before Jelly Bean :) + return new ThrowingCreator(); + } catch (ClassNotFoundException e) { + // Should be impossible, but we want to return *something* so that class init succeeds. + return new ThrowingCreator(); + } catch (IllegalAccessException e) { + // ditto + return new ThrowingCreator(); + } + + // Android isolates apps' temporary directories since Jelly Bean: + // https://github.com/google/guava/issues/4011#issuecomment-770020802 + // So we can create files there with any permissions and still get security from the isolation. + return new JavaIoCreator(); + } + + /** + * Creates the permissions normally used for Windows filesystems, looking up the user afresh, even + * if previous calls have initialized the {@code PermissionSupplier} fields. + * + *

      This lets us test the effects of different values of the {@code user.name} system property + * without needing a separate VM or classloader. + */ + @IgnoreJRERequirement // used only when Path is available (and only from tests) + @VisibleForTesting + static void testMakingUserPermissionsFromScratch() throws IOException { + // All we're testing is whether it throws. + FileAttribute unused = JavaNioCreator.userPermissions().get(); + } + + @IgnoreJRERequirement // used only when Path is available + private static final class JavaNioCreator extends TempFileCreator { + @Override + File createTempDir() { + try { + return java.nio.file.Files.createTempDirectory( + Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, directoryPermissions.get()) + .toFile(); + } catch (IOException e) { + throw new IllegalStateException("Failed to create directory", e); + } + } + + @Override + File createTempFile(String prefix) throws IOException { + return java.nio.file.Files.createTempFile( + Paths.get(JAVA_IO_TMPDIR.value()), + /* prefix= */ prefix, + /* suffix= */ null, + filePermissions.get()) + .toFile(); + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private interface PermissionSupplier { + FileAttribute get() throws IOException; + } + + private static final PermissionSupplier filePermissions; + private static final PermissionSupplier directoryPermissions; + + static { + Set views = FileSystems.getDefault().supportedFileAttributeViews(); + if (views.contains("posix")) { + filePermissions = () -> asFileAttribute(PosixFilePermissions.fromString("rw-------")); + directoryPermissions = () -> asFileAttribute(PosixFilePermissions.fromString("rwx------")); + } else if (views.contains("acl")) { + filePermissions = directoryPermissions = userPermissions(); + } else { + filePermissions = + directoryPermissions = + () -> { + throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault()); + }; + } + } + + private static PermissionSupplier userPermissions() { + try { + UserPrincipal user = + FileSystems.getDefault() + .getUserPrincipalLookupService() + .lookupPrincipalByName(getUsername()); + ImmutableList acl = + ImmutableList.of( + AclEntry.newBuilder() + .setType(ALLOW) + .setPrincipal(user) + .setPermissions(EnumSet.allOf(AclEntryPermission.class)) + .setFlags(DIRECTORY_INHERIT, FILE_INHERIT) + .build()); + FileAttribute> attribute = + new FileAttribute>() { + @Override + public String name() { + return "acl:acl"; + } + + @Override + public ImmutableList value() { + return acl; + } + }; + return () -> attribute; + } catch (IOException e) { + // We throw a new exception each time so that the stack trace is right. + return () -> { + throw new IOException("Could not find user", e); + }; + } + } + + private static String getUsername() { + /* + * https://github.com/google/guava/issues/6634: ProcessHandle has more accurate information, + * but that class isn't available under all environments that we support. We use it if + * available and fall back if not. + */ + String fromSystemProperty = requireNonNull(USER_NAME.value()); + + try { + Class processHandleClass = Class.forName("java.lang.ProcessHandle"); + Class processHandleInfoClass = Class.forName("java.lang.ProcessHandle$Info"); + Class optionalClass = Class.forName("java.util.Optional"); + /* + * We don't *need* to use reflection to access Optional: It's available on all JDKs we + * support, and Android code won't get this far, anyway, because ProcessHandle is + * unavailable. But given how much other reflection we're using, we might as well use it + * here, too, so that we don't need to also suppress an AndroidApiChecker error. + */ + + Method currentMethod = processHandleClass.getMethod("current"); + Method infoMethod = processHandleClass.getMethod("info"); + Method userMethod = processHandleInfoClass.getMethod("user"); + Method orElseMethod = optionalClass.getMethod("orElse", Object.class); + + Object current = currentMethod.invoke(null); + Object info = infoMethod.invoke(current); + Object user = userMethod.invoke(info); + return (String) requireNonNull(orElseMethod.invoke(user, fromSystemProperty)); + } catch (ClassNotFoundException runningUnderAndroidOrJava8) { + /* + * I'm not sure that we could actually get here for *Android*: I would expect us to enter + * the POSIX code path instead. And if we tried this code path, we'd have trouble unless we + * were running under a new enough version of Android to support NIO. + * + * So this is probably just the "Windows Java 8" case. In that case, if we wanted *another* + * layer of fallback before consulting the system property, we could try + * com.sun.security.auth.module.NTSystem. + * + * But for now, we use the value from the system property as our best guess. + */ + return fromSystemProperty; + } catch (InvocationTargetException e) { + throwIfUnchecked(e.getCause()); // in case it's an Error or something + return fromSystemProperty; // should be impossible + } catch (NoSuchMethodException shouldBeImpossible) { + return fromSystemProperty; + } catch (IllegalAccessException shouldBeImpossible) { + /* + * We don't merge these into `catch (ReflectiveOperationException ...)` or an equivalent + * multicatch because ReflectiveOperationException isn't available under Android: + * b/124188803 + */ + return fromSystemProperty; + } + } + } + + private static final class JavaIoCreator extends TempFileCreator { + @Override + File createTempDir() { + File baseDir = new File(JAVA_IO_TMPDIR.value()); + @SuppressWarnings("GoodTime") // reading system time without TimeSource + String baseName = System.currentTimeMillis() + "-"; + + for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { + File tempDir = new File(baseDir, baseName + counter); + if (tempDir.mkdir()) { + return tempDir; + } + } + throw new IllegalStateException( + "Failed to create directory within " + + TEMP_DIR_ATTEMPTS + + " attempts (tried " + + baseName + + "0 to " + + baseName + + (TEMP_DIR_ATTEMPTS - 1) + + ')'); + } + + @Override + File createTempFile(String prefix) throws IOException { + return File.createTempFile( + /* prefix= */ prefix, + /* suffix= */ null, + /* directory= */ null /* defaults to java.io.tmpdir */); + } + + /** Maximum loop count when creating temp directories. */ + private static final int TEMP_DIR_ATTEMPTS = 10000; + } + + private static final class ThrowingCreator extends TempFileCreator { + private static final String MESSAGE = + "Guava cannot securely create temporary files or directories under SDK versions before" + + " Jelly Bean. You can create one yourself, either in the insecure default directory" + + " or in a more secure directory, such as context.getCacheDir(). For more information," + + " see the Javadoc for Files.createTempDir()."; + + @Override + File createTempDir() { + throw new IllegalStateException(MESSAGE); + } + + @Override + File createTempFile(String prefix) throws IOException { + throw new IOException(MESSAGE); + } + } + + private TempFileCreator() {} +} diff --git a/android/guava/src/com/google/common/io/package-info.java b/android/guava/src/com/google/common/io/package-info.java index f0666b26f4b2..30cff81d2fd2 100644 --- a/android/guava/src/com/google/common/io/package-info.java +++ b/android/guava/src/com/google/common/io/package-info.java @@ -13,24 +13,23 @@ */ /** - * This package contains utility methods and classes for working with Java I/O; for example input - * streams, output streams, readers, writers, and files. + * Utility methods and classes for I/O; for example input streams, output streams, readers, writers, + * and files. * - *

      At the core of this package are the Source/Sink types: {@link com.google.common.io.ByteSource - * ByteSource}, {@link com.google.common.io.CharSource CharSource}, {@link - * com.google.common.io.ByteSink ByteSink} and {@link com.google.common.io.CharSink CharSink}. They - * are factories for I/O streams that provide many convenience methods that handle both opening and + *

      At the core of this package are the Source/Sink types: {@link ByteSource ByteSource}, {@link + * CharSource CharSource}, {@link ByteSink ByteSink} and {@link CharSink CharSink}. They are + * factories for I/O streams that provide many convenience methods that handle both opening and * closing streams for you. * - *

      This package is a part of the open-source Guava + *

      This package is a part of the open-source Guava * library. For more information on Sources and Sinks as well as other features of this package, see * I/O Explained on the Guava wiki. * * @author Chris Nokleberg */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.io; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/math/BigDecimalMath.java b/android/guava/src/com/google/common/math/BigDecimalMath.java index 33a55d356312..e1a7a10d4b98 100644 --- a/android/guava/src/com/google/common/math/BigDecimalMath.java +++ b/android/guava/src/com/google/common/math/BigDecimalMath.java @@ -15,6 +15,7 @@ package com.google.common.math; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.math.BigDecimal; import java.math.RoundingMode; @@ -24,8 +25,8 @@ * @author Louis Wasserman * @since 30.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public class BigDecimalMath { private BigDecimalMath() {} diff --git a/android/guava/src/com/google/common/math/BigIntegerMath.java b/android/guava/src/com/google/common/math/BigIntegerMath.java index 6ef1e81ed4d7..7220596ae3a9 100644 --- a/android/guava/src/com/google/common/math/BigIntegerMath.java +++ b/android/guava/src/com/google/common/math/BigIntegerMath.java @@ -21,11 +21,8 @@ import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary; import static java.math.RoundingMode.CEILING; import static java.math.RoundingMode.FLOOR; -import static java.math.RoundingMode.HALF_DOWN; import static java.math.RoundingMode.HALF_EVEN; -import static java.math.RoundingMode.UNNECESSARY; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; @@ -48,7 +45,6 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class BigIntegerMath { /** * Returns the smallest power of two greater than or equal to {@code x}. This is equivalent to @@ -57,7 +53,6 @@ public final class BigIntegerMath { * @throws IllegalArgumentException if {@code x <= 0} * @since 20.0 */ - @Beta public static BigInteger ceilingPowerOfTwo(BigInteger x) { return BigInteger.ZERO.setBit(log2(x, CEILING)); } @@ -69,7 +64,6 @@ public static BigInteger ceilingPowerOfTwo(BigInteger x) { * @throws IllegalArgumentException if {@code x <= 0} * @since 20.0 */ - @Beta public static BigInteger floorPowerOfTwo(BigInteger x) { return BigInteger.ZERO.setBit(log2(x, FLOOR)); } @@ -122,10 +116,8 @@ public static int log2(BigInteger x, RoundingMode mode) { BigInteger x2 = x.pow(2); int logX2Floor = x2.bitLength() - 1; return (logX2Floor < 2 * logFloor + 1) ? logFloor : logFloor + 1; - - default: - throw new AssertionError(); } + throw new AssertionError(); } /* @@ -193,7 +185,7 @@ public static int log10(BigInteger x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(floorCmp == 0); - // fall through + // fall through case FLOOR: case DOWN: return floorLog; @@ -209,9 +201,8 @@ public static int log10(BigInteger x, RoundingMode mode) { BigInteger x2 = x.pow(2); BigInteger halfPowerSquared = floorPow.pow(2).multiply(BigInteger.TEN); return (x2.compareTo(halfPowerSquared) <= 0) ? floorLog : floorLog + 1; - default: - throw new AssertionError(); } + throw new AssertionError(); } private static final double LN_10 = Math.log(10); @@ -255,9 +246,8 @@ public static BigInteger sqrt(BigInteger x, RoundingMode mode) { * halfSquare. */ return (halfSquare.compareTo(x) >= 0) ? sqrtFloor : sqrtFloor.add(BigInteger.ONE); - default: - throw new AssertionError(); } + throw new AssertionError(); } @GwtIncompatible // TODO diff --git a/android/guava/src/com/google/common/math/DoubleMath.java b/android/guava/src/com/google/common/math/DoubleMath.java index cdd0a4b39e3b..86ac100efc8c 100644 --- a/android/guava/src/com/google/common/math/DoubleMath.java +++ b/android/guava/src/com/google/common/math/DoubleMath.java @@ -33,7 +33,6 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Booleans; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.math.BigInteger; import java.math.RoundingMode; @@ -46,7 +45,6 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class DoubleMath { /* * This method returns a value y such that rounding y DOWN (towards zero) gives the same result as @@ -108,10 +106,8 @@ static double roundIntermediate(double x, RoundingMode mode) { return z; } } - - default: - throw new AssertionError(); } + throw new AssertionError(); } /** @@ -129,6 +125,8 @@ static double roundIntermediate(double x, RoundingMode mode) { *

    */ @GwtIncompatible // #roundIntermediate + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static int roundToInt(double x, RoundingMode mode) { double z = roundIntermediate(x, mode); checkInRangeForRoundingInputs( @@ -154,6 +152,8 @@ public static int roundToInt(double x, RoundingMode mode) { * */ @GwtIncompatible // #roundIntermediate + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long roundToLong(double x, RoundingMode mode) { double z = roundIntermediate(x, mode); checkInRangeForRoundingInputs( @@ -181,6 +181,8 @@ public static long roundToLong(double x, RoundingMode mode) { */ // #roundIntermediate, java.lang.Math.getExponent, com.google.common.math.DoubleUtils @GwtIncompatible + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static BigInteger roundToBigInteger(double x, RoundingMode mode) { x = roundIntermediate(x, mode); if (MIN_LONG_AS_DOUBLE - x < 1.0 & x < MAX_LONG_AS_DOUBLE_PLUS_ONE) { @@ -235,7 +237,8 @@ public static double log2(double x) { * infinite */ @GwtIncompatible // java.lang.Math.getExponent, com.google.common.math.DoubleUtils - @SuppressWarnings("fallthrough") + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings({"fallthrough", "ShortCircuitBoolean"}) public static int log2(double x, RoundingMode mode) { checkArgument(x > 0.0 && isFinite(x), "x must be positive and finite"); int exponent = getExponent(x); @@ -248,7 +251,7 @@ public static int log2(double x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(isPowerOfTwo(x)); - // fall through + // fall through case FLOOR: increment = false; break; @@ -386,7 +389,7 @@ public static int fuzzyCompare(double a, double b, double tolerance) { } else if (a > b) { return 1; } else { - return Booleans.compare(Double.isNaN(a), Double.isNaN(b)); + return Boolean.compare(Double.isNaN(a), Double.isNaN(b)); } } diff --git a/android/guava/src/com/google/common/math/DoubleUtils.java b/android/guava/src/com/google/common/math/DoubleUtils.java index d4a07e9dfcfe..e2331a71c624 100644 --- a/android/guava/src/com/google/common/math/DoubleUtils.java +++ b/android/guava/src/com/google/common/math/DoubleUtils.java @@ -22,6 +22,7 @@ import static java.lang.Double.isNaN; import static java.lang.Double.longBitsToDouble; import static java.lang.Math.getExponent; +import static java.lang.Math.max; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; @@ -33,7 +34,6 @@ * @author Louis Wasserman */ @GwtIncompatible -@ElementTypesAreNonnullByDefault final class DoubleUtils { private DoubleUtils() {} @@ -132,7 +132,7 @@ static double bigToDouble(BigInteger x) { /** Returns its argument if it is non-negative, zero if it is negative. */ static double ensureNonNegative(double value) { checkArgument(!isNaN(value)); - return Math.max(value, 0.0); + return max(value, 0.0); } @VisibleForTesting static final long ONE_BITS = 0x3ff0000000000000L; diff --git a/android/guava/src/com/google/common/math/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/math/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 84999ea49499..000000000000 --- a/android/guava/src/com/google/common/math/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.math; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/math/IgnoreJRERequirement.java b/android/guava/src/com/google/common/math/IgnoreJRERequirement.java new file mode 100644 index 000000000000..4d9e99fd0df0 --- /dev/null +++ b/android/guava/src/com/google/common/math/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.math; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/math/IntMath.java b/android/guava/src/com/google/common/math/IntMath.java index 17a18c69999a..8c6e9bb76437 100644 --- a/android/guava/src/com/google/common/math/IntMath.java +++ b/android/guava/src/com/google/common/math/IntMath.java @@ -25,7 +25,6 @@ import static java.math.RoundingMode.HALF_EVEN; import static java.math.RoundingMode.HALF_UP; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; @@ -48,10 +47,7 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class IntMath { - // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || - @VisibleForTesting static final int MAX_SIGNED_POWER_OF_TWO = 1 << (Integer.SIZE - 2); /** @@ -63,7 +59,6 @@ public final class IntMath { * int}, i.e. when {@code x > 2^30} * @since 20.0 */ - @Beta public static int ceilingPowerOfTwo(int x) { checkPositive("x", x); if (x > MAX_SIGNED_POWER_OF_TWO) { @@ -79,7 +74,6 @@ public static int ceilingPowerOfTwo(int x) { * @throws IllegalArgumentException if {@code x <= 0} * @since 20.0 */ - @Beta public static int floorPowerOfTwo(int x) { checkPositive("x", x); return Integer.highestOneBit(x); @@ -91,6 +85,8 @@ public static int floorPowerOfTwo(int x) { *

    This differs from {@code Integer.bitCount(x) == 1}, because {@code * Integer.bitCount(Integer.MIN_VALUE) == 1}, but {@link Integer#MIN_VALUE} is not a power of two. */ + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static boolean isPowerOfTwo(int x) { return x > 0 & (x & (x - 1)) == 0; } @@ -121,7 +117,7 @@ public static int log2(int x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(isPowerOfTwo(x)); - // fall through + // fall through case DOWN: case FLOOR: return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(x); @@ -139,10 +135,8 @@ public static int log2(int x, RoundingMode mode) { // floor(2^(logFloor + 0.5)) int logFloor = (Integer.SIZE - 1) - leadingZeros; return logFloor + lessThanBranchFree(cmp, x); - - default: - throw new AssertionError(); } + throw new AssertionError(); } /** The biggest half power of two that can fit in an unsigned int. */ @@ -164,7 +158,7 @@ public static int log10(int x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(x == floorPow); - // fall through + // fall through case FLOOR: case DOWN: return logFloor; @@ -176,9 +170,8 @@ public static int log10(int x, RoundingMode mode) { case HALF_EVEN: // sqrt(10) is irrational, so log10(x) - logFloor is never exactly 0.5 return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); - default: - throw new AssertionError(); } + throw new AssertionError(); } private static int log10Floor(int x) { @@ -232,11 +225,11 @@ public static int pow(int b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: return (k < Integer.SIZE) ? (1 << k) : 0; - case (-2): + case -2: if (k < Integer.SIZE) { return ((k & 1) == 0) ? (1 << k) : -(1 << k); } else { @@ -295,9 +288,8 @@ public static int sqrt(int x, RoundingMode mode) { * signed int, so lessThanBranchFree is safe for use. */ return sqrtFloor + lessThanBranchFree(halfSquare, x); - default: - throw new AssertionError(); } + throw new AssertionError(); } private static int sqrtFloor(int x) { @@ -313,7 +305,8 @@ private static int sqrtFloor(int x) { * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} * is not an integer multiple of {@code b} */ - @SuppressWarnings("fallthrough") + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings({"fallthrough", "ShortCircuitBoolean"}) public static int divide(int p, int q, RoundingMode mode) { checkNotNull(mode); if (q == 0) { @@ -338,7 +331,7 @@ public static int divide(int p, int q, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(rem == 0); - // fall through + // fall through case DOWN: increment = false; break; @@ -488,6 +481,8 @@ public static int checkedMultiply(int a, int b) { * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed {@code * int} arithmetic */ + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static int checkedPow(int b, int k) { checkNonNegative("exponent", k); switch (b) { @@ -495,12 +490,12 @@ public static int checkedPow(int b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: checkNoOverflow(k < Integer.SIZE - 1, "checkedPow", b, k); return 1 << k; - case (-2): + case -2: checkNoOverflow(k < Integer.SIZE, "checkedPow", b, k); return ((k & 1) == 0) ? 1 << k : -1 << k; default: @@ -532,7 +527,6 @@ public static int checkedPow(int b, int k) { * * @since 20.0 */ - @Beta public static int saturatedAdd(int a, int b) { return Ints.saturatedCast((long) a + b); } @@ -543,7 +537,6 @@ public static int saturatedAdd(int a, int b) { * * @since 20.0 */ - @Beta public static int saturatedSubtract(int a, int b) { return Ints.saturatedCast((long) a - b); } @@ -554,7 +547,6 @@ public static int saturatedSubtract(int a, int b) { * * @since 20.0 */ - @Beta public static int saturatedMultiply(int a, int b) { return Ints.saturatedCast((long) a * b); } @@ -565,7 +557,8 @@ public static int saturatedMultiply(int a, int b) { * * @since 20.0 */ - @Beta + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static int saturatedPow(int b, int k) { checkNonNegative("exponent", k); switch (b) { @@ -573,14 +566,14 @@ public static int saturatedPow(int b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: if (k >= Integer.SIZE - 1) { return Integer.MAX_VALUE; } return 1 << k; - case (-2): + case -2: if (k >= Integer.SIZE) { return Integer.MAX_VALUE + (k & 1); } @@ -674,7 +667,7 @@ public static int binomial(int n, int k) { // binomial(biggestBinomials[k], k) fits in an int, but not binomial(biggestBinomials[k]+1,k). @VisibleForTesting - static int[] biggestBinomials = { + static final int[] biggestBinomials = { Integer.MAX_VALUE, Integer.MAX_VALUE, 65536, @@ -720,7 +713,6 @@ public static int mean(int x, int y) { * @since 20.0 */ @GwtIncompatible // TODO - @Beta public static boolean isPrime(int n) { return LongMath.isPrime(n); } diff --git a/android/guava/src/com/google/common/math/LinearTransformation.java b/android/guava/src/com/google/common/math/LinearTransformation.java index 4cc1eb87dbe9..379898d51842 100644 --- a/android/guava/src/com/google/common/math/LinearTransformation.java +++ b/android/guava/src/com/google/common/math/LinearTransformation.java @@ -18,10 +18,10 @@ import static com.google.common.math.DoubleUtils.isFinite; import static java.lang.Double.NaN; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.concurrent.LazyInit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * The representation of a linear transformation between real numbers {@code x} and {@code y}. @@ -34,10 +34,16 @@ * @author Pete Gillin * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class LinearTransformation { + /** + * Constructor for use by subclasses inside Guava. + * + * @deprecated Create instances by using the static factory methods of the class. + */ + @Deprecated + public LinearTransformation() {} /** * Start building an instance which maps {@code x = x1} to {@code y = y1}. Both arguments must be @@ -163,7 +169,7 @@ private static final class RegularLinearTransformation extends LinearTransformat final double slope; final double yIntercept; - @CheckForNull @LazyInit LinearTransformation inverse; + @LazyInit @Nullable LinearTransformation inverse; RegularLinearTransformation(double slope, double yIntercept) { this.slope = slope; @@ -221,7 +227,7 @@ private static final class VerticalLinearTransformation extends LinearTransforma final double x; - @CheckForNull @LazyInit LinearTransformation inverse; + @LazyInit @Nullable LinearTransformation inverse; VerticalLinearTransformation(double x) { this.x = x; diff --git a/android/guava/src/com/google/common/math/LongMath.java b/android/guava/src/com/google/common/math/LongMath.java index 1e67812b733b..750c9d39f8a1 100644 --- a/android/guava/src/com/google/common/math/LongMath.java +++ b/android/guava/src/com/google/common/math/LongMath.java @@ -25,11 +25,9 @@ import static java.math.RoundingMode.HALF_EVEN; import static java.math.RoundingMode.HALF_UP; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Longs; import com.google.common.primitives.UnsignedLongs; import java.math.BigInteger; import java.math.RoundingMode; @@ -49,10 +47,7 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class LongMath { - // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || - @VisibleForTesting static final long MAX_SIGNED_POWER_OF_TWO = 1L << (Long.SIZE - 2); /** @@ -64,7 +59,6 @@ public final class LongMath { * long}, i.e. when {@code x > 2^62} * @since 20.0 */ - @Beta public static long ceilingPowerOfTwo(long x) { checkPositive("x", x); if (x > MAX_SIGNED_POWER_OF_TWO) { @@ -80,7 +74,6 @@ public static long ceilingPowerOfTwo(long x) { * @throws IllegalArgumentException if {@code x <= 0} * @since 20.0 */ - @Beta public static long floorPowerOfTwo(long x) { checkPositive("x", x); @@ -95,6 +88,8 @@ public static long floorPowerOfTwo(long x) { *

    This differs from {@code Long.bitCount(x) == 1}, because {@code * Long.bitCount(Long.MIN_VALUE) == 1}, but {@link Long#MIN_VALUE} is not a power of two. */ + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static boolean isPowerOfTwo(long x) { return x > 0 & (x & (x - 1)) == 0; } @@ -124,7 +119,7 @@ public static int log2(long x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(isPowerOfTwo(x)); - // fall through + // fall through case DOWN: case FLOOR: return (Long.SIZE - 1) - Long.numberOfLeadingZeros(x); @@ -142,10 +137,8 @@ public static int log2(long x, RoundingMode mode) { // floor(2^(logFloor + 0.5)) int logFloor = (Long.SIZE - 1) - leadingZeros; return logFloor + lessThanBranchFree(cmp, x); - - default: - throw new AssertionError("impossible"); } + throw new AssertionError("impossible"); } /** The biggest half power of two that fits into an unsigned long */ @@ -168,7 +161,7 @@ public static int log10(long x, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(x == floorPow); - // fall through + // fall through case FLOOR: case DOWN: return logFloor; @@ -180,9 +173,8 @@ public static int log10(long x, RoundingMode mode) { case HALF_EVEN: // sqrt(10) is irrational, so log10(x)-logFloor is never exactly 0.5 return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); - default: - throw new AssertionError(); } + throw new AssertionError(); } @GwtIncompatible // TODO @@ -275,11 +267,11 @@ public static long pow(long b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: return (k < Long.SIZE) ? 1L << k : 0; - case (-2): + case -2: if (k < Long.SIZE) { return ((k & 1) == 0) ? 1L << k : -(1L << k); } else { @@ -310,7 +302,6 @@ public static long pow(long b, int k) { * sqrt(x)} is not an integer */ @GwtIncompatible // TODO - @SuppressWarnings("fallthrough") public static long sqrt(long x, RoundingMode mode) { checkNonNegative("x", x); if (fitsInInt(x)) { @@ -331,7 +322,7 @@ public static long sqrt(long x, RoundingMode mode) { * since (long) Math.sqrt(k * k) == k, as checked exhaustively in * {@link LongMathTest#testSqrtOfPerfectSquareAsDoubleIsPerfect} */ - long guess = (long) Math.sqrt(x); + long guess = (long) Math.sqrt((double) x); // Note: guess is always <= FLOOR_SQRT_MAX_LONG. long guessSquared = guess * guess; // Note (2013-2-26): benchmarks indicate that, inscrutably enough, using if statements is @@ -369,9 +360,8 @@ public static long sqrt(long x, RoundingMode mode) { * signed long, so lessThanBranchFree is safe for use. */ return sqrtFloor + lessThanBranchFree(halfSquare, x); - default: - throw new AssertionError(); } + throw new AssertionError(); } /** @@ -404,7 +394,7 @@ public static long divide(long p, long q, RoundingMode mode) { switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(rem == 0); - // fall through + // fall through case DOWN: increment = false; break; @@ -425,7 +415,7 @@ public static long divide(long p, long q, RoundingMode mode) { // subtracting two nonnegative longs can't overflow // cmpRemToHalfDivisor has the same sign as compare(abs(rem), abs(q) / 2). if (cmpRemToHalfDivisor == 0) { // exactly on the half mark - increment = (mode == HALF_UP | (mode == HALF_EVEN & (div & 1) != 0)); + increment = (mode == HALF_UP || (mode == HALF_EVEN && (div & 1) != 0)); } else { increment = cmpRemToHalfDivisor > 0; // closer to the UP value } @@ -543,7 +533,8 @@ public static long gcd(long a, long b) { * * @throws ArithmeticException if {@code a + b} overflows in signed {@code long} arithmetic */ - @GwtIncompatible // TODO + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long checkedAdd(long a, long b) { long result = a + b; checkNoOverflow((a ^ b) < 0 | (a ^ result) >= 0, "checkedAdd", a, b); @@ -555,7 +546,8 @@ public static long checkedAdd(long a, long b) { * * @throws ArithmeticException if {@code a - b} overflows in signed {@code long} arithmetic */ - @GwtIncompatible // TODO + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long checkedSubtract(long a, long b) { long result = a - b; checkNoOverflow((a ^ b) >= 0 | (a ^ result) >= 0, "checkedSubtract", a, b); @@ -567,6 +559,8 @@ public static long checkedSubtract(long a, long b) { * * @throws ArithmeticException if {@code a * b} overflows in signed {@code long} arithmetic */ + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long checkedMultiply(long a, long b) { // Hacker's Delight, Section 2-12 int leadingZeros = @@ -601,6 +595,8 @@ public static long checkedMultiply(long a, long b) { * long} arithmetic */ @GwtIncompatible // TODO + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long checkedPow(long b, int k) { checkNonNegative("exponent", k); if (b >= -2 & b <= 2) { @@ -609,12 +605,12 @@ public static long checkedPow(long b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: checkNoOverflow(k < Long.SIZE - 1, "checkedPow", b, k); return 1L << k; - case (-2): + case -2: checkNoOverflow(k < Long.SIZE, "checkedPow", b, k); return ((k & 1) == 0) ? (1L << k) : (-1L << k); default: @@ -648,7 +644,8 @@ public static long checkedPow(long b, int k) { * * @since 20.0 */ - @Beta + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long saturatedAdd(long a, long b) { long naiveSum = a + b; if ((a ^ b) < 0 | (a ^ naiveSum) >= 0) { @@ -666,7 +663,8 @@ public static long saturatedAdd(long a, long b) { * * @since 20.0 */ - @Beta + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long saturatedSubtract(long a, long b) { long naiveDifference = a - b; if ((a ^ b) >= 0 | (a ^ naiveDifference) >= 0) { @@ -684,7 +682,8 @@ public static long saturatedSubtract(long a, long b) { * * @since 20.0 */ - @Beta + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long saturatedMultiply(long a, long b) { // see checkedMultiply for explanation int leadingZeros = @@ -714,7 +713,8 @@ public static long saturatedMultiply(long a, long b) { * * @since 20.0 */ - @Beta + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + @SuppressWarnings("ShortCircuitBoolean") public static long saturatedPow(long b, int k) { checkNonNegative("exponent", k); if (b >= -2 & b <= 2) { @@ -723,14 +723,14 @@ public static long saturatedPow(long b, int k) { return (k == 0) ? 1 : 0; case 1: return 1; - case (-1): + case -1: return ((k & 1) == 0) ? 1 : -1; case 2: if (k >= Long.SIZE - 1) { return Long.MAX_VALUE; } return 1L << k; - case (-2): + case -2: if (k >= Long.SIZE) { return Long.MAX_VALUE + (k & 1); } @@ -741,7 +741,7 @@ public static long saturatedPow(long b, int k) { } long accum = 1; // if b is negative and k is odd then the limit is MIN otherwise the limit is MAX - long limit = Long.MAX_VALUE + ((b >>> Long.SIZE - 1) & (k & 1)); + long limit = Long.MAX_VALUE + ((b >>> (Long.SIZE - 1)) & (k & 1)); while (true) { switch (k) { case 0: @@ -958,6 +958,7 @@ static long multiplyFraction(long x, long numerator, long denominator) { 61, 61 }; + // These values were generated by using checkedMultiply to see when the simple multiply/divide // algorithm would lead to an overflow. @@ -979,7 +980,7 @@ public static long mean(long x, long y) { } /* - * This bitmask is used as an optimization for cheaply testing for divisiblity by 2, 3, or 5. + * This bitmask is used as an optimization for cheaply testing for divisibility by 2, 3, or 5. * Each bit is set to 1 for all remainders that indicate divisibility by 2, 3, or 5, so * 1, 7, 11, 13, 17, 19, 23, 29 are set to 0. 30 and up don't matter because they won't be hit. */ @@ -1000,7 +1001,6 @@ public static long mean(long x, long y) { * @since 20.0 */ @GwtIncompatible // TODO - @Beta public static boolean isPrime(long n) { if (n < 2) { checkNonNegative("n", n); @@ -1116,7 +1116,7 @@ private long plusMod(long a, long b, long m) { private long times2ToThe32Mod(long a, long m) { int remainingPowersOf2 = 32; do { - int shift = Math.min(remainingPowersOf2, Long.numberOfLeadingZeros(a)); + int shift = min(remainingPowersOf2, Long.numberOfLeadingZeros(a)); // shift is either the number of powers of 2 left to multiply a by, or the biggest shift // possible while keeping a in an unsigned long. a = UnsignedLongs.remainder(a << shift, m); @@ -1242,7 +1242,6 @@ private boolean testWitness(long base, long n) { * is not precisely representable as a {@code double} * @since 30.0 */ - @SuppressWarnings("deprecation") @GwtIncompatible public static double roundToDouble(long x, RoundingMode mode) { // Logic adapted from ToDoubleRounder. @@ -1253,7 +1252,7 @@ public static double roundToDouble(long x, RoundingMode mode) { if (roundArbitrarilyAsLong == Long.MAX_VALUE) { /* * For most values, the conversion from roundArbitrarily to roundArbitrarilyAsLong is - * lossless. In that case we can compare x to roundArbitrarily using Longs.compare(x, + * lossless. In that case we can compare x to roundArbitrarily using Long.compare(x, * roundArbitrarilyAsLong). The exception is for values where the conversion to double rounds * up to give roundArbitrarily equal to 2^63, so the conversion back to long overflows and * roundArbitrarilyAsLong is Long.MAX_VALUE. (This is the only way this condition can occur as @@ -1263,7 +1262,7 @@ public static double roundToDouble(long x, RoundingMode mode) { */ cmpXToRoundArbitrarily = -1; } else { - cmpXToRoundArbitrarily = Longs.compare(x, roundArbitrarilyAsLong); + cmpXToRoundArbitrarily = Long.compare(x, roundArbitrarilyAsLong); } switch (mode) { @@ -1322,7 +1321,7 @@ public static double roundToDouble(long x, RoundingMode mode) { deltaToCeiling++; } - int diff = Longs.compare(deltaToFloor, deltaToCeiling); + int diff = Long.compare(deltaToFloor, deltaToCeiling); if (diff < 0) { // closer to floor return roundFloorAsDouble; } else if (diff > 0) { // closer to ceiling diff --git a/android/guava/src/com/google/common/math/MathPreconditions.java b/android/guava/src/com/google/common/math/MathPreconditions.java index 37d608a82329..33e142d0b7e3 100644 --- a/android/guava/src/com/google/common/math/MathPreconditions.java +++ b/android/guava/src/com/google/common/math/MathPreconditions.java @@ -25,9 +25,8 @@ * @author Louis Wasserman */ @GwtCompatible -@CanIgnoreReturnValue -@ElementTypesAreNonnullByDefault final class MathPreconditions { + @CanIgnoreReturnValue static int checkPositive(String role, int x) { if (x <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); @@ -35,6 +34,7 @@ static int checkPositive(String role, int x) { return x; } + @CanIgnoreReturnValue static long checkPositive(String role, long x) { if (x <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); @@ -42,6 +42,7 @@ static long checkPositive(String role, long x) { return x; } + @CanIgnoreReturnValue static BigInteger checkPositive(String role, BigInteger x) { if (x.signum() <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); @@ -49,6 +50,7 @@ static BigInteger checkPositive(String role, BigInteger x) { return x; } + @CanIgnoreReturnValue static int checkNonNegative(String role, int x) { if (x < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); @@ -56,6 +58,7 @@ static int checkNonNegative(String role, int x) { return x; } + @CanIgnoreReturnValue static long checkNonNegative(String role, long x) { if (x < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); @@ -63,6 +66,7 @@ static long checkNonNegative(String role, long x) { return x; } + @CanIgnoreReturnValue static BigInteger checkNonNegative(String role, BigInteger x) { if (x.signum() < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); @@ -70,6 +74,7 @@ static BigInteger checkNonNegative(String role, BigInteger x) { return x; } + @CanIgnoreReturnValue static double checkNonNegative(String role, double x) { if (!(x >= 0)) { // not x < 0, to work with NaN. throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); diff --git a/android/guava/src/com/google/common/math/PairedStats.java b/android/guava/src/com/google/common/math/PairedStats.java index 31ab9b71966f..5985f8498651 100644 --- a/android/guava/src/com/google/common/math/PairedStats.java +++ b/android/guava/src/com/google/common/math/PairedStats.java @@ -21,14 +21,14 @@ import static java.lang.Double.doubleToLongBits; import static java.lang.Double.isNaN; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable value object capturing some basic statistics about a collection of paired double @@ -37,9 +37,8 @@ * @author Pete Gillin * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class PairedStats implements Serializable { private final Stats xStats; @@ -214,7 +213,7 @@ public LinearTransformation leastSquaresFit() { * guarantees {@code strictfp}-like semantics.) */ @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } diff --git a/android/guava/src/com/google/common/math/PairedStatsAccumulator.java b/android/guava/src/com/google/common/math/PairedStatsAccumulator.java index 072ef13b3b17..e8a772e9539b 100644 --- a/android/guava/src/com/google/common/math/PairedStatsAccumulator.java +++ b/android/guava/src/com/google/common/math/PairedStatsAccumulator.java @@ -19,8 +19,8 @@ import static java.lang.Double.NaN; import static java.lang.Double.isNaN; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Doubles; /** @@ -30,10 +30,11 @@ * @author Pete Gillin * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class PairedStatsAccumulator { + /** Creates a new accumulator. */ + public PairedStatsAccumulator() {} // These fields must satisfy the requirements of PairedStats' constructor as well as those of the // stat methods of this class. diff --git a/android/guava/src/com/google/common/math/ParametricNullness.java b/android/guava/src/com/google/common/math/ParametricNullness.java index 8e57826e4335..34901185ab04 100644 --- a/android/guava/src/com/google/common/math/ParametricNullness.java +++ b/android/guava/src/com/google/common/math/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/math/Quantiles.java b/android/guava/src/com/google/common/math/Quantiles.java index 6ddea9bcfdc7..37ba5a93f68b 100644 --- a/android/guava/src/com/google/common/math/Quantiles.java +++ b/android/guava/src/com/google/common/math/Quantiles.java @@ -21,8 +21,8 @@ import static java.util.Arrays.sort; import static java.util.Collections.unmodifiableMap; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Doubles; import com.google.common.primitives.Ints; import java.math.RoundingMode; @@ -126,10 +126,17 @@ * @author Pete Gillin * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Quantiles { + /** + * Constructor for a type that is not meant to be instantiated. + * + * @deprecated Use the static factory methods of the class. There is no reason to create an + * instance of {@link Quantiles}. + */ + @Deprecated + public Quantiles() {} /** Specifies the computation of a median (i.e. the 1st 2-quantile). */ public static ScaleAndIndex median() { diff --git a/android/guava/src/com/google/common/math/Stats.java b/android/guava/src/com/google/common/math/Stats.java index c0837496dec3..f902ba777048 100644 --- a/android/guava/src/com/google/common/math/Stats.java +++ b/android/guava/src/com/google/common/math/Stats.java @@ -24,15 +24,19 @@ import static java.lang.Double.doubleToLongBits; import static java.lang.Double.isNaN; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Iterator; -import javax.annotation.CheckForNull; +import java.util.stream.Collector; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import org.jspecify.annotations.Nullable; /** * A bundle of statistical summary values -- sum, count, mean/average, min and max, and several @@ -51,16 +55,15 @@ *

    Static convenience methods called {@code meanOf} are also provided for users who wish to * calculate only the mean. * - *

    Java 8 users: If you are not using any of the variance statistics, you may wish to use + *

    Java 8+ users: If you are not using any of the variance statistics, you may wish to use * built-in JDK libraries instead of this class. * * @author Pete Gillin * @author Kevin Bourrillion * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Stats implements Serializable { private final long count; @@ -122,9 +125,9 @@ public static Stats of(Iterator values) { * @param values a series of values */ public static Stats of(double... values) { - StatsAccumulator acummulator = new StatsAccumulator(); - acummulator.addAll(values); - return acummulator.snapshot(); + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); } /** @@ -133,9 +136,9 @@ public static Stats of(double... values) { * @param values a series of values */ public static Stats of(int... values) { - StatsAccumulator acummulator = new StatsAccumulator(); - acummulator.addAll(values); - return acummulator.snapshot(); + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); } /** @@ -145,9 +148,89 @@ public static Stats of(int... values) { * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) */ public static Stats of(long... values) { - StatsAccumulator acummulator = new StatsAccumulator(); - acummulator.addAll(values); - return acummulator.snapshot(); + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

    If you have a {@code Stream} rather than a {@code DoubleStream}, you should collect + * the values using {@link #toStats()} instead. + * + * @param values a series of values + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Stats of(DoubleStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

    If you have a {@code Stream} rather than an {@code IntStream}, you should collect + * the values using {@link #toStats()} instead. + * + * @param values a series of values + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Stats of(IntStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

    If you have a {@code Stream} rather than a {@code LongStream}, you should collect the + * values using {@link #toStats()} instead. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Stats of(LongStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns a {@link Collector} which accumulates statistics from a {@link java.util.stream.Stream} + * of any type of boxed {@link Number} into a {@link Stats}. Use by calling {@code + * boxedNumericStream.collect(toStats())}. The numbers will be converted to {@code double} values + * (which may cause loss of precision). + * + *

    If you have any of the primitive streams {@code DoubleStream}, {@code IntStream}, or {@code + * LongStream}, you should use the factory method {@link #of} instead. + * + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static Collector toStats() { + return Collector.of( + StatsAccumulator::new, + (a, x) -> a.add(x.doubleValue()), + (l, r) -> { + l.addAll(r); + return l; + }, + StatsAccumulator::snapshot, + Collector.Characteristics.UNORDERED); } /** Returns the number of values. */ @@ -341,7 +424,7 @@ public double max() { * {@code strictfp}-like semantics.) */ @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } diff --git a/android/guava/src/com/google/common/math/StatsAccumulator.java b/android/guava/src/com/google/common/math/StatsAccumulator.java index f7d7a629032d..1a2b528225be 100644 --- a/android/guava/src/com/google/common/math/StatsAccumulator.java +++ b/android/guava/src/com/google/common/math/StatsAccumulator.java @@ -20,9 +20,12 @@ import static java.lang.Double.NaN; import static java.lang.Double.isNaN; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.Iterator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; /** * A mutable object which accumulates double values and tracks some basic statistics over all the @@ -32,10 +35,11 @@ * @author Kevin Bourrillion * @since 20.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class StatsAccumulator { + /** Creates a new accumulator. */ + public StatsAccumulator() {} // These fields must satisfy the requirements of Stats' constructor as well as those of the stat // methods of this class. @@ -129,6 +133,43 @@ public void addAll(long... values) { } } + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public void addAll(DoubleStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public void addAll(IntStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @since 33.4.0 (but since 28.2 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public void addAll(LongStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + /** * Adds the given statistics to the dataset, as if the individual values used to compute the * statistics had been added directly. diff --git a/android/guava/src/com/google/common/math/ToDoubleRounder.java b/android/guava/src/com/google/common/math/ToDoubleRounder.java index 2e7e7fae0944..7525e3f990f1 100644 --- a/android/guava/src/com/google/common/math/ToDoubleRounder.java +++ b/android/guava/src/com/google/common/math/ToDoubleRounder.java @@ -25,7 +25,6 @@ * a {@link RoundingMode}. */ @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class ToDoubleRounder> { /** * Returns x rounded to either the greatest double less than or equal to the precise value of x, diff --git a/android/guava/src/com/google/common/math/package-info.java b/android/guava/src/com/google/common/math/package-info.java index 0408246e7452..663d3bced554 100644 --- a/android/guava/src/com/google/common/math/package-info.java +++ b/android/guava/src/com/google/common/math/package-info.java @@ -13,17 +13,18 @@ */ /** - * Arithmetic functions operating on primitive values and {@link java.math.BigInteger} instances. + * Arithmetic functions operating on primitive values and on {@link java.math.BigInteger} and {@link + * java.math.BigDecimal} instances. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. * *

    See the Guava User Guide article on math utilities. */ -@ParametersAreNonnullByDefault @CheckReturnValue +@NullMarked package com.google.common.math; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/net/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/net/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index d8cfd7377d0e..000000000000 --- a/android/guava/src/com/google/common/net/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.net; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/net/HostAndPort.java b/android/guava/src/com/google/common/net/HostAndPort.java index 19e6b6729dd2..1b49be7220af 100644 --- a/android/guava/src/com/google/common/net/HostAndPort.java +++ b/android/guava/src/com/google/common/net/HostAndPort.java @@ -17,15 +17,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Strings.isNullOrEmpty; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.CharMatcher; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Objects; -import com.google.common.base.Strings; +import com.google.common.primitives.Ints; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; import java.io.Serializable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable representation of a host and port. @@ -60,10 +62,8 @@ * @author Paul Marks * @since 10.0 */ -@Beta @Immutable @GwtCompatible -@ElementTypesAreNonnullByDefault public final class HostAndPort implements Serializable { /** Magic value indicating the absence of a port number. */ private static final int NO_PORT = -1; @@ -164,6 +164,7 @@ public static HostAndPort fromHost(String host) { * @return if parsing was successful, a populated HostAndPort object. * @throws IllegalArgumentException if nothing meaningful could be parsed. */ + @CanIgnoreReturnValue // TODO(b/219820829): consider removing public static HostAndPort fromString(String hostPortString) { checkNotNull(hostPortString); String host; @@ -187,19 +188,12 @@ public static HostAndPort fromString(String hostPortString) { } } - int port = NO_PORT; - if (!Strings.isNullOrEmpty(portString)) { - // Try to parse the whole port string as a number. - // JDK7 accepts leading plus signs. We don't want to. - checkArgument( - !portString.startsWith("+") && CharMatcher.ascii().matchesAllOf(portString), - "Unparseable port number: %s", - hostPortString); - try { - port = Integer.parseInt(portString); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Unparseable port number: " + hostPortString); - } + Integer port; + if (isNullOrEmpty(portString)) { + port = NO_PORT; + } else { + port = Ints.tryParse(portString); + checkArgument(port != null, "Unparseable port number: %s", hostPortString); checkArgument(isValidPort(port), "Port number out of range: %s", hostPortString); } @@ -209,7 +203,7 @@ public static HostAndPort fromString(String hostPortString) { /** * Parses a bracketed host-port string, throwing IllegalArgumentException if parsing fails. * - * @param hostPortString the full bracketed host-port specification. Post might not be specified. + * @param hostPortString the full bracketed host-port specification. Port might not be specified. * @return an array with 2 strings: host and port, in that order. * @throws IllegalArgumentException if parsing the bracketed host-port string fails. */ @@ -274,13 +268,14 @@ public HostAndPort withDefaultPort(int defaultPort) { * @return {@code this}, to enable chaining of calls. * @throws IllegalArgumentException if bracketless IPv6 is detected. */ + @CanIgnoreReturnValue public HostAndPort requireBracketsForIPv6() { checkArgument(!hasBracketlessColons, "Possible bracketless IPv6 literal: %s", host); return this; } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } @@ -317,5 +312,5 @@ private static boolean isValidPort(int port) { return port >= 0 && port <= 65535; } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/net/HostSpecifier.java b/android/guava/src/com/google/common/net/HostSpecifier.java index 9077f99d7f99..51d80fbcc218 100644 --- a/android/guava/src/com/google/common/net/HostSpecifier.java +++ b/android/guava/src/com/google/common/net/HostSpecifier.java @@ -14,12 +14,13 @@ package com.google.common.net; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.net.InetAddress; import java.text.ParseException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A syntactically valid host specifier, suitable for use in a URI. This may be either a numeric IP @@ -41,9 +42,8 @@ * @author Craig Berry * @since 5.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class HostSpecifier { private final String canonicalForm; @@ -110,6 +110,7 @@ public static HostSpecifier fromValid(String specifier) { * * @throws ParseException if the specifier is not valid. */ + @CanIgnoreReturnValue // TODO(b/219820829): consider removing public static HostSpecifier from(String specifier) throws ParseException { try { return fromValid(specifier); @@ -130,7 +131,7 @@ public static HostSpecifier from(String specifier) throws ParseException { */ public static boolean isValid(String specifier) { try { - fromValid(specifier); + HostSpecifier unused = fromValid(specifier); return true; } catch (IllegalArgumentException e) { return false; @@ -138,7 +139,7 @@ public static boolean isValid(String specifier) { } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } diff --git a/android/guava/src/com/google/common/net/HttpHeaders.java b/android/guava/src/com/google/common/net/HttpHeaders.java index f318da151486..4d2c0d0f5df4 100644 --- a/android/guava/src/com/google/common/net/HttpHeaders.java +++ b/android/guava/src/com/google/common/net/HttpHeaders.java @@ -14,7 +14,6 @@ package com.google.common.net; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -32,7 +31,6 @@ * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class HttpHeaders { private HttpHeaders() {} @@ -40,16 +38,22 @@ private HttpHeaders() {} /** The HTTP {@code Cache-Control} header field name. */ public static final String CACHE_CONTROL = "Cache-Control"; + /** The HTTP {@code Content-Length} header field name. */ public static final String CONTENT_LENGTH = "Content-Length"; + /** The HTTP {@code Content-Type} header field name. */ public static final String CONTENT_TYPE = "Content-Type"; + /** The HTTP {@code Date} header field name. */ public static final String DATE = "Date"; + /** The HTTP {@code Pragma} header field name. */ public static final String PRAGMA = "Pragma"; + /** The HTTP {@code Via} header field name. */ public static final String VIA = "Via"; + /** The HTTP {@code Warning} header field name. */ public static final String WARNING = "Warning"; @@ -57,22 +61,31 @@ private HttpHeaders() {} /** The HTTP {@code Accept} header field name. */ public static final String ACCEPT = "Accept"; + /** The HTTP {@code Accept-Charset} header field name. */ public static final String ACCEPT_CHARSET = "Accept-Charset"; + /** The HTTP {@code Accept-Encoding} header field name. */ public static final String ACCEPT_ENCODING = "Accept-Encoding"; + /** The HTTP {@code Accept-Language} header field name. */ public static final String ACCEPT_LANGUAGE = "Accept-Language"; + /** The HTTP {@code Access-Control-Request-Headers} header field name. */ public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + /** The HTTP {@code Access-Control-Request-Method} header field name. */ public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + /** The HTTP {@code Authorization} header field name. */ public static final String AUTHORIZATION = "Authorization"; + /** The HTTP {@code Connection} header field name. */ public static final String CONNECTION = "Connection"; + /** The HTTP {@code Cookie} header field name. */ public static final String COOKIE = "Cookie"; + /** * The HTTP {@code * Cross-Origin-Resource-Policy} header field name. @@ -80,55 +93,71 @@ private HttpHeaders() {} * @since 28.0 */ public static final String CROSS_ORIGIN_RESOURCE_POLICY = "Cross-Origin-Resource-Policy"; + /** - * The HTTP {@code Early-Data} header field - * name. + * The HTTP {@code Early-Data} header + * field name. * * @since 27.0 */ public static final String EARLY_DATA = "Early-Data"; + /** The HTTP {@code Expect} header field name. */ public static final String EXPECT = "Expect"; + /** The HTTP {@code From} header field name. */ public static final String FROM = "From"; + /** - * The HTTP {@code Forwarded} header field name. + * The HTTP {@code Forwarded} header + * field name. * * @since 20.0 */ public static final String FORWARDED = "Forwarded"; + /** * The HTTP {@code Follow-Only-When-Prerender-Shown} header field name. * * @since 17.0 */ - @Beta public static final String FOLLOW_ONLY_WHEN_PRERENDER_SHOWN = "Follow-Only-When-Prerender-Shown"; + /** The HTTP {@code Host} header field name. */ public static final String HOST = "Host"; + /** - * The HTTP {@code HTTP2-Settings} - * header field name. + * The HTTP {@code + * HTTP2-Settings} header field name. * * @since 24.0 */ public static final String HTTP2_SETTINGS = "HTTP2-Settings"; + /** The HTTP {@code If-Match} header field name. */ public static final String IF_MATCH = "If-Match"; + /** The HTTP {@code If-Modified-Since} header field name. */ public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + /** The HTTP {@code If-None-Match} header field name. */ public static final String IF_NONE_MATCH = "If-None-Match"; + /** The HTTP {@code If-Range} header field name. */ public static final String IF_RANGE = "If-Range"; + /** The HTTP {@code If-Unmodified-Since} header field name. */ public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + /** The HTTP {@code Last-Event-ID} header field name. */ public static final String LAST_EVENT_ID = "Last-Event-ID"; + /** The HTTP {@code Max-Forwards} header field name. */ public static final String MAX_FORWARDS = "Max-Forwards"; + /** The HTTP {@code Origin} header field name. */ public static final String ORIGIN = "Origin"; + /** * The HTTP {@code Origin-Isolation} header * field name. @@ -136,12 +165,16 @@ private HttpHeaders() {} * @since 30.1 */ public static final String ORIGIN_ISOLATION = "Origin-Isolation"; + /** The HTTP {@code Proxy-Authorization} header field name. */ public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + /** The HTTP {@code Range} header field name. */ public static final String RANGE = "Range"; + /** The HTTP {@code Referer} header field name. */ public static final String REFERER = "Referer"; + /** * The HTTP {@code Referrer-Policy} header * field name. @@ -176,10 +209,13 @@ private ReferrerPolicyValues() {} * @since 20.0 */ public static final String SERVICE_WORKER = "Service-Worker"; + /** The HTTP {@code TE} header field name. */ public static final String TE = "TE"; + /** The HTTP {@code Upgrade} header field name. */ public static final String UPGRADE = "Upgrade"; + /** * The HTTP {@code * Upgrade-Insecure-Requests} header field name. @@ -195,34 +231,58 @@ private ReferrerPolicyValues() {} /** The HTTP {@code Accept-Ranges} header field name. */ public static final String ACCEPT_RANGES = "Accept-Ranges"; + /** The HTTP {@code Access-Control-Allow-Headers} header field name. */ public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + /** The HTTP {@code Access-Control-Allow-Methods} header field name. */ public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + /** The HTTP {@code Access-Control-Allow-Origin} header field name. */ public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + + /** + * The HTTP {@code + * Access-Control-Allow-Private-Network} header field name. + * + * @since 31.1 + */ + public static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = + "Access-Control-Allow-Private-Network"; + /** The HTTP {@code Access-Control-Allow-Credentials} header field name. */ public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + /** The HTTP {@code Access-Control-Expose-Headers} header field name. */ public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; + /** The HTTP {@code Access-Control-Max-Age} header field name. */ public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + /** The HTTP {@code Age} header field name. */ public static final String AGE = "Age"; + /** The HTTP {@code Allow} header field name. */ public static final String ALLOW = "Allow"; + /** The HTTP {@code Content-Disposition} header field name. */ public static final String CONTENT_DISPOSITION = "Content-Disposition"; + /** The HTTP {@code Content-Encoding} header field name. */ public static final String CONTENT_ENCODING = "Content-Encoding"; + /** The HTTP {@code Content-Language} header field name. */ public static final String CONTENT_LANGUAGE = "Content-Language"; + /** The HTTP {@code Content-Location} header field name. */ public static final String CONTENT_LOCATION = "Content-Location"; + /** The HTTP {@code Content-MD5} header field name. */ public static final String CONTENT_MD5 = "Content-MD5"; + /** The HTTP {@code Content-Range} header field name. */ public static final String CONTENT_RANGE = "Content-Range"; + /** * The HTTP {@code * Content-Security-Policy} header field name. @@ -230,6 +290,7 @@ private ReferrerPolicyValues() {} * @since 15.0 */ public static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy"; + /** * The HTTP * {@code Content-Security-Policy-Report-Only} header field name. @@ -238,6 +299,7 @@ private ReferrerPolicyValues() {} */ public static final String CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"; + /** * The HTTP nonstandard {@code X-Content-Security-Policy} header field name. It was introduced in * CSP v.1 and used by the Firefox until @@ -247,6 +309,7 @@ private ReferrerPolicyValues() {} * @since 20.0 */ public static final String X_CONTENT_SECURITY_POLICY = "X-Content-Security-Policy"; + /** * The HTTP nonstandard {@code X-Content-Security-Policy-Report-Only} header field name. It was * introduced in CSP v.1 and used by the @@ -257,6 +320,7 @@ private ReferrerPolicyValues() {} */ public static final String X_CONTENT_SECURITY_POLICY_REPORT_ONLY = "X-Content-Security-Policy-Report-Only"; + /** * The HTTP nonstandard {@code X-WebKit-CSP} header field name. It was introduced in CSP v.1 and used by the Chrome until @@ -265,6 +329,7 @@ private ReferrerPolicyValues() {} * @since 20.0 */ public static final String X_WEBKIT_CSP = "X-WebKit-CSP"; + /** * The HTTP nonstandard {@code X-WebKit-CSP-Report-Only} header field name. It was introduced in * CSP v.1 and used by the Chrome until @@ -273,6 +338,7 @@ private ReferrerPolicyValues() {} * @since 20.0 */ public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only"; + /** * The HTTP {@code * Cross-Origin-Embedder-Policy} header field name. @@ -280,6 +346,7 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String CROSS_ORIGIN_EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy"; + /** * The HTTP {@code * Cross-Origin-Embedder-Policy-Report-Only} header field name. @@ -288,28 +355,44 @@ private ReferrerPolicyValues() {} */ public static final String CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY = "Cross-Origin-Embedder-Policy-Report-Only"; + /** * The HTTP Cross-Origin-Opener-Policy header field name. * * @since 28.2 */ public static final String CROSS_ORIGIN_OPENER_POLICY = "Cross-Origin-Opener-Policy"; + /** The HTTP {@code ETag} header field name. */ public static final String ETAG = "ETag"; + /** The HTTP {@code Expires} header field name. */ public static final String EXPIRES = "Expires"; + /** The HTTP {@code Last-Modified} header field name. */ public static final String LAST_MODIFIED = "Last-Modified"; + /** The HTTP {@code Link} header field name. */ public static final String LINK = "Link"; + /** The HTTP {@code Location} header field name. */ public static final String LOCATION = "Location"; + /** * The HTTP {@code Keep-Alive} header field name. * * @since 31.0 */ public static final String KEEP_ALIVE = "Keep-Alive"; + + /** + * The HTTP {@code + * No-Vary-Seearch} header field name. + * + * @since 32.0.0 + */ + public static final String NO_VARY_SEARCH = "No-Vary-Search"; + /** * The HTTP {@code Origin-Trial} * header field name. @@ -317,22 +400,29 @@ private ReferrerPolicyValues() {} * @since 27.1 */ public static final String ORIGIN_TRIAL = "Origin-Trial"; + /** The HTTP {@code P3P} header field name. Limited browser support. */ public static final String P3P = "P3P"; + /** The HTTP {@code Proxy-Authenticate} header field name. */ public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + /** The HTTP {@code Refresh} header field name. Non-standard header supported by most browsers. */ public static final String REFRESH = "Refresh"; + /** * The HTTP {@code Report-To} header field name. * * @since 27.1 */ public static final String REPORT_TO = "Report-To"; + /** The HTTP {@code Retry-After} header field name. */ public static final String RETRY_AFTER = "Retry-After"; + /** The HTTP {@code Server} header field name. */ public static final String SERVER = "Server"; + /** * The HTTP {@code Server-Timing} header field * name. @@ -340,6 +430,7 @@ private ReferrerPolicyValues() {} * @since 23.6 */ public static final String SERVER_TIMING = "Server-Timing"; + /** * The HTTP {@code * Service-Worker-Allowed} header field name. @@ -347,17 +438,30 @@ private ReferrerPolicyValues() {} * @since 20.0 */ public static final String SERVICE_WORKER_ALLOWED = "Service-Worker-Allowed"; + /** The HTTP {@code Set-Cookie} header field name. */ public static final String SET_COOKIE = "Set-Cookie"; + /** The HTTP {@code Set-Cookie2} header field name. */ public static final String SET_COOKIE2 = "Set-Cookie2"; /** - * The HTTP {@code SourceMap} header field name. + * The HTTP {@code + * SourceMap} header field name. * * @since 27.1 */ - @Beta public static final String SOURCE_MAP = "SourceMap"; + public static final String SOURCE_MAP = "SourceMap"; + + /** + * The HTTP {@code + * Supports-Loading-Mode} header field name. This can be used to specify, for example, fenced + * frames. + * + * @since 32.0.0 + */ + public static final String SUPPORTS_LOADING_MODE = "Supports-Loading-Mode"; /** * The HTTP {@code @@ -366,6 +470,7 @@ private ReferrerPolicyValues() {} * @since 15.0 */ public static final String STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"; + /** * The HTTP {@code * Timing-Allow-Origin} header field name. @@ -373,12 +478,16 @@ private ReferrerPolicyValues() {} * @since 15.0 */ public static final String TIMING_ALLOW_ORIGIN = "Timing-Allow-Origin"; + /** The HTTP {@code Trailer} header field name. */ public static final String TRAILER = "Trailer"; + /** The HTTP {@code Transfer-Encoding} header field name. */ public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + /** The HTTP {@code Vary} header field name. */ public static final String VARY = "Vary"; + /** The HTTP {@code WWW-Authenticate} header field name. */ public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; @@ -386,8 +495,10 @@ private ReferrerPolicyValues() {} /** The HTTP {@code DNT} header field name. */ public static final String DNT = "DNT"; + /** The HTTP {@code X-Content-Type-Options} header field name. */ public static final String X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; + /** * The HTTP {@code @@ -397,6 +508,7 @@ private ReferrerPolicyValues() {} * @since 31.0 */ public static final String X_DEVICE_IP = "X-Device-IP"; + /** * The HTTP {@code @@ -407,6 +519,7 @@ private ReferrerPolicyValues() {} * @since 31.0 */ public static final String X_DEVICE_REFERER = "X-Device-Referer"; + /** * The HTTP {@code @@ -417,6 +530,7 @@ private ReferrerPolicyValues() {} * @since 31.0 */ public static final String X_DEVICE_ACCEPT_LANGUAGE = "X-Device-Accept-Language"; + /** * The HTTP {@code @@ -427,54 +541,73 @@ private ReferrerPolicyValues() {} * @since 31.0 */ public static final String X_DEVICE_REQUESTED_WITH = "X-Device-Requested-With"; + /** The HTTP {@code X-Do-Not-Track} header field name. */ public static final String X_DO_NOT_TRACK = "X-Do-Not-Track"; + /** The HTTP {@code X-Forwarded-For} header field name (superseded by {@code Forwarded}). */ public static final String X_FORWARDED_FOR = "X-Forwarded-For"; + /** The HTTP {@code X-Forwarded-Proto} header field name. */ public static final String X_FORWARDED_PROTO = "X-Forwarded-Proto"; + /** - * The HTTP {@code X-Forwarded-Host} header field name. + * The HTTP {@code + * X-Forwarded-Host} header field name. * * @since 20.0 */ public static final String X_FORWARDED_HOST = "X-Forwarded-Host"; + /** - * The HTTP {@code X-Forwarded-Port} header field name. + * The HTTP {@code + * X-Forwarded-Port} header field name. * * @since 20.0 */ public static final String X_FORWARDED_PORT = "X-Forwarded-Port"; + /** The HTTP {@code X-Frame-Options} header field name. */ public static final String X_FRAME_OPTIONS = "X-Frame-Options"; + /** The HTTP {@code X-Powered-By} header field name. */ public static final String X_POWERED_BY = "X-Powered-By"; + /** * The HTTP {@code * Public-Key-Pins} header field name. * * @since 15.0 */ - @Beta public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; + public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; + /** * The HTTP {@code * Public-Key-Pins-Report-Only} header field name. * * @since 15.0 */ - @Beta public static final String PUBLIC_KEY_PINS_REPORT_ONLY = "Public-Key-Pins-Report-Only"; + public static final String PUBLIC_KEY_PINS_REPORT_ONLY = "Public-Key-Pins-Report-Only"; + /** * The HTTP {@code X-Request-ID} header field name. * * @since 30.1 */ public static final String X_REQUEST_ID = "X-Request-ID"; + /** The HTTP {@code X-Requested-With} header field name. */ public static final String X_REQUESTED_WITH = "X-Requested-With"; + /** The HTTP {@code X-User-IP} header field name. */ public static final String X_USER_IP = "X-User-IP"; + /** - * The HTTP {@code X-Download-Options} header field name. + * The HTTP {@code + * X-Download-Options} header field name. * *

    When the new X-Download-Options header is present with the value {@code noopen}, the user is * prevented from opening a file download directly; instead, they must first save the file @@ -482,9 +615,11 @@ private ReferrerPolicyValues() {} * * @since 24.1 */ - @Beta public static final String X_DOWNLOAD_OPTIONS = "X-Download-Options"; + public static final String X_DOWNLOAD_OPTIONS = "X-Download-Options"; + /** The HTTP {@code X-XSS-Protection} header field name. */ public static final String X_XSS_PROTECTION = "X-XSS-Protection"; + /** * The HTTP {@code @@ -492,6 +627,7 @@ private ReferrerPolicyValues() {} * By default, DNS prefetching is "on" for HTTP pages and "off" for HTTPS pages. */ public static final String X_DNS_PREFETCH_CONTROL = "X-DNS-Prefetch-Control"; + /** * The HTTP * {@code Ping-From} header field name. @@ -499,6 +635,7 @@ private ReferrerPolicyValues() {} * @since 19.0 */ public static final String PING_FROM = "Ping-From"; + /** * The HTTP * {@code Ping-To} header field name. @@ -515,6 +652,7 @@ private ReferrerPolicyValues() {} * @since 28.0 */ public static final String PURPOSE = "Purpose"; + /** * The HTTP {@code @@ -523,6 +661,7 @@ private ReferrerPolicyValues() {} * @since 28.0 */ public static final String X_PURPOSE = "X-Purpose"; + /** * The HTTP {@code @@ -598,6 +737,15 @@ private ReferrerPolicyValues() {} */ public static final String PERMISSIONS_POLICY = "Permissions-Policy"; + /** + * The HTTP {@code + * Permissions-Policy-Report-Only} header field name. + * + * @since 33.2.0 + */ + public static final String PERMISSIONS_POLICY_REPORT_ONLY = "Permissions-Policy-Report-Only"; + /** * The HTTP {@code @@ -617,6 +765,7 @@ private ReferrerPolicyValues() {} * @since 31.0 */ public static final String ACCEPT_CH = "Accept-CH"; + /** * The HTTP {@code @@ -633,6 +782,7 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA = "Sec-CH-UA"; + /** * The HTTP {@code * Sec-CH-UA-Arch} header field name. @@ -640,6 +790,7 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA_ARCH = "Sec-CH-UA-Arch"; + /** * The HTTP {@code * Sec-CH-UA-Model} header field name. @@ -647,6 +798,7 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA_MODEL = "Sec-CH-UA-Model"; + /** * The HTTP {@code * Sec-CH-UA-Platform} header field name. @@ -654,6 +806,7 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA_PLATFORM = "Sec-CH-UA-Platform"; + /** * The HTTP {@code * Sec-CH-UA-Platform-Version} header field name. @@ -661,13 +814,24 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA_PLATFORM_VERSION = "Sec-CH-UA-Platform-Version"; + /** * The HTTP {@code * Sec-CH-UA-Full-Version} header field name. * + * @deprecated Prefer {@link SEC_CH_UA_FULL_VERSION_LIST}. * @since 30.0 */ - public static final String SEC_CH_UA_FULL_VERSION = "Sec-CH-UA-Full-Version"; + @Deprecated public static final String SEC_CH_UA_FULL_VERSION = "Sec-CH-UA-Full-Version"; + + /** + * The HTTP {@code + * Sec-CH-UA-Full-Version} header field name. + * + * @since 31.1 + */ + public static final String SEC_CH_UA_FULL_VERSION_LIST = "Sec-CH-UA-Full-Version-List"; + /** * The HTTP {@code * Sec-CH-UA-Mobile} header field name. @@ -675,6 +839,15 @@ private ReferrerPolicyValues() {} * @since 30.0 */ public static final String SEC_CH_UA_MOBILE = "Sec-CH-UA-Mobile"; + + /** + * The HTTP {@code + * Sec-CH-UA-WoW64} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_UA_WOW64 = "Sec-CH-UA-WoW64"; + /** * The HTTP {@code * Sec-CH-UA-Bitness} header field name. @@ -683,6 +856,49 @@ private ReferrerPolicyValues() {} */ public static final String SEC_CH_UA_BITNESS = "Sec-CH-UA-Bitness"; + /** + * The HTTP {@code + * Sec-CH-UA-Form-Factor} header field name. + * + * @deprecated Prefer {@link SEC_CH_UA_FORM_FACTORS}. + * @since 32.0.0 + */ + @Deprecated public static final String SEC_CH_UA_FORM_FACTOR = "Sec-CH-UA-Form-Factor"; + + /** + * The HTTP {@code + * Sec-CH-UA-Form-Factors} header field name. + * + * @since 33.3.0 + */ + public static final String SEC_CH_UA_FORM_FACTORS = "Sec-CH-UA-Form-Factors"; + + /** + * The HTTP {@code + * Sec-CH-Viewport-Width} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_VIEWPORT_WIDTH = "Sec-CH-Viewport-Width"; + + /** + * The HTTP {@code + * Sec-CH-Viewport-Height} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_VIEWPORT_HEIGHT = "Sec-CH-Viewport-Height"; + + /** + * The HTTP {@code + * Sec-CH-DPR} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_DPR = "Sec-CH-DPR"; + /** * The HTTP {@code Sec-Fetch-Dest} * header field name. @@ -690,6 +906,7 @@ private ReferrerPolicyValues() {} * @since 27.1 */ public static final String SEC_FETCH_DEST = "Sec-Fetch-Dest"; + /** * The HTTP {@code Sec-Fetch-Mode} * header field name. @@ -697,6 +914,7 @@ private ReferrerPolicyValues() {} * @since 27.1 */ public static final String SEC_FETCH_MODE = "Sec-Fetch-Mode"; + /** * The HTTP {@code Sec-Fetch-Site} * header field name. @@ -704,6 +922,7 @@ private ReferrerPolicyValues() {} * @since 27.1 */ public static final String SEC_FETCH_SITE = "Sec-Fetch-Site"; + /** * The HTTP {@code Sec-Fetch-User} * header field name. @@ -711,6 +930,7 @@ private ReferrerPolicyValues() {} * @since 27.1 */ public static final String SEC_FETCH_USER = "Sec-Fetch-User"; + /** * The HTTP {@code Sec-Metadata} * header field name. @@ -718,66 +938,135 @@ private ReferrerPolicyValues() {} * @since 26.0 */ public static final String SEC_METADATA = "Sec-Metadata"; + /** - * The HTTP {@code + * The HTTP {@code * Sec-Token-Binding} header field name. * * @since 25.1 */ public static final String SEC_TOKEN_BINDING = "Sec-Token-Binding"; + /** - * The HTTP {@code + * The HTTP {@code * Sec-Provided-Token-Binding-ID} header field name. * * @since 25.1 */ public static final String SEC_PROVIDED_TOKEN_BINDING_ID = "Sec-Provided-Token-Binding-ID"; + /** - * The HTTP {@code + * The HTTP {@code * Sec-Referred-Token-Binding-ID} header field name. * * @since 25.1 */ public static final String SEC_REFERRED_TOKEN_BINDING_ID = "Sec-Referred-Token-Binding-ID"; + /** - * The HTTP {@code Sec-WebSocket-Accept} header - * field name. + * The HTTP {@code + * Sec-WebSocket-Accept} header field name. * * @since 28.0 */ public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; + /** - * The HTTP {@code Sec-WebSocket-Extensions} - * header field name. + * The HTTP {@code + * Sec-WebSocket-Extensions} header field name. * * @since 28.0 */ public static final String SEC_WEBSOCKET_EXTENSIONS = "Sec-WebSocket-Extensions"; + /** - * The HTTP {@code Sec-WebSocket-Key} header - * field name. + * The HTTP {@code Sec-WebSocket-Key} + * header field name. * * @since 28.0 */ public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; + /** - * The HTTP {@code Sec-WebSocket-Protocol} - * header field name. + * The HTTP {@code + * Sec-WebSocket-Protocol} header field name. * * @since 28.0 */ public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + /** - * The HTTP {@code Sec-WebSocket-Version} header - * field name. + * The HTTP {@code + * Sec-WebSocket-Version} header field name. * * @since 28.0 */ public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; + /** - * The HTTP {@code CDN-Loop} header field name. + * The HTTP {@code + * Sec-Browsing-Topics} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_BROWSING_TOPICS = "Sec-Browsing-Topics"; + + /** + * The HTTP {@code + * Observe-Browsing-Topics} header field name. + * + * @since 32.0.0 + */ + public static final String OBSERVE_BROWSING_TOPICS = "Observe-Browsing-Topics"; + + /** + * The HTTP {@code + * Sec-Ad-Auction-Fetch} header field name. + * + * @since 33.0.0 + */ + public static final String SEC_AD_AUCTION_FETCH = "Sec-Ad-Auction-Fetch"; + + /** + * The HTTP {@code + * Sec-GPC} header field name. + * + * @since 33.2.0 + */ + public static final String SEC_GPC = "Sec-GPC"; + + /** + * The HTTP {@code + * Ad-Auction-Signals} header field name. + * + * @since 33.0.0 + */ + public static final String AD_AUCTION_SIGNALS = "Ad-Auction-Signals"; + + /** + * The HTTP {@code + * Ad-Auction-Allowed} header field name. + * + * @since 33.2.0 + */ + public static final String AD_AUCTION_ALLOWED = "Ad-Auction-Allowed"; + + /** + * The HTTP {@code CDN-Loop} header + * field name. * * @since 28.0 */ public static final String CDN_LOOP = "CDN-Loop"; + + /** + * The HTTP {@code Alt-Svc} + * header field name. + * + * @since 33.4.0 + */ + public static final String ALT_SVC = "Alt-Svc"; } diff --git a/android/guava/src/com/google/common/net/InetAddresses.java b/android/guava/src/com/google/common/net/InetAddresses.java index 4e2aa699533f..c8cdda10c435 100644 --- a/android/guava/src/com/google/common/net/InetAddresses.java +++ b/android/guava/src/com/google/common/net/InetAddresses.java @@ -16,23 +16,28 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.max; +import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.CharMatcher; import com.google.common.base.MoreObjects; import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import com.google.common.primitives.Ints; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Locale; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@link InetAddress} instances. @@ -95,9 +100,8 @@ * @author Erik Kline * @since 5.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class InetAddresses { private static final int IPV4_PART_COUNT = 4; private static final int IPV6_PART_COUNT = 8; @@ -124,7 +128,7 @@ private static Inet4Address getInet4Address(byte[] bytes) { bytes.length); // Given a 4-byte array, this cast should always succeed. - return (Inet4Address) bytesToInetAddress(bytes); + return (Inet4Address) bytesToInetAddress(bytes, null); } /** @@ -132,27 +136,34 @@ private static Inet4Address getInet4Address(byte[] bytes) { * *

    This deliberately avoids all nameservice lookups (e.g. no DNS). * - *

    Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID). - * *

    This method accepts non-ASCII digits, for example {@code "192.168.0.1"} (those are fullwidth * characters). That is consistent with {@link InetAddress}, but not with various RFCs. If you * want to accept ASCII digits only, you can use something like {@code * CharMatcher.ascii().matchesAllOf(ipString)}. * + *

    The scope ID is validated against the interfaces on the machine, which requires permissions + * under Android. + * + *

    Android users on API >= 29: Prefer {@code InetAddresses.parseNumericAddress}. + * * @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code - * "192.168.0.1"} or {@code "2001:db8::1"} + * "192.168.0.1"} or {@code "2001:db8::1"} or with a scope ID, e.g. {@code "2001:db8::1%eth0"} * @return {@link InetAddress} representing the argument - * @throws IllegalArgumentException if the argument is not a valid IP string literal + * @throws IllegalArgumentException if the argument is not a valid IP string literal or if the + * address has a scope ID that fails validation against the interfaces on the machine (as + * required by Java's {@link InetAddress}) */ + @CanIgnoreReturnValue // TODO(b/219820829): consider removing public static InetAddress forString(String ipString) { - byte[] addr = ipStringToBytes(ipString); + Scope scope = new Scope(); + byte[] addr = ipStringToBytes(ipString, scope); // The argument was malformed, i.e. not an IP string literal. if (addr == null) { throw formatIllegalArgumentException("'%s' is not an IP string literal.", ipString); } - return bytesToInetAddress(addr); + return bytesToInetAddress(addr, scope.scope); } /** @@ -164,16 +175,23 @@ public static InetAddress forString(String ipString) { * want to accept ASCII digits only, you can use something like {@code * CharMatcher.ascii().matchesAllOf(ipString)}. * + *

    Note that if this method returns {@code true}, a call to {@link #forString(String)} can + * still throw if the address has a scope ID that fails validation against the interfaces on the + * machine. + * * @param ipString {@code String} to evaluated as an IP string literal * @return {@code true} if the argument is a valid IP string literal */ public static boolean isInetAddress(String ipString) { - return ipStringToBytes(ipString) != null; + return ipStringToBytes(ipString, null) != null; + } + + private static final class Scope { + private String scope; } /** Returns {@code null} if unable to parse into a {@code byte[]}. */ - @CheckForNull - private static byte[] ipStringToBytes(String ipStringParam) { + private static byte @Nullable [] ipStringToBytes(String ipStringParam, @Nullable Scope scope) { String ipString = ipStringParam; // Make a first pass to categorize the characters in this string. boolean hasColon = false; @@ -190,7 +208,7 @@ private static byte[] ipStringToBytes(String ipStringParam) { hasColon = true; } else if (c == '%') { percentIndex = i; - break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753 + break; } else if (Character.digit(c, 16) == -1) { return null; // Everything else must be a decimal or hex digit. } @@ -205,6 +223,9 @@ private static byte[] ipStringToBytes(String ipStringParam) { } } if (percentIndex != -1) { + if (scope != null) { + scope.scope = ipString.substring(percentIndex + 1); + } ipString = ipString.substring(0, percentIndex); } return textToNumericFormatV6(ipString); @@ -217,8 +238,7 @@ private static byte[] ipStringToBytes(String ipStringParam) { return null; } - @CheckForNull - private static byte[] textToNumericFormatV4(String ipString) { + private static byte @Nullable [] textToNumericFormatV4(String ipString) { if (IPV4_DELIMITER_MATCHER.countIn(ipString) + 1 != IPV4_PART_COUNT) { return null; // Wrong number of parts } @@ -243,8 +263,7 @@ private static byte[] textToNumericFormatV4(String ipString) { return bytes; } - @CheckForNull - private static byte[] textToNumericFormatV6(String ipString) { + private static byte @Nullable [] textToNumericFormatV6(String ipString) { // An address can have [2..8] colons. int delimiterCount = IPV6_DELIMITER_MATCHER.countIn(ipString); if (delimiterCount < 2 || delimiterCount > IPV6_PART_COUNT) { @@ -314,8 +333,7 @@ private static byte[] textToNumericFormatV6(String ipString) { return rawBytes.array(); } - @CheckForNull - private static String convertDottedQuadToHex(String ipString) { + private static @Nullable String convertDottedQuadToHex(String ipString) { int lastColon = ipString.lastIndexOf(':'); String initialPart = ipString.substring(0, lastColon + 1); String dottedQuad = ipString.substring(lastColon + 1); @@ -355,6 +373,24 @@ private static byte parseOctet(String ipString, int start, int end) { return (byte) octet; } + /** Returns a -1 if unable to parse */ + private static int tryParseDecimal(String string, int start, int end) { + int decimal = 0; + int max = Integer.MAX_VALUE / 10; // for int overflow detection + for (int i = start; i < end; i++) { + if (decimal > max) { + return -1; + } + decimal *= 10; + int digit = Character.digit(string.charAt(i), 10); + if (digit < 0) { + return -1; + } + decimal += digit; + } + return decimal; + } + // Parse a hextet out of the ipString from start (inclusive) to end (exclusive) private static short parseHextet(String ipString, int start, int end) { // Note: we already verified that this string contains only hex digits. @@ -380,9 +416,30 @@ private static short parseHextet(String ipString, int start, int end) { * @param addr the raw 4-byte or 16-byte IP address in big-endian order * @return an InetAddress object created from the raw IP address */ - private static InetAddress bytesToInetAddress(byte[] addr) { + private static InetAddress bytesToInetAddress(byte[] addr, @Nullable String scope) { try { - return InetAddress.getByAddress(addr); + InetAddress address = InetAddress.getByAddress(addr); + if (scope == null) { + return address; + } + checkArgument( + address instanceof Inet6Address, "Unexpected state, scope should only appear for ipv6"); + Inet6Address v6Address = (Inet6Address) address; + int interfaceIndex = tryParseDecimal(scope, 0, scope.length()); + if (interfaceIndex != -1) { + return Inet6Address.getByAddress( + v6Address.getHostAddress(), v6Address.getAddress(), interfaceIndex); + } + try { + NetworkInterface asInterface = NetworkInterface.getByName(scope); + if (asInterface == null) { + throw formatIllegalArgumentException("No such interface: '%s'", scope); + } + return Inet6Address.getByAddress( + v6Address.getHostAddress(), v6Address.getAddress(), asInterface); + } catch (SocketException | UnknownHostException e) { + throw new IllegalArgumentException("No such interface: " + scope, e); + } } catch (UnknownHostException e) { throw new AssertionError(e); } @@ -394,10 +451,13 @@ private static InetAddress bytesToInetAddress(byte[] addr) { *

    For IPv4 addresses, this is identical to {@link InetAddress#getHostAddress()}, but for IPv6 * addresses, the output follows RFC 5952 section * 4. The main difference is that this method uses "::" for zero compression, while Java's version - * uses the uncompressed form. + * uses the uncompressed form (except on Android, where the zero compression is also done). The + * other difference is that this method outputs any scope ID in the format that it was provided at + * creation time, while Android may always output it as an interface name, even if it was supplied + * as a numeric ID. * *

    This method uses hexadecimal for all IPv6 addresses, including IPv4-mapped IPv6 addresses - * such as "::c000:201". The output does not include a Scope ID. + * such as "::c000:201". * * @param ip {@link InetAddress} to be converted to an address string * @return {@code String} containing the text-formatted IP address @@ -407,16 +467,32 @@ public static String toAddrString(InetAddress ip) { checkNotNull(ip); if (ip instanceof Inet4Address) { // For IPv4, Java's formatting is good enough. - return ip.getHostAddress(); + // requireNonNull accommodates Android's @RecentlyNullable annotation on getHostAddress + return requireNonNull(ip.getHostAddress()); } - checkArgument(ip instanceof Inet6Address); byte[] bytes = ip.getAddress(); int[] hextets = new int[IPV6_PART_COUNT]; for (int i = 0; i < hextets.length; i++) { hextets[i] = Ints.fromBytes((byte) 0, (byte) 0, bytes[2 * i], bytes[2 * i + 1]); } compressLongestRunOfZeroes(hextets); - return hextetsToIPv6String(hextets); + + return hextetsToIPv6String(hextets) + scopeWithDelimiter((Inet6Address) ip); + } + + private static String scopeWithDelimiter(Inet6Address ip) { + // getHostAddress on android sometimes maps the scope ID to an invalid interface name; if the + // mapped interface isn't present, fallback to use the scope ID (which has no validation against + // present interfaces) + NetworkInterface scopedInterface = ip.getScopedInterface(); + if (scopedInterface != null) { + return "%" + scopedInterface.getName(); + } + int scope = ip.getScopeId(); + if (scope != 0) { + return "%" + scope; + } + return ""; } /** @@ -522,13 +598,15 @@ public static String toUriString(InetAddress ip) { * want to accept ASCII digits only, you can use something like {@code * CharMatcher.ascii().matchesAllOf(ipString)}. * - * @param hostAddr A RFC 3986 section 3.2.2 encoded IPv4 or IPv6 address + * @param hostAddr an RFC 3986 section 3.2.2 encoded IPv4 or IPv6 address * @return an InetAddress representing the address in {@code hostAddr} * @throws IllegalArgumentException if {@code hostAddr} is not a valid IPv4 address, or IPv6 - * address surrounded by square brackets + * address surrounded by square brackets, or if the address has a scope ID that fails + * validation against the interfaces on the machine (as required by Java's {@link + * InetAddress}) */ public static InetAddress forUriString(String hostAddr) { - InetAddress addr = forUriStringNoThrow(hostAddr); + InetAddress addr = forUriStringOrNull(hostAddr, /* parseScope= */ true); if (addr == null) { throw formatIllegalArgumentException("Not a valid URI IP literal: '%s'", hostAddr); } @@ -536,8 +614,7 @@ public static InetAddress forUriString(String hostAddr) { return addr; } - @CheckForNull - private static InetAddress forUriStringNoThrow(String hostAddr) { + private static @Nullable InetAddress forUriStringOrNull(String hostAddr, boolean parseScope) { checkNotNull(hostAddr); // Decide if this should be an IPv6 or IPv4 address. @@ -552,12 +629,13 @@ private static InetAddress forUriStringNoThrow(String hostAddr) { } // Parse the address, and make sure the length/version is correct. - byte[] addr = ipStringToBytes(ipString); + Scope scope = parseScope ? new Scope() : null; + byte[] addr = ipStringToBytes(ipString, scope); if (addr == null || addr.length != expectBytes) { return null; } - return bytesToInetAddress(addr); + return bytesToInetAddress(addr, (scope != null) ? scope.scope : null); } /** @@ -569,11 +647,15 @@ private static InetAddress forUriStringNoThrow(String hostAddr) { * want to accept ASCII digits only, you can use something like {@code * CharMatcher.ascii().matchesAllOf(ipString)}. * + *

    Note that if this method returns {@code true}, a call to {@link #forUriString(String)} can + * still throw if the address has a scope ID that fails validation against the interfaces on the + * machine. + * * @param ipString {@code String} to evaluated as an IP URI host string literal * @return {@code true} if the argument is a valid IP URI host */ public static boolean isUriInetAddress(String ipString) { - return forUriStringNoThrow(ipString) != null; + return forUriStringOrNull(ipString, /* parseScope= */ false) != null; } /** @@ -667,7 +749,6 @@ public static Inet4Address get6to4IPv4Address(Inet6Address ip) { * * @since 5.0 */ - @Beta public static final class TeredoInfo { private final Inet4Address server; private final Inet4Address client; @@ -685,7 +766,7 @@ public static final class TeredoInfo { */ // TODO: why is this public? public TeredoInfo( - @CheckForNull Inet4Address server, @CheckForNull Inet4Address client, int port, int flags) { + @Nullable Inet4Address server, @Nullable Inet4Address client, int port, int flags) { checkArgument( (port >= 0) && (port <= 0xffff), "port '%s' is out of range (0 <= port <= 0xffff)", port); checkArgument( @@ -873,7 +954,7 @@ public static Inet4Address getEmbeddedIPv4ClientAddress(Inet6Address ip) { * @since 10.0 */ public static boolean isMappedIPv4Address(String ipString) { - byte[] bytes = ipStringToBytes(ipString); + byte[] bytes = ipStringToBytes(ipString, null); if (bytes != null && bytes.length == 16) { for (int i = 0; i < 10; i++) { if (bytes[i] != 0) { @@ -1013,6 +1094,7 @@ public static Inet4Address fromInteger(int address) { public static Inet4Address fromIPv4BigInteger(BigInteger address) { return (Inet4Address) fromBigInteger(address, false); } + /** * Returns the {@code Inet6Address} corresponding to a given {@code BigInteger}. * @@ -1027,7 +1109,7 @@ public static Inet6Address fromIPv6BigInteger(BigInteger address) { /** * Converts a BigInteger to either an IPv4 or IPv6 address. If the IP is IPv4, it must be - * constrainted to 32 bits, otherwise it is constrained to 128 bits. + * constrained to 32 bits, otherwise it is constrained to 128 bits. * * @param address the address represented as a big integer * @param isIpv6 whether the created address should be IPv4 or IPv6 @@ -1043,7 +1125,7 @@ private static InetAddress fromBigInteger(BigInteger address, boolean isIpv6) { byte[] addressBytes = address.toByteArray(); byte[] targetCopyArray = new byte[numBytes]; - int srcPos = Math.max(0, addressBytes.length - numBytes); + int srcPos = max(0, addressBytes.length - numBytes); int copyLength = addressBytes.length - srcPos; int destPos = numBytes - copyLength; @@ -1105,7 +1187,7 @@ public static InetAddress decrement(InetAddress address) { checkArgument(i >= 0, "Decrementing %s would wrap.", address); addr[i]--; - return bytesToInetAddress(addr); + return bytesToInetAddress(addr, null); } /** @@ -1128,7 +1210,7 @@ public static InetAddress increment(InetAddress address) { checkArgument(i >= 0, "Incrementing %s would wrap.", address); addr[i]++; - return bytesToInetAddress(addr); + return bytesToInetAddress(addr, null); } /** diff --git a/android/guava/src/com/google/common/net/InternetDomainName.java b/android/guava/src/com/google/common/net/InternetDomainName.java index 3f158736acdd..c3b3d91f3c40 100644 --- a/android/guava/src/com/google/common/net/InternetDomainName.java +++ b/android/guava/src/com/google/common/net/InternetDomainName.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Ascii; import com.google.common.base.CharMatcher; @@ -26,11 +25,13 @@ import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.thirdparty.publicsuffix.PublicSuffixPatterns; import com.google.thirdparty.publicsuffix.PublicSuffixType; import java.util.List; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An immutable well-formed internet domain name, such as {@code com} or {@code foo.co.uk}. Only @@ -71,10 +72,8 @@ * @author Catherine Berry * @since 5.0 */ -@Beta @GwtCompatible(emulated = true) @Immutable -@ElementTypesAreNonnullByDefault public final class InternetDomainName { private static final CharMatcher DOTS_MATCHER = CharMatcher.anyOf(".\u3002\uFF0E\uFF61"); @@ -82,11 +81,17 @@ public final class InternetDomainName { private static final Joiner DOT_JOINER = Joiner.on('.'); /** - * Value of {@link #publicSuffixIndex} or {@link #registrySuffixIndex} which indicates that no + * Value of {@link #publicSuffixIndex()} or {@link #registrySuffixIndex()} which indicates that no * relevant suffix was found. */ private static final int NO_SUFFIX_FOUND = -1; + /** + * Value of {@link #publicSuffixIndexCache} or {@link #registrySuffixIndexCache} which indicates + * that they were not initialized yet. + */ + private static final int SUFFIX_NOT_INITIALIZED = -2; + /** * Maximum parts (labels) in a domain name. This value arises from the 255-octet limit described * in RFC 2181 part 11 with the fact that the @@ -114,20 +119,26 @@ public final class InternetDomainName { private final ImmutableList parts; /** - * The index in the {@link #parts()} list at which the public suffix begins. For example, for the - * domain name {@code myblog.blogspot.co.uk}, the value would be 1 (the index of the {@code - * blogspot} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no public - * suffix was found. + * Cached value of #publicSuffixIndex(). Do not use directly. + * + *

    Since this field isn't {@code volatile}, if an instance of this class is shared across + * threads before it is initialized, then each thread is likely to compute their own copy of the + * value. */ - private final int publicSuffixIndex; + @SuppressWarnings("Immutable") + @LazyInit + private int publicSuffixIndexCache = SUFFIX_NOT_INITIALIZED; /** - * The index in the {@link #parts()} list at which the registry suffix begins. For example, for - * the domain name {@code myblog.blogspot.co.uk}, the value would be 2 (the index of the {@code - * co} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no registry suffix - * was found. + * Cached value of #registrySuffixIndex(). Do not use directly. + * + *

    Since this field isn't {@code volatile}, if an instance of this class is shared across + * threads before it is initialized, then each thread is likely to compute their own copy of the + * value. */ - private final int registrySuffixIndex; + @SuppressWarnings("Immutable") + @LazyInit + private int registrySuffixIndexCache = SUFFIX_NOT_INITIALIZED; /** Constructor used to implement {@link #from(String)}, and from subclasses. */ InternetDomainName(String name) { @@ -148,9 +159,46 @@ public final class InternetDomainName { this.parts = ImmutableList.copyOf(DOT_SPLITTER.split(name)); checkArgument(parts.size() <= MAX_PARTS, "Domain has too many parts: '%s'", name); checkArgument(validateSyntax(parts), "Not a valid domain name: '%s'", name); + } - this.publicSuffixIndex = findSuffixOfType(Optional.absent()); - this.registrySuffixIndex = findSuffixOfType(Optional.of(PublicSuffixType.REGISTRY)); + /** + * Internal constructor that skips validations when creating an instance from parts of an + * already-validated InternetDomainName, as in {@link ancestor}. + */ + private InternetDomainName(String name, ImmutableList parts) { + checkArgument(!parts.isEmpty(), "Cannot create an InternetDomainName with zero parts."); + this.name = name; + this.parts = parts; + } + + /** + * The index in the {@link #parts()} list at which the public suffix begins. For example, for the + * domain name {@code myblog.blogspot.co.uk}, the value would be 1 (the index of the {@code + * blogspot} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no public + * suffix was found. + */ + private int publicSuffixIndex() { + int publicSuffixIndexLocal = publicSuffixIndexCache; + if (publicSuffixIndexLocal == SUFFIX_NOT_INITIALIZED) { + publicSuffixIndexCache = + publicSuffixIndexLocal = findSuffixOfType(Optional.absent()); + } + return publicSuffixIndexLocal; + } + + /** + * The index in the {@link #parts()} list at which the registry suffix begins. For example, for + * the domain name {@code myblog.blogspot.co.uk}, the value would be 2 (the index of the {@code + * co} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no registry suffix + * was found. + */ + private int registrySuffixIndex() { + int registrySuffixIndexLocal = registrySuffixIndexCache; + if (registrySuffixIndexLocal == SUFFIX_NOT_INITIALIZED) { + registrySuffixIndexCache = + registrySuffixIndexLocal = findSuffixOfType(Optional.of(PublicSuffixType.REGISTRY)); + } + return registrySuffixIndexLocal; } /** @@ -168,6 +216,12 @@ private int findSuffixOfType(Optional desiredType) { for (int i = 0; i < partsSize; i++) { String ancestorName = DOT_JOINER.join(parts.subList(i, partsSize)); + if (i > 0 + && matchesType( + desiredType, Optional.fromNullable(PublicSuffixPatterns.UNDER.get(ancestorName)))) { + return i - 1; + } + if (matchesType( desiredType, Optional.fromNullable(PublicSuffixPatterns.EXACT.get(ancestorName)))) { return i; @@ -179,10 +233,6 @@ private int findSuffixOfType(Optional desiredType) { if (PublicSuffixPatterns.EXCLUDED.containsKey(ancestorName)) { return i + 1; } - - if (matchesWildcardSuffixType(desiredType, ancestorName)) { - return i; - } } return NO_SUFFIX_FOUND; @@ -206,6 +256,7 @@ private int findSuffixOfType(Optional desiredType) { * {@link #isValid} * @since 10.0 (previously named {@code fromLenient}) */ + @CanIgnoreReturnValue // TODO(b/219820829): consider removing public static InternetDomainName from(String domain) { return new InternetDomainName(checkNotNull(domain)); } @@ -329,7 +380,7 @@ public ImmutableList parts() { * @since 6.0 */ public boolean isPublicSuffix() { - return publicSuffixIndex == 0; + return publicSuffixIndex() == 0; } /** @@ -345,7 +396,7 @@ public boolean isPublicSuffix() { * @since 6.0 */ public boolean hasPublicSuffix() { - return publicSuffixIndex != NO_SUFFIX_FOUND; + return publicSuffixIndex() != NO_SUFFIX_FOUND; } /** @@ -354,9 +405,8 @@ public boolean hasPublicSuffix() { * * @since 6.0 */ - @CheckForNull - public InternetDomainName publicSuffix() { - return hasPublicSuffix() ? ancestor(publicSuffixIndex) : null; + public @Nullable InternetDomainName publicSuffix() { + return hasPublicSuffix() ? ancestor(publicSuffixIndex()) : null; } /** @@ -372,7 +422,7 @@ public InternetDomainName publicSuffix() { * @since 6.0 */ public boolean isUnderPublicSuffix() { - return publicSuffixIndex > 0; + return publicSuffixIndex() > 0; } /** @@ -388,7 +438,7 @@ public boolean isUnderPublicSuffix() { * @since 6.0 */ public boolean isTopPrivateDomain() { - return publicSuffixIndex == 1; + return publicSuffixIndex() == 1; } /** @@ -412,7 +462,7 @@ public InternetDomainName topPrivateDomain() { return this; } checkState(isUnderPublicSuffix(), "Not under a public suffix: %s", name); - return ancestor(publicSuffixIndex - 1); + return ancestor(publicSuffixIndex() - 1); } /** @@ -439,7 +489,7 @@ public InternetDomainName topPrivateDomain() { * @since 23.3 */ public boolean isRegistrySuffix() { - return registrySuffixIndex == 0; + return registrySuffixIndex() == 0; } /** @@ -454,7 +504,7 @@ public boolean isRegistrySuffix() { * @since 23.3 */ public boolean hasRegistrySuffix() { - return registrySuffixIndex != NO_SUFFIX_FOUND; + return registrySuffixIndex() != NO_SUFFIX_FOUND; } /** @@ -463,9 +513,8 @@ public boolean hasRegistrySuffix() { * * @since 23.3 */ - @CheckForNull - public InternetDomainName registrySuffix() { - return hasRegistrySuffix() ? ancestor(registrySuffixIndex) : null; + public @Nullable InternetDomainName registrySuffix() { + return hasRegistrySuffix() ? ancestor(registrySuffixIndex()) : null; } /** @@ -477,7 +526,7 @@ public InternetDomainName registrySuffix() { * @since 23.3 */ public boolean isUnderRegistrySuffix() { - return registrySuffixIndex > 0; + return registrySuffixIndex() > 0; } /** @@ -492,7 +541,7 @@ public boolean isUnderRegistrySuffix() { * @since 23.3 */ public boolean isTopDomainUnderRegistrySuffix() { - return registrySuffixIndex == 1; + return registrySuffixIndex() == 1; } /** @@ -515,7 +564,7 @@ public InternetDomainName topDomainUnderRegistrySuffix() { return this; } checkState(isUnderRegistrySuffix(), "Not under a registry suffix: %s", name); - return ancestor(registrySuffixIndex - 1); + return ancestor(registrySuffixIndex() - 1); } /** Indicates whether this domain is composed of two or more parts. */ @@ -543,7 +592,17 @@ public InternetDomainName parent() { *

    TODO: Reasonable candidate for addition to public API. */ private InternetDomainName ancestor(int levels) { - return from(DOT_JOINER.join(parts.subList(levels, parts.size()))); + ImmutableList ancestorParts = parts.subList(levels, parts.size()); + + // levels equals the number of dots that are getting clipped away, then add the length of each + // clipped part to get the length of the leading substring that is being removed. + int substringFrom = levels; + for (int i = 0; i < levels; i++) { + substringFrom += parts.get(i).length(); + } + String ancestorName = name.substring(substringFrom); + + return new InternetDomainName(ancestorName, ancestorParts); } /** @@ -584,25 +643,13 @@ public InternetDomainName child(String leftParts) { */ public static boolean isValid(String name) { try { - from(name); + InternetDomainName unused = from(name); return true; } catch (IllegalArgumentException e) { return false; } } - /** - * Does the domain name match one of the "wildcard" patterns (e.g. {@code "*.ar"})? If a {@code - * desiredType} is specified, the wildcard pattern must also match that type. - */ - private static boolean matchesWildcardSuffixType( - Optional desiredType, String domain) { - List pieces = DOT_SPLITTER.limit(2).splitToList(domain); - return pieces.size() == 2 - && matchesType( - desiredType, Optional.fromNullable(PublicSuffixPatterns.UNDER.get(pieces.get(1)))); - } - /** * If a {@code desiredType} is specified, returns true only if the {@code actualType} is * identical. Otherwise, returns true as long as {@code actualType} is present. @@ -624,7 +671,7 @@ public String toString() { * version of the same domain name would not be considered equal. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } diff --git a/android/guava/src/com/google/common/net/MediaType.java b/android/guava/src/com/google/common/net/MediaType.java index fe6fe6edcc11..fc0e9392365e 100644 --- a/android/guava/src/com/google/common/net/MediaType.java +++ b/android/guava/src/com/google/common/net/MediaType.java @@ -16,19 +16,18 @@ import static com.google.common.base.CharMatcher.ascii; import static com.google.common.base.CharMatcher.javaIsoControl; -import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.hash; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Ascii; import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; import com.google.common.base.Joiner.MapJoiner; -import com.google.common.base.MoreObjects; -import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMultiset; @@ -36,6 +35,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.concurrent.LazyInit; import java.nio.charset.Charset; @@ -43,7 +43,7 @@ import java.nio.charset.UnsupportedCharsetException; import java.util.Map; import java.util.Map.Entry; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Represents an Internet Media Type @@ -70,10 +70,8 @@ * @since 12.0 * @author Gregory Kick */ -@Beta @GwtCompatible @Immutable -@ElementTypesAreNonnullByDefault public final class MediaType { private static final String CHARSET_ATTRIBUTE = "charset"; private static final ImmutableListMultimap UTF_8_CONSTANT_PARAMETERS = @@ -104,7 +102,7 @@ public final class MediaType { private static final String WILDCARD = "*"; - private static final Map KNOWN_TYPES = Maps.newHashMap(); + private static final Map knownTypes = Maps.newHashMap(); private static MediaType createConstant(String type, String subtype) { MediaType mediaType = @@ -119,8 +117,9 @@ private static MediaType createConstantUtf8(String type, String subtype) { return mediaType; } + @CanIgnoreReturnValue private static MediaType addKnownType(MediaType mediaType) { - KNOWN_TYPES.put(mediaType, mediaType); + knownTypes.put(mediaType, mediaType); return mediaType; } @@ -155,6 +154,15 @@ private static MediaType addKnownType(MediaType mediaType) { public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv"); public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html"); public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar"); + + /** + * As described in RFC 7763, this + * constant ({@code text/markdown}) is used for Markdown documents. + * + * @since 33.3.0 + */ + public static final MediaType MD_UTF_8 = createConstantUtf8(TEXT_TYPE, "markdown"); + public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain"); /** @@ -163,6 +171,7 @@ private static MediaType addKnownType(MediaType mediaType) { * may be necessary in certain situations for compatibility. */ public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript"); + /** * Tab separated * values. @@ -397,7 +406,9 @@ private static MediaType addKnownType(MediaType mediaType) { public static final MediaType DART_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "dart"); /** - * Apple Passbook. + * Apple + * Passbook. * * @since 19.0 */ @@ -448,6 +459,15 @@ private static MediaType addKnownType(MediaType mediaType) { */ public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary"); + /** + * As described in RFC 8949, this + * constant ({@code application/cbor}) is used for the Concise Binary Object Representation (CBOR) + * data format. + * + * @since 33.4.0 + */ + public static final MediaType CBOR = createConstant(APPLICATION_TYPE, "cbor"); + /** * Media type for the GeoJSON Format, a * geospatial data interchange format based on JSON. @@ -492,6 +512,13 @@ private static MediaType addKnownType(MediaType mediaType) { public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json"); + /** + * For JWT objects using the compact Serialization. + * + * @since 32.0.0 + */ + public static final MediaType JWT = createConstant(APPLICATION_TYPE, "jwt"); + /** * The Manifest for a web application. * @@ -519,29 +546,44 @@ private static MediaType addKnownType(MediaType mediaType) { public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox"); /** - * Apple over-the-air mobile configuration profiles. + * Apple + * over-the-air mobile configuration profiles. * * @since 18.0 */ public static final MediaType APPLE_MOBILE_CONFIG = createConstant(APPLICATION_TYPE, "x-apple-aspen-config"); - /** Microsoft Excel spreadsheets. */ + /** + * Microsoft + * Excel spreadsheets. + */ public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel"); /** - * Microsoft Outlook items. + * Microsoft + * Outlook items. * * @since 27.1 */ public static final MediaType MICROSOFT_OUTLOOK = createConstant(APPLICATION_TYPE, "vnd.ms-outlook"); - /** Microsoft Powerpoint presentations. */ + /** + * Microsoft + * Powerpoint presentations. + */ public static final MediaType MICROSOFT_POWERPOINT = createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint"); - /** Microsoft Word documents. */ + /** + * Microsoft + * Word documents. + */ public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword"); /** @@ -762,11 +804,14 @@ private static MediaType addKnownType(MediaType mediaType) { private final String subtype; private final ImmutableListMultimap parameters; - @LazyInit @CheckForNull private String toString; + @LazyInit private @Nullable String toString; @LazyInit private int hashCode; - @LazyInit @CheckForNull private Optional parsedCharset; + // We need to differentiate between "not computed" and "computed to be absent." + @SuppressWarnings("NullableOptional") + @LazyInit + private @Nullable Optional parsedCharset; private MediaType(String type, String subtype, ImmutableListMultimap parameters) { this.type = type; @@ -865,7 +910,9 @@ public MediaType withParameters(String attribute, Iterable values) { mediaType.parsedCharset = this.parsedCharset; } // Return one of the constants if the media type is a known type. - return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + @SuppressWarnings("GetOrDefaultNotNull") // getOrDefault requires API Level 24 + MediaType result = firstNonNull(knownTypes.get(mediaType), mediaType); + return result; } /** @@ -887,7 +934,7 @@ public MediaType withParameter(String attribute, String value) { * one. * *

    If a charset must be specified that is not supported on this JVM (and thus is not - * representable as a {@link Charset} instance, use {@link #withParameter}. + * representable as a {@link Charset} instance), use {@link #withParameter}. */ public MediaType withCharset(Charset charset) { checkNotNull(charset); @@ -899,7 +946,7 @@ public MediaType withCharset(Charset charset) { /** Returns true if either the type or subtype is the wildcard. */ public boolean hasWildcard() { - return WILDCARD.equals(type) || WILDCARD.equals(subtype); + return type.equals(WILDCARD) || subtype.equals(WILDCARD); } /** @@ -957,7 +1004,7 @@ private static MediaType create( String normalizedType = normalizeToken(type); String normalizedSubtype = normalizeToken(subtype); checkArgument( - !WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), + !normalizedType.equals(WILDCARD) || normalizedSubtype.equals(WILDCARD), "A wildcard type cannot be used with a non-wildcard subtype"); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (Entry entry : parameters.entries()) { @@ -966,7 +1013,9 @@ private static MediaType create( } MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); // Return one of the constants if the media type is a known type. - return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + @SuppressWarnings("GetOrDefaultNotNull") // getOrDefault requires API Level 24 + MediaType result = firstNonNull(knownTypes.get(mediaType), mediaType); + return result; } /** @@ -1032,7 +1081,7 @@ private static String normalizeToken(String token) { private static String normalizeParameterValue(String attribute, String value) { checkNotNull(value); // for GWT checkArgument(ascii().matchesAllOf(value), "parameter values must be ASCII: %s", value); - return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; + return attribute.equals(CHARSET_ATTRIBUTE) ? Ascii.toLowerCase(value) : value; } /** @@ -1040,26 +1089,25 @@ private static String normalizeParameterValue(String attribute, String value) { * * @throws IllegalArgumentException if the input is not parsable */ + @CanIgnoreReturnValue // TODO(b/219820829): consider removing public static MediaType parse(String input) { checkNotNull(input); Tokenizer tokenizer = new Tokenizer(input); try { String type = tokenizer.consumeToken(TOKEN_MATCHER); - tokenizer.consumeCharacter('/'); + consumeSeparator(tokenizer, '/'); String subtype = tokenizer.consumeToken(TOKEN_MATCHER); ImmutableListMultimap.Builder parameters = ImmutableListMultimap.builder(); while (tokenizer.hasMore()) { - tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); - tokenizer.consumeCharacter(';'); - tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + consumeSeparator(tokenizer, ';'); String attribute = tokenizer.consumeToken(TOKEN_MATCHER); - tokenizer.consumeCharacter('='); + consumeSeparator(tokenizer, '='); String value; - if ('"' == tokenizer.previewChar()) { + if (tokenizer.previewChar() == '"') { tokenizer.consumeCharacter('"'); StringBuilder valueBuilder = new StringBuilder(); - while ('"' != tokenizer.previewChar()) { - if ('\\' == tokenizer.previewChar()) { + while (tokenizer.previewChar() != '"') { + if (tokenizer.previewChar() == '\\') { tokenizer.consumeCharacter('\\'); valueBuilder.append(tokenizer.consumeCharacter(ascii())); } else { @@ -1079,6 +1127,12 @@ public static MediaType parse(String input) { } } + private static void consumeSeparator(Tokenizer tokenizer, char c) { + tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + tokenizer.consumeCharacter(c); + tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + } + private static final class Tokenizer { final String input; int position = 0; @@ -1087,6 +1141,7 @@ private static final class Tokenizer { this.input = input; } + @CanIgnoreReturnValue String consumeTokenIfPresent(CharMatcher matcher) { checkState(hasMore()); int startPosition = position; @@ -1109,6 +1164,7 @@ char consumeCharacter(CharMatcher matcher) { return c; } + @CanIgnoreReturnValue char consumeCharacter(char c) { checkState(hasMore()); checkState(previewChar() == c); @@ -1127,7 +1183,7 @@ boolean hasMore() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } else if (obj instanceof MediaType) { @@ -1146,7 +1202,7 @@ public int hashCode() { // racy single-check idiom int h = hashCode; if (h == 0) { - h = Objects.hashCode(type, subtype, parametersAsMap()); + h = hash(type, subtype, parametersAsMap()); hashCode = h; } return h; diff --git a/android/guava/src/com/google/common/net/ParametricNullness.java b/android/guava/src/com/google/common/net/ParametricNullness.java index 1ad2e27c0f4f..d79abc1991ed 100644 --- a/android/guava/src/com/google/common/net/ParametricNullness.java +++ b/android/guava/src/com/google/common/net/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/net/PercentEscaper.java b/android/guava/src/com/google/common/net/PercentEscaper.java index 7c7de9bb365a..259bc674f14d 100644 --- a/android/guava/src/com/google/common/net/PercentEscaper.java +++ b/android/guava/src/com/google/common/net/PercentEscaper.java @@ -15,11 +15,11 @@ package com.google.common.net; import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Math.max; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.escape.UnicodeEscaper; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@code UnicodeEscaper} that escapes some set of Java characters using a UTF-8 based percent @@ -50,16 +50,14 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class PercentEscaper extends UnicodeEscaper { // In some escapers spaces are escaped to '+' - private static final char[] PLUS_SIGN = {'+'}; + private static final char[] plusSign = {'+'}; // Percent escapers output upper case hex digits (uri escapers require this). - private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + private static final char[] upperHexDigits = "0123456789ABCDEF".toCharArray(); /** If true we should convert space to the {@code +} character. */ private final boolean plusForSpace; @@ -76,10 +74,10 @@ public final class PercentEscaper extends UnicodeEscaper { * space character. * *

    Not that it is allowed, but not necessarily desirable to specify {@code %} as a safe - * character. This has the effect of creating an escaper which has no well defined inverse but it + * character. This has the effect of creating an escaper which has no well-defined inverse but it * can be useful when escaping additional characters. * - * @param safeChars a non null string specifying additional safe characters for this escaper (the + * @param safeChars a non-null string specifying additional safe characters for this escaper (the * ranges 0..9, a..z and A..Z are always safe and should not be specified here) * @param plusForSpace true if ASCII space should be escaped to {@code +} rather than {@code %20} * @throws IllegalArgumentException if any of the parameters were invalid @@ -113,7 +111,7 @@ private static boolean[] createSafeOctets(String safeChars) { int maxChar = -1; char[] safeCharArray = safeChars.toCharArray(); for (char c : safeCharArray) { - maxChar = Math.max(c, maxChar); + maxChar = max(c, maxChar); } boolean[] octets = new boolean[maxChar + 1]; for (char c : safeCharArray) { @@ -157,21 +155,20 @@ public String escape(String s) { /** Escapes the given Unicode code point in UTF-8. */ @Override - @CheckForNull - protected char[] escape(int cp) { + protected char @Nullable [] escape(int cp) { // We should never get negative values here but if we do it will throw an // IndexOutOfBoundsException, so at least it will get spotted. if (cp < safeOctets.length && safeOctets[cp]) { return null; } else if (cp == ' ' && plusForSpace) { - return PLUS_SIGN; + return plusSign; } else if (cp <= 0x7F) { // Single byte UTF-8 characters // Start with "%--" and fill in the blanks char[] dest = new char[3]; dest[0] = '%'; - dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; - dest[1] = UPPER_HEX_DIGITS[cp >>> 4]; + dest[2] = upperHexDigits[cp & 0xF]; + dest[1] = upperHexDigits[cp >>> 4]; return dest; } else if (cp <= 0x7ff) { // Two byte UTF-8 characters [cp >= 0x80 && cp <= 0x7ff] @@ -179,13 +176,13 @@ protected char[] escape(int cp) { char[] dest = new char[6]; dest[0] = '%'; dest[3] = '%'; - dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[5] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[4] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[2] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[1] = UPPER_HEX_DIGITS[0xC | cp]; + dest[1] = upperHexDigits[0xC | cp]; return dest; } else if (cp <= 0xffff) { // Three byte UTF-8 characters [cp >= 0x800 && cp <= 0xffff] @@ -195,15 +192,15 @@ protected char[] escape(int cp) { dest[1] = 'E'; dest[3] = '%'; dest[6] = '%'; - dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[8] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[7] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[5] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[4] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[2] = UPPER_HEX_DIGITS[cp]; + dest[2] = upperHexDigits[cp]; return dest; } else if (cp <= 0x10ffff) { char[] dest = new char[12]; @@ -214,19 +211,19 @@ protected char[] escape(int cp) { dest[3] = '%'; dest[6] = '%'; dest[9] = '%'; - dest[11] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[11] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[10] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[10] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[8] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[7] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[5] = upperHexDigits[cp & 0xF]; cp >>>= 4; - dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + dest[4] = upperHexDigits[0x8 | (cp & 0x3)]; cp >>>= 2; - dest[2] = UPPER_HEX_DIGITS[cp & 0x7]; + dest[2] = upperHexDigits[cp & 0x7]; return dest; } else { // If this ever happens it is due to bug in UnicodeEscaper, not bad input. diff --git a/android/guava/src/com/google/common/net/UrlEscapers.java b/android/guava/src/com/google/common/net/UrlEscapers.java index c7e15efc6d91..60fadbfea894 100644 --- a/android/guava/src/com/google/common/net/UrlEscapers.java +++ b/android/guava/src/com/google/common/net/UrlEscapers.java @@ -29,7 +29,6 @@ * @since 15.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class UrlEscapers { private UrlEscapers() {} @@ -45,10 +44,12 @@ private UrlEscapers() {} /** * Returns an {@link Escaper} instance that escapes strings so they can be safely included in URL form parameter names and values. Escaping is performed - * with the UTF-8 character encoding. The caller is responsible for replacing any unpaired carriage return or line feed characters - * with a CR+LF pair on any non-file inputs before escaping them with this escaper. + * href="https://url.spec.whatwg.org/#application-x-www-form-urlencoded-percent-encode-set">URL + * form parameter names and values. Escaping is performed with the UTF-8 character encoding. + * The caller is responsible for replacing + * any unpaired carriage return or line feed characters with a CR+LF pair on any non-file + * inputs before escaping them with this escaper. * *

    When escaping a String, the following rules apply: * @@ -63,9 +64,9 @@ private UrlEscapers() {} * * *

    This escaper is suitable for escaping parameter names and values even when using the non-standard semicolon, rather than the ampersand, as - * a parameter delimiter. Nevertheless, we recommend using the ampersand unless you must - * interoperate with systems that require semicolons. + * href="https://www.w3.org/TR/html401/appendix/notes.html#h-B.2.2">using the non-standard + * semicolon, rather than the ampersand, as a parameter delimiter. Nevertheless, we recommend + * using the ampersand unless you must interoperate with systems that require semicolons. * *

    Note: Unlike other escapers, URL escapers produce uppercase hexadecimal sequences. @@ -80,14 +81,15 @@ public static Escaper urlFormParameterEscaper() { /** * Returns an {@link Escaper} instance that escapes strings so they can be safely included in URL path segments. The returned escaper escapes all non-ASCII - * characters, even though many of these are accepted in modern - * URLs. (If the escaper were to leave these characters - * unescaped, they would be escaped by the consumer at parse time, anyway.) Additionally, the - * escaper escapes the slash character ("/"). While slashes are acceptable in URL paths, they are - * considered by the specification to be separators between "path segments." This implies that, if - * you wish for your path to contain slashes, you must escape each segment separately and then - * join them. + * href="https://url.spec.whatwg.org/#syntax-url-path-segment">URL path segments. The returned + * escaper escapes all non-ASCII characters, even though many of these are accepted in modern + * URLs. (If the escaper were to leave these + * characters unescaped, they would be escaped by the consumer at parse time, anyway.) + * Additionally, the escaper escapes the slash character ("/"). While slashes are acceptable in + * URL paths, they are considered by the specification to be separators between "path segments." + * This implies that, if you wish for your path to contain slashes, you must escape each segment + * separately and then join them. * *

    When escaping a String, the following rules apply: * @@ -116,9 +118,8 @@ public static Escaper urlPathSegmentEscaper() { /** * Returns an {@link Escaper} instance that escapes strings so they can be safely included in a URL fragment. The returned escaper escapes all non-ASCII - * characters, even though many of these are accepted in modern - * URLs. + * href="https://url.spec.whatwg.org/#concept-url-fragment">URL fragment. The returned escaper + * escapes all non-ASCII characters. * *

    When escaping a String, the following rules apply: * diff --git a/android/guava/src/com/google/common/net/package-info.java b/android/guava/src/com/google/common/net/package-info.java index d9db26637974..562bb10e4f99 100644 --- a/android/guava/src/com/google/common/net/package-info.java +++ b/android/guava/src/com/google/common/net/package-info.java @@ -13,15 +13,16 @@ */ /** - * This package contains utility methods and classes for working with net addresses (numeric IP and - * domain names). + * Utility methods and classes for networking (such as IP addresses and domain names). * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. * * @author Craig Berry */ -@ParametersAreNonnullByDefault +@CheckReturnValue +@NullMarked package com.google.common.net; -import javax.annotation.ParametersAreNonnullByDefault; +import com.google.errorprone.annotations.CheckReturnValue; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/primitives/Booleans.java b/android/guava/src/com/google/common/primitives/Booleans.java index 522049bfe3eb..211936da04f0 100644 --- a/android/guava/src/com/google/common/primitives/Booleans.java +++ b/android/guava/src/com/google/common/primitives/Booleans.java @@ -18,9 +18,12 @@ import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.lang.Math.min; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -29,7 +32,7 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code boolean} primitives, that are not already found in @@ -42,7 +45,6 @@ * @since 1.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Booleans { private Booleans() {} @@ -75,12 +77,11 @@ public String toString() { /** * Returns a {@code Comparator} that sorts {@code true} before {@code false}. * - *

    This is particularly useful in Java 8+ in combination with {@code Comparators.comparing}, - * e.g. {@code Comparators.comparing(Foo::hasBar, trueFirst())}. + *

    This is particularly useful in Java 8+ in combination with {@code Comparator.comparing}, + * e.g. {@code Comparator.comparing(Foo::hasBar, trueFirst())}. * * @since 21.0 */ - @Beta public static Comparator trueFirst() { return BooleanComparator.TRUE_FIRST; } @@ -88,12 +89,11 @@ public static Comparator trueFirst() { /** * Returns a {@code Comparator} that sorts {@code false} before {@code true}. * - *

    This is particularly useful in Java 8+ in combination with {@code Comparators.comparing}, - * e.g. {@code Comparators.comparing(Foo::hasBar, falseFirst())}. + *

    This is particularly useful in Java 8+ in combination with {@code Comparator.comparing}, + * e.g. {@code Comparator.comparing(Foo::hasBar, falseFirst())}. * * @since 21.0 */ - @Beta public static Comparator falseFirst() { return BooleanComparator.FALSE_FIRST; } @@ -102,7 +102,7 @@ public static Comparator falseFirst() { * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Boolean) * value).hashCode()}. * - *

    Java 8 users: use {@link Boolean#hashCode(boolean)} instead. + *

    Java 8+ users: use {@link Boolean#hashCode(boolean)} instead. * * @param value a primitive {@code boolean} value * @return a hash code for the value @@ -116,7 +116,7 @@ public static int hashCode(boolean value) { * considered less than {@code true}). The sign of the value returned is the same as that of * {@code ((Boolean) a).compareTo(b)}. * - *

    Note for Java 7 and later: this method should be treated as deprecated; use the + *

    Note: this method is now unnecessary and should be treated as deprecated; use the * equivalent {@link Boolean#compare} method instead. * * @param a the first {@code boolean} to compare @@ -124,8 +124,9 @@ public static int hashCode(boolean value) { * @return a positive number if only {@code a} is {@code true}, a negative number if only {@code * b} is true, or zero if {@code a == b} */ + @InlineMe(replacement = "Boolean.compare(a, b)") public static int compare(boolean a, boolean b) { - return (a == b) ? 0 : (a ? 1 : -1); + return Boolean.compare(a, b); } /** @@ -231,13 +232,15 @@ private static int lastIndexOf(boolean[] array, boolean target, int start, int e * * @param arrays zero or more {@code boolean} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static boolean[] concat(boolean[]... arrays) { - int length = 0; + long length = 0; for (boolean[] array : arrays) { length += array.length; } - boolean[] result = new boolean[length]; + boolean[] result = new boolean[checkNoOverflow(length)]; int pos = 0; for (boolean[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -246,6 +249,14 @@ public static boolean[] concat(boolean[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns an array containing the same values as {@code array}, but guaranteed to be of a * specified minimum length. If {@code array} already has a length of at least {@code minLength}, @@ -311,9 +322,9 @@ private enum LexicographicalComparator implements Comparator { @Override public int compare(boolean[] left, boolean[] right) { - int minLength = Math.min(left.length, right.length); + int minLength = min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Booleans.compare(left[i], right[i]); + int result = Boolean.compare(left[i], right[i]); if (result != 0) { return result; } @@ -361,9 +372,10 @@ public static boolean[] toArray(Collection collection) { * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to * set a value to {@code null} will result in a {@link NullPointerException}. * - *

    The returned list maintains the values, but not the identities, of {@code Boolean} objects - * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for - * the returned list is unspecified. + *

    There are at most two distinct objects in this list, {@code (Boolean) true} and {@code + * (Boolean) false}. Java guarantees that those are always represented by the same objects. + * + *

    The returned list is serializable. * * @param backingArray the array to back the list * @return a list view of the array @@ -409,14 +421,14 @@ public Boolean get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Boolean) && Booleans.indexOf(array, (Boolean) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Boolean) { int i = Booleans.indexOf(array, (Boolean) target, start, end); @@ -428,7 +440,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Boolean) { int i = Booleans.lastIndexOf(array, (Boolean) target, start, end); @@ -459,7 +471,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -502,7 +514,7 @@ boolean[] toBooleanArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -510,7 +522,6 @@ boolean[] toBooleanArray() { * * @since 16.0 */ - @Beta public static int countTrue(boolean... values) { int count = 0; for (boolean value : values) { @@ -551,4 +562,54 @@ public static void reverse(boolean[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Booleans.asList(array), + * distance)}, but is somewhat faster. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(boolean[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Booleans.asList(array).subList(fromIndex, toIndex), distance)}, but is + * somewhat faster. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(boolean[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/android/guava/src/com/google/common/primitives/Bytes.java b/android/guava/src/com/google/common/primitives/Bytes.java index 62997f34aac7..e9ececf2c8a4 100644 --- a/android/guava/src/com/google/common/primitives/Bytes.java +++ b/android/guava/src/com/google/common/primitives/Bytes.java @@ -20,6 +20,8 @@ import static com.google.common.base.Preconditions.checkPositionIndexes; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -27,7 +29,7 @@ import java.util.Collections; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code byte} primitives, that are not already found in @@ -44,7 +46,6 @@ // TODO(kevinb): how to prevent warning on UnsignedBytes when building GWT // javadoc? @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Bytes { private Bytes() {} @@ -52,7 +53,7 @@ private Bytes() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Byte) * value).hashCode()}. * - *

    Java 8 users: use {@link Byte#hashCode(byte)} instead. + *

    Java 8+ users: use {@link Byte#hashCode(byte)} instead. * * @param value a primitive {@code byte} value * @return a hash code for the value @@ -156,13 +157,15 @@ private static int lastIndexOf(byte[] array, byte target, int start, int end) { * * @param arrays zero or more {@code byte} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static byte[] concat(byte[]... arrays) { - int length = 0; + long length = 0; for (byte[] array : arrays) { length += array.length; } - byte[] result = new byte[length]; + byte[] result = new byte[checkNoOverflow(length)]; int pos = 0; for (byte[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -171,6 +174,14 @@ public static byte[] concat(byte[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns an array containing the same values as {@code array}, but guaranteed to be of a * specified minimum length. If {@code array} already has a length of at least {@code minLength}, @@ -227,6 +238,8 @@ public static byte[] toArray(Collection collection) { * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for * the returned list is unspecified. * + *

    The returned list is serializable. + * * @param backingArray the array to back the list * @return a list view of the array */ @@ -271,13 +284,13 @@ public Byte get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Byte) && Bytes.indexOf(array, (Byte) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Byte) { int i = Bytes.indexOf(array, (Byte) target, start, end); @@ -289,7 +302,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Byte) { int i = Bytes.lastIndexOf(array, (Byte) target, start, end); @@ -320,7 +333,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -363,7 +376,7 @@ byte[] toByteArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -396,4 +409,54 @@ public static void reverse(byte[] array, int fromIndex, int toIndex) { array[j] = tmp; } } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is somewhat faster. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(byte[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is somewhat + * faster. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(byte[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } } diff --git a/android/guava/src/com/google/common/primitives/Chars.java b/android/guava/src/com/google/common/primitives/Chars.java index 4a2e3a3449cb..b728466c9b8c 100644 --- a/android/guava/src/com/google/common/primitives/Chars.java +++ b/android/guava/src/com/google/common/primitives/Chars.java @@ -19,9 +19,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -30,7 +31,7 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code char} primitives, that are not already found in @@ -46,14 +47,13 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Chars { private Chars() {} /** * The number of bytes required to represent a primitive {@code char} value. * - *

    Java 8 users: use {@link Character#BYTES} instead. + *

    Java 8+ users: use {@link Character#BYTES} instead. */ public static final int BYTES = Character.SIZE / Byte.SIZE; @@ -61,7 +61,7 @@ private Chars() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Character) * value).hashCode()}. * - *

    Java 8 users: use {@link Character#hashCode(char)} instead. + *

    Java 8+ users: use {@link Character#hashCode(char)} instead. * * @param value a primitive {@code char} value * @return a hash code for the value @@ -106,7 +106,7 @@ public static char saturatedCast(long value) { * Compares the two specified {@code char} values. The sign of the value returned is the same as * that of {@code ((Character) a).compareTo(b)}. * - *

    Note for Java 7 and later: this method should be treated as deprecated; use the + *

    Note: this method is now unnecessary and should be treated as deprecated; use the * equivalent {@link Character#compare} method instead. * * @param a the first {@code char} to compare @@ -114,8 +114,9 @@ public static char saturatedCast(long value) { * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @InlineMe(replacement = "Character.compare(a, b)") public static int compare(char a, char b) { - return a - b; // safe due to restricted range + return Character.compare(a, b); } /** @@ -258,7 +259,6 @@ public static char max(char... array) { * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta public static char constrainToRange(char value, char min, char max) { checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); return value < min ? min : value < max ? value : max; @@ -270,13 +270,15 @@ public static char constrainToRange(char value, char min, char max) { * * @param arrays zero or more {@code char} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static char[] concat(char[]... arrays) { - int length = 0; + long length = 0; for (char[] array : arrays) { length += array.length; } - char[] result = new char[length]; + char[] result = new char[checkNoOverflow(length)]; int pos = 0; for (char[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -285,6 +287,14 @@ public static char[] concat(char[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to * {@code ByteBuffer.allocate(2).putChar(value).array()}. For example, the input value {@code @@ -393,7 +403,7 @@ private enum LexicographicalComparator implements Comparator { public int compare(char[] left, char[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Chars.compare(left[i], right[i]); + int result = Character.compare(left[i], right[i]); if (result != 0) { return result; } @@ -488,6 +498,56 @@ public static void reverse(char[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Chars.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(char[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Chars.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(char[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns a fixed-size list backed by the specified array, similar to {@link * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to @@ -497,6 +557,8 @@ public static void reverse(char[] array, int fromIndex, int toIndex) { * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for * the returned list is unspecified. * + *

    The returned list is serializable. + * * @param backingArray the array to back the list * @return a list view of the array */ @@ -541,14 +603,14 @@ public Character get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Character) && Chars.indexOf(array, (Character) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Character) { int i = Chars.indexOf(array, (Character) target, start, end); @@ -560,7 +622,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Character) { int i = Chars.lastIndexOf(array, (Character) target, start, end); @@ -591,7 +653,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -634,6 +696,6 @@ char[] toCharArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/primitives/Doubles.java b/android/guava/src/com/google/common/primitives/Doubles.java index ce5df2e394db..828db51f07b4 100644 --- a/android/guava/src/com/google/common/primitives/Doubles.java +++ b/android/guava/src/com/google/common/primitives/Doubles.java @@ -22,10 +22,11 @@ import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.POSITIVE_INFINITY; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -34,7 +35,9 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code double} primitives, that are not already found in @@ -47,14 +50,13 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Doubles extends DoublesMethodsForWeb { private Doubles() {} /** * The number of bytes required to represent a primitive {@code double} value. * - *

    Java 8 users: use {@link Double#BYTES} instead. + *

    Java 8+ users: use {@link Double#BYTES} instead. * * @since 10.0 */ @@ -64,7 +66,7 @@ private Doubles() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Double) * value).hashCode()}. * - *

    Java 8 users: use {@link Double#hashCode(double)} instead. + *

    Java 8+ users: use {@link Double#hashCode(double)} instead. * * @param value a primitive {@code double} value * @return a hash code for the value @@ -90,6 +92,7 @@ public static int hashCode(double value) { * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @InlineMe(replacement = "Double.compare(a, b)") public static int compare(double a, double b) { return Double.compare(a, b); } @@ -98,7 +101,7 @@ public static int compare(double a, double b) { * Returns {@code true} if {@code value} represents a real number. This is equivalent to, but not * necessarily implemented as, {@code !(Double.isInfinite(value) || Double.isNaN(value))}. * - *

    Java 8 users: use {@link Double#isFinite(double)} instead. + *

    Java 8+ users: use {@link Double#isFinite(double)} instead. * * @since 10.0 */ @@ -247,13 +250,14 @@ public static double max(double... array) { * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code * value} is greater than {@code max}, {@code max} is returned. * + *

    Java 21+ users: Use {@code Math.clamp} instead. + * * @param value the {@code double} value to constrain * @param min the lower bound (inclusive) of the range to constrain {@code value} to * @param max the upper bound (inclusive) of the range to constrain {@code value} to * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta public static double constrainToRange(double value, double min, double max) { // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). @@ -271,13 +275,15 @@ public static double constrainToRange(double value, double min, double max) { * * @param arrays zero or more {@code double} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static double[] concat(double[]... arrays) { - int length = 0; + long length = 0; for (double[] array : arrays) { length += array.length; } - double[] result = new double[length]; + double[] result = new double[checkNoOverflow(length)]; int pos = 0; for (double[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -286,9 +292,17 @@ public static double[] concat(double[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + private static final class DoubleConverter extends Converter implements Serializable { - static final DoubleConverter INSTANCE = new DoubleConverter(); + static final Converter INSTANCE = new DoubleConverter(); @Override protected Double doForward(String value) { @@ -309,7 +323,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } /** @@ -318,7 +332,6 @@ private Object readResolve() { * * @since 16.0 */ - @Beta public static Converter stringConverter() { return DoubleConverter.INSTANCE; } @@ -467,6 +480,56 @@ public static void reverse(double[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(double[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(double[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code double} * value in the manner of {@link Number#doubleValue}. @@ -507,6 +570,8 @@ public static double[] toArray(Collection collection) { *

    The returned list may have unexpected behavior if it contains {@code NaN}, or if {@code NaN} * is used as a parameter to any of its methods. * + *

    The returned list is serializable. + * *

    Note: when possible, you should represent your data as an {@link * ImmutableDoubleArray} instead, which has an {@link ImmutableDoubleArray#asList asList} view. * @@ -554,14 +619,25 @@ public Double get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator.OfDouble spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Double) && Doubles.indexOf(array, (Double) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Double) { int i = Doubles.indexOf(array, (Double) target, start, end); @@ -573,7 +649,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Double) { int i = Doubles.lastIndexOf(array, (Double) target, start, end); @@ -604,7 +680,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -647,7 +723,7 @@ public String toString() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -701,10 +777,8 @@ public String toString() { * @throws NullPointerException if {@code string} is {@code null} * @since 14.0 */ - @Beta @GwtIncompatible // regular expressions - @CheckForNull - public static Double tryParse(String string) { + public static @Nullable Double tryParse(String string) { if (FLOATING_POINT_PATTERN.matcher(string).matches()) { // TODO(lowasser): could be potentially optimized, but only with // extensive testing diff --git a/android/guava/src/com/google/common/primitives/DoublesMethodsForWeb.java b/android/guava/src/com/google/common/primitives/DoublesMethodsForWeb.java index 949cbe0f5a6b..04d96512211d 100644 --- a/android/guava/src/com/google/common/primitives/DoublesMethodsForWeb.java +++ b/android/guava/src/com/google/common/primitives/DoublesMethodsForWeb.java @@ -21,5 +21,4 @@ * version. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class DoublesMethodsForWeb {} diff --git a/android/guava/src/com/google/common/primitives/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/primitives/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 44f6869c7cda..000000000000 --- a/android/guava/src/com/google/common/primitives/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.primitives; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/primitives/Floats.java b/android/guava/src/com/google/common/primitives/Floats.java index b038cb289636..ad7d83101cbd 100644 --- a/android/guava/src/com/google/common/primitives/Floats.java +++ b/android/guava/src/com/google/common/primitives/Floats.java @@ -22,10 +22,11 @@ import static java.lang.Float.NEGATIVE_INFINITY; import static java.lang.Float.POSITIVE_INFINITY; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -34,7 +35,7 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code float} primitives, that are not already found in @@ -47,14 +48,13 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Floats extends FloatsMethodsForWeb { private Floats() {} /** * The number of bytes required to represent a primitive {@code float} value. * - *

    Java 8 users: use {@link Float#BYTES} instead. + *

    Java 8+ users: use {@link Float#BYTES} instead. * * @since 10.0 */ @@ -64,7 +64,7 @@ private Floats() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Float) * value).hashCode()}. * - *

    Java 8 users: use {@link Float#hashCode(float)} instead. + *

    Java 8+ users: use {@link Float#hashCode(float)} instead. * * @param value a primitive {@code float} value * @return a hash code for the value @@ -87,6 +87,7 @@ public static int hashCode(float value) { * @param b the second {@code float} to compare * @return the result of invoking {@link Float#compare(float, float)} */ + @InlineMe(replacement = "Float.compare(a, b)") public static int compare(float a, float b) { return Float.compare(a, b); } @@ -95,7 +96,7 @@ public static int compare(float a, float b) { * Returns {@code true} if {@code value} represents a real number. This is equivalent to, but not * necessarily implemented as, {@code !(Float.isInfinite(value) || Float.isNaN(value))}. * - *

    Java 8 users: use {@link Float#isFinite(float)} instead. + *

    Java 8+ users: use {@link Float#isFinite(float)} instead. * * @since 10.0 */ @@ -244,13 +245,14 @@ public static float max(float... array) { * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code * value} is greater than {@code max}, {@code max} is returned. * + *

    Java 21+ users: Use {@code Math.clamp} instead. + * * @param value the {@code float} value to constrain * @param min the lower bound (inclusive) of the range to constrain {@code value} to * @param max the upper bound (inclusive) of the range to constrain {@code value} to * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta public static float constrainToRange(float value, float min, float max) { // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). @@ -268,13 +270,15 @@ public static float constrainToRange(float value, float min, float max) { * * @param arrays zero or more {@code float} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static float[] concat(float[]... arrays) { - int length = 0; + long length = 0; for (float[] array : arrays) { length += array.length; } - float[] result = new float[length]; + float[] result = new float[checkNoOverflow(length)]; int pos = 0; for (float[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -283,9 +287,17 @@ public static float[] concat(float[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + private static final class FloatConverter extends Converter implements Serializable { - static final FloatConverter INSTANCE = new FloatConverter(); + static final Converter INSTANCE = new FloatConverter(); @Override protected Float doForward(String value) { @@ -306,7 +318,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } /** @@ -315,7 +327,6 @@ private Object readResolve() { * * @since 16.0 */ - @Beta public static Converter stringConverter() { return FloatConverter.INSTANCE; } @@ -464,6 +475,56 @@ public static void reverse(float[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Floats.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(float[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Floats.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(float[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code float} * value in the manner of {@link Number#floatValue}. @@ -504,6 +565,8 @@ public static float[] toArray(Collection collection) { *

    The returned list may have unexpected behavior if it contains {@code NaN}, or if {@code NaN} * is used as a parameter to any of its methods. * + *

    The returned list is serializable. + * * @param backingArray the array to back the list * @return a list view of the array */ @@ -548,13 +611,13 @@ public Float get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Float) && Floats.indexOf(array, (Float) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Float) { int i = Floats.indexOf(array, (Float) target, start, end); @@ -566,7 +629,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Float) { int i = Floats.lastIndexOf(array, (Float) target, start, end); @@ -597,7 +660,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -640,7 +703,7 @@ float[] toFloatArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -660,10 +723,8 @@ float[] toFloatArray() { * @throws NullPointerException if {@code string} is {@code null} * @since 14.0 */ - @Beta @GwtIncompatible // regular expressions - @CheckForNull - public static Float tryParse(String string) { + public static @Nullable Float tryParse(String string) { if (Doubles.FLOATING_POINT_PATTERN.matcher(string).matches()) { // TODO(lowasser): could be potentially optimized, but only with // extensive testing diff --git a/android/guava/src/com/google/common/primitives/FloatsMethodsForWeb.java b/android/guava/src/com/google/common/primitives/FloatsMethodsForWeb.java index 801e2f3ef110..acdb42f275b1 100644 --- a/android/guava/src/com/google/common/primitives/FloatsMethodsForWeb.java +++ b/android/guava/src/com/google/common/primitives/FloatsMethodsForWeb.java @@ -21,5 +21,4 @@ * version. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class FloatsMethodsForWeb {} diff --git a/android/guava/src/com/google/common/primitives/Platform.java b/android/guava/src/com/google/common/primitives/IgnoreJRERequirement.java similarity index 54% rename from android/guava/src/com/google/common/primitives/Platform.java rename to android/guava/src/com/google/common/primitives/IgnoreJRERequirement.java index 83b248517993..0013432f9912 100644 --- a/android/guava/src/com/google/common/primitives/Platform.java +++ b/android/guava/src/com/google/common/primitives/IgnoreJRERequirement.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Guava Authors + * Copyright 2019 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -14,13 +14,16 @@ package com.google.common.primitives; -import com.google.common.annotations.GwtCompatible; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; -/** Methods factored out so that they can be emulated differently in GWT. */ -@GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault -final class Platform { - static void checkGwtRpcEnabled() {} +import java.lang.annotation.Target; - private Platform() {} -} +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/primitives/ImmutableDoubleArray.java b/android/guava/src/com/google/common/primitives/ImmutableDoubleArray.java index 1627fab2baa8..d05637fa239c 100644 --- a/android/guava/src/com/google/common/primitives/ImmutableDoubleArray.java +++ b/android/guava/src/com/google/common/primitives/ImmutableDoubleArray.java @@ -15,12 +15,11 @@ package com.google.common.primitives; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Immutable; import java.io.Serializable; import java.util.AbstractList; @@ -28,7 +27,11 @@ import java.util.Collection; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.DoubleConsumer; +import java.util.stream.DoubleStream; +import org.jspecify.annotations.Nullable; /** * An immutable array of {@code double} values, with an API resembling {@link List}. @@ -44,6 +47,7 @@ * hunt through classes like {@link Arrays} and {@link Doubles} for them. *

  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarDoubles().stream()...}. *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of * allocating garbage). * @@ -65,6 +69,8 @@ *
      *
    • Improved memory compactness and locality. *
    • Can be queried without allocating garbage. + *
    • Access to {@code DoubleStream} features (like {@link DoubleStream#sum}) using {@code + * stream()} instead of the awkward {@code stream().mapToDouble(v -> v)}. *
    * *

    Disadvantages compared to {@code ImmutableList}: @@ -77,10 +83,8 @@ * * @since 22.0 */ -@Beta @GwtCompatible @Immutable -@ElementTypesAreNonnullByDefault public final class ImmutableDoubleArray implements Serializable { private static final ImmutableDoubleArray EMPTY = new ImmutableDoubleArray(new double[0]); @@ -164,6 +168,19 @@ public static ImmutableDoubleArray copyOf(Iterable values) { return builder().addAll(values).build(); } + /** + * Returns an immutable array containing all the values from {@code stream}, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static ImmutableDoubleArray copyOf(DoubleStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + double[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableDoubleArray(array); + } + /** * Returns a new, empty builder for {@link ImmutableDoubleArray} instances, sized to hold up to * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. @@ -195,7 +212,6 @@ public static Builder builder() { * A builder for {@link ImmutableDoubleArray} instances; obtained using {@link * ImmutableDoubleArray#builder}. */ - @CanIgnoreReturnValue public static final class Builder { private double[] array; private int count = 0; // <= array.length @@ -208,6 +224,7 @@ public static final class Builder { * Appends {@code value} to the end of the values the built {@link ImmutableDoubleArray} will * contain. */ + @CanIgnoreReturnValue public Builder add(double value) { ensureRoomFor(1); array[count] = value; @@ -219,6 +236,7 @@ public Builder add(double value) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableDoubleArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(double[] values) { ensureRoomFor(values.length); System.arraycopy(values, 0, array, count, values.length); @@ -230,6 +248,7 @@ public Builder addAll(double[] values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableDoubleArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Iterable values) { if (values instanceof Collection) { return addAll((Collection) values); @@ -244,6 +263,7 @@ public Builder addAll(Iterable values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableDoubleArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Collection values) { ensureRoomFor(values.size()); for (Double value : values) { @@ -252,10 +272,30 @@ public Builder addAll(Collection values) { return this; } + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + @CanIgnoreReturnValue + public Builder addAll(DoubleStream stream) { + Spliterator.OfDouble spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((DoubleConsumer) this::add); + return this; + } + /** * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableDoubleArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(ImmutableDoubleArray values) { ensureRoomFor(values.length()); System.arraycopy(values.array, values.start, array, count, values.length()); @@ -294,7 +334,6 @@ private static int expandedCapacity(int oldCapacity, int minCapacity) { * no data is copied as part of this step, but this may occupy more memory than strictly * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. */ - @CheckReturnValue public ImmutableDoubleArray build() { return count == 0 ? EMPTY : new ImmutableDoubleArray(array, 0, count); } @@ -383,6 +422,32 @@ public boolean contains(double target) { return indexOf(target) >= 0; } + /** + * Invokes {@code consumer} for each value contained in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // We rely on users not to call this without library desugaring. + public void forEach(DoubleConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** + * Returns a stream over the values in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent stream calls + @IgnoreJRERequirement + public DoubleStream stream() { + return Arrays.stream(array, start, end); + } + /** Returns a new, mutable copy of this array's values, as a primitive {@code double[]}. */ public double[] toArray() { return Arrays.copyOfRange(array, start, end); @@ -402,6 +467,16 @@ public ImmutableDoubleArray subArray(int startIndex, int endIndex) { : new ImmutableDoubleArray(array, start + startIndex, start + endIndex); } + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // used only from APIs that use streams + /* + * We declare this as package-private, rather than private, to avoid generating a synthetic + * accessor method (under -target 8) that would lack the Android flavor's @IgnoreJRERequirement. + */ + Spliterator.OfDouble spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + /** * Returns an immutable view of this array's values as a {@code List}; note that {@code * double} values are boxed into {@link Double} instances on demand, which can be very expensive. @@ -425,7 +500,7 @@ private AsList(ImmutableDoubleArray parent) { this.parent = parent; } - // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, mutations + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations @Override public int size() { @@ -438,17 +513,17 @@ public Double get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { return indexOf(target) >= 0; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { return target instanceof Double ? parent.indexOf((Double) target) : -1; } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { return target instanceof Double ? parent.lastIndexOf((Double) target) : -1; } @@ -457,8 +532,20 @@ public List subList(int fromIndex, int toIndex) { return parent.subArray(fromIndex, toIndex).asList(); } + // The default List spliterator is not efficiently splittable + @Override + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator spliterator() { + return parent.spliterator(); + } + @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof AsList) { AsList that = (AsList) object; return this.parent.equals(that.parent); @@ -498,7 +585,7 @@ public String toString() { * values as this one, in the same order. Values are compared as if by {@link Double#equals}. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } diff --git a/android/guava/src/com/google/common/primitives/ImmutableIntArray.java b/android/guava/src/com/google/common/primitives/ImmutableIntArray.java index 8f972c49b4fc..5b26c5b7d3eb 100644 --- a/android/guava/src/com/google/common/primitives/ImmutableIntArray.java +++ b/android/guava/src/com/google/common/primitives/ImmutableIntArray.java @@ -15,12 +15,11 @@ package com.google.common.primitives; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Immutable; import java.io.Serializable; import java.util.AbstractList; @@ -28,7 +27,11 @@ import java.util.Collection; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; +import org.jspecify.annotations.Nullable; /** * An immutable array of {@code int} values, with an API resembling {@link List}. @@ -39,11 +42,12 @@ *

  • All the many well-known advantages of immutability (read Effective Java, third * edition, Item 17). *
  • Has the value-based (not identity-based) {@link #equals}, {@link #hashCode}, and {@link - * #toString} behavior you expect + * #toString} behavior you expect. *
  • Offers useful operations beyond just {@code get} and {@code length}, so you don't have to * hunt through classes like {@link Arrays} and {@link Ints} for them. *
  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarInts().stream()...}. *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of * allocating garbage). * @@ -63,8 +67,10 @@ * }: * *
      - *
    • Improved memory compactness and locality - *
    • Can be queried without allocating garbage + *
    • Improved memory compactness and locality. + *
    • Can be queried without allocating garbage. + *
    • Access to {@code IntStream} features (like {@link IntStream#sum}) using {@code stream()} + * instead of the awkward {@code stream().mapToInt(v -> v)}. *
    * *

    Disadvantages compared to {@code ImmutableList}: @@ -77,10 +83,8 @@ * * @since 22.0 */ -@Beta @GwtCompatible @Immutable -@ElementTypesAreNonnullByDefault public final class ImmutableIntArray implements Serializable { private static final ImmutableIntArray EMPTY = new ImmutableIntArray(new int[0]); @@ -161,6 +165,19 @@ public static ImmutableIntArray copyOf(Iterable values) { return builder().addAll(values).build(); } + /** + * Returns an immutable array containing all the values from {@code stream}, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static ImmutableIntArray copyOf(IntStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + int[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableIntArray(array); + } + /** * Returns a new, empty builder for {@link ImmutableIntArray} instances, sized to hold up to * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. @@ -192,7 +209,6 @@ public static Builder builder() { * A builder for {@link ImmutableIntArray} instances; obtained using {@link * ImmutableIntArray#builder}. */ - @CanIgnoreReturnValue public static final class Builder { private int[] array; private int count = 0; // <= array.length @@ -205,6 +221,7 @@ public static final class Builder { * Appends {@code value} to the end of the values the built {@link ImmutableIntArray} will * contain. */ + @CanIgnoreReturnValue public Builder add(int value) { ensureRoomFor(1); array[count] = value; @@ -216,6 +233,7 @@ public Builder add(int value) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableIntArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(int[] values) { ensureRoomFor(values.length); System.arraycopy(values, 0, array, count, values.length); @@ -227,6 +245,7 @@ public Builder addAll(int[] values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableIntArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Iterable values) { if (values instanceof Collection) { return addAll((Collection) values); @@ -241,6 +260,7 @@ public Builder addAll(Iterable values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableIntArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Collection values) { ensureRoomFor(values.size()); for (Integer value : values) { @@ -249,10 +269,30 @@ public Builder addAll(Collection values) { return this; } + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + @CanIgnoreReturnValue + public Builder addAll(IntStream stream) { + Spliterator.OfInt spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((IntConsumer) this::add); + return this; + } + /** * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableIntArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(ImmutableIntArray values) { ensureRoomFor(values.length()); System.arraycopy(values.array, values.start, array, count, values.length()); @@ -291,7 +331,6 @@ private static int expandedCapacity(int oldCapacity, int minCapacity) { * no data is copied as part of this step, but this may occupy more memory than strictly * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. */ - @CheckReturnValue public ImmutableIntArray build() { return count == 0 ? EMPTY : new ImmutableIntArray(array, 0, count); } @@ -378,6 +417,32 @@ public boolean contains(int target) { return indexOf(target) >= 0; } + /** + * Invokes {@code consumer} for each value contained in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // We rely on users not to call this without library desugaring. + public void forEach(IntConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** + * Returns a stream over the values in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent stream calls + @IgnoreJRERequirement + public IntStream stream() { + return Arrays.stream(array, start, end); + } + /** Returns a new, mutable copy of this array's values, as a primitive {@code int[]}. */ public int[] toArray() { return Arrays.copyOfRange(array, start, end); @@ -397,6 +462,16 @@ public ImmutableIntArray subArray(int startIndex, int endIndex) { : new ImmutableIntArray(array, start + startIndex, start + endIndex); } + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // used only from APIs that use streams + /* + * We declare this as package-private, rather than private, to avoid generating a synthetic + * accessor method (under -target 8) that would lack the Android flavor's @IgnoreJRERequirement. + */ + Spliterator.OfInt spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + /** * Returns an immutable view of this array's values as a {@code List}; note that {@code * int} values are boxed into {@link Integer} instances on demand, which can be very expensive. @@ -420,7 +495,7 @@ private AsList(ImmutableIntArray parent) { this.parent = parent; } - // inherit: isEmpty, containsAll, toArray x2, {,list,spl}iterator, stream, forEach, mutations + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations @Override public int size() { @@ -433,17 +508,17 @@ public Integer get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { return indexOf(target) >= 0; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { return target instanceof Integer ? parent.indexOf((Integer) target) : -1; } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { return target instanceof Integer ? parent.lastIndexOf((Integer) target) : -1; } @@ -452,8 +527,20 @@ public List subList(int fromIndex, int toIndex) { return parent.subArray(fromIndex, toIndex).asList(); } + // The default List spliterator is not efficiently splittable + @Override + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator spliterator() { + return parent.spliterator(); + } + @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof AsList) { AsList that = (AsList) object; return this.parent.equals(that.parent); @@ -493,7 +580,7 @@ public String toString() { * values as this one, in the same order. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } diff --git a/android/guava/src/com/google/common/primitives/ImmutableLongArray.java b/android/guava/src/com/google/common/primitives/ImmutableLongArray.java index 4ebf5b406fb9..726238c9f0f0 100644 --- a/android/guava/src/com/google/common/primitives/ImmutableLongArray.java +++ b/android/guava/src/com/google/common/primitives/ImmutableLongArray.java @@ -15,12 +15,11 @@ package com.google.common.primitives; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Immutable; import java.io.Serializable; import java.util.AbstractList; @@ -28,7 +27,11 @@ import java.util.Collection; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.LongConsumer; +import java.util.stream.LongStream; +import org.jspecify.annotations.Nullable; /** * An immutable array of {@code long} values, with an API resembling {@link List}. @@ -44,6 +47,7 @@ * hunt through classes like {@link Arrays} and {@link Longs} for them. *

  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarLongs().stream()...}. *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of * allocating garbage). * @@ -65,6 +69,8 @@ *
      *
    • Improved memory compactness and locality. *
    • Can be queried without allocating garbage. + *
    • Access to {@code LongStream} features (like {@link LongStream#sum}) using {@code stream()} + * instead of the awkward {@code stream().mapToLong(v -> v)}. *
    * *

    Disadvantages compared to {@code ImmutableList}: @@ -77,10 +83,8 @@ * * @since 22.0 */ -@Beta @GwtCompatible @Immutable -@ElementTypesAreNonnullByDefault public final class ImmutableLongArray implements Serializable { private static final ImmutableLongArray EMPTY = new ImmutableLongArray(new long[0]); @@ -163,6 +167,19 @@ public static ImmutableLongArray copyOf(Iterable values) { return builder().addAll(values).build(); } + /** + * Returns an immutable array containing all the values from {@code stream}, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + public static ImmutableLongArray copyOf(LongStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + long[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableLongArray(array); + } + /** * Returns a new, empty builder for {@link ImmutableLongArray} instances, sized to hold up to * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. @@ -194,7 +211,6 @@ public static Builder builder() { * A builder for {@link ImmutableLongArray} instances; obtained using {@link * ImmutableLongArray#builder}. */ - @CanIgnoreReturnValue public static final class Builder { private long[] array; private int count = 0; // <= array.length @@ -207,6 +223,7 @@ public static final class Builder { * Appends {@code value} to the end of the values the built {@link ImmutableLongArray} will * contain. */ + @CanIgnoreReturnValue public Builder add(long value) { ensureRoomFor(1); array[count] = value; @@ -218,6 +235,7 @@ public Builder add(long value) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableLongArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(long[] values) { ensureRoomFor(values.length); System.arraycopy(values, 0, array, count, values.length); @@ -229,6 +247,7 @@ public Builder addAll(long[] values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableLongArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Iterable values) { if (values instanceof Collection) { return addAll((Collection) values); @@ -243,6 +262,7 @@ public Builder addAll(Iterable values) { * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableLongArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(Collection values) { ensureRoomFor(values.size()); for (Long value : values) { @@ -251,10 +271,30 @@ public Builder addAll(Collection values) { return this; } + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using streams. + @CanIgnoreReturnValue + public Builder addAll(LongStream stream) { + Spliterator.OfLong spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((LongConsumer) this::add); + return this; + } + /** * Appends {@code values}, in order, to the end of the values the built {@link * ImmutableLongArray} will contain. */ + @CanIgnoreReturnValue public Builder addAll(ImmutableLongArray values) { ensureRoomFor(values.length()); System.arraycopy(values.array, values.start, array, count, values.length()); @@ -293,7 +333,6 @@ private static int expandedCapacity(int oldCapacity, int minCapacity) { * no data is copied as part of this step, but this may occupy more memory than strictly * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. */ - @CheckReturnValue public ImmutableLongArray build() { return count == 0 ? EMPTY : new ImmutableLongArray(array, 0, count); } @@ -380,6 +419,32 @@ public boolean contains(long target) { return indexOf(target) >= 0; } + /** + * Invokes {@code consumer} for each value contained in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // We rely on users not to call this without library desugaring. + public void forEach(LongConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** + * Returns a stream over the values in this array, in order. + * + * @since 33.4.0 (but since 22.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent stream calls + @IgnoreJRERequirement + public LongStream stream() { + return Arrays.stream(array, start, end); + } + /** Returns a new, mutable copy of this array's values, as a primitive {@code long[]}. */ public long[] toArray() { return Arrays.copyOfRange(array, start, end); @@ -399,6 +464,16 @@ public ImmutableLongArray subArray(int startIndex, int endIndex) { : new ImmutableLongArray(array, start + startIndex, start + endIndex); } + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // used only from APIs that use streams + /* + * We declare this as package-private, rather than private, to avoid generating a synthetic + * accessor method (under -target 8) that would lack the Android flavor's @IgnoreJRERequirement. + */ + Spliterator.OfLong spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + /** * Returns an immutable view of this array's values as a {@code List}; note that {@code * long} values are boxed into {@link Long} instances on demand, which can be very expensive. The @@ -422,7 +497,7 @@ private AsList(ImmutableLongArray parent) { this.parent = parent; } - // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, mutations + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations @Override public int size() { @@ -435,17 +510,17 @@ public Long get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { return indexOf(target) >= 0; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { return target instanceof Long ? parent.indexOf((Long) target) : -1; } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { return target instanceof Long ? parent.lastIndexOf((Long) target) : -1; } @@ -454,8 +529,20 @@ public List subList(int fromIndex, int toIndex) { return parent.subArray(fromIndex, toIndex).asList(); } + // The default List spliterator is not efficiently splittable + @Override + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator spliterator() { + return parent.spliterator(); + } + @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object instanceof AsList) { AsList that = (AsList) object; return this.parent.equals(that.parent); @@ -495,7 +582,7 @@ public String toString() { * values as this one, in the same order. */ @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } diff --git a/android/guava/src/com/google/common/primitives/Ints.java b/android/guava/src/com/google/common/primitives/Ints.java index e07bdd88d93e..e5a4d7155d35 100644 --- a/android/guava/src/com/google/common/primitives/Ints.java +++ b/android/guava/src/com/google/common/primitives/Ints.java @@ -19,10 +19,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -31,7 +32,9 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code int} primitives, that are not already found in either @@ -44,14 +47,13 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Ints extends IntsMethodsForWeb { private Ints() {} /** * The number of bytes required to represent a primitive {@code int} value. * - *

    Java 8 users: use {@link Integer#BYTES} instead. + *

    Java 8+ users: use {@link Integer#BYTES} instead. */ public static final int BYTES = Integer.SIZE / Byte.SIZE; @@ -66,7 +68,7 @@ private Ints() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Integer) * value).hashCode()}. * - *

    Java 8 users: use {@link Integer#hashCode(int)} instead. + *

    Java 8+ users: use {@link Integer#hashCode(int)} instead. * * @param value a primitive {@code int} value * @return a hash code for the value @@ -111,7 +113,7 @@ public static int saturatedCast(long value) { * Compares the two specified {@code int} values. The sign of the value returned is the same as * that of {@code ((Integer) a).compareTo(b)}. * - *

    Note for Java 7 and later: this method should be treated as deprecated; use the + *

    Note: this method is now unnecessary and should be treated as deprecated; use the * equivalent {@link Integer#compare} method instead. * * @param a the first {@code int} to compare @@ -119,8 +121,9 @@ public static int saturatedCast(long value) { * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @InlineMe(replacement = "Integer.compare(a, b)") public static int compare(int a, int b) { - return (a < b) ? -1 : ((a > b) ? 1 : 0); + return Integer.compare(a, b); } /** @@ -261,13 +264,17 @@ public static int max(int... array) { * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code * value} is greater than {@code max}, {@code max} is returned. * + *

    Java 21+ users: Use {@code Math.clamp} instead. Note that that method is capable of + * constraining a {@code long} input to an {@code int} range. + * * @param value the {@code int} value to constrain * @param min the lower bound (inclusive) of the range to constrain {@code value} to * @param max the upper bound (inclusive) of the range to constrain {@code value} to * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta + // A call to bare "min" or "max" would resolve to our varargs method, not to any static import. + @SuppressWarnings("StaticImportPreferred") public static int constrainToRange(int value, int min, int max) { checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); return Math.min(Math.max(value, min), max); @@ -279,13 +286,15 @@ public static int constrainToRange(int value, int min, int max) { * * @param arrays zero or more {@code int} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static int[] concat(int[]... arrays) { - int length = 0; + long length = 0; for (int[] array : arrays) { length += array.length; } - int[] result = new int[length]; + int[] result = new int[checkNoOverflow(length)]; int pos = 0; for (int[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -294,6 +303,14 @@ public static int[] concat(int[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code @@ -337,7 +354,7 @@ public static int fromBytes(byte b1, byte b2, byte b3, byte b4) { private static final class IntConverter extends Converter implements Serializable { - static final IntConverter INSTANCE = new IntConverter(); + static final Converter INSTANCE = new IntConverter(); @Override protected Integer doForward(String value) { @@ -358,7 +375,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } /** @@ -372,7 +389,6 @@ private Object readResolve() { * * @since 16.0 */ - @Beta public static Converter stringConverter() { return IntConverter.INSTANCE; } @@ -439,10 +455,12 @@ private enum LexicographicalComparator implements Comparator { INSTANCE; @Override + // A call to bare "min" or "max" would resolve to our varargs method, not to any static import. + @SuppressWarnings("StaticImportPreferred") public int compare(int[] left, int[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Ints.compare(left[i], right[i]); + int result = Integer.compare(left[i], right[i]); if (result != 0) { return result; } @@ -510,6 +528,82 @@ public static void reverse(int[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Ints.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(int[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Ints.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(int[] array, int distance, int fromIndex, int toIndex) { + // There are several well-known algorithms for rotating part of an array (or, equivalently, + // exchanging two blocks of memory). This classic text by Gries and Mills mentions several: + // https://ecommons.cornell.edu/bitstream/handle/1813/6292/81-452.pdf. + // (1) "Reversal", the one we have here. + // (2) "Dolphin". If we're rotating an array a of size n by a distance of d, then element a[0] + // ends up at a[d], which in turn ends up at a[2d], and so on until we get back to a[0]. + // (All indices taken mod n.) If d and n are mutually prime, all elements will have been + // moved at that point. Otherwise, we can rotate the cycle a[1], a[1 + d], a[1 + 2d], etc, + // then a[2] etc, and so on until we have rotated all elements. There are gcd(d, n) cycles + // in all. + // (3) "Successive". We can consider that we are exchanging a block of size d (a[0..d-1]) with a + // block of size n-d (a[d..n-1]), where in general these blocks have different sizes. If we + // imagine a line separating the first block from the second, we can proceed by exchanging + // the smaller of these blocks with the far end of the other one. That leaves us with a + // smaller version of the same problem. + // Say we are rotating abcdefgh by 5. We start with abcde|fgh. The smaller block is [fgh]: + // [abc]de|[fgh] -> [fgh]de|[abc]. Now [fgh] is in the right place, but we need to swap [de] + // with [abc]: fgh[de]|a[bc] -> fgh[bc]|a[de]. Now we need to swap [a] with [bc]: + // fgh[b]c|[a]de -> fgh[a]c|[b]de. Finally we need to swap [c] with [b]: + // fgha[c]|[b]de -> fgha[b]|[c]de. Because these two blocks are the same size, we are done. + // The Dolphin algorithm is attractive because it does the fewest array reads and writes: each + // array slot is read and written exactly once. However, it can have very poor memory locality: + // benchmarking shows it can take 7 times longer than the other two in some cases. The other two + // do n swaps, minus a delta (0 or 2 for Reversal, gcd(d, n) for Successive), so that's about + // twice as many reads and writes. But benchmarking shows that they usually perform better than + // Dolphin. Reversal is about as good as Successive on average, and it is much simpler, + // especially since we already have a `reverse` method. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code int} value * in the manner of {@link Number#intValue}. @@ -547,6 +641,8 @@ public static int[] toArray(Collection collection) { * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for * the returned list is unspecified. * + *

    The returned list is serializable. + * *

    Note: when possible, you should represent your data as an {@link ImmutableIntArray} * instead, which has an {@link ImmutableIntArray#asList asList} view. * @@ -594,13 +690,24 @@ public Integer get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator.OfInt spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Integer) && Ints.indexOf(array, (Integer) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Integer) { int i = Ints.indexOf(array, (Integer) target, start, end); @@ -612,7 +719,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Integer) { int i = Ints.lastIndexOf(array, (Integer) target, start, end); @@ -643,7 +750,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -686,7 +793,7 @@ int[] toIntArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } /** @@ -697,8 +804,8 @@ int[] toIntArray() { * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, * and returns {@code null} if non-ASCII digits are present in the string. * - *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite - * the change to {@link Integer#parseInt(String)} for that version. + *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even though {@link + * Integer#parseInt(String)} accepts them. * * @param string the string representation of an integer value * @return the integer value represented by {@code string}, or {@code null} if {@code string} has @@ -706,9 +813,7 @@ int[] toIntArray() { * @throws NullPointerException if {@code string} is {@code null} * @since 11.0 */ - @Beta - @CheckForNull - public static Integer tryParse(String string) { + public static @Nullable Integer tryParse(String string) { return tryParse(string, 10); } @@ -720,8 +825,8 @@ public static Integer tryParse(String string) { * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, * and returns {@code null} if non-ASCII digits are present in the string. * - *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite - * the change to {@link Integer#parseInt(String, int)} for that version. + *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even though {@link + * Integer#parseInt(String)} accepts them. * * @param string the string representation of an integer value * @param radix the radix to use when parsing @@ -732,9 +837,7 @@ public static Integer tryParse(String string) { * @throws NullPointerException if {@code string} is {@code null} * @since 19.0 */ - @Beta - @CheckForNull - public static Integer tryParse(String string, int radix) { + public static @Nullable Integer tryParse(String string, int radix) { Long result = Longs.tryParse(string, radix); if (result == null || result.longValue() != result.intValue()) { return null; diff --git a/android/guava/src/com/google/common/primitives/IntsMethodsForWeb.java b/android/guava/src/com/google/common/primitives/IntsMethodsForWeb.java index c59c6b05862d..cb87bd2929f8 100644 --- a/android/guava/src/com/google/common/primitives/IntsMethodsForWeb.java +++ b/android/guava/src/com/google/common/primitives/IntsMethodsForWeb.java @@ -21,5 +21,4 @@ * version. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class IntsMethodsForWeb {} diff --git a/android/guava/src/com/google/common/primitives/Longs.java b/android/guava/src/com/google/common/primitives/Longs.java index 6f60656bcb1a..be7c9165fc5c 100644 --- a/android/guava/src/com/google/common/primitives/Longs.java +++ b/android/guava/src/com/google/common/primitives/Longs.java @@ -19,9 +19,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -30,7 +32,9 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import java.util.Spliterator; +import java.util.Spliterators; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code long} primitives, that are not already found in @@ -43,14 +47,13 @@ * @since 1.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Longs { private Longs() {} /** * The number of bytes required to represent a primitive {@code long} value. * - *

    Java 8 users: use {@link Long#BYTES} instead. + *

    Java 8+ users: use {@link Long#BYTES} instead. */ public static final int BYTES = Long.SIZE / Byte.SIZE; @@ -69,7 +72,7 @@ private Longs() {} * might be different from {@code ((Long) value).hashCode()} in GWT because {@link * Long#hashCode()} in GWT does not obey the JRE contract. * - *

    Java 8 users: use {@link Long#hashCode(long)} instead. + *

    Java 8+ users: use {@link Long#hashCode(long)} instead. * * @param value a primitive {@code long} value * @return a hash code for the value @@ -82,7 +85,7 @@ public static int hashCode(long value) { * Compares the two specified {@code long} values. The sign of the value returned is the same as * that of {@code ((Long) a).compareTo(b)}. * - *

    Note for Java 7 and later: this method should be treated as deprecated; use the + *

    Note: this method is now unnecessary and should be treated as deprecated; use the * equivalent {@link Long#compare} method instead. * * @param a the first {@code long} to compare @@ -90,8 +93,9 @@ public static int hashCode(long value) { * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @InlineMe(replacement = "Long.compare(a, b)") public static int compare(long a, long b) { - return (a < b) ? -1 : ((a > b) ? 1 : 0); + return Long.compare(a, b); } /** @@ -228,13 +232,15 @@ public static long max(long... array) { * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code * value} is greater than {@code max}, {@code max} is returned. * + *

    Java 21+ users: Use {@code Math.clamp} instead. Note that that method is capable of + * constraining a {@code long} input to an {@code int} range. + * * @param value the {@code long} value to constrain * @param min the lower bound (inclusive) of the range to constrain {@code value} to * @param max the upper bound (inclusive) of the range to constrain {@code value} to * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta public static long constrainToRange(long value, long min, long max) { checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); return Math.min(Math.max(value, min), max); @@ -246,13 +252,15 @@ public static long constrainToRange(long value, long min, long max) { * * @param arrays zero or more {@code long} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static long[] concat(long[]... arrays) { - int length = 0; + long length = 0; for (long[] array : arrays) { length += array.length; } - long[] result = new long[length]; + long[] result = new long[checkNoOverflow(length)]; int pos = 0; for (long[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -261,6 +269,14 @@ public static long[] concat(long[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns a big-endian representation of {@code value} in an 8-element byte array; equivalent to * {@code ByteBuffer.allocate(8).putLong(value).array()}. For example, the input value {@code @@ -352,8 +368,8 @@ static int digit(char c) { * an exception if parsing fails. Additionally, this method only accepts ASCII digits, and returns * {@code null} if non-ASCII digits are present in the string. * - *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite - * the change to {@link Long#parseLong(String)} for that version. + *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even though {@link + * Integer#parseInt(String)} accepts them. * * @param string the string representation of a long value * @return the long value represented by {@code string}, or {@code null} if {@code string} has a @@ -361,9 +377,7 @@ static int digit(char c) { * @throws NullPointerException if {@code string} is {@code null} * @since 14.0 */ - @Beta - @CheckForNull - public static Long tryParse(String string) { + public static @Nullable Long tryParse(String string) { return tryParse(string, 10); } @@ -375,10 +389,10 @@ public static Long tryParse(String string) { * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, * and returns {@code null} if non-ASCII digits are present in the string. * - *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite - * the change to {@link Long#parseLong(String, int)} for that version. + *

    Note that strings prefixed with ASCII {@code '+'} are rejected, even though {@link + * Integer#parseInt(String)} accepts them. * - * @param string the string representation of an long value + * @param string the string representation of a long value * @param radix the radix to use when parsing * @return the long value represented by {@code string} using {@code radix}, or {@code null} if * {@code string} has a length of zero or cannot be parsed as a long value @@ -387,9 +401,7 @@ public static Long tryParse(String string) { * @throws NullPointerException if {@code string} is {@code null} * @since 19.0 */ - @Beta - @CheckForNull - public static Long tryParse(String string, int radix) { + public static @Nullable Long tryParse(String string, int radix) { if (checkNotNull(string).isEmpty()) { return null; } @@ -432,7 +444,7 @@ public static Long tryParse(String string, int radix) { } private static final class LongConverter extends Converter implements Serializable { - static final LongConverter INSTANCE = new LongConverter(); + static final Converter INSTANCE = new LongConverter(); @Override protected Long doForward(String value) { @@ -453,7 +465,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } /** @@ -467,7 +479,6 @@ private Object readResolve() { * * @since 16.0 */ - @Beta public static Converter stringConverter() { return LongConverter.INSTANCE; } @@ -538,7 +549,7 @@ private enum LexicographicalComparator implements Comparator { public int compare(long[] left, long[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Longs.compare(left[i], right[i]); + int result = Long.compare(left[i], right[i]); if (result != 0) { return result; } @@ -606,6 +617,56 @@ public static void reverse(long[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Longs.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(long[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Longs.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(long[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code long} value * in the manner of {@link Number#longValue}. @@ -643,6 +704,8 @@ public static long[] toArray(Collection collection) { * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for * the returned list is unspecified. * + *

    The returned list is serializable. + * *

    Note: when possible, you should represent your data as an {@link ImmutableLongArray} * instead, which has an {@link ImmutableLongArray#asList asList} view. * @@ -690,13 +753,24 @@ public Long get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + @SuppressWarnings("Java7ApiChecker") + /* + * This is an override that is not directly visible to callers, so NewApi will catch calls to + * Collection.spliterator() where necessary. + */ + @IgnoreJRERequirement + public Spliterator.OfLong spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Long) && Longs.indexOf(array, (Long) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Long) { int i = Longs.indexOf(array, (Long) target, start, end); @@ -708,7 +782,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Long) { int i = Longs.lastIndexOf(array, (Long) target, start, end); @@ -739,7 +813,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -782,6 +856,6 @@ long[] toLongArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/primitives/ParametricNullness.java b/android/guava/src/com/google/common/primitives/ParametricNullness.java index 4289b9b164f1..598e5e68bcf6 100644 --- a/android/guava/src/com/google/common/primitives/ParametricNullness.java +++ b/android/guava/src/com/google/common/primitives/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/primitives/ParseRequest.java b/android/guava/src/com/google/common/primitives/ParseRequest.java index a102d69b06e1..97b0f1b57abc 100644 --- a/android/guava/src/com/google/common/primitives/ParseRequest.java +++ b/android/guava/src/com/google/common/primitives/ParseRequest.java @@ -18,7 +18,6 @@ /** A string to be parsed as a number and the radix to interpret it in. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class ParseRequest { final String rawValue; final int radix; diff --git a/android/guava/src/com/google/common/primitives/Primitives.java b/android/guava/src/com/google/common/primitives/Primitives.java index 7ceed036555a..9e2f71093b06 100644 --- a/android/guava/src/com/google/common/primitives/Primitives.java +++ b/android/guava/src/com/google/common/primitives/Primitives.java @@ -16,7 +16,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.GwtCompatible; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -29,15 +29,18 @@ * @author Kevin Bourrillion * @since 1.0 */ -@GwtIncompatible -@ElementTypesAreNonnullByDefault +@GwtCompatible public final class Primitives { private Primitives() {} /** A map from primitive types to their corresponding wrapper types. */ + // It's a constant, and we can't use ImmutableMap here without creating a circular dependency. + @SuppressWarnings("ConstantCaseForConstants") private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; /** A map from wrapper types to their corresponding primitive types. */ + // It's a constant, and we can't use ImmutableMap here without creating a circular dependency. + @SuppressWarnings("ConstantCaseForConstants") private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; // Sad that we can't use a BiMap. :( diff --git a/android/guava/src/com/google/common/primitives/Shorts.java b/android/guava/src/com/google/common/primitives/Shorts.java index 09e0f7cfc312..9af7d3918e96 100644 --- a/android/guava/src/com/google/common/primitives/Shorts.java +++ b/android/guava/src/com/google/common/primitives/Shorts.java @@ -19,10 +19,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Converter; +import com.google.errorprone.annotations.InlineMe; import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; @@ -31,7 +32,7 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to {@code short} primitives, that are not already found in @@ -44,14 +45,13 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Shorts extends ShortsMethodsForWeb { private Shorts() {} /** * The number of bytes required to represent a primitive {@code short} value. * - *

    Java 8 users: use {@link Short#BYTES} instead. + *

    Java 8+ users: use {@link Short#BYTES} instead. */ public static final int BYTES = Short.SIZE / Byte.SIZE; @@ -66,7 +66,7 @@ private Shorts() {} * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Short) * value).hashCode()}. * - *

    Java 8 users: use {@link Short#hashCode(short)} instead. + *

    Java 8+ users: use {@link Short#hashCode(short)} instead. * * @param value a primitive {@code short} value * @return a hash code for the value @@ -110,7 +110,7 @@ public static short saturatedCast(long value) { * Compares the two specified {@code short} values. The sign of the value returned is the same as * that of {@code ((Short) a).compareTo(b)}. * - *

    Note for Java 7 and later: this method should be treated as deprecated; use the + *

    Note: this method is now unnecessary and should be treated as deprecated; use the * equivalent {@link Short#compare} method instead. * * @param a the first {@code short} to compare @@ -118,8 +118,9 @@ public static short saturatedCast(long value) { * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @InlineMe(replacement = "Short.compare(a, b)") public static int compare(short a, short b) { - return a - b; // safe due to restricted range + return Short.compare(a, b); } /** @@ -266,7 +267,6 @@ public static short max(short... array) { * @throws IllegalArgumentException if {@code min > max} * @since 21.0 */ - @Beta public static short constrainToRange(short value, short min, short max) { checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); return value < min ? min : value < max ? value : max; @@ -279,13 +279,15 @@ public static short constrainToRange(short value, short min, short max) { * * @param arrays zero or more {@code short} arrays * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} */ public static short[] concat(short[]... arrays) { - int length = 0; + long length = 0; for (short[] array : arrays) { length += array.length; } - short[] result = new short[length]; + short[] result = new short[checkNoOverflow(length)]; int pos = 0; for (short[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -294,6 +296,14 @@ public static short[] concat(short[]... arrays) { return result; } + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + /** * Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to * {@code ByteBuffer.allocate(2).putShort(value).array()}. For example, the input value {@code @@ -337,7 +347,7 @@ public static short fromBytes(byte b1, byte b2) { private static final class ShortConverter extends Converter implements Serializable { - static final ShortConverter INSTANCE = new ShortConverter(); + static final Converter INSTANCE = new ShortConverter(); @Override protected Short doForward(String value) { @@ -358,7 +368,7 @@ private Object readResolve() { return INSTANCE; } - private static final long serialVersionUID = 1; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 1; } /** @@ -372,7 +382,6 @@ private Object readResolve() { * * @since 16.0 */ - @Beta public static Converter stringConverter() { return ShortConverter.INSTANCE; } @@ -444,7 +453,7 @@ private enum LexicographicalComparator implements Comparator { public int compare(short[] left, short[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Shorts.compare(left[i], right[i]); + int result = Short.compare(left[i], right[i]); if (result != 0) { return result; } @@ -512,6 +521,56 @@ public static void reverse(short[] array, int fromIndex, int toIndex) { } } + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Shorts.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(short[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Shorts.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

    The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(short[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + /** * Returns an array containing each value of {@code collection}, converted to a {@code short} * value in the manner of {@link Number#shortValue}. @@ -549,6 +608,8 @@ public static short[] toArray(Collection collection) { * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for * the returned list is unspecified. * + *

    The returned list is serializable. + * * @param backingArray the array to back the list * @return a list view of the array */ @@ -593,13 +654,13 @@ public Short get(int index) { } @Override - public boolean contains(@CheckForNull Object target) { + public boolean contains(@Nullable Object target) { // Overridden to prevent a ton of boxing return (target instanceof Short) && Shorts.indexOf(array, (Short) target, start, end) != -1; } @Override - public int indexOf(@CheckForNull Object target) { + public int indexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Short) { int i = Shorts.indexOf(array, (Short) target, start, end); @@ -611,7 +672,7 @@ public int indexOf(@CheckForNull Object target) { } @Override - public int lastIndexOf(@CheckForNull Object target) { + public int lastIndexOf(@Nullable Object target) { // Overridden to prevent a ton of boxing if (target instanceof Short) { int i = Shorts.lastIndexOf(array, (Short) target, start, end); @@ -642,7 +703,7 @@ public List subList(int fromIndex, int toIndex) { } @Override - public boolean equals(@CheckForNull Object object) { + public boolean equals(@Nullable Object object) { if (object == this) { return true; } @@ -685,6 +746,6 @@ short[] toShortArray() { return Arrays.copyOfRange(array, start, end); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } } diff --git a/android/guava/src/com/google/common/primitives/ShortsMethodsForWeb.java b/android/guava/src/com/google/common/primitives/ShortsMethodsForWeb.java index bb0ff103ce2f..c36276838cc6 100644 --- a/android/guava/src/com/google/common/primitives/ShortsMethodsForWeb.java +++ b/android/guava/src/com/google/common/primitives/ShortsMethodsForWeb.java @@ -21,5 +21,4 @@ * version. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class ShortsMethodsForWeb {} diff --git a/android/guava/src/com/google/common/primitives/SignedBytes.java b/android/guava/src/com/google/common/primitives/SignedBytes.java index 5fabaab6bd93..0204de6dcb59 100644 --- a/android/guava/src/com/google/common/primitives/SignedBytes.java +++ b/android/guava/src/com/google/common/primitives/SignedBytes.java @@ -36,7 +36,6 @@ // TODO(kevinb): how to prevent warning on UnsignedBytes when building GWT // javadoc? @GwtCompatible -@ElementTypesAreNonnullByDefault public final class SignedBytes { private SignedBytes() {} @@ -82,17 +81,15 @@ public static byte saturatedCast(long value) { * Compares the two specified {@code byte} values. The sign of the value returned is the same as * that of {@code ((Byte) a).compareTo(b)}. * - *

    Note: this method behaves identically to the JDK 7 method {@link Byte#compare}. + *

    Note: this method behaves identically to {@link Byte#compare}. * * @param a the first {@code byte} to compare * @param b the second {@code byte} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ - // TODO(kevinb): if Ints.compare etc. are ever removed, *maybe* remove this - // one too, which would leave compare methods only on the Unsigned* classes. public static int compare(byte a, byte b) { - return a - b; // safe due to restricted range + return Byte.compare(a, b); } /** @@ -181,7 +178,7 @@ private enum LexicographicalComparator implements Comparator { public int compare(byte[] left, byte[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = SignedBytes.compare(left[i], right[i]); + int result = Byte.compare(left[i], right[i]); if (result != 0) { return result; } diff --git a/android/guava/src/com/google/common/primitives/UnsignedBytes.java b/android/guava/src/com/google/common/primitives/UnsignedBytes.java index db4f489e491f..cb6a6ac1407f 100644 --- a/android/guava/src/com/google/common/primitives/UnsignedBytes.java +++ b/android/guava/src/com/google/common/primitives/UnsignedBytes.java @@ -17,15 +17,20 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; +import static java.security.AccessController.doPrivileged; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.lang.reflect.Field; import java.nio.ByteOrder; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Comparator; +import java.util.Objects; import sun.misc.Unsafe; /** @@ -43,8 +48,8 @@ * @author Louis Wasserman * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class UnsignedBytes { private UnsignedBytes() {} @@ -68,7 +73,7 @@ private UnsignedBytes() {} * Returns the value of the given byte as an integer, when treated as unsigned. That is, returns * {@code value + 256} if {@code value} is negative; {@code value} itself otherwise. * - *

    Java 8 users: use {@link Byte#toUnsignedInt(byte)} instead. + *

    Java 8+ users: use {@link Byte#toUnsignedInt(byte)} instead. * * @since 6.0 */ @@ -167,7 +172,6 @@ public static byte max(byte... array) { * * @since 13.0 */ - @Beta public static String toString(byte x) { return toString(x, 10); } @@ -182,7 +186,6 @@ public static String toString(byte x) { * and {@link Character#MAX_RADIX}. * @since 13.0 */ - @Beta public static String toString(byte x, int radix) { checkArgument( radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX, @@ -201,7 +204,6 @@ public static String toString(byte x, int radix) { * Byte#parseByte(String)}) * @since 13.0 */ - @Beta @CanIgnoreReturnValue public static byte parseUnsignedByte(String string) { return parseUnsignedByte(string, 10); @@ -219,7 +221,6 @@ public static byte parseUnsignedByte(String string) { * Byte#parseByte(String)}) * @since 13.0 */ - @Beta @CanIgnoreReturnValue public static byte parseUnsignedByte(String string, int radix) { int parse = Integer.parseInt(checkNotNull(string), radix); @@ -267,6 +268,9 @@ public static String join(String separator, byte... array) { * support only identity equality), but it is consistent with {@link * java.util.Arrays#equals(byte[], byte[])}. * + *

    Java 9+ users: Use {@link Arrays#compareUnsigned(byte[], byte[]) + * Arrays::compareUnsigned}. + * * @since 2.0 */ public static Comparator lexicographicalComparator() { @@ -292,6 +296,7 @@ static class LexicographicalComparatorHolder { static final Comparator BEST_COMPARATOR = getBestComparator(); + @SuppressWarnings("SunApi") // b/345822163 @VisibleForTesting enum UnsafeComparator implements Comparator { INSTANCE; @@ -322,7 +327,7 @@ enum UnsafeComparator implements Comparator { static { // fall back to the safer pure java implementation unless we're in // a 64-bit JVM with an 8-byte aligned field offset. - if (!("64".equals(System.getProperty("sun.arch.data.model")) + if (!(Objects.equals(System.getProperty("sun.arch.data.model"), "64") && (BYTE_ARRAY_BASE_OFFSET % 8) == 0 // sanity check - this should never fail && theUnsafe.arrayIndexScale(byte[].class) == 1)) { @@ -336,34 +341,34 @@ enum UnsafeComparator implements Comparator { * * @return a sun.misc.Unsafe */ - private static sun.misc.Unsafe getUnsafe() { + private static Unsafe getUnsafe() { try { - return sun.misc.Unsafe.getUnsafe(); + return Unsafe.getUnsafe(); } catch (SecurityException e) { // that's okay; try reflection instead } try { - return java.security.AccessController.doPrivileged( - new java.security.PrivilegedExceptionAction() { - @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) { - return k.cast(x); + return doPrivileged( + (PrivilegedExceptionAction) + () -> { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } } - } - throw new NoSuchFieldError("the Unsafe"); - } - }); - } catch (java.security.PrivilegedActionException e) { + throw new NoSuchFieldError("the Unsafe"); + }); + } catch (PrivilegedActionException e) { throw new RuntimeException("Could not initialize intrinsics", e.getCause()); } } @Override + // Long.compareUnsigned is available under Android, which is what we really care about. + @SuppressWarnings("Java7ApiChecker") public int compare(byte[] left, byte[] right) { int stride = 8; int minLength = Math.min(left.length, right.length); @@ -379,7 +384,7 @@ public int compare(byte[] left, byte[] right) { long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); if (lw != rw) { if (BIG_ENDIAN) { - return UnsignedLongs.compare(lw, rw); + return Long.compareUnsigned(lw, rw); } /* diff --git a/android/guava/src/com/google/common/primitives/UnsignedInteger.java b/android/guava/src/com/google/common/primitives/UnsignedInteger.java index 0b30cef3f4c6..0bc2eb467a74 100644 --- a/android/guava/src/com/google/common/primitives/UnsignedInteger.java +++ b/android/guava/src/com/google/common/primitives/UnsignedInteger.java @@ -22,8 +22,9 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.math.BigInteger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A wrapper class for unsigned {@code int} values, supporting arithmetic operations. @@ -39,7 +40,6 @@ * @since 11.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class UnsignedInteger extends Number implements Comparable { public static final UnsignedInteger ZERO = fromIntBits(0); public static final UnsignedInteger ONE = fromIntBits(1); @@ -143,6 +143,7 @@ public UnsignedInteger minus(UnsignedInteger val) { * * @since 14.0 */ + @J2ktIncompatible @GwtIncompatible // Does not truncate correctly public UnsignedInteger times(UnsignedInteger val) { // TODO(lowasser): make this GWT-compatible @@ -197,7 +198,7 @@ public float floatValue() { } /** - * Returns the value of this {@code UnsignedInteger} as a {@code float}, analogous to a widening + * Returns the value of this {@code UnsignedInteger} as a {@code double}, analogous to a widening * primitive conversion from {@code int} to {@code double}, and correctly rounded. */ @Override @@ -227,7 +228,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof UnsignedInteger) { UnsignedInteger other = (UnsignedInteger) obj; return value == other.value; diff --git a/android/guava/src/com/google/common/primitives/UnsignedInts.java b/android/guava/src/com/google/common/primitives/UnsignedInts.java index ec6474e20f8a..d3fd623baa7a 100644 --- a/android/guava/src/com/google/common/primitives/UnsignedInts.java +++ b/android/guava/src/com/google/common/primitives/UnsignedInts.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Arrays; @@ -45,9 +44,7 @@ * @author Louis Wasserman * @since 11.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class UnsignedInts { static final long INT_MASK = 0xffffffffL; @@ -61,13 +58,15 @@ static int flip(int value) { * Compares the two specified {@code int} values, treating them as unsigned values between {@code * 0} and {@code 2^32 - 1} inclusive. * - *

    Java 8 users: use {@link Integer#compareUnsigned(int, int)} instead. + *

    Note: this method is now unnecessary and should be treated as deprecated; use the + * equivalent {@link Integer#compareUnsigned(int, int)} method instead. * * @param a the first unsigned {@code int} to compare * @param b the second unsigned {@code int} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @SuppressWarnings("InlineMeInliner") // Integer.compare unavailable under GWT+J2CL public static int compare(int a, int b) { return Ints.compare(flip(a), flip(b)); } @@ -75,7 +74,7 @@ public static int compare(int a, int b) { /** * Returns the value of the given {@code int} as a {@code long}, when treated as unsigned. * - *

    Java 8 users: use {@link Integer#toUnsignedLong(int)} instead. + *

    Java 8+ users: use {@link Integer#toUnsignedLong(int)} instead. */ public static long toLong(int value) { return value & INT_MASK; @@ -187,6 +186,9 @@ public static String join(String separator, int... array) { * *

    The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays * support only identity equality), but it is consistent with {@link Arrays#equals(int[], int[])}. + * + *

    Java 9+ users: Use {@link Arrays#compareUnsigned(int[], int[]) + * Arrays::compareUnsigned}. */ public static Comparator lexicographicalComparator() { return LexicographicalComparator.INSTANCE; @@ -196,6 +198,8 @@ enum LexicographicalComparator implements Comparator { INSTANCE; @Override + // A call to bare "min" or "max" would resolve to our varargs method, not to any static import. + @SuppressWarnings("StaticImportPreferred") public int compare(int[] left, int[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { @@ -273,7 +277,7 @@ public static void sortDescending(int[] array, int fromIndex, int toIndex) { * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 32-bit * quantities. * - *

    Java 8 users: use {@link Integer#divideUnsigned(int, int)} instead. + *

    Java 8+ users: use {@link Integer#divideUnsigned(int, int)} instead. * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) @@ -287,7 +291,7 @@ public static int divide(int dividend, int divisor) { * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 32-bit * quantities. * - *

    Java 8 users: use {@link Integer#remainderUnsigned(int, int)} instead. + *

    Java 8+ users: use {@link Integer#remainderUnsigned(int, int)} instead. * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) @@ -329,7 +333,7 @@ public static int decode(String stringValue) { /** * Returns the unsigned {@code int} value represented by the given decimal string. * - *

    Java 8 users: use {@link Integer#parseUnsignedInt(String)} instead. + *

    Java 8+ users: use {@link Integer#parseUnsignedInt(String)} instead. * * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value * @throws NullPointerException if {@code s} is null (in contrast to {@link @@ -343,7 +347,7 @@ public static int parseUnsignedInt(String s) { /** * Returns the unsigned {@code int} value represented by a string with the given radix. * - *

    Java 8 users: use {@link Integer#parseUnsignedInt(String, int)} instead. + *

    Java 8+ users: use {@link Integer#parseUnsignedInt(String, int)} instead. * * @param string the string containing the unsigned integer representation to be parsed. * @param radix the radix to use while parsing {@code s}; must be between {@link @@ -367,7 +371,7 @@ public static int parseUnsignedInt(String string, int radix) { /** * Returns a string representation of x, where x is treated as unsigned. * - *

    Java 8 users: use {@link Integer#toUnsignedString(int)} instead. + *

    Java 8+ users: use {@link Integer#toUnsignedString(int)} instead. */ public static String toString(int x) { return toString(x, 10); @@ -377,7 +381,7 @@ public static String toString(int x) { * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as * unsigned. * - *

    Java 8 users: use {@link Integer#toUnsignedString(int, int)} instead. + *

    Java 8+ users: use {@link Integer#toUnsignedString(int, int)} instead. * * @param x the value to convert to a string. * @param radix the radix to use while working with {@code x} diff --git a/android/guava/src/com/google/common/primitives/UnsignedLong.java b/android/guava/src/com/google/common/primitives/UnsignedLong.java index d803634f4946..f7580204dd33 100644 --- a/android/guava/src/com/google/common/primitives/UnsignedLong.java +++ b/android/guava/src/com/google/common/primitives/UnsignedLong.java @@ -19,9 +19,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import java.io.Serializable; import java.math.BigInteger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A wrapper class for unsigned {@code long} values, supporting arithmetic operations. @@ -38,8 +37,7 @@ * @since 11.0 */ @GwtCompatible(serializable = true) -@ElementTypesAreNonnullByDefault -public final class UnsignedLong extends Number implements Comparable, Serializable { +public final class UnsignedLong extends Number implements Comparable { private static final long UNSIGNED_MASK = 0x7fffffffffffffffL; @@ -242,7 +240,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof UnsignedLong) { UnsignedLong other = (UnsignedLong) obj; return value == other.value; diff --git a/android/guava/src/com/google/common/primitives/UnsignedLongs.java b/android/guava/src/com/google/common/primitives/UnsignedLongs.java index 31c51cc3464d..d057077ca39d 100644 --- a/android/guava/src/com/google/common/primitives/UnsignedLongs.java +++ b/android/guava/src/com/google/common/primitives/UnsignedLongs.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkPositionIndexes; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.math.BigInteger; @@ -48,9 +47,7 @@ * @author Colin Evans * @since 10.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class UnsignedLongs { private UnsignedLongs() {} @@ -69,13 +66,15 @@ private static long flip(long a) { * Compares the two specified {@code long} values, treating them as unsigned values between {@code * 0} and {@code 2^64 - 1} inclusive. * - *

    Java 8 users: use {@link Long#compareUnsigned(long, long)} instead. + *

    Note: this method is now unnecessary and should be treated as deprecated; use the + * equivalent {@link Long#compareUnsigned(long, long)} method instead. * * @param a the first unsigned {@code long} to compare * @param b the second unsigned {@code long} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ + @SuppressWarnings("InlineMeInliner") // Integer.compare unavailable under GWT+J2CL public static int compare(long a, long b) { return Longs.compare(flip(a), flip(b)); } @@ -153,6 +152,9 @@ public static String join(String separator, long... array) { *

    The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays * support only identity equality), but it is consistent with {@link Arrays#equals(long[], * long[])}. + * + *

    Java 9+ users: Use {@link Arrays#compareUnsigned(long[], long[]) + * Arrays::compareUnsigned}. */ public static Comparator lexicographicalComparator() { return LexicographicalComparator.INSTANCE; @@ -239,7 +241,7 @@ public static void sortDescending(long[] array, int fromIndex, int toIndex) { * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit * quantities. * - *

    Java 8 users: use {@link Long#divideUnsigned(long, long)} instead. + *

    Java 8+ users: use {@link Long#divideUnsigned(long, long)} instead. * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) @@ -274,7 +276,7 @@ public static long divide(long dividend, long divisor) { * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 64-bit * quantities. * - *

    Java 8 users: use {@link Long#remainderUnsigned(long, long)} instead. + *

    Java 8+ users: use {@link Long#remainderUnsigned(long, long)} instead. * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) @@ -309,7 +311,7 @@ public static long remainder(long dividend, long divisor) { /** * Returns the unsigned {@code long} value represented by the given decimal string. * - *

    Java 8 users: use {@link Long#parseUnsignedLong(String)} instead. + *

    Java 8+ users: use {@link Long#parseUnsignedLong(String)} instead. * * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} * value @@ -324,7 +326,7 @@ public static long parseUnsignedLong(String string) { /** * Returns the unsigned {@code long} value represented by a string with the given radix. * - *

    Java 8 users: use {@link Long#parseUnsignedLong(String, int)} instead. + *

    Java 8+ users: use {@link Long#parseUnsignedLong(String, int)} instead. * * @param string the string containing the unsigned {@code long} representation to be parsed. * @param radix the radix to use while parsing {@code string} @@ -437,7 +439,7 @@ static boolean overflowInParse(long current, int digit, int radix) { /** * Returns a string representation of x, where x is treated as unsigned. * - *

    Java 8 users: use {@link Long#toUnsignedString(long)} instead. + *

    Java 8+ users: use {@link Long#toUnsignedString(long)} instead. */ public static String toString(long x) { return toString(x, 10); @@ -447,7 +449,7 @@ public static String toString(long x) { * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as * unsigned. * - *

    Java 8 users: use {@link Long#toUnsignedString(long, int)} instead. + *

    Java 8+ users: use {@link Long#toUnsignedString(long, int)} instead. * * @param x the value to convert to a string. * @param radix the radix to use while working with {@code x} diff --git a/android/guava/src/com/google/common/primitives/package-info.java b/android/guava/src/com/google/common/primitives/package-info.java index 9504fa79be1a..1262afce6d72 100644 --- a/android/guava/src/com/google/common/primitives/package-info.java +++ b/android/guava/src/com/google/common/primitives/package-info.java @@ -13,10 +13,10 @@ */ /** - * Static utilities for working with the eight primitive types and {@code void}, and value types for - * treating them as unsigned. + * Static utilities for the eight primitive types and {@code void}, and value types for treating + * them as unsigned or storing them in immutable arrays. * - *

    This package is a part of the open-source Guava + *

    This package is a part of the open-source Guava * library. * *

    See the Guava User Guide article on Contents * - *

    General static utilities

    + *

    Value types

    * *
      - *
    • {@link com.google.common.primitives.Primitives} + *
    • {@link ImmutableDoubleArray} + *
    • {@link ImmutableIntArray} + *
    • {@link ImmutableLongArray} + *
    • {@link UnsignedInteger} + *
    • {@link UnsignedLong} *
    * *

    Per-type static utilities

    * *
      - *
    • {@link com.google.common.primitives.Booleans} - *
    • {@link com.google.common.primitives.Bytes} + *
    • {@link Booleans} + *
    • {@link Bytes} *
        - *
      • {@link com.google.common.primitives.SignedBytes} - *
      • {@link com.google.common.primitives.UnsignedBytes} + *
      • {@link SignedBytes} + *
      • {@link UnsignedBytes} *
      - *
    • {@link com.google.common.primitives.Chars} - *
    • {@link com.google.common.primitives.Doubles} - *
    • {@link com.google.common.primitives.Floats} - *
    • {@link com.google.common.primitives.Ints} + *
    • {@link Chars} + *
    • {@link Doubles} + *
    • {@link Floats} + *
    • {@link Ints} *
        - *
      • {@link com.google.common.primitives.UnsignedInts} + *
      • {@link UnsignedInts} *
      - *
    • {@link com.google.common.primitives.Longs} + *
    • {@link Longs} *
        - *
      • {@link com.google.common.primitives.UnsignedLongs} + *
      • {@link UnsignedLongs} *
      - *
    • {@link com.google.common.primitives.Shorts} + *
    • {@link Shorts} *
    * - *

    Value types

    + *

    General static utilities

    * *
      - *
    • {@link com.google.common.primitives.UnsignedInteger} - *
    • {@link com.google.common.primitives.UnsignedLong} + *
    • {@link Primitives} *
    */ -@ParametersAreNonnullByDefault @CheckReturnValue +@NullMarked package com.google.common.primitives; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/reflect/AbstractInvocationHandler.java b/android/guava/src/com/google/common/reflect/AbstractInvocationHandler.java index 4666f992c3fa..622596817bdd 100644 --- a/android/guava/src/com/google/common/reflect/AbstractInvocationHandler.java +++ b/android/guava/src/com/google/common/reflect/AbstractInvocationHandler.java @@ -14,13 +14,11 @@ package com.google.common.reflect; -import com.google.common.annotations.Beta; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link @@ -39,9 +37,9 @@ * @author Ben Yu * @since 12.0 */ -@Beta -@ElementTypesAreNonnullByDefault public abstract class AbstractInvocationHandler implements InvocationHandler { + /** Constructor for use by subclasses. */ + public AbstractInvocationHandler() {} private static final Object[] NO_ARGS = {}; @@ -61,9 +59,8 @@ public abstract class AbstractInvocationHandler implements InvocationHandler { * */ @Override - @CheckForNull - public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args) - throws Throwable { + public final @Nullable Object invoke( + Object proxy, Method method, @Nullable Object @Nullable [] args) throws Throwable { if (args == null) { args = NO_ARGS; } @@ -97,9 +94,8 @@ public final Object invoke(Object proxy, Method method, @CheckForNull @Nullable *

    Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, * an empty array is passed in. */ - @CheckForNull - protected abstract Object handleInvocation(Object proxy, Method method, @Nullable Object[] args) - throws Throwable; + protected abstract @Nullable Object handleInvocation( + Object proxy, Method method, @Nullable Object[] args) throws Throwable; /** * By default delegates to {@link Object#equals} so instances are only equal if they are @@ -113,7 +109,7 @@ protected abstract Object handleInvocation(Object proxy, Method method, @Nullabl *

    Subclasses can override this method to provide custom equality. */ @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { return super.equals(obj); } diff --git a/android/guava/src/com/google/common/reflect/ClassPath.java b/android/guava/src/com/google/common/reflect/ClassPath.java index 4b9f7acfc046..34f9eb82ffbd 100644 --- a/android/guava/src/com/google/common/reflect/ClassPath.java +++ b/android/guava/src/com/google/common/reflect/ClassPath.java @@ -20,7 +20,6 @@ import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; import static java.util.logging.Level.WARNING; -import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; @@ -50,7 +49,7 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Scans the source of a {@link ClassLoader} and finds all loadable classes and resources. @@ -91,8 +90,6 @@ * @author Ben Yu * @since 14.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class ClassPath { private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); @@ -204,7 +201,6 @@ public ImmutableSet getTopLevelClassesRecursive(String packageName) { * * @since 14.0 */ - @Beta public static class ResourceInfo { private final File file; private final String resourceName; @@ -280,7 +276,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof ResourceInfo) { ResourceInfo that = (ResourceInfo) obj; return resourceName.equals(that.resourceName) && loader == that.loader; @@ -300,7 +296,6 @@ public String toString() { * * @since 14.0 */ - @Beta public static final class ClassInfo extends ResourceInfo { private final String className; @@ -554,7 +549,7 @@ private void scanDirectory( } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof LocationInfo) { LocationInfo that = (LocationInfo) obj; return home.equals(that.home) && classloader.equals(that.classloader); @@ -581,8 +576,7 @@ public String toString() { * an empty set will be returned. */ @VisibleForTesting - static ImmutableSet getClassPathFromManifest( - File jarFile, @CheckForNull Manifest manifest) { + static ImmutableSet getClassPathFromManifest(File jarFile, @Nullable Manifest manifest) { if (manifest == null) { return ImmutableSet.of(); } diff --git a/android/guava/src/com/google/common/reflect/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/reflect/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 0e8ef3cb7bcb..000000000000 --- a/android/guava/src/com/google/common/reflect/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.reflect; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/reflect/IgnoreJRERequirement.java b/android/guava/src/com/google/common/reflect/IgnoreJRERequirement.java new file mode 100644 index 000000000000..a56902603307 --- /dev/null +++ b/android/guava/src/com/google/common/reflect/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.reflect; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java b/android/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java index 6d8eb7bf75c7..18bce983963d 100644 --- a/android/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java +++ b/android/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java @@ -14,13 +14,12 @@ package com.google.common.reflect; -import com.google.common.annotations.Beta; import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotCall; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link @@ -29,8 +28,6 @@ * @author Ben Yu * @since 13.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class ImmutableTypeToInstanceMap extends ForwardingMap, B> implements TypeToInstanceMap { @@ -60,7 +57,6 @@ public static Builder builder() { * * @since 13.0 */ - @Beta public static final class Builder { private final ImmutableMap.Builder, B> mapBuilder = ImmutableMap.builder(); @@ -93,7 +89,7 @@ public Builder put(TypeToken key, T value) { * @throws IllegalArgumentException if duplicate keys were added */ public ImmutableTypeToInstanceMap build() { - return new ImmutableTypeToInstanceMap<>(mapBuilder.build()); + return new ImmutableTypeToInstanceMap<>(mapBuilder.buildOrThrow()); } } @@ -104,14 +100,12 @@ private ImmutableTypeToInstanceMap(ImmutableMap, B> deleg } @Override - @CheckForNull - public T getInstance(TypeToken type) { + public @Nullable T getInstance(TypeToken type) { return trustedGet(type.rejectTypeVariables()); } @Override - @CheckForNull - public T getInstance(Class type) { + public @Nullable T getInstance(Class type) { return trustedGet(TypeToken.of(type)); } @@ -125,8 +119,7 @@ public T getInstance(Class type) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public T putInstance(TypeToken type, T value) { + public @Nullable T putInstance(TypeToken type, T value) { throw new UnsupportedOperationException(); } @@ -140,8 +133,7 @@ public T putInstance(TypeToken type, T value) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public T putInstance(Class type, T value) { + public @Nullable T putInstance(Class type, T value) { throw new UnsupportedOperationException(); } @@ -155,8 +147,7 @@ public T putInstance(Class type, T value) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public B put(TypeToken key, B value) { + public @Nullable B put(TypeToken key, B value) { throw new UnsupportedOperationException(); } @@ -179,8 +170,7 @@ protected Map, B> delegate() { } @SuppressWarnings("unchecked") // value could not get in if not a T - @CheckForNull - private T trustedGet(TypeToken type) { + private @Nullable T trustedGet(TypeToken type) { return (T) delegate.get(type); } } diff --git a/android/guava/src/com/google/common/reflect/Invokable.java b/android/guava/src/com/google/common/reflect/Invokable.java index 8e75aa856a3e..f33f207f69d9 100644 --- a/android/guava/src/com/google/common/reflect/Invokable.java +++ b/android/guava/src/com/google/common/reflect/Invokable.java @@ -16,7 +16,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Annotation; @@ -30,8 +29,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Arrays; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Wrapper around either a {@link Method} or a {@link Constructor}. Convenience API is provided to @@ -62,8 +60,6 @@ * @since 14.0 (no longer implements {@link AccessibleObject} or {@code GenericDeclaration} since * 31.0) */ -@Beta -@ElementTypesAreNonnullByDefault public abstract class Invokable implements AnnotatedElement, Member { private final AccessibleObject accessibleObject; private final Member member; @@ -90,8 +86,7 @@ public final boolean isAnnotationPresent(Class annotationC } @Override - @CheckForNull - public final A getAnnotation(Class annotationClass) { + public final @Nullable A getAnnotation(Class annotationClass) { return accessibleObject.getAnnotation(annotationClass); } @@ -118,13 +113,14 @@ public final void setAccessible(boolean flag) { } /** See {@link java.lang.reflect.AccessibleObject#trySetAccessible()}. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public final boolean trySetAccessible() { // We can't call accessibleObject.trySetAccessible since that was added in Java 9 and this code // should work on Java 8. So we emulate it this way. try { accessibleObject.setAccessible(true); return true; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception return false; } } @@ -211,7 +207,7 @@ final boolean isTransient() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Invokable) { Invokable that = (Invokable) obj; return getOwnerType().equals(that.getOwnerType()) && member.equals(that.member); @@ -254,8 +250,7 @@ public String toString() { // All subclasses are owned by us and we'll make sure to get the R type right, including nullness. @SuppressWarnings({"unchecked", "nullness"}) @CanIgnoreReturnValue - @CheckForNull - public final R invoke(@CheckForNull T receiver, @Nullable Object... args) + public final @Nullable R invoke(@Nullable T receiver, @Nullable Object... args) throws InvocationTargetException, IllegalAccessException { return (R) invokeInternal(receiver, checkNotNull(args)); } @@ -272,12 +267,17 @@ public final TypeToken getReturnType() { * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code * this} parameter of the enclosing class is excluded from the returned parameters. */ + @IgnoreJRERequirement public final ImmutableList getParameters() { Type[] parameterTypes = getGenericParameterTypes(); Annotation[][] annotations = getParameterAnnotations(); + @Nullable Object[] annotatedTypes = + new Object[parameterTypes.length]; ImmutableList.Builder builder = ImmutableList.builder(); for (int i = 0; i < parameterTypes.length; i++) { - builder.add(new Parameter(this, i, TypeToken.of(parameterTypes[i]), annotations[i])); + builder.add( + new Parameter( + this, i, TypeToken.of(parameterTypes[i]), annotations[i], annotatedTypes[i])); } return builder.build(); } @@ -331,8 +331,7 @@ public TypeToken getOwnerType() { return (TypeToken) TypeToken.of(getDeclaringClass()); } - @CheckForNull - abstract Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) + abstract @Nullable Object invokeInternal(@Nullable Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException; abstract Type[] getGenericParameterTypes(); @@ -354,8 +353,7 @@ static class MethodInvokable extends Invokable { } @Override - @CheckForNull - final Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) + final @Nullable Object invokeInternal(@Nullable Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException { return method.invoke(receiver, args); } @@ -409,7 +407,7 @@ static class ConstructorInvokable extends Invokable { } @Override - final Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) + final Object invokeInternal(@Nullable Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException { try { return constructor.newInstance(args); @@ -510,4 +508,15 @@ private boolean mayNeedHiddenThis() { } } } + + private static final boolean ANNOTATED_TYPE_EXISTS = initAnnotatedTypeExists(); + + private static boolean initAnnotatedTypeExists() { + try { + Class.forName("java.lang.reflect.AnnotatedType"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } } diff --git a/android/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java b/android/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java index 10fc1f3ec1f7..3904ecd699ef 100644 --- a/android/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java +++ b/android/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java @@ -16,7 +16,6 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.collect.ForwardingMap; import com.google.common.collect.ForwardingMapEntry; import com.google.common.collect.ForwardingSet; @@ -27,49 +26,44 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A mutable type-to-instance map. See also {@link ImmutableTypeToInstanceMap}. * - *

    This implementation does support null values, despite how it is annotated; see - * discussion at {@link TypeToInstanceMap}. - * * @author Ben Yu * @since 13.0 */ -@Beta -@ElementTypesAreNonnullByDefault -public final class MutableTypeToInstanceMap extends ForwardingMap, B> - implements TypeToInstanceMap { +public final class MutableTypeToInstanceMap + extends ForwardingMap, B> implements TypeToInstanceMap { + /** Creates a new map. */ + public MutableTypeToInstanceMap() {} - private final Map, B> backingMap = Maps.newHashMap(); + private final Map, B> backingMap = Maps.newHashMap(); @Override - @CheckForNull - public T getInstance(Class type) { + public @Nullable T getInstance(Class type) { return trustedGet(TypeToken.of(type)); } @Override - @CheckForNull - public T getInstance(TypeToken type) { + public @Nullable T getInstance(TypeToken type) { return trustedGet(type.rejectTypeVariables()); } @Override @CanIgnoreReturnValue - @CheckForNull - public T putInstance(Class type, T value) { + public @Nullable T putInstance( + Class<@NonNull T> type, @ParametricNullness T value) { return trustedPut(TypeToken.of(type), value); } @Override @CanIgnoreReturnValue - @CheckForNull - public T putInstance(TypeToken type, T value) { - return trustedPut(type.rejectTypeVariables(), value); + public @Nullable T putInstance( + TypeToken<@NonNull T> type, @ParametricNullness T value) { + return this.trustedPut(type.rejectTypeVariables(), value); } /** @@ -82,8 +76,7 @@ public T putInstance(TypeToken type, T value) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - @CheckForNull - public B put(TypeToken key, B value) { + public @Nullable B put(TypeToken key, @ParametricNullness B value) { throw new UnsupportedOperationException("Please use putInstance() instead."); } @@ -96,37 +89,38 @@ public B put(TypeToken key, B value) { @Deprecated @Override @DoNotCall("Always throws UnsupportedOperationException") - public void putAll(Map, ? extends B> map) { + public void putAll(Map, ? extends B> map) { throw new UnsupportedOperationException("Please use putInstance() instead."); } @Override - public Set, B>> entrySet() { + public Set, B>> entrySet() { return UnmodifiableEntry.transformEntries(super.entrySet()); } @Override - protected Map, B> delegate() { + protected Map, B> delegate() { return backingMap; } @SuppressWarnings("unchecked") // value could not get in if not a T - @CheckForNull - private T trustedPut(TypeToken type, T value) { + private @Nullable T trustedPut( + TypeToken<@NonNull T> type, @ParametricNullness T value) { return (T) backingMap.put(type, value); } @SuppressWarnings("unchecked") // value could not get in if not a T - @CheckForNull - private T trustedGet(TypeToken type) { + private @Nullable T trustedGet(TypeToken type) { return (T) backingMap.get(type); } - private static final class UnmodifiableEntry extends ForwardingMapEntry { + private static final class UnmodifiableEntry + extends ForwardingMapEntry { private final Entry delegate; - static Set> transformEntries(Set> entries) { + static Set> transformEntries( + Set> entries) { return new ForwardingSet>() { @Override protected Set> delegate() { @@ -159,11 +153,12 @@ public Object[] toArray() { }; } - private static Iterator> transformEntries(Iterator> entries) { + private static Iterator> transformEntries( + Iterator> entries) { return Iterators.transform(entries, UnmodifiableEntry::new); } - private UnmodifiableEntry(java.util.Map.Entry delegate) { + private UnmodifiableEntry(Entry delegate) { this.delegate = checkNotNull(delegate); } @@ -173,7 +168,8 @@ protected Entry delegate() { } @Override - public V setValue(V value) { + @ParametricNullness + public V setValue(@ParametricNullness V value) { throw new UnsupportedOperationException(); } } diff --git a/android/guava/src/com/google/common/reflect/Parameter.java b/android/guava/src/com/google/common/reflect/Parameter.java index a55734a2c8c8..8f4bca276225 100644 --- a/android/guava/src/com/google/common/reflect/Parameter.java +++ b/android/guava/src/com/google/common/reflect/Parameter.java @@ -16,13 +16,11 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Represents a method or constructor parameter. @@ -30,8 +28,6 @@ * @author Ben Yu * @since 14.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class Parameter implements AnnotatedElement { private final Invokable declaration; @@ -39,12 +35,26 @@ public final class Parameter implements AnnotatedElement { private final TypeToken type; private final ImmutableList annotations; + /** + * An {@code AnnotatedType} instance, or {@code null} under Android VMs (possible only when using + * the Android flavor of Guava). The field is declared with a type of {@code Object} to avoid + * compatibility problems on Android VMs. The corresponding accessor method, however, can have the + * more specific return type as long as users are careful to guard calls to it with version checks + * or reflection: Android VMs ignore the types of elements that aren't used. + */ + private final @Nullable Object annotatedType; + Parameter( - Invokable declaration, int position, TypeToken type, Annotation[] annotations) { + Invokable declaration, + int position, + TypeToken type, + Annotation[] annotations, + @Nullable Object annotatedType) { this.declaration = declaration; this.position = position; this.type = type; this.annotations = ImmutableList.copyOf(annotations); + this.annotatedType = annotatedType; } /** Returns the type of the parameter. */ @@ -63,8 +73,7 @@ public boolean isAnnotationPresent(Class annotationType) { } @Override - @CheckForNull - public A getAnnotation(Class annotationType) { + public @Nullable A getAnnotation(Class annotationType) { checkNotNull(annotationType); for (Annotation annotation : annotations) { if (annotationType.isInstance(annotation)) { @@ -79,39 +88,45 @@ public Annotation[] getAnnotations() { return getDeclaredAnnotations(); } - /** @since 18.0 */ - // @Override on JDK8 + /** + * @since 18.0 + */ + @Override public A[] getAnnotationsByType(Class annotationType) { return getDeclaredAnnotationsByType(annotationType); } - /** @since 18.0 */ - // @Override on JDK8 + /** + * @since 18.0 + */ @Override public Annotation[] getDeclaredAnnotations() { return annotations.toArray(new Annotation[0]); } - /** @since 18.0 */ - // @Override on JDK8 - @CheckForNull - public A getDeclaredAnnotation(Class annotationType) { + /** + * @since 18.0 + */ + @Override + public @Nullable A getDeclaredAnnotation(Class annotationType) { checkNotNull(annotationType); return FluentIterable.from(annotations).filter(annotationType).first().orNull(); } - /** @since 18.0 */ - // @Override on JDK8 + /** + * @since 18.0 + */ + @Override public A[] getDeclaredAnnotationsByType(Class annotationType) { - @Nullable - A[] result = FluentIterable.from(annotations).filter(annotationType).toArray(annotationType); + @Nullable A[] result = + FluentIterable.from(annotations).filter(annotationType).toArray(annotationType); @SuppressWarnings("nullness") // safe because the input list contains no nulls A[] cast = (A[]) result; return cast; } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Parameter) { Parameter that = (Parameter) obj; return position == that.position && declaration.equals(that.declaration); diff --git a/android/guava/src/com/google/common/reflect/ParametricNullness.java b/android/guava/src/com/google/common/reflect/ParametricNullness.java index b6331ac26b0b..4c9afbe59684 100644 --- a/android/guava/src/com/google/common/reflect/ParametricNullness.java +++ b/android/guava/src/com/google/common/reflect/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

      + *
    • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
    • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
    + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
      + *
    • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
    • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
    * *

    Consumers of this annotation include: * *

      - *
    • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
    • J2ObjC - *
    • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
    • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
    • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
    * + *

    This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/reflect/Reflection.java b/android/guava/src/com/google/common/reflect/Reflection.java index fa35f7f2de6a..3cc06250c344 100644 --- a/android/guava/src/com/google/common/reflect/Reflection.java +++ b/android/guava/src/com/google/common/reflect/Reflection.java @@ -17,7 +17,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; @@ -26,8 +25,6 @@ * * @since 12.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class Reflection { /** @@ -56,7 +53,7 @@ public static String getPackageName(String classFullName) { * *

    WARNING: Normally it's a smell if a class needs to be explicitly initialized, because static * state hurts system maintainability and testability. In cases when you have no choice while - * inter-operating with a legacy framework, this method helps to keep the code less ugly. + * interoperating with a legacy framework, this method helps to keep the code less ugly. * * @throws ExceptionInInitializerError if an exception is thrown during initialization of a class */ diff --git a/android/guava/src/com/google/common/reflect/TypeCapture.java b/android/guava/src/com/google/common/reflect/TypeCapture.java index 2be7b4fddad1..effb382b2826 100644 --- a/android/guava/src/com/google/common/reflect/TypeCapture.java +++ b/android/guava/src/com/google/common/reflect/TypeCapture.java @@ -24,7 +24,6 @@ * * @author Ben Yu */ -@ElementTypesAreNonnullByDefault abstract class TypeCapture { /** Returns the captured type. */ diff --git a/android/guava/src/com/google/common/reflect/TypeParameter.java b/android/guava/src/com/google/common/reflect/TypeParameter.java index 9c64abb72794..0d04e847df88 100644 --- a/android/guava/src/com/google/common/reflect/TypeParameter.java +++ b/android/guava/src/com/google/common/reflect/TypeParameter.java @@ -16,10 +16,9 @@ import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.annotations.Beta; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Captures a free type variable that can be used in {@link TypeToken#where}. For example: @@ -34,8 +33,6 @@ * @author Ben Yu * @since 12.0 */ -@Beta -@ElementTypesAreNonnullByDefault /* * A nullable bound would let users create a TypeParameter instance for a parameter with a nullable * bound. However, it would also let them create `new TypeParameter<@Nullable T>() {}`, which @@ -62,7 +59,7 @@ public final int hashCode() { } @Override - public final boolean equals(@CheckForNull Object o) { + public final boolean equals(@Nullable Object o) { if (o instanceof TypeParameter) { TypeParameter that = (TypeParameter) o; return typeVariable.equals(that.typeVariable); diff --git a/android/guava/src/com/google/common/reflect/TypeResolver.java b/android/guava/src/com/google/common/reflect/TypeResolver.java index f72e19576de8..4efab5f5fab5 100644 --- a/android/guava/src/com/google/common/reflect/TypeResolver.java +++ b/android/guava/src/com/google/common/reflect/TypeResolver.java @@ -19,11 +19,11 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.Arrays.asList; -import com.google.common.annotations.Beta; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -35,7 +35,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * An object of this class encapsulates type mappings from type variables. Mappings are established @@ -43,7 +43,7 @@ * *

    Note that usually type mappings are already implied by the static type hierarchy (for example, * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in - * the context of {@code class MyStringList implements List}. In such case, prefer to use + * the context of {@code class MyStringList implements List}). In such case, prefer to use * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be * used when the type mapping isn't implied by the static type hierarchy, but provided through other * means such as an annotation or external configuration file. @@ -51,8 +51,6 @@ * @author Ben Yu * @since 15.0 */ -@Beta -@ElementTypesAreNonnullByDefault public final class TypeResolver { private final TypeTable typeTable; @@ -228,6 +226,7 @@ public Type resolveType(Type type) { } } + @CanIgnoreReturnValue Type[] resolveTypesInPlace(Type[] types) { for (int i = 0; i < types.length; i++) { types[i] = resolveType(types[i]); @@ -296,7 +295,7 @@ final TypeTable where(Map mappings) { checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable); builder.put(variable, type); } - return new TypeTable(builder.build()); + return new TypeTable(builder.buildOrThrow()); } final Type resolve(TypeVariable var) { @@ -428,7 +427,7 @@ private void map(TypeVariableKey var, Type arg) { if (var.equalsType(t)) { // cycle detected, remove the entire cycle from the mapping so that // each type variable resolves deterministically to itself. - // Otherwise, a F -> T cycle will end up resolving both F and T + // Otherwise, an F -> T cycle will end up resolving both F and T // nondeterministically to either F or T. for (Type x = arg; x != null; x = mappings.remove(TypeVariableKey.forLookup(x))) {} return; @@ -528,8 +527,7 @@ private WildcardCapturer notForTypeVariable() { return new WildcardCapturer(id); } - @CheckForNull - private Type captureNullable(@CheckForNull Type type) { + private @Nullable Type captureNullable(@Nullable Type type) { if (type == null) { return null; } @@ -563,7 +561,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof TypeVariableKey) { TypeVariableKey that = (TypeVariableKey) obj; return equalsTypeVariable(that.var); @@ -578,8 +576,7 @@ public String toString() { } /** Wraps {@code t} in a {@code TypeVariableKey} if it's a type variable. */ - @CheckForNull - static TypeVariableKey forLookup(Type t) { + static @Nullable TypeVariableKey forLookup(Type t) { if (t instanceof TypeVariable) { return new TypeVariableKey((TypeVariable) t); } else { diff --git a/android/guava/src/com/google/common/reflect/TypeToInstanceMap.java b/android/guava/src/com/google/common/reflect/TypeToInstanceMap.java index 8a329bdb38ed..b1ffbadb52ec 100644 --- a/android/guava/src/com/google/common/reflect/TypeToInstanceMap.java +++ b/android/guava/src/com/google/common/reflect/TypeToInstanceMap.java @@ -14,11 +14,11 @@ package com.google.common.reflect; -import com.google.common.annotations.Beta; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; import java.util.Map; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * A map, each entry of which maps a {@link TypeToken} to an instance of that type. In addition to @@ -35,17 +35,13 @@ *

    Like any other {@code Map}, this map may contain entries for primitive types, * and a primitive type and its corresponding wrapper type may map to different values. * - *

    This class's support for {@code null} requires some explanation. For details, see {@link - * ClassToInstanceMap}. Its explanation applies equally well to {@code TypeToInstanceMap}. - * * @param the common supertype that all entries must share; often this is simply {@link Object} * @author Ben Yu * @since 13.0 */ -@Beta @DoNotMock("Use ImmutableTypeToInstanceMap or MutableTypeToInstanceMap") -@ElementTypesAreNonnullByDefault -public interface TypeToInstanceMap extends Map, B> { +public interface TypeToInstanceMap + extends Map, B> { /** * Returns the value the specified class is mapped to, or {@code null} if no entry for this class @@ -55,16 +51,14 @@ public interface TypeToInstanceMap extends Map, B> { *

    {@code getInstance(Foo.class)} is equivalent to {@code * getInstance(TypeToken.of(Foo.class))}. */ - @CheckForNull - T getInstance(Class type); + @Nullable T getInstance(Class type); /** * Returns the value the specified type is mapped to, or {@code null} if no entry for this type is * present. This will only return a value that was bound to this specific type, not a value that * may have been bound to a subtype. */ - @CheckForNull - T getInstance(TypeToken type); + @Nullable T getInstance(TypeToken type); /** * Maps the specified class to the specified value. Does not associate this value with any @@ -77,8 +71,7 @@ public interface TypeToInstanceMap extends Map, B> { * null} if there was no previous entry. */ @CanIgnoreReturnValue - @CheckForNull - T putInstance(Class type, T value); + @Nullable T putInstance(Class<@NonNull T> type, @ParametricNullness T value); /** * Maps the specified type to the specified value. Does not associate this value with any @@ -88,6 +81,5 @@ public interface TypeToInstanceMap extends Map, B> { * if there was no previous entry. */ @CanIgnoreReturnValue - @CheckForNull - T putInstance(TypeToken type, T value); + @Nullable T putInstance(TypeToken<@NonNull T> type, @ParametricNullness T value); } diff --git a/android/guava/src/com/google/common/reflect/TypeToken.java b/android/guava/src/com/google/common/reflect/TypeToken.java index 845221956a51..77d613eb66f9 100644 --- a/android/guava/src/com/google/common/reflect/TypeToken.java +++ b/android/guava/src/com/google/common/reflect/TypeToken.java @@ -17,9 +17,9 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static java.lang.Math.max; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Predicate; @@ -32,6 +32,7 @@ import com.google.common.collect.Ordering; import com.google.common.primitives.Primitives; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.GenericArrayType; @@ -47,7 +48,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link Type} with generics. @@ -89,7 +90,7 @@ * *

    {@code TypeToken} is serializable when no type variable is contained in the type. * - *

    Note to Guice users: {@code} TypeToken is similar to Guice's {@code TypeLiteral} class except + *

    Note to Guice users: {@code TypeToken} is similar to Guice's {@code TypeLiteral} class except * that it is serializable and offers numerous additional utility methods. * * @author Bob Lee @@ -97,18 +98,16 @@ * @author Ben Yu * @since 12.0 */ -@Beta @SuppressWarnings("serial") // SimpleTypeToken is the serialized form. -@ElementTypesAreNonnullByDefault public abstract class TypeToken extends TypeCapture implements Serializable { private final Type runtimeType; /** Resolver for resolving parameter and field types with {@link #runtimeType} as context. */ - @CheckForNull private transient TypeResolver invariantTypeResolver; + @LazyInit private transient @Nullable TypeResolver invariantTypeResolver; /** Resolver for resolving covariant types with {@link #runtimeType} as context. */ - @CheckForNull private transient TypeResolver covariantTypeResolver; + @LazyInit private transient @Nullable TypeResolver covariantTypeResolver; /** * Constructs a new type token of {@code T}. @@ -305,8 +304,7 @@ private TypeToken resolveSupertype(Type type) { * if the bound is a class or extends from a class. This means that the returned type could be a * type variable too. */ - @CheckForNull - final TypeToken getGenericSuperclass() { + final @Nullable TypeToken getGenericSuperclass() { if (runtimeType instanceof TypeVariable) { // First bound is always the super class, if one exists. return boundAsSuperclass(((TypeVariable) runtimeType).getBounds()[0]); @@ -324,8 +322,7 @@ final TypeToken getGenericSuperclass() { return superToken; } - @CheckForNull - private TypeToken boundAsSuperclass(Type bound) { + private @Nullable TypeToken boundAsSuperclass(Type bound) { TypeToken token = of(bound); if (token.getRawType().isInterface()) { return null; @@ -577,8 +574,7 @@ public final TypeToken unwrap() { * Returns the array component type if this type represents an array ({@code int[]}, {@code T[]}, * {@code []>} etc.), or else {@code null} is returned. */ - @CheckForNull - public final TypeToken getComponentType() { + public final @Nullable TypeToken getComponentType() { Type componentType = Types.getComponentType(runtimeType); if (componentType == null) { return null; @@ -672,7 +668,7 @@ public String toString() { */ public class TypeSet extends ForwardingSet> implements Serializable { - @CheckForNull private transient ImmutableSet> types; + private transient @Nullable ImmutableSet> types; TypeSet() {} @@ -718,7 +714,7 @@ public Set> rawTypes() { private final class InterfaceSet extends TypeSet { private final transient TypeSet allTypes; - @CheckForNull private transient ImmutableSet> interfaces; + private transient @Nullable ImmutableSet> interfaces; InterfaceSet(TypeSet allTypes) { this.allTypes = allTypes; @@ -763,7 +759,7 @@ private Object readResolve() { private final class ClassSet extends TypeSet { - @CheckForNull private transient ImmutableSet> classes; + private transient @Nullable ImmutableSet> classes; @Override protected Set> delegate() { @@ -828,7 +824,7 @@ public boolean apply(TypeToken type) { * Returns true if {@code o} is another {@code TypeToken} that represents the same {@link Type}. */ @Override - public boolean equals(@CheckForNull Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof TypeToken) { TypeToken that = (TypeToken) o; return runtimeType.equals(that.runtimeType); @@ -1142,8 +1138,7 @@ private boolean isOwnedBySubtypeOf(Type supertype) { * Returns the owner type of a {@link ParameterizedType} or enclosing class of a {@link Class}, or * null otherwise. */ - @CheckForNull - private Type getOwnerTypeIfPresent() { + private @Nullable Type getOwnerTypeIfPresent() { if (runtimeType instanceof ParameterizedType) { return ((ParameterizedType) runtimeType).getOwnerType(); } else if (runtimeType instanceof Class) { @@ -1315,7 +1310,7 @@ private static final class SimpleTypeToken extends TypeToken { } /** - * Collects parent types from a sub type. + * Collects parent types from a subtype. * * @param The type "kind". Either a TypeToken, or Class. */ @@ -1334,8 +1329,7 @@ Iterable> getInterfaces(TypeToken type) { } @Override - @CheckForNull - TypeToken getSuperclass(TypeToken type) { + @Nullable TypeToken getSuperclass(TypeToken type) { return type.getGenericSuperclass(); } }; @@ -1353,8 +1347,7 @@ Iterable> getInterfaces(Class type) { } @Override - @CheckForNull - Class getSuperclass(Class type) { + @Nullable Class getSuperclass(Class type) { return type.getSuperclass(); } }; @@ -1404,11 +1397,11 @@ private int collectTypes(K type, Map map) { // Interfaces should be listed before Object. int aboveMe = getRawType(type).isInterface() ? 1 : 0; for (K interfaceType : getInterfaces(type)) { - aboveMe = Math.max(aboveMe, collectTypes(interfaceType, map)); + aboveMe = max(aboveMe, collectTypes(interfaceType, map)); } K superclass = getSuperclass(type); if (superclass != null) { - aboveMe = Math.max(aboveMe, collectTypes(superclass, map)); + aboveMe = max(aboveMe, collectTypes(superclass, map)); } /* * TODO(benyu): should we include Object for interface? Also, CharSequence[] and Object[] for @@ -1437,8 +1430,7 @@ public int compare(K left, K right) { abstract Iterable getInterfaces(K type); - @CheckForNull - abstract K getSuperclass(K type); + abstract @Nullable K getSuperclass(K type); private static class ForwardingTypeCollector extends TypeCollector { @@ -1459,8 +1451,7 @@ Iterable getInterfaces(K type) { } @Override - @CheckForNull - K getSuperclass(K type) { + @Nullable K getSuperclass(K type) { return delegate.getSuperclass(type); } } diff --git a/android/guava/src/com/google/common/reflect/TypeVisitor.java b/android/guava/src/com/google/common/reflect/TypeVisitor.java index 416397bc77d6..3cff3f90f809 100644 --- a/android/guava/src/com/google/common/reflect/TypeVisitor.java +++ b/android/guava/src/com/google/common/reflect/TypeVisitor.java @@ -21,7 +21,7 @@ import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Set; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Based on what a {@link Type} is, dispatch it to the corresponding {@code visit*} method. By @@ -54,7 +54,6 @@ * * @author Ben Yu */ -@ElementTypesAreNonnullByDefault abstract class TypeVisitor { private final Set visited = Sets.newHashSet(); diff --git a/android/guava/src/com/google/common/reflect/Types.java b/android/guava/src/com/google/common/reflect/Types.java index 122bd7502ed0..0e9885168472 100644 --- a/android/guava/src/com/google/common/reflect/Types.java +++ b/android/guava/src/com/google/common/reflect/Types.java @@ -44,15 +44,13 @@ import java.util.Collection; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Utilities for working with {@link Type}. * * @author Ben Yu */ -@ElementTypesAreNonnullByDefault final class Types { /** Class#toString without the "class " and "interface " prefixes */ @@ -80,7 +78,7 @@ static Type newArrayType(Type componentType) { * {@code ownerType}. */ static ParameterizedType newParameterizedTypeWithOwner( - @CheckForNull Type ownerType, Class rawType, Type... arguments) { + @Nullable Type ownerType, Class rawType, Type... arguments) { if (ownerType == null) { return newParameterizedType(rawType, arguments); } @@ -100,15 +98,13 @@ static ParameterizedType newParameterizedType(Class rawType, Type... argument private enum ClassOwnership { OWNED_BY_ENCLOSING_CLASS { @Override - @CheckForNull - Class getOwnerType(Class rawType) { + @Nullable Class getOwnerType(Class rawType) { return rawType.getEnclosingClass(); } }, LOCAL_CLASS_HAS_NO_OWNER { @Override - @CheckForNull - Class getOwnerType(Class rawType) { + @Nullable Class getOwnerType(Class rawType) { if (rawType.isLocalClass()) { return null; } else { @@ -117,8 +113,7 @@ Class getOwnerType(Class rawType) { } }; - @CheckForNull - abstract Class getOwnerType(Class rawType); + abstract @Nullable Class getOwnerType(Class rawType); static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); @@ -160,7 +155,7 @@ static WildcardType supertypeOf(Type lowerBound) { } /** - * Returns human readable string representation of {@code type}. + * Returns a human-readable string representation of {@code type}. * *

    The format is subject to change. */ @@ -168,8 +163,7 @@ static String toString(Type type) { return (type instanceof Class) ? ((Class) type).getName() : type.toString(); } - @CheckForNull - static Type getComponentType(Type type) { + static @Nullable Type getComponentType(Type type) { checkNotNull(type); AtomicReference<@Nullable Type> result = new AtomicReference<>(); new TypeVisitor() { @@ -200,8 +194,7 @@ void visitClass(Class t) { * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null * otherwise. */ - @CheckForNull - private static Type subtypeOfComponentType(Type[] bounds) { + private static @Nullable Type subtypeOfComponentType(Type[] bounds) { for (Type bound : bounds) { Type componentType = getComponentType(bound); if (componentType != null) { @@ -243,7 +236,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof GenericArrayType) { GenericArrayType that = (GenericArrayType) obj; return Objects.equal(getGenericComponentType(), that.getGenericComponentType()); @@ -256,11 +249,11 @@ public boolean equals(@CheckForNull Object obj) { private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { - @CheckForNull private final Type ownerType; + private final @Nullable Type ownerType; private final ImmutableList argumentsList; private final Class rawType; - ParameterizedTypeImpl(@CheckForNull Type ownerType, Class rawType, Type[] typeArguments) { + ParameterizedTypeImpl(@Nullable Type ownerType, Class rawType, Type[] typeArguments) { checkNotNull(rawType); checkArgument(typeArguments.length == rawType.getTypeParameters().length); disallowPrimitiveType(typeArguments, "type parameter"); @@ -280,8 +273,7 @@ public Type getRawType() { } @Override - @CheckForNull - public Type getOwnerType() { + public @Nullable Type getOwnerType() { return ownerType; } @@ -307,7 +299,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object other) { + public boolean equals(@Nullable Object other) { if (!(other instanceof ParameterizedType)) { return false; } @@ -356,6 +348,7 @@ private static TypeVariable newTypeVariableImp *

    This workaround should be removed at a distant future time when we no longer support Java * versions earlier than 8. */ + @SuppressWarnings("removal") // b/318391980 private static final class TypeVariableInvocationHandler implements InvocationHandler { private static final ImmutableMap typeVariableMethods; @@ -372,7 +365,7 @@ private static final class TypeVariableInvocationHandler implements InvocationHa builder.put(method.getName(), method); } } - typeVariableMethods = builder.build(); + typeVariableMethods = builder.buildKeepingLast(); } private final TypeVariableImpl typeVariableImpl; @@ -382,8 +375,7 @@ private static final class TypeVariableInvocationHandler implements InvocationHa } @Override - @CheckForNull - public Object invoke(Object proxy, Method method, @CheckForNull @Nullable Object[] args) + public @Nullable Object invoke(Object proxy, Method method, @Nullable Object @Nullable [] args) throws Throwable { String methodName = method.getName(); Method typeVariableMethod = typeVariableMethods.get(methodName); @@ -439,7 +431,7 @@ public int hashCode() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { // equal only to our TypeVariable implementation with identical bounds if (obj != null @@ -488,7 +480,7 @@ public Type[] getUpperBounds() { } @Override - public boolean equals(@CheckForNull Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof WildcardType) { WildcardType that = (WildcardType) obj; return lowerBounds.equals(Arrays.asList(that.getLowerBounds())) @@ -595,14 +587,7 @@ String typeName(Type type) { return (String) getTypeName.invoke(type); } catch (NoSuchMethodException e) { throw new AssertionError("Type.getTypeName should be available in Java 8"); - /* - * Do not merge the 2 catch blocks below. javac would infer a type of - * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of - * Android don't *seem* to mind, but there might be edge cases of which we're unaware.) - */ - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { + } catch (InvocationTargetException | IllegalAccessException e) { throw new RuntimeException(e); } } @@ -669,14 +654,13 @@ boolean jdkTypeDuplicatesOwnerName() { } /** - * Per issue 1635, - * In JDK 1.7.0_51-b13, {@link TypeVariableImpl#equals(Object)} is changed to no longer be equal - * to custom TypeVariable implementations. As a result, we need to make sure our TypeVariable - * implementation respects symmetry. Moreover, we don't want to reconstruct a native type variable - * {@code } using our implementation unless some of its bounds have changed in resolution. This - * avoids creating unequal TypeVariable implementation unnecessarily. When the bounds do change, - * however, it's fine for the synthetic TypeVariable to be unequal to any native TypeVariable - * anyway. + * Per issue 1635, In JDK 1.7.0_51-b13, + * {@link TypeVariableImpl#equals(Object)} is changed to no longer be equal to custom TypeVariable + * implementations. As a result, we need to make sure our TypeVariable implementation respects + * symmetry. Moreover, we don't want to reconstruct a native type variable {@code } using our + * implementation unless some of its bounds have changed in resolution. This avoids creating + * unequal TypeVariable implementation unnecessarily. When the bounds do change, however, it's + * fine for the synthetic TypeVariable to be unequal to any native TypeVariable anyway. */ static final class NativeTypeVariableEquals { static final boolean NATIVE_TYPE_VARIABLE_ONLY = diff --git a/android/guava/src/com/google/common/reflect/package-info.java b/android/guava/src/com/google/common/reflect/package-info.java index 6b6047169c13..7a3f435e530b 100644 --- a/android/guava/src/com/google/common/reflect/package-info.java +++ b/android/guava/src/com/google/common/reflect/package-info.java @@ -13,12 +13,12 @@ */ /** - * This package contains utilities to work with Java reflection. It is a part of the open-source Guava library. + * Utilities for reflection. This package is a part of the open-source Guava library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.reflect; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java index 76eada214052..9482e213eeb4 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractCatchingFuture.java @@ -19,21 +19,23 @@ import static com.google.common.util.concurrent.MoreExecutors.rejectionPropagatingExecutor; import static com.google.common.util.concurrent.NullnessCasts.uncheckedCastNullableTToT; import static com.google.common.util.concurrent.Platform.isInstanceOfThrowableClass; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; import com.google.common.util.concurrent.internal.InternalFutures; import com.google.errorprone.annotations.ForOverride; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Implementations of {@code Futures.catching*}. */ @GwtCompatible -@ElementTypesAreNonnullByDefault -@SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. +// Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || +@SuppressWarnings("ShortCircuitBoolean") abstract class AbstractCatchingFuture< V extends @Nullable Object, X extends Throwable, F, T extends @Nullable Object> extends FluentFuture.TrustedFuture implements Runnable { @@ -42,28 +44,28 @@ abstract class AbstractCatchingFuture< Class exceptionType, Function fallback, Executor executor) { - CatchingFuture future = new CatchingFuture<>(input, exceptionType, fallback); - input.addListener(future, rejectionPropagatingExecutor(executor, future)); - return future; + CatchingFuture output = new CatchingFuture<>(input, exceptionType, fallback); + input.addListener(output, rejectionPropagatingExecutor(executor, output)); + return output; } - static ListenableFuture create( + static ListenableFuture createAsync( ListenableFuture input, Class exceptionType, AsyncFunction fallback, Executor executor) { - AsyncCatchingFuture future = new AsyncCatchingFuture<>(input, exceptionType, fallback); - input.addListener(future, rejectionPropagatingExecutor(executor, future)); - return future; + AsyncCatchingFuture output = new AsyncCatchingFuture<>(input, exceptionType, fallback); + input.addListener(output, rejectionPropagatingExecutor(executor, output)); + return output; } /* * In certain circumstances, this field might theoretically not be visible to an afterDone() call * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. */ - @CheckForNull ListenableFuture inputFuture; - @CheckForNull Class exceptionType; - @CheckForNull F fallback; + @LazyInit @Nullable ListenableFuture inputFuture; + @LazyInit @Nullable Class exceptionType; + @LazyInit @Nullable F fallback; AbstractCatchingFuture( ListenableFuture inputFuture, Class exceptionType, F fallback) { @@ -73,10 +75,11 @@ abstract class AbstractCatchingFuture< } @Override + @SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. public final void run() { - ListenableFuture localInputFuture = inputFuture; - Class localExceptionType = exceptionType; - F localFallback = fallback; + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + @RetainedLocalRef Class localExceptionType = exceptionType; + @RetainedLocalRef F localFallback = fallback; if (localInputFuture == null | localExceptionType == null | localFallback == null // This check, unlike all the others, is a volatile read || isCancelled()) { @@ -107,8 +110,8 @@ public final void run() { + e.getClass() + " without a cause"); } - } catch (Throwable e) { // this includes cancellation exception - throwable = e; + } catch (Throwable t) { // this includes CancellationException and sneaky checked exception + throwable = t; } if (throwable == null) { @@ -132,6 +135,7 @@ public final void run() { try { fallbackResult = doFallback(localFallback, castThrowable); } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); setException(t); return; } finally { @@ -142,12 +146,29 @@ public final void run() { setResult(fallbackResult); } + /** Template method for subtypes to actually run the fallback. */ + @ForOverride + @ParametricNullness + abstract T doFallback(F fallback, X throwable) throws Exception; + + /** Template method for subtypes to actually set the result. */ + @ForOverride + abstract void setResult(@ParametricNullness T result); + + @Override + protected final void afterDone() { + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + maybePropagateCancellationTo(localInputFuture); + this.inputFuture = null; + this.exceptionType = null; + this.fallback = null; + } + @Override - @CheckForNull - protected String pendingToString() { - ListenableFuture localInputFuture = inputFuture; - Class localExceptionType = exceptionType; - F localFallback = fallback; + protected @Nullable String pendingToString() { + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + @RetainedLocalRef Class localExceptionType = exceptionType; + @RetainedLocalRef F localFallback = fallback; String superString = super.pendingToString(); String resultString = ""; if (localInputFuture != null) { @@ -166,23 +187,6 @@ protected String pendingToString() { return null; } - /** Template method for subtypes to actually run the fallback. */ - @ForOverride - @ParametricNullness - abstract T doFallback(F fallback, X throwable) throws Exception; - - /** Template method for subtypes to actually set the result. */ - @ForOverride - abstract void setResult(@ParametricNullness T result); - - @Override - protected final void afterDone() { - maybePropagateCancellationTo(inputFuture); - this.inputFuture = null; - this.exceptionType = null; - this.fallback = null; - } - /** * An {@link AbstractCatchingFuture} that delegates to an {@link AsyncFunction} and {@link * #setFuture(ListenableFuture)}. @@ -200,13 +204,13 @@ private static final class AsyncCatchingFuture doFallback( AsyncFunction fallback, X cause) throws Exception { - ListenableFuture replacement = fallback.apply(cause); + ListenableFuture output = fallback.apply(cause); checkNotNull( - replacement, + output, "AsyncFunction.apply returned null instead of a Future. " + "Did you mean to return immediateFuture(null)? %s", fallback); - return replacement; + return output; } @Override diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java index 0dbafd3f8b89..d1dae236c13e 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -14,15 +14,14 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Supplier; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown} @@ -33,59 +32,42 @@ * @since 1.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public abstract class AbstractExecutionThreadService implements Service { - private static final Logger logger = - Logger.getLogger(AbstractExecutionThreadService.class.getName()); - /* use AbstractService for state management */ private final Service delegate = new AbstractService() { @Override protected final void doStart() { - Executor executor = - MoreExecutors.renamingDecorator( - executor(), - new Supplier() { - @Override - public String get() { - return serviceName(); - } - }); + Executor executor = MoreExecutors.renamingDecorator(executor(), () -> serviceName()); executor.execute( - new Runnable() { - @Override - public void run() { - try { - startUp(); - notifyStarted(); - // If stopAsync() is called while starting we may be in the STOPPING state in - // which case we should skip right down to shutdown. - if (isRunning()) { + () -> { + try { + startUp(); + notifyStarted(); + // If stopAsync() is called while starting we may be in the STOPPING state in + // which case we should skip right down to shutdown. + if (isRunning()) { + try { + AbstractExecutionThreadService.this.run(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); try { - AbstractExecutionThreadService.this.run(); - } catch (Throwable t) { - try { - shutDown(); - } catch (Exception ignored) { - // TODO(lukes): if guava ever moves to java7, this would be a good - // candidate for a suppressed exception, or maybe we could generalize - // Closer.Suppressor - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); - } - notifyFailed(t); - return; + shutDown(); + } catch (Exception ignored) { + restoreInterruptIfIsInterruptedException(ignored); + t.addSuppressed(ignored); } + notifyFailed(t); + return; } - - shutDown(); - notifyStopped(); - } catch (Throwable t) { - notifyFailed(t); } + + shutDown(); + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); } }); } @@ -147,7 +129,6 @@ protected void shutDown() throws Exception {} * implementing {@code stopping}. Note, however, that {@code stopping} does not run at exactly the * same times as {@code triggerShutdown}. */ - @Beta protected void triggerShutdown() {} /** @@ -161,12 +142,7 @@ protected void triggerShutdown() {} * to the string returned by {@link #serviceName} */ protected Executor executor() { - return new Executor() { - @Override - public void execute(Runnable command) { - MoreExecutors.newThread(serviceName(), command).start(); - } - }; + return command -> MoreExecutors.newThread(serviceName(), command).start(); } @Override @@ -184,19 +160,25 @@ public final State state() { return delegate.state(); } - /** @since 13.0 */ + /** + * @since 13.0 + */ @Override public final void addListener(Listener listener, Executor executor) { delegate.addListener(listener, executor); } - /** @since 14.0 */ + /** + * @since 14.0 + */ @Override public final Throwable failureCause() { return delegate.failureCause(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service startAsync() { @@ -204,7 +186,9 @@ public final Service startAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service stopAsync() { @@ -212,25 +196,33 @@ public final Service stopAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning() { delegate.awaitRunning(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitRunning(timeout, unit); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated() { delegate.awaitTerminated(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitTerminated(timeout, unit); diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java index 624caa993d5a..608328e35dbc 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -15,14 +15,15 @@ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.util.concurrent.NullnessCasts.uncheckedNull; +import static com.google.common.util.concurrent.Platform.interruptCurrentThread; +import static com.google.common.util.concurrent.Platform.rethrowIfErrorOtherThanStackOverflow; import static java.lang.Integer.toHexString; import static java.lang.System.identityHashCode; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.logging.Level.SEVERE; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Strings; import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; @@ -30,10 +31,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.ForOverride; import com.google.j2objc.annotations.ReflectionSupport; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.Locale; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -41,12 +39,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.util.concurrent.locks.LockSupport; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An abstract implementation of {@link ListenableFuture}, intended for advanced users only. More @@ -67,31 +60,21 @@ * @author Luke Sandberg * @since 1.0 */ -@SuppressWarnings({ - "ShortCircuitBoolean", // we use non-short circuiting comparisons intentionally - "nullness", // TODO(b/147136275): Remove once our checker understands & and |. -}) -@GwtCompatible(emulated = true) +// Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || +@SuppressWarnings("ShortCircuitBoolean") +@GwtCompatible +/* + * TODO(cpovirk): Do we still need @ReflectionSupport on *this* class now that the fields live in + * the superclass? Note that Listener (which we also reflect on) still lives here. + */ @ReflectionSupport(value = ReflectionSupport.Level.FULL) -@ElementTypesAreNonnullByDefault -public abstract class AbstractFuture extends InternalFutureFailureAccess - implements ListenableFuture { - // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || - - private static final boolean GENERATE_CANCELLATION_CAUSES; - - static { - // System.getProperty may throw if the security policy does not permit access. - boolean generateCancellationCauses; - try { - generateCancellationCauses = - Boolean.parseBoolean( - System.getProperty("guava.concurrent.generate_cancellation_cause", "false")); - } catch (SecurityException e) { - generateCancellationCauses = false; - } - GENERATE_CANCELLATION_CAUSES = generateCancellationCauses; - } +public abstract class AbstractFuture extends AbstractFutureState { + /* + * All static initialization should be performed in AbstractFutureState: AbstractFutureState's + * initialization may trigger logging, which may assume that AbstractFuture is initialized. + * + * TODO(cpovirk): Write a test that asserts that AbstractFuture has no clinit? + */ /** * Tag interface marking trusted subclasses. This enables some optimizations. The implementation @@ -143,145 +126,16 @@ public final boolean cancel(boolean mayInterruptIfRunning) { } } - // Logger to log exceptions caught when running listeners. - private static final Logger log = Logger.getLogger(AbstractFuture.class.getName()); - - // A heuristic for timed gets. If the remaining timeout is less than this, spin instead of - // blocking. This value is what AbstractQueuedSynchronizer uses. - private static final long SPIN_THRESHOLD_NANOS = 1000L; - - private static final AtomicHelper ATOMIC_HELPER; - - static { - AtomicHelper helper; - Throwable thrownUnsafeFailure = null; - Throwable thrownAtomicReferenceFieldUpdaterFailure = null; - - try { - helper = new UnsafeAtomicHelper(); - } catch (Throwable unsafeFailure) { - thrownUnsafeFailure = unsafeFailure; - // catch absolutely everything and fall through to our 'SafeAtomicHelper' - // The access control checks that ARFU does means the caller class has to be AbstractFuture - // instead of SafeAtomicHelper, so we annoyingly define these here - try { - helper = - new SafeAtomicHelper( - newUpdater(Waiter.class, Thread.class, "thread"), - newUpdater(Waiter.class, Waiter.class, "next"), - newUpdater(AbstractFuture.class, Waiter.class, "waiters"), - newUpdater(AbstractFuture.class, Listener.class, "listeners"), - newUpdater(AbstractFuture.class, Object.class, "value")); - } catch (Throwable atomicReferenceFieldUpdaterFailure) { - // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause - // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. - // For these users fallback to a suboptimal implementation, based on synchronized. This will - // be a definite performance hit to those users. - thrownAtomicReferenceFieldUpdaterFailure = atomicReferenceFieldUpdaterFailure; - helper = new SynchronizedHelper(); - } - } - ATOMIC_HELPER = helper; - - // Prevent rare disastrous classloading in first call to LockSupport.park. - // See: https://bugs.openjdk.java.net/browse/JDK-8074773 - @SuppressWarnings("unused") - Class ensureLoaded = LockSupport.class; - - // Log after all static init is finished; if an installed logger uses any Futures methods, it - // shouldn't break in cases where reflection is missing/broken. - if (thrownAtomicReferenceFieldUpdaterFailure != null) { - log.log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); - log.log( - Level.SEVERE, "SafeAtomicHelper is broken!", thrownAtomicReferenceFieldUpdaterFailure); - } - } - - /** Waiter links form a Treiber stack, in the {@link #waiters} field. */ - private static final class Waiter { - static final Waiter TOMBSTONE = new Waiter(false /* ignored param */); - - @CheckForNull volatile Thread thread; - @CheckForNull volatile Waiter next; - - /** - * Constructor for the TOMBSTONE, avoids use of ATOMIC_HELPER in case this class is loaded - * before the ATOMIC_HELPER. Apparently this is possible on some android platforms. - */ - Waiter(boolean unused) {} - - Waiter() { - // avoid volatile write, write is made visible by subsequent CAS on waiters field - ATOMIC_HELPER.putThread(this, Thread.currentThread()); - } - - // non-volatile write to the next field. Should be made visible by subsequent CAS on waiters - // field. - void setNext(@CheckForNull Waiter next) { - ATOMIC_HELPER.putNext(this, next); - } - - void unpark() { - // This is racy with removeWaiter. The consequence of the race is that we may spuriously call - // unpark even though the thread has already removed itself from the list. But even if we did - // use a CAS, that race would still exist (it would just be ever so slightly smaller). - Thread w = thread; - if (w != null) { - thread = null; - LockSupport.unpark(w); - } - } - } - - /** - * Marks the given node as 'deleted' (null waiter) and then scans the list to unlink all deleted - * nodes. This is an O(n) operation in the common case (and O(n^2) in the worst), but we are saved - * by two things. - * - *

      - *
    • This is only called when a waiting thread times out or is interrupted. Both of which - * should be rare. - *
    • The waiters list should be very short. - *
    - */ - private void removeWaiter(Waiter node) { - node.thread = null; // mark as 'deleted' - restart: - while (true) { - Waiter pred = null; - Waiter curr = waiters; - if (curr == Waiter.TOMBSTONE) { - return; // give up if someone is calling complete - } - Waiter succ; - while (curr != null) { - succ = curr.next; - if (curr.thread != null) { // we aren't unlinking this node, update pred. - pred = curr; - } else if (pred != null) { // We are unlinking this node and it has a predecessor. - pred.next = succ; - if (pred.thread == null) { // We raced with another node that unlinked pred. Restart. - continue restart; - } - } else if (!ATOMIC_HELPER.casWaiters(this, curr, succ)) { // We are unlinking head - continue restart; // We raced with an add or complete - } - curr = succ; - } - break; - } - } - - /** Listeners also form a stack through the {@link #listeners} field. */ - private static final class Listener { + /** Listeners form a Treiber stack through the {@link #listeners} field. */ + static final class Listener { static final Listener TOMBSTONE = new Listener(); - @CheckForNull // null only for TOMBSTONE - final Runnable task; - @CheckForNull // null only for TOMBSTONE - final Executor executor; + // null only for TOMBSTONE + final @Nullable Runnable task; + // null only for TOMBSTONE + final @Nullable Executor executor; // writes to next are made visible by subsequent CAS's on the listeners field - @CheckForNull Listener next; + @Nullable Listener next; Listener(Runnable task, Executor executor) { this.task = task; @@ -294,16 +148,13 @@ private static final class Listener { } } - /** A special value to represent {@code null}. */ - private static final Object NULL = new Object(); - /** A special value to represent failure, when {@link #setException} is called successfully. */ private static final class Failure { static final Failure FALLBACK_INSTANCE = new Failure( new Throwable("Failure occurred while trying to finish a future.") { @Override - public synchronized Throwable fillInStackTrace() { + public Throwable fillInStackTrace() { return this; // no stack trace } }); @@ -317,8 +168,8 @@ public synchronized Throwable fillInStackTrace() { /** A special value to represent cancellation and the 'wasInterrupted' bit. */ private static final class Cancellation { // constants to use when GENERATE_CANCELLATION_CAUSES = false - @CheckForNull static final Cancellation CAUSELESS_INTERRUPTED; - @CheckForNull static final Cancellation CAUSELESS_CANCELLED; + static final @Nullable Cancellation CAUSELESS_INTERRUPTED; + static final @Nullable Cancellation CAUSELESS_CANCELLED; static { if (GENERATE_CANCELLATION_CAUSES) { @@ -331,89 +182,46 @@ private static final class Cancellation { } final boolean wasInterrupted; - @CheckForNull final Throwable cause; + final @Nullable Throwable cause; - Cancellation(boolean wasInterrupted, @CheckForNull Throwable cause) { + Cancellation(boolean wasInterrupted, @Nullable Throwable cause) { this.wasInterrupted = wasInterrupted; this.cause = cause; } } /** A special value that encodes the 'setFuture' state. */ - private static final class SetFuture implements Runnable { + private static final class DelegatingToFuture implements Runnable { final AbstractFuture owner; final ListenableFuture future; - SetFuture(AbstractFuture owner, ListenableFuture future) { + DelegatingToFuture(AbstractFuture owner, ListenableFuture future) { this.owner = owner; this.future = future; } @Override public void run() { - if (owner.value != this) { + if (owner.value() != this) { // nothing to do, we must have been cancelled, don't bother inspecting the future. return; } Object valueToSet = getFutureValue(future); - if (ATOMIC_HELPER.casValue(owner, this, valueToSet)) { - complete(owner); + if (casValue(owner, this, valueToSet)) { + complete( + owner, + /* + * Interruption doesn't propagate through a DelegatingToFuture chain (see + * getFutureValue), so don't invoke interruptTask. + */ + false); } } } - // TODO(lukes): investigate using the @Contended annotation on these fields when jdk8 is - // available. - /** - * This field encodes the current state of the future. - * - *

    The valid values are: - * - *

      - *
    • {@code null} initial state, nothing has happened. - *
    • {@link Cancellation} terminal state, {@code cancel} was called. - *
    • {@link Failure} terminal state, {@code setException} was called. - *
    • {@link SetFuture} intermediate state, {@code setFuture} was called. - *
    • {@link #NULL} terminal state, {@code set(null)} was called. - *
    • Any other non-null value, terminal state, {@code set} was called with a non-null - * argument. - *
    - */ - @CheckForNull private volatile Object value; - - /** All listeners. */ - @CheckForNull private volatile Listener listeners; - - /** All waiting threads. */ - @CheckForNull private volatile Waiter waiters; - /** Constructor for use by subclasses. */ protected AbstractFuture() {} - // Gets and Timed Gets - // - // * Be responsive to interruption - // * Don't create Waiter nodes if you aren't going to park, this helps reduce contention on the - // waiters field. - // * Future completion is defined by when #value becomes non-null/non SetFuture - // * Future completion can be observed if the waiters field contains a TOMBSTONE - - // Timed Get - // There are a few design constraints to consider - // * We want to be responsive to small timeouts, unpark() has non trivial latency overheads (I - // have observed 12 micros on 64 bit linux systems to wake up a parked thread). So if the - // timeout is small we shouldn't park(). This needs to be traded off with the cpu overhead of - // spinning, so we use SPIN_THRESHOLD_NANOS which is what AbstractQueuedSynchronizer uses for - // similar purposes. - // * We want to behave reasonably for timeouts of 0 - // * We are more responsive to completion than timeouts. This is because parkNanos depends on - // system scheduling and as such we could either miss our deadline, or unpark() could be delayed - // so that it looks like we timed out even though we didn't. For comparison FutureTask respects - // completion preferably and AQS is non-deterministic (depends on where in the queue the waiter - // is). If we wanted to be strict about it, we could store the unpark() time in the Waiter node - // and we could use that to make a decision about whether or not we timed out prior to being - // unparked. - /** * {@inheritDoc} * @@ -427,104 +235,7 @@ protected AbstractFuture() {} @ParametricNullness public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException { - // NOTE: if timeout < 0, remainingNanos will be < 0 and we will fall into the while(true) loop - // at the bottom and throw a timeoutexception. - final long timeoutNanos = unit.toNanos(timeout); // we rely on the implicit null check on unit. - long remainingNanos = timeoutNanos; - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Object localValue = value; - if (localValue != null & !(localValue instanceof SetFuture)) { - return getDoneValue(localValue); - } - // we delay calling nanoTime until we know we will need to either park or spin - final long endNanos = remainingNanos > 0 ? System.nanoTime() + remainingNanos : 0; - long_wait_loop: - if (remainingNanos >= SPIN_THRESHOLD_NANOS) { - Waiter oldHead = waiters; - if (oldHead != Waiter.TOMBSTONE) { - Waiter node = new Waiter(); - do { - node.setNext(oldHead); - if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { - while (true) { - OverflowAvoidingLockSupport.parkNanos(this, remainingNanos); - // Check interruption first, if we woke up due to interruption we need to honor that. - if (Thread.interrupted()) { - removeWaiter(node); - throw new InterruptedException(); - } - - // Otherwise re-read and check doneness. If we loop then it must have been a spurious - // wakeup - localValue = value; - if (localValue != null & !(localValue instanceof SetFuture)) { - return getDoneValue(localValue); - } - - // timed out? - remainingNanos = endNanos - System.nanoTime(); - if (remainingNanos < SPIN_THRESHOLD_NANOS) { - // Remove the waiter, one way or another we are done parking this thread. - removeWaiter(node); - break long_wait_loop; // jump down to the busy wait loop - } - } - } - oldHead = waiters; // re-read and loop. - } while (oldHead != Waiter.TOMBSTONE); - } - // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a - // waiter. - // requireNonNull is safe because value is always set before TOMBSTONE. - return getDoneValue(requireNonNull(value)); - } - // If we get here then we have remainingNanos < SPIN_THRESHOLD_NANOS and there is no node on the - // waiters list - while (remainingNanos > 0) { - localValue = value; - if (localValue != null & !(localValue instanceof SetFuture)) { - return getDoneValue(localValue); - } - if (Thread.interrupted()) { - throw new InterruptedException(); - } - remainingNanos = endNanos - System.nanoTime(); - } - - String futureToString = toString(); - final String unitString = unit.toString().toLowerCase(Locale.ROOT); - String message = "Waited " + timeout + " " + unit.toString().toLowerCase(Locale.ROOT); - // Only report scheduling delay if larger than our spin threshold - otherwise it's just noise - if (remainingNanos + SPIN_THRESHOLD_NANOS < 0) { - // We over-waited for our timeout. - message += " (plus "; - long overWaitNanos = -remainingNanos; - long overWaitUnits = unit.convert(overWaitNanos, TimeUnit.NANOSECONDS); - long overWaitLeftoverNanos = overWaitNanos - unit.toNanos(overWaitUnits); - boolean shouldShowExtraNanos = - overWaitUnits == 0 || overWaitLeftoverNanos > SPIN_THRESHOLD_NANOS; - if (overWaitUnits > 0) { - message += overWaitUnits + " " + unitString; - if (shouldShowExtraNanos) { - message += ","; - } - message += " "; - } - if (shouldShowExtraNanos) { - message += overWaitLeftoverNanos + " nanoseconds "; - } - - message += "delay)"; - } - // It's confusing to see a completed future in a timeout message; if isDone() returns false, - // then we know it must have given a pending toString value earlier. If not, then the future - // completed after the timeout expired, and the message might be success. - if (isDone()) { - throw new TimeoutException(message + " but future completed as timeout expired"); - } - throw new TimeoutException(message + " for " + futureToString); + return Platform.get(this, timeout, unit); } /** @@ -539,53 +250,33 @@ public V get(long timeout, TimeUnit unit) @Override @ParametricNullness public V get() throws InterruptedException, ExecutionException { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Object localValue = value; - if (localValue != null & !(localValue instanceof SetFuture)) { - return getDoneValue(localValue); - } - Waiter oldHead = waiters; - if (oldHead != Waiter.TOMBSTONE) { - Waiter node = new Waiter(); - do { - node.setNext(oldHead); - if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { - // we are on the stack, now wait for completion. - while (true) { - LockSupport.park(this); - // Check interruption first, if we woke up due to interruption we need to honor that. - if (Thread.interrupted()) { - removeWaiter(node); - throw new InterruptedException(); - } - // Otherwise re-read and check doneness. If we loop then it must have been a spurious - // wakeup - localValue = value; - if (localValue != null & !(localValue instanceof SetFuture)) { - return getDoneValue(localValue); - } - } - } - oldHead = waiters; // re-read and loop. - } while (oldHead != Waiter.TOMBSTONE); + return Platform.get(this); + } + + @ParametricNullness + @SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. + final V getFromAlreadyDoneFuture() throws ExecutionException { + @RetainedLocalRef Object localValue = value(); + if (localValue == null | localValue instanceof DelegatingToFuture) { + throw new IllegalStateException("Cannot get() on a pending future."); } - // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a - // waiter. - // requireNonNull is safe because value is always set before TOMBSTONE. - return getDoneValue(requireNonNull(value)); + return getDoneValue(localValue); } - /** Unboxes {@code obj}. Assumes that obj is not {@code null} or a {@link SetFuture}. */ + /** Unboxes {@code obj}. Assumes that obj is not {@code null} or a {@link DelegatingToFuture}. */ @ParametricNullness - private V getDoneValue(Object obj) throws ExecutionException { + @SuppressWarnings("TypeParameterUnusedInFormals") // sorry not sorry + static V getDoneValue(Object obj) throws ExecutionException { // While this seems like it might be too branch-y, simple benchmarking proves it to be // unmeasurable (comparing done AbstractFutures with immediateFuture) if (obj instanceof Cancellation) { - throw cancellationExceptionWithCause("Task was cancelled.", ((Cancellation) obj).cause); + Cancellation cancellation = (Cancellation) obj; + Throwable cause = cancellation.cause; + throw cancellationExceptionWithCause("Task was cancelled.", cause); } else if (obj instanceof Failure) { - throw new ExecutionException(((Failure) obj).exception); + Failure failure = (Failure) obj; + Throwable exception = failure.exception; + throw new ExecutionException(exception); } else if (obj == NULL) { /* * It's safe to return null because we would only have stored it in the first place if it were @@ -599,15 +290,23 @@ private V getDoneValue(Object obj) throws ExecutionException { } } + /** Returns whether {@code obj} is not an instance of {@code DelegatingToFuture}. */ + // This method lets us: + // - avoid exposing DelegatingToFuture to the whole package + // - avoid fighting with the relative operator precedence of `instanceof` and `!` + static boolean notInstanceOfDelegatingToFuture(@Nullable Object obj) { + return !(obj instanceof DelegatingToFuture); + } + @Override public boolean isDone() { - final Object localValue = value; - return localValue != null & !(localValue instanceof SetFuture); + @RetainedLocalRef Object localValue = value(); + return localValue != null & notInstanceOfDelegatingToFuture(localValue); } @Override public boolean isCancelled() { - final Object localValue = value; + @RetainedLocalRef Object localValue = value(); return localValue instanceof Cancellation; } @@ -630,9 +329,9 @@ public boolean isCancelled() { @CanIgnoreReturnValue @Override public boolean cancel(boolean mayInterruptIfRunning) { - Object localValue = value; + @RetainedLocalRef Object localValue = value(); boolean rValue = false; - if (localValue == null | localValue instanceof SetFuture) { + if (localValue == null | localValue instanceof DelegatingToFuture) { // Try to delay allocating the exception. At this point we may still lose the CAS, but it is // certainly less likely. Object valueToSet = @@ -655,45 +354,41 @@ mayInterruptIfRunning, new CancellationException("Future.cancel() was called.")) : Cancellation.CAUSELESS_CANCELLED); AbstractFuture abstractFuture = this; while (true) { - if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) { + if (casValue(abstractFuture, localValue, valueToSet)) { rValue = true; - // We call interruptTask before calling complete(), which is consistent with - // FutureTask - if (mayInterruptIfRunning) { - abstractFuture.interruptTask(); - } - complete(abstractFuture); - if (localValue instanceof SetFuture) { + complete(abstractFuture, mayInterruptIfRunning); + if (localValue instanceof DelegatingToFuture) { // propagate cancellation to the future set in setfuture, this is racy, and we don't // care if we are successful or not. - ListenableFuture futureToPropagateTo = ((SetFuture) localValue).future; + ListenableFuture futureToPropagateTo = ((DelegatingToFuture) localValue).future; if (futureToPropagateTo instanceof Trusted) { - // If the future is a TrustedFuture then we specifically avoid calling cancel() + // If the future is a Trusted instance then we specifically avoid calling cancel() // this has 2 benefits // 1. for long chains of futures strung together with setFuture we consume less stack // 2. we avoid allocating Cancellation objects at every level of the cancellation // chain - // We can only do this for TrustedFuture, because TrustedFuture.cancel is final and - // does nothing but delegate to this method. + // We can only do this for Trusted, because Trusted implementations of cancel do + // nothing but delegate to this method and do not permit user overrides. AbstractFuture trusted = (AbstractFuture) futureToPropagateTo; - localValue = trusted.value; - if (localValue == null | localValue instanceof SetFuture) { + localValue = trusted.value(); + if (localValue == null | localValue instanceof DelegatingToFuture) { abstractFuture = trusted; continue; // loop back up and try to complete the new future } } else { - // not a TrustedFuture, call cancel directly. + // not a Trusted instance, call cancel directly. futureToPropagateTo.cancel(mayInterruptIfRunning); } } break; } // obj changed, reread - localValue = abstractFuture.value; - if (!(localValue instanceof SetFuture)) { + localValue = abstractFuture.value(); + if (notInstanceOfDelegatingToFuture(localValue)) { // obj cannot be null at this point, because value can only change from null to non-null. // So if value changed (and it did since we lost the CAS), then it cannot be null and - // since it isn't a SetFuture, then the future must be done and we should exit the loop + // since it isn't a DelegatingToFuture, then the future must be done and we should exit + // the loop break; } } @@ -707,7 +402,7 @@ mayInterruptIfRunning, new CancellationException("Future.cancel() was called.")) * *

    The default implementation does nothing. * - *

    This method is likely to be deprecated. Prefer to override {@link #afterDone}, consulting + *

    This method is likely to be deprecated. Prefer to override {@link #afterDone}, checking * {@link #wasInterrupted} to decide whether to interrupt your task. * * @since 10.0 @@ -721,7 +416,7 @@ protected void interruptTask() {} * @since 14.0 */ protected final boolean wasInterrupted() { - final Object localValue = value; + @RetainedLocalRef Object localValue = value(); return (localValue instanceof Cancellation) && ((Cancellation) localValue).wasInterrupted; } @@ -744,15 +439,15 @@ public void addListener(Runnable listener, Executor executor) { // get into the loop we know that we weren't done when we entered and therefore we aren't under // an obligation to execute 'immediately'. if (!isDone()) { - Listener oldHead = listeners; + Listener oldHead = listeners(); if (oldHead != Listener.TOMBSTONE) { Listener newNode = new Listener(listener, executor); do { newNode.next = oldHead; - if (ATOMIC_HELPER.casListeners(this, oldHead, newNode)) { + if (casListeners(oldHead, newNode)) { return; } - oldHead = listeners; // re-read + oldHead = listeners(); // re-read } while (oldHead != Listener.TOMBSTONE); } } @@ -779,8 +474,8 @@ public void addListener(Runnable listener, Executor executor) { @CanIgnoreReturnValue protected boolean set(@ParametricNullness V value) { Object valueToSet = value == null ? NULL : value; - if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { - complete(this); + if (casValue(this, null, valueToSet)) { + complete(this, /* callInterruptTask= */ false); return true; } return false; @@ -804,8 +499,8 @@ protected boolean set(@ParametricNullness V value) { @CanIgnoreReturnValue protected boolean setException(Throwable throwable) { Object valueToSet = new Failure(checkNotNull(throwable)); - if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { - complete(this); + if (casValue(this, null, valueToSet)) { + complete(this, /* callInterruptTask= */ false); return true; } return false; @@ -841,40 +536,49 @@ protected boolean setException(Throwable throwable) { * @since 19.0 */ @CanIgnoreReturnValue + @SuppressWarnings("Interruption") // We are propagating an interrupt from a caller. protected boolean setFuture(ListenableFuture future) { checkNotNull(future); - Object localValue = value; + @RetainedLocalRef Object localValue = value(); if (localValue == null) { if (future.isDone()) { Object value = getFutureValue(future); - if (ATOMIC_HELPER.casValue(this, null, value)) { - complete(this); + if (casValue(this, null, value)) { + complete( + this, + /* + * Interruption doesn't propagate through a DelegatingToFuture chain (see + * getFutureValue), so don't invoke interruptTask. + */ + false); return true; } return false; } - SetFuture valueToSet = new SetFuture(this, future); - if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { + DelegatingToFuture valueToSet = new DelegatingToFuture<>(this, future); + if (casValue(this, null, valueToSet)) { // the listener is responsible for calling completeWithFuture, directExecutor is appropriate // since all we are doing is unpacking a completed future which should be fast. try { future.addListener(valueToSet, DirectExecutor.INSTANCE); } catch (Throwable t) { - // addListener has thrown an exception! SetFuture.run can't throw any exceptions so this - // must have been caused by addListener itself. The most likely explanation is a + // Any Exception is either a RuntimeException or sneaky checked exception. + // + // addListener has thrown an exception! DelegatingToFuture.run can't throw any exceptions + // so this must have been caused by addListener itself. The most likely explanation is a // misconfigured mock. Try to switch to Failure. Failure failure; try { failure = new Failure(t); - } catch (Throwable oomMostLikely) { + } catch (Exception | Error oomMostLikely) { // sneaky checked exception failure = Failure.FALLBACK_INSTANCE; } // Note: The only way this CAS could fail is if cancel() has raced with us. That is ok. - boolean unused = ATOMIC_HELPER.casValue(this, valueToSet, failure); + boolean unused = casValue(this, valueToSet, failure); } return true; } - localValue = value; // we lost the cas, fall through and maybe cancel + localValue = value(); // we lost the cas, fall through and maybe cancel } // The future has already been set to something. If it is cancellation we should cancel the // incoming future. @@ -893,11 +597,11 @@ protected boolean setFuture(ListenableFuture future) { */ private static Object getFutureValue(ListenableFuture future) { if (future instanceof Trusted) { - // Break encapsulation for TrustedFuture instances since we know that subclasses cannot - // override .get() (since it is final) and therefore this is equivalent to calling .get() - // and unpacking the exceptions like we do below (just much faster because it is a single - // field read instead of a read, several branches and possibly creating exceptions). - Object v = ((AbstractFuture) future).value; + // Break encapsulation for Trusted instances since we know that subclasses cannot override + // .get() and therefore this is equivalent to calling .get() and unpacking the exceptions like + // we do below (just much faster because it is a single field read instead of a read, several + // branches and possibly creating exceptions). + Object v = ((AbstractFuture) future).value(); if (v instanceof Cancellation) { // If the other future was interrupted, clear the interrupted bit while preserving the cause // this will make it consistent with how non-trustedfutures work which cannot propagate the @@ -961,7 +665,7 @@ private static Object getFutureValue(ListenableFuture future) { cancellation)); } return new Cancellation(false, cancellation); - } catch (Throwable t) { + } catch (Exception | Error t) { // sneaky checked exception return new Failure(t); } } @@ -984,13 +688,13 @@ private static Object getFutureValue(ListenableFuture future) { } } finally { if (interrupted) { - Thread.currentThread().interrupt(); + interruptCurrentThread(); } } } /** Unblocks all threads and runs all listeners. */ - private static void complete(AbstractFuture param) { + private static void complete(AbstractFuture param, boolean callInterruptTask) { // Declare a "true" local variable so that the Checker Framework will infer nullness. AbstractFuture future = param; @@ -998,6 +702,18 @@ private static void complete(AbstractFuture param) { outer: while (true) { future.releaseWaiters(); + /* + * We call interruptTask() immediately before afterDone() so that migrating between the two + * can be a no-op. + */ + if (callInterruptTask) { + future.interruptTask(); + /* + * Interruption doesn't propagate through a DelegatingToFuture chain (see getFutureValue), + * so don't invoke interruptTask on any subsequent futures. + */ + callInterruptTask = false; + } // We call this before the listeners in order to avoid needing to manage a separate stack data // structure for them. Also, some implementations rely on this running prior to listeners // so that the cleanup work is visible to listeners. @@ -1015,21 +731,21 @@ private static void complete(AbstractFuture param) { * clearListeners. */ Runnable task = requireNonNull(curr.task); - if (task instanceof SetFuture) { - SetFuture setFuture = (SetFuture) task; + if (task instanceof DelegatingToFuture) { + DelegatingToFuture setFuture = (DelegatingToFuture) task; // We unwind setFuture specifically to avoid StackOverflowErrors in the case of long - // chains of SetFutures + // chains of DelegatingToFutures // Handling this special case is important because there is no way to pass an executor to // setFuture, so a user couldn't break the chain by doing this themselves. It is also // potentially common if someone writes a recursive Futures.transformAsync transformer. future = setFuture.owner; - if (future.value == setFuture) { + if (future.value() == setFuture) { Object valueToSet = getFutureValue(setFuture.future); - if (ATOMIC_HELPER.casValue(future, setFuture, valueToSet)) { + if (casValue(future, setFuture, valueToSet)) { continue outer; } } - // other wise the future we were trying to set is already done. + // otherwise the future we were trying to set is already done. } else { /* * requireNonNull is safe because the listener stack never contains TOMBSTONE until after @@ -1053,7 +769,6 @@ private static void complete(AbstractFuture param) { * * @since 20.0 */ - @Beta @ForOverride protected void afterDone() {} @@ -1084,13 +799,11 @@ protected void afterDone() {} * method returns @Nullable, too. However, we're not sure if we want to make any changes to that * class, since it's in a separate artifact that we planned to release only a single version of. */ - @SuppressWarnings("nullness") - @CheckForNull - protected final Throwable tryInternalFastPathGetFailure() { + protected final @Nullable Throwable tryInternalFastPathGetFailure() { if (this instanceof Trusted) { - Object obj = value; - if (obj instanceof Failure) { - return ((Failure) obj).exception; + @RetainedLocalRef Object localValue = value(); + if (localValue instanceof Failure) { + return ((Failure) localValue).exception; } } return null; @@ -1100,39 +813,25 @@ protected final Throwable tryInternalFastPathGetFailure() { * If this future has been cancelled (and possibly interrupted), cancels (and possibly interrupts) * the given future (if available). */ - final void maybePropagateCancellationTo(@CheckForNull Future related) { + @SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. + final void maybePropagateCancellationTo(@Nullable Future related) { if (related != null & isCancelled()) { related.cancel(wasInterrupted()); } } - /** Releases all threads in the {@link #waiters} list, and clears the list. */ - private void releaseWaiters() { - Waiter head; - do { - head = waiters; - } while (!ATOMIC_HELPER.casWaiters(this, head, Waiter.TOMBSTONE)); - for (Waiter currentWaiter = head; currentWaiter != null; currentWaiter = currentWaiter.next) { - currentWaiter.unpark(); - } - } - /** * Clears the {@link #listeners} list and prepends its contents to {@code onto}, least recently * added first. */ - @CheckForNull - private Listener clearListeners(@CheckForNull Listener onto) { + private @Nullable Listener clearListeners(@Nullable Listener onto) { // We need to - // 1. atomically swap the listeners with TOMBSTONE, this is because addListener uses that to + // 1. atomically swap the listeners with TOMBSTONE, this is because addListener uses that // to synchronize with us // 2. reverse the linked list, because despite our rather clear contract, people depend on us // executing listeners in the order they were added // 3. push all the items onto 'onto' and return the new head of the stack - Listener head; - do { - head = listeners; - } while (!ATOMIC_HELPER.casListeners(this, head, Listener.TOMBSTONE)); + Listener head = gasListeners(Listener.TOMBSTONE); Listener reversedList = onto; while (head != null) { Listener tmp = head; @@ -1159,7 +858,7 @@ public String toString() { } else if (isDone()) { addDoneString(builder); } else { - addPendingString(builder); // delegates to addDoneString if future completes mid-way + addPendingString(builder); // delegates to addDoneString if future completes midway } return builder.append("]").toString(); } @@ -1170,17 +869,15 @@ public String toString() { * @return null if an explanation cannot be provided (e.g. because the future is done). * @since 23.0 */ - @CheckForNull - protected String pendingToString() { + protected @Nullable String pendingToString() { // TODO(diamondm) consider moving this into addPendingString so it's always in the output if (this instanceof ScheduledFuture) { - return "remaining delay=[" - + ((ScheduledFuture) this).getDelay(TimeUnit.MILLISECONDS) - + " ms]"; + return "remaining delay=[" + ((ScheduledFuture) this).getDelay(MILLISECONDS) + " ms]"; } return null; } + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private void addPendingString(StringBuilder builder) { // Capture current builder length so it can be truncated if this future ends up completing while // the toString is being calculated @@ -1188,16 +885,23 @@ private void addPendingString(StringBuilder builder) { builder.append("PENDING"); - Object localValue = value; - if (localValue instanceof SetFuture) { + @RetainedLocalRef Object localValue = value(); + if (localValue instanceof DelegatingToFuture) { builder.append(", setFuture=["); - appendUserObject(builder, ((SetFuture) localValue).future); + appendUserObject(builder, ((DelegatingToFuture) localValue).future); builder.append("]"); } else { String pendingDescription; try { pendingDescription = Strings.emptyToNull(pendingToString()); - } catch (RuntimeException | StackOverflowError e) { + } catch (Throwable e) { + /* + * We want to catch (Exception | StackOverflowError), but we can't under environments where + * StackOverflowError doesn't exist. + */ + rethrowIfErrorOtherThanStackOverflow(e); + // The Throwable is either a RuntimeException, an Error, or sneaky checked exception. + // // Don't call getMessage or toString() on the exception, in case the exception thrown by the // subclass is implemented with bugs similar to the subclass. pendingDescription = "Exception thrown from implementation: " + e.getClass(); @@ -1216,6 +920,7 @@ private void addPendingString(StringBuilder builder) { } } + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private void addDoneString(StringBuilder builder) { try { V value = getUninterruptibly(this); @@ -1226,7 +931,7 @@ private void addDoneString(StringBuilder builder) { builder.append("FAILURE, cause=[").append(e.getCause()).append("]"); } catch (CancellationException e) { builder.append("CANCELLED"); // shouldn't be reachable - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception builder.append("UNKNOWN, cause=[").append(e.getClass()).append(" thrown from get()]"); } } @@ -1236,7 +941,7 @@ private void addDoneString(StringBuilder builder) { * implementation. Using a reconstruction of the default Object.toString() prevents OOMs and stack * overflows, and helps avoid sensitive data inadvertently ending up in exception messages. */ - private void appendResultObject(StringBuilder builder, @CheckForNull Object o) { + private void appendResultObject(StringBuilder builder, @Nullable Object o) { if (o == null) { builder.append("null"); } else if (o == this) { @@ -1250,9 +955,10 @@ private void appendResultObject(StringBuilder builder, @CheckForNull Object o) { } /** Helper for printing user supplied objects into our toString method. */ - private void appendUserObject(StringBuilder builder, @CheckForNull Object o) { + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception + private void appendUserObject(StringBuilder builder, @Nullable Object o) { // This is some basic recursion detection for when people create cycles via set/setFuture or - // when deep chains of futures exist resulting in a StackOverflowException. We could detect + // when deep chains of futures exist resulting in a StackOverflowError. We could detect // arbitrary cycles using a thread local but this should be a good enough solution (it is also // what jdk collections do in these cases) try { @@ -1261,7 +967,14 @@ private void appendUserObject(StringBuilder builder, @CheckForNull Object o) { } else { builder.append(o); } - } catch (RuntimeException | StackOverflowError e) { + } catch (Throwable e) { + /* + * We want to catch (Exception | StackOverflowError), but we can't under environments where + * StackOverflowError doesn't exist. + */ + rethrowIfErrorOtherThanStackOverflow(e); + // The Throwable is either a RuntimeException, an Error, or sneaky checked exception. + // // Don't call getMessage or toString() on the exception, in case the exception thrown by the // user object is implemented with bugs similar to the user object. builder.append("Exception thrown from implementation: ").append(e.getClass()); @@ -1272,227 +985,26 @@ private void appendUserObject(StringBuilder builder, @CheckForNull Object o) { * Submits the given runnable to the given {@link Executor} catching and logging all {@linkplain * RuntimeException runtime exceptions} thrown by the executor. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private static void executeListener(Runnable runnable, Executor executor) { try { executor.execute(runnable); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if - // we're given a bad one. We only catch RuntimeException because we want Errors to propagate - // up. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); - } - } - - private abstract static class AtomicHelper { - /** Non volatile write of the thread to the {@link Waiter#thread} field. */ - abstract void putThread(Waiter waiter, Thread newValue); - - /** Non volatile write of the waiter to the {@link Waiter#next} field. */ - abstract void putNext(Waiter waiter, @CheckForNull Waiter newValue); - - /** Performs a CAS operation on the {@link #waiters} field. */ - abstract boolean casWaiters( - AbstractFuture future, @CheckForNull Waiter expect, @CheckForNull Waiter update); - - /** Performs a CAS operation on the {@link #listeners} field. */ - abstract boolean casListeners( - AbstractFuture future, @CheckForNull Listener expect, Listener update); - - /** Performs a CAS operation on the {@link #value} field. */ - abstract boolean casValue(AbstractFuture future, @CheckForNull Object expect, Object update); - } - - /** - * {@link AtomicHelper} based on {@link sun.misc.Unsafe}. - * - *

    Static initialization of this class will fail if the {@link sun.misc.Unsafe} object cannot - * be accessed. - */ - @SuppressWarnings("sunapi") - private static final class UnsafeAtomicHelper extends AtomicHelper { - static final sun.misc.Unsafe UNSAFE; - static final long LISTENERS_OFFSET; - static final long WAITERS_OFFSET; - static final long VALUE_OFFSET; - static final long WAITER_THREAD_OFFSET; - static final long WAITER_NEXT_OFFSET; - - static { - sun.misc.Unsafe unsafe = null; - try { - unsafe = sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) { - try { - unsafe = - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public sun.misc.Unsafe run() throws Exception { - Class k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) { - return k.cast(x); - } - } - throw new NoSuchFieldError("the Unsafe"); - } - }); - } catch (PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", e.getCause()); - } - } - try { - Class abstractFuture = AbstractFuture.class; - WAITERS_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("waiters")); - LISTENERS_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("listeners")); - VALUE_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("value")); - WAITER_THREAD_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("thread")); - WAITER_NEXT_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("next")); - UNSAFE = unsafe; - } catch (Exception e) { - throwIfUnchecked(e); - throw new RuntimeException(e); - } - } - - @Override - void putThread(Waiter waiter, Thread newValue) { - UNSAFE.putObject(waiter, WAITER_THREAD_OFFSET, newValue); - } - - @Override - void putNext(Waiter waiter, @CheckForNull Waiter newValue) { - UNSAFE.putObject(waiter, WAITER_NEXT_OFFSET, newValue); - } - - /** Performs a CAS operation on the {@link #waiters} field. */ - @Override - boolean casWaiters( - AbstractFuture future, @CheckForNull Waiter expect, @CheckForNull Waiter update) { - return UNSAFE.compareAndSwapObject(future, WAITERS_OFFSET, expect, update); - } - - /** Performs a CAS operation on the {@link #listeners} field. */ - @Override - boolean casListeners(AbstractFuture future, @CheckForNull Listener expect, Listener update) { - return UNSAFE.compareAndSwapObject(future, LISTENERS_OFFSET, expect, update); - } - - /** Performs a CAS operation on the {@link #value} field. */ - @Override - boolean casValue(AbstractFuture future, @CheckForNull Object expect, Object update) { - return UNSAFE.compareAndSwapObject(future, VALUE_OFFSET, expect, update); - } - } - - /** {@link AtomicHelper} based on {@link AtomicReferenceFieldUpdater}. */ - @SuppressWarnings("rawtypes") - private static final class SafeAtomicHelper extends AtomicHelper { - final AtomicReferenceFieldUpdater waiterThreadUpdater; - final AtomicReferenceFieldUpdater waiterNextUpdater; - final AtomicReferenceFieldUpdater waitersUpdater; - final AtomicReferenceFieldUpdater listenersUpdater; - final AtomicReferenceFieldUpdater valueUpdater; - - SafeAtomicHelper( - AtomicReferenceFieldUpdater waiterThreadUpdater, - AtomicReferenceFieldUpdater waiterNextUpdater, - AtomicReferenceFieldUpdater waitersUpdater, - AtomicReferenceFieldUpdater listenersUpdater, - AtomicReferenceFieldUpdater valueUpdater) { - this.waiterThreadUpdater = waiterThreadUpdater; - this.waiterNextUpdater = waiterNextUpdater; - this.waitersUpdater = waitersUpdater; - this.listenersUpdater = listenersUpdater; - this.valueUpdater = valueUpdater; - } - - @Override - void putThread(Waiter waiter, Thread newValue) { - waiterThreadUpdater.lazySet(waiter, newValue); - } - - @Override - void putNext(Waiter waiter, @CheckForNull Waiter newValue) { - waiterNextUpdater.lazySet(waiter, newValue); - } - - @Override - boolean casWaiters( - AbstractFuture future, @CheckForNull Waiter expect, @CheckForNull Waiter update) { - return waitersUpdater.compareAndSet(future, expect, update); - } - - @Override - boolean casListeners(AbstractFuture future, @CheckForNull Listener expect, Listener update) { - return listenersUpdater.compareAndSet(future, expect, update); - } - - @Override - boolean casValue(AbstractFuture future, @CheckForNull Object expect, Object update) { - return valueUpdater.compareAndSet(future, expect, update); - } - } - - /** - * {@link AtomicHelper} based on {@code synchronized} and volatile writes. - * - *

    This is an implementation of last resort for when certain basic VM features are broken (like - * AtomicReferenceFieldUpdater). - */ - private static final class SynchronizedHelper extends AtomicHelper { - @Override - void putThread(Waiter waiter, Thread newValue) { - waiter.thread = newValue; - } - - @Override - void putNext(Waiter waiter, @CheckForNull Waiter newValue) { - waiter.next = newValue; - } - - @Override - boolean casWaiters( - AbstractFuture future, @CheckForNull Waiter expect, @CheckForNull Waiter update) { - synchronized (future) { - if (future.waiters == expect) { - future.waiters = update; - return true; - } - return false; - } - } - - @Override - boolean casListeners(AbstractFuture future, @CheckForNull Listener expect, Listener update) { - synchronized (future) { - if (future.listeners == expect) { - future.listeners = update; - return true; - } - return false; - } - } - - @Override - boolean casValue(AbstractFuture future, @CheckForNull Object expect, Object update) { - synchronized (future) { - if (future.value == expect) { - future.value = update; - return true; - } - return false; - } + // we're given a bad one. We only catch Exception because we want Errors to propagate up. + log.get() + .log( + SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } private static CancellationException cancellationExceptionWithCause( - String message, @CheckForNull Throwable cause) { + String message, @Nullable Throwable cause) { CancellationException exception = new CancellationException(message); exception.initCause(cause); return exception; diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractFutureState.java b/android/guava/src/com/google/common/util/concurrent/AbstractFutureState.java new file mode 100644 index 000000000000..e54b40293673 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/AbstractFutureState.java @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import static com.google.common.util.concurrent.AbstractFuture.getDoneValue; +import static com.google.common.util.concurrent.AbstractFuture.notInstanceOfDelegatingToFuture; +import static java.lang.Boolean.parseBoolean; +import static java.security.AccessController.doPrivileged; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; +import static java.util.logging.Level.SEVERE; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.util.concurrent.AbstractFuture.Listener; +import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import com.google.j2objc.annotations.ReflectionSupport; +import com.google.j2objc.annotations.RetainedLocalRef; +import java.lang.reflect.Field; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Locale; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.locks.LockSupport; +import org.jspecify.annotations.Nullable; +import sun.misc.Unsafe; + +/** Supertype of {@link AbstractFuture} that contains platform-specific functionality. */ +// Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || +@SuppressWarnings("ShortCircuitBoolean") +@GwtCompatible(emulated = true) +@ReflectionSupport(value = ReflectionSupport.Level.FULL) +abstract class AbstractFutureState extends InternalFutureFailureAccess + implements ListenableFuture { + /** + * Performs a {@linkplain java.lang.invoke.VarHandle#compareAndSet compare-and-set} operation on + * the {@link #listeners} field. + */ + final boolean casListeners(@Nullable Listener expect, Listener update) { + return ATOMIC_HELPER.casListeners(this, expect, update); + } + + /** + * Performs a {@linkplain java.lang.invoke.VarHandle#getAndSet get-and-set} operation on the + * {@link #listeners} field.. + */ + final @Nullable Listener gasListeners(Listener update) { + return ATOMIC_HELPER.gasListeners(this, update); + } + + /** + * Performs a {@linkplain java.lang.invoke.VarHandle#compareAndSet compare-and-set} operation on + * the {@link #value} field of {@code future}. + */ + static boolean casValue(AbstractFutureState future, @Nullable Object expect, Object update) { + return ATOMIC_HELPER.casValue(future, expect, update); + } + + /** Returns the value of the future, using a volatile read. */ + final @Nullable Object value() { + return value; + } + + /** Returns the head of the listener stack, using a volatile read. */ + final @Nullable Listener listeners() { + return listeners; + } + + /** Releases all threads in the {@link #waiters} list, and clears the list. */ + final void releaseWaiters() { + Waiter head = gasWaiters(Waiter.TOMBSTONE); + for (Waiter currentWaiter = head; currentWaiter != null; currentWaiter = currentWaiter.next) { + currentWaiter.unpark(); + } + } + + // Gets and Timed Gets + // + // * Be responsive to interruption + // * Don't create Waiter nodes if you aren't going to park, this helps reduce contention on the + // waiters field. + // * Future completion is defined by when #value becomes non-null/non DelegatingToFuture + // * Future completion can be observed if the waiters field contains a TOMBSTONE + + // Timed Get + // There are a few design constraints to consider + // * We want to be responsive to small timeouts, unpark() has non trivial latency overheads (I + // have observed 12 micros on 64-bit linux systems to wake up a parked thread). So if the + // timeout is small we shouldn't park(). This needs to be traded off with the cpu overhead of + // spinning, so we use SPIN_THRESHOLD_NANOS which is what AbstractQueuedSynchronizer uses for + // similar purposes. + // * We want to behave reasonably for timeouts of 0 + // * We are more responsive to completion than timeouts. This is because parkNanos depends on + // system scheduling and as such we could either miss our deadline, or unpark() could be delayed + // so that it looks like we timed out even though we didn't. For comparison FutureTask respects + // completion preferably and AQS is non-deterministic (depends on where in the queue the waiter + // is). If we wanted to be strict about it, we could store the unpark() time in the Waiter node + // and we could use that to make a decision about whether or not we timed out prior to being + // unparked. + + @SuppressWarnings({ + "LabelledBreakTarget", // TODO(b/345814817): Maybe fix? + "nullness", // TODO(b/147136275): Remove once our checker understands & and |. + }) + @ParametricNullness + final V blockingGet(long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException, ExecutionException { + // NOTE: if timeout < 0, remainingNanos will be < 0 and we will fall into the while(true) loop + // at the bottom and throw a timeoutexception. + long timeoutNanos = unit.toNanos(timeout); // we rely on the implicit null check on unit. + long remainingNanos = timeoutNanos; + if (Thread.interrupted()) { + throw new InterruptedException(); + } + @RetainedLocalRef Object localValue = value; + if (localValue != null & notInstanceOfDelegatingToFuture(localValue)) { + return getDoneValue(localValue); + } + // we delay calling nanoTime until we know we will need to either park or spin + long endNanos = remainingNanos > 0 ? System.nanoTime() + remainingNanos : 0; + long_wait_loop: + if (remainingNanos >= SPIN_THRESHOLD_NANOS) { + Waiter oldHead = waiters; + if (oldHead != Waiter.TOMBSTONE) { + Waiter node = new Waiter(); + do { + node.setNext(oldHead); + if (casWaiters(oldHead, node)) { + while (true) { + OverflowAvoidingLockSupport.parkNanos(this, remainingNanos); + // Check interruption first, if we woke up due to interruption we need to honor that. + if (Thread.interrupted()) { + removeWaiter(node); + throw new InterruptedException(); + } + + // Otherwise re-read and check doneness. If we loop then it must have been a spurious + // wakeup + localValue = value; + if (localValue != null & notInstanceOfDelegatingToFuture(localValue)) { + return getDoneValue(localValue); + } + + // timed out? + remainingNanos = endNanos - System.nanoTime(); + if (remainingNanos < SPIN_THRESHOLD_NANOS) { + // Remove the waiter, one way or another we are done parking this thread. + removeWaiter(node); + break long_wait_loop; // jump down to the busy wait loop + } + } + } + oldHead = waiters; // re-read and loop. + } while (oldHead != Waiter.TOMBSTONE); + } + // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a + // waiter. + // requireNonNull is safe because value is always set before TOMBSTONE. + return getDoneValue(requireNonNull(value)); + } + // If we get here then we have remainingNanos < SPIN_THRESHOLD_NANOS and there is no node on the + // waiters list + while (remainingNanos > 0) { + localValue = value; + if (localValue != null & notInstanceOfDelegatingToFuture(localValue)) { + return getDoneValue(localValue); + } + if (Thread.interrupted()) { + throw new InterruptedException(); + } + remainingNanos = endNanos - System.nanoTime(); + } + + String futureToString = toString(); + String unitString = unit.toString().toLowerCase(Locale.ROOT); + String message = "Waited " + timeout + " " + unit.toString().toLowerCase(Locale.ROOT); + // Only report scheduling delay if larger than our spin threshold - otherwise it's just noise + if (remainingNanos + SPIN_THRESHOLD_NANOS < 0) { + // We over-waited for our timeout. + message += " (plus "; + long overWaitNanos = -remainingNanos; + long overWaitUnits = unit.convert(overWaitNanos, NANOSECONDS); + long overWaitLeftoverNanos = overWaitNanos - unit.toNanos(overWaitUnits); + boolean shouldShowExtraNanos = + overWaitUnits == 0 || overWaitLeftoverNanos > SPIN_THRESHOLD_NANOS; + if (overWaitUnits > 0) { + message += overWaitUnits + " " + unitString; + if (shouldShowExtraNanos) { + message += ","; + } + message += " "; + } + if (shouldShowExtraNanos) { + message += overWaitLeftoverNanos + " nanoseconds "; + } + + message += "delay)"; + } + // It's confusing to see a completed future in a timeout message; if isDone() returns false, + // then we know it must have given a pending toString value earlier. If not, then the future + // completed after the timeout expired, and the message might be success. + if (isDone()) { + throw new TimeoutException(message + " but future completed as timeout expired"); + } + throw new TimeoutException(message + " for " + futureToString); + } + + @ParametricNullness + @SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. + final V blockingGet() throws InterruptedException, ExecutionException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + @RetainedLocalRef Object localValue = value; + if (localValue != null & notInstanceOfDelegatingToFuture(localValue)) { + return getDoneValue(localValue); + } + Waiter oldHead = waiters; + if (oldHead != Waiter.TOMBSTONE) { + Waiter node = new Waiter(); + do { + node.setNext(oldHead); + if (casWaiters(oldHead, node)) { + // we are on the stack, now wait for completion. + while (true) { + LockSupport.park(this); + // Check interruption first, if we woke up due to interruption we need to honor that. + if (Thread.interrupted()) { + removeWaiter(node); + throw new InterruptedException(); + } + // Otherwise re-read and check doneness. If we loop then it must have been a spurious + // wakeup + localValue = value; + if (localValue != null & notInstanceOfDelegatingToFuture(localValue)) { + return getDoneValue(localValue); + } + } + } + oldHead = waiters; // re-read and loop. + } while (oldHead != Waiter.TOMBSTONE); + } + // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a + // waiter. + // requireNonNull is safe because value is always set before TOMBSTONE. + return getDoneValue(requireNonNull(value)); + } + + /** Constructor for use by {@link AbstractFuture}. */ + AbstractFutureState() {} + + /* + * We put various static objects here rather than in AbstractFuture so that they're initialized in + * time for AbstractFutureState to potentially use them during class initialization. + * (AbstractFutureState class initialization can log, and that logging could in theory call into + * AbstractFuture, which wouldn't yet have had the chance to perform any class initialization of + * its own.) + */ + + /** A special value to represent {@code null}. */ + static final Object NULL = new Object(); + + /* + * Despite declaring this field in AbstractFutureState, we still use the logger for + * AbstractFuture: Users may have tests or log configuration that expects that to be the logger + * used for exceptions from listeners, as it's been in the past. + */ + static final LazyLogger log = new LazyLogger(AbstractFuture.class); + + static final boolean GENERATE_CANCELLATION_CAUSES; + + static { + // System.getProperty may throw if the security policy does not permit access. + boolean generateCancellationCauses; + try { + generateCancellationCauses = + parseBoolean(System.getProperty("guava.concurrent.generate_cancellation_cause", "false")); + } catch (SecurityException e) { + generateCancellationCauses = false; + } + GENERATE_CANCELLATION_CAUSES = generateCancellationCauses; + } + + /** Waiter links form a Treiber stack, in the {@link #waiters} field. */ + static final class Waiter { + static final Waiter TOMBSTONE = new Waiter(false /* ignored param */); + + volatile @Nullable Thread thread; + volatile @Nullable Waiter next; + + /** + * Constructor for the TOMBSTONE, avoids use of ATOMIC_HELPER in case this class is loaded + * before the ATOMIC_HELPER. Apparently this is possible on some android platforms. + */ + Waiter(boolean unused) {} + + Waiter() { + // avoid volatile write, write is made visible by subsequent CAS on waiters field + putThread(this, Thread.currentThread()); + } + + // non-volatile write to the next field. Should be made visible by subsequent CAS on waiters + // field. + void setNext(@Nullable Waiter next) { + putNext(this, next); + } + + void unpark() { + // This is racy with removeWaiter. The consequence of the race is that we may spuriously call + // unpark even though the thread has already removed itself from the list. But even if we did + // use a CAS, that race would still exist (it would just be ever so slightly smaller). + Thread w = thread; + if (w != null) { + thread = null; + LockSupport.unpark(w); + } + } + } + + /* + * Now that we've initialized everything else, we can run the initialization code for + * ATOMIC_HELPER. That initialization code may log after we assign to ATOMIC_HELPER. + */ + + private static final AtomicHelper ATOMIC_HELPER; + + static { + AtomicHelper helper; + Throwable thrownUnsafeFailure = null; + Throwable thrownAtomicReferenceFieldUpdaterFailure = null; + + try { + helper = new UnsafeAtomicHelper(); + } catch (Exception | Error unsafeFailure) { // sneaky checked exception + thrownUnsafeFailure = unsafeFailure; + // Catch absolutely everything and fall through to AtomicReferenceFieldUpdaterAtomicHelper. + try { + helper = new AtomicReferenceFieldUpdaterAtomicHelper(); + } catch (Exception // sneaky checked exception + | Error atomicReferenceFieldUpdaterFailure) { + // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause + // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. + // For these users fallback to a suboptimal implementation, based on synchronized. This + // will be a definite performance hit to those users. + thrownAtomicReferenceFieldUpdaterFailure = atomicReferenceFieldUpdaterFailure; + helper = new SynchronizedHelper(); + } + } + ATOMIC_HELPER = helper; + + // Prevent rare disastrous classloading in first call to LockSupport.park. + // See: https://bugs.openjdk.java.net/browse/JDK-8074773 + @SuppressWarnings("unused") + Class ensureLoaded = LockSupport.class; + + // Log after all static init is finished; if an installed logger uses any Futures methods, it + // shouldn't break in cases where reflection is missing/broken. + if (thrownAtomicReferenceFieldUpdaterFailure != null) { + log.get().log(SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); + log.get() + .log( + SEVERE, + "AtomicReferenceFieldUpdaterAtomicHelper is broken!", + thrownAtomicReferenceFieldUpdaterFailure); + } + } + + // TODO(lukes): investigate using the @Contended annotation on these fields when jdk8 is + // available. + /** + * This field encodes the current state of the future. + * + *

    The valid values are: + * + *

      + *
    • {@code null} initial state, nothing has happened. + *
    • {@link Cancellation} terminal state, {@code cancel} was called. + *
    • {@link Failure} terminal state, {@code setException} was called. + *
    • {@link DelegatingToFuture} intermediate state, {@code setFuture} was called. + *
    • {@link #NULL} terminal state, {@code set(null)} was called. + *
    • Any other non-null value, terminal state, {@code set} was called with a non-null + * argument. + *
    + */ + private volatile @Nullable Object value; + + /** All listeners. */ + private volatile @Nullable Listener listeners; + + /** All waiting threads. */ + private volatile @Nullable Waiter waiters; + + /** Non-volatile write of the thread to the {@link Waiter#thread} field. */ + private static void putThread(Waiter waiter, Thread newValue) { + ATOMIC_HELPER.putThread(waiter, newValue); + } + + /** Non-volatile write of the waiter to the {@link Waiter#next} field. */ + private static void putNext(Waiter waiter, @Nullable Waiter newValue) { + ATOMIC_HELPER.putNext(waiter, newValue); + } + + /** + * Performs a {@linkplain java.lang.invoke.VarHandle#compareAndSet compare-and-set} operation on + * the {@link #waiters} field. + */ + private boolean casWaiters(@Nullable Waiter expect, @Nullable Waiter update) { + return ATOMIC_HELPER.casWaiters(this, expect, update); + } + + /** + * Performs a {@linkplain java.lang.invoke.VarHandle#getAndSet get-and-set} operation on the + * {@link #waiters} field. + */ + private final @Nullable Waiter gasWaiters(Waiter update) { + return ATOMIC_HELPER.gasWaiters(this, update); + } + + /** + * Marks the given node as 'deleted' (null waiter) and then scans the list to unlink all deleted + * nodes. This is an O(n) operation in the common case (and O(n^2) in the worst), but we are saved + * by two things. + * + *
      + *
    • This is only called when a waiting thread times out or is interrupted. Both of which + * should be rare. + *
    • The waiters list should be very short. + *
    + */ + private void removeWaiter(Waiter node) { + node.thread = null; // mark as 'deleted' + restart: + while (true) { + Waiter pred = null; + Waiter curr = waiters; + if (curr == Waiter.TOMBSTONE) { + return; // give up if someone is calling complete + } + Waiter succ; + while (curr != null) { + succ = curr.next; + if (curr.thread != null) { // we aren't unlinking this node, update pred. + pred = curr; + } else if (pred != null) { // We are unlinking this node and it has a predecessor. + pred.next = succ; + if (pred.thread == null) { // We raced with another node that unlinked pred. Restart. + continue restart; + } + } else if (!casWaiters(curr, succ)) { // We are unlinking head + continue restart; // We raced with an add or complete + } + curr = succ; + } + break; + } + } + + // A heuristic for timed gets. If the remaining timeout is less than this, spin instead of + // blocking. This value is what AbstractQueuedSynchronizer uses. + private static final long SPIN_THRESHOLD_NANOS = 1000L; + + private abstract static class AtomicHelper { + /** Non-volatile write of the thread to the {@link Waiter#thread} field. */ + abstract void putThread(Waiter waiter, Thread newValue); + + /** Non-volatile write of the waiter to the {@link Waiter#next} field. */ + abstract void putNext(Waiter waiter, @Nullable Waiter newValue); + + /** Performs a CAS operation on the {@link AbstractFutureState#waiters} field. */ + abstract boolean casWaiters( + AbstractFutureState future, @Nullable Waiter expect, @Nullable Waiter update); + + /** Performs a CAS operation on the {@link AbstractFutureState#listeners} field. */ + abstract boolean casListeners( + AbstractFutureState future, @Nullable Listener expect, Listener update); + + /** Performs a GAS operation on the {@link AbstractFutureState#waiters} field. */ + abstract @Nullable Waiter gasWaiters(AbstractFutureState future, Waiter update); + + /** Performs a GAS operation on the {@link AbstractFutureState#listeners} field. */ + abstract @Nullable Listener gasListeners(AbstractFutureState future, Listener update); + + /** Performs a CAS operation on the {@link AbstractFutureState#value} field. */ + abstract boolean casValue( + AbstractFutureState future, @Nullable Object expect, Object update); + } + + /** + * {@link AtomicHelper} based on {@link sun.misc.Unsafe}. + * + *

    Static initialization of this class will fail if the {@link sun.misc.Unsafe} object cannot + * be accessed. + */ + @SuppressWarnings("SunApi") // b/345822163 + private static final class UnsafeAtomicHelper extends AtomicHelper { + static final Unsafe UNSAFE; + static final long LISTENERS_OFFSET; + static final long WAITERS_OFFSET; + static final long VALUE_OFFSET; + static final long WAITER_THREAD_OFFSET; + static final long WAITER_NEXT_OFFSET; + + static { + Unsafe unsafe = null; + try { + unsafe = Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) { + try { + unsafe = + doPrivileged( + (PrivilegedExceptionAction) + () -> { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + throw new NoSuchFieldError("the Unsafe"); + }); + } catch (PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } + try { + Class abstractFutureState = AbstractFutureState.class; + WAITERS_OFFSET = unsafe.objectFieldOffset(abstractFutureState.getDeclaredField("waiters")); + LISTENERS_OFFSET = + unsafe.objectFieldOffset(abstractFutureState.getDeclaredField("listeners")); + VALUE_OFFSET = unsafe.objectFieldOffset(abstractFutureState.getDeclaredField("value")); + WAITER_THREAD_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("thread")); + WAITER_NEXT_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("next")); + UNSAFE = unsafe; + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + @Override + void putThread(Waiter waiter, Thread newValue) { + UNSAFE.putObject(waiter, WAITER_THREAD_OFFSET, newValue); + } + + @Override + void putNext(Waiter waiter, @Nullable Waiter newValue) { + UNSAFE.putObject(waiter, WAITER_NEXT_OFFSET, newValue); + } + + @Override + boolean casWaiters( + AbstractFutureState future, @Nullable Waiter expect, @Nullable Waiter update) { + return UNSAFE.compareAndSwapObject(future, WAITERS_OFFSET, expect, update); + } + + @Override + boolean casListeners( + AbstractFutureState future, @Nullable Listener expect, Listener update) { + return UNSAFE.compareAndSwapObject(future, LISTENERS_OFFSET, expect, update); + } + + @Override + @Nullable Listener gasListeners(AbstractFutureState future, Listener update) { + while (true) { + Listener listener = future.listeners; + if (update == listener) { + return listener; + } + if (casListeners(future, listener, update)) { + return listener; + } + } + } + + @Override + @Nullable Waiter gasWaiters(AbstractFutureState future, Waiter update) { + while (true) { + Waiter waiter = future.waiters; + if (update == waiter) { + return waiter; + } + if (casWaiters(future, waiter, update)) { + return waiter; + } + } + } + + @Override + boolean casValue(AbstractFutureState future, @Nullable Object expect, Object update) { + return UNSAFE.compareAndSwapObject(future, VALUE_OFFSET, expect, update); + } + } + + /** {@link AtomicHelper} based on {@link AtomicReferenceFieldUpdater}. */ + private static final class AtomicReferenceFieldUpdaterAtomicHelper extends AtomicHelper { + private static final AtomicReferenceFieldUpdater waiterThreadUpdater = + AtomicReferenceFieldUpdater.newUpdater( + Waiter.class, Thread.class, "thread"); + private static final AtomicReferenceFieldUpdater waiterNextUpdater = + AtomicReferenceFieldUpdater.newUpdater( + Waiter.class, Waiter.class, "next"); + private static final AtomicReferenceFieldUpdater< + ? super AbstractFutureState, @Nullable Waiter> + waitersUpdater = waitersUpdaterFromWithinAbstractFutureState(); + private static final AtomicReferenceFieldUpdater< + ? super AbstractFutureState, @Nullable Listener> + listenersUpdater = listenersUpdaterFromWithinAbstractFutureState(); + private static final AtomicReferenceFieldUpdater< + ? super AbstractFutureState, @Nullable Object> + valueUpdater = valueUpdaterFromWithinAbstractFutureState(); + + @Override + void putThread(Waiter waiter, Thread newValue) { + waiterThreadUpdater.lazySet(waiter, newValue); + } + + @Override + void putNext(Waiter waiter, @Nullable Waiter newValue) { + waiterNextUpdater.lazySet(waiter, newValue); + } + + @Override + boolean casWaiters( + AbstractFutureState future, @Nullable Waiter expect, @Nullable Waiter update) { + return waitersUpdater.compareAndSet(future, expect, update); + } + + @Override + boolean casListeners( + AbstractFutureState future, @Nullable Listener expect, Listener update) { + return listenersUpdater.compareAndSet(future, expect, update); + } + + @Override + @Nullable Listener gasListeners(AbstractFutureState future, Listener update) { + return listenersUpdater.getAndSet(future, update); + } + + @Override + @Nullable Waiter gasWaiters(AbstractFutureState future, Waiter update) { + return waitersUpdater.getAndSet(future, update); + } + + @Override + boolean casValue(AbstractFutureState future, @Nullable Object expect, Object update) { + return valueUpdater.compareAndSet(future, expect, update); + } + } + + /** + * Returns an {@link AtomicReferenceFieldUpdater} for {@link #waiters}. + * + *

    The creation of the updater has to happen directly inside {@link AbstractFutureState}, as + * discussed in {@link #methodHandlesLookupFromWithinAbstractFutureState}. + */ + private static AtomicReferenceFieldUpdater, @Nullable Waiter> + waitersUpdaterFromWithinAbstractFutureState() { + return newUpdater(AbstractFutureState.class, Waiter.class, "waiters"); + } + + /** + * Returns an {@link AtomicReferenceFieldUpdater} for {@link #listeners}. + * + *

    The creation of the updater has to happen directly inside {@link AbstractFutureState}, as + * discussed in {@link #methodHandlesLookupFromWithinAbstractFutureState}. + */ + private static AtomicReferenceFieldUpdater, @Nullable Listener> + listenersUpdaterFromWithinAbstractFutureState() { + return newUpdater(AbstractFutureState.class, Listener.class, "listeners"); + } + + /** + * Returns an {@link AtomicReferenceFieldUpdater} for {@link #value}. + * + *

    The creation of the updater has to happen directly inside {@link AbstractFutureState}, as + * discussed in {@link #methodHandlesLookupFromWithinAbstractFutureState}. + */ + private static AtomicReferenceFieldUpdater, @Nullable Object> + valueUpdaterFromWithinAbstractFutureState() { + return newUpdater(AbstractFutureState.class, Object.class, "value"); + } + + /** + * {@link AtomicHelper} based on {@code synchronized} and volatile writes. + * + *

    This is an implementation of last resort for when certain basic VM features are broken (like + * AtomicReferenceFieldUpdater). + */ + private static final class SynchronizedHelper extends AtomicHelper { + @Override + void putThread(Waiter waiter, Thread newValue) { + waiter.thread = newValue; + } + + @Override + void putNext(Waiter waiter, @Nullable Waiter newValue) { + waiter.next = newValue; + } + + @Override + boolean casWaiters( + AbstractFutureState future, @Nullable Waiter expect, @Nullable Waiter update) { + synchronized (future) { + if (future.waiters == expect) { + future.waiters = update; + return true; + } + return false; + } + } + + @Override + boolean casListeners( + AbstractFutureState future, @Nullable Listener expect, Listener update) { + synchronized (future) { + if (future.listeners == expect) { + future.listeners = update; + return true; + } + return false; + } + } + + @Override + @Nullable Listener gasListeners(AbstractFutureState future, Listener update) { + synchronized (future) { + Listener old = future.listeners; + if (old != update) { + future.listeners = update; + } + return old; + } + } + + @Override + @Nullable Waiter gasWaiters(AbstractFutureState future, Waiter update) { + synchronized (future) { + Waiter old = future.waiters; + if (old != update) { + future.waiters = update; + } + return old; + } + } + + @Override + boolean casValue(AbstractFutureState future, @Nullable Object expect, Object update) { + synchronized (future) { + if (future.value == expect) { + future.value = update; + return true; + } + return false; + } + } + } +} diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractIdleService.java b/android/guava/src/com/google/common/util/concurrent/AbstractIdleService.java index 05bc4b9a7cf3..a1b9e53abf82 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractIdleService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractIdleService.java @@ -14,7 +14,10 @@ package com.google.common.util.concurrent; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.j2objc.annotations.WeakOuter; @@ -25,13 +28,13 @@ /** * Base class for services that do not need a thread while "running" but may need one during startup * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which - * run in a executor which by default uses a separate thread for each method. + * run in an executor which by default uses a separate thread for each method. * * @author Chris Nokleberg * @since 1.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public abstract class AbstractIdleService implements Service { /* Thread names will look like {@code "MyService STARTING"}. */ @@ -54,15 +57,13 @@ private final class DelegateService extends AbstractService { protected final void doStart() { MoreExecutors.renamingDecorator(executor(), threadNameSupplier) .execute( - new Runnable() { - @Override - public void run() { - try { - startUp(); - notifyStarted(); - } catch (Throwable t) { - notifyFailed(t); - } + () -> { + try { + startUp(); + notifyStarted(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); } }); } @@ -71,15 +72,13 @@ public void run() { protected final void doStop() { MoreExecutors.renamingDecorator(executor(), threadNameSupplier) .execute( - new Runnable() { - @Override - public void run() { - try { - shutDown(); - notifyStopped(); - } catch (Throwable t) { - notifyFailed(t); - } + () -> { + try { + shutDown(); + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); } }); } @@ -107,12 +106,7 @@ protected AbstractIdleService() {} * stopped, and should return promptly. */ protected Executor executor() { - return new Executor() { - @Override - public void execute(Runnable command) { - MoreExecutors.newThread(threadNameSupplier.get(), command).start(); - } - }; + return command -> MoreExecutors.newThread(threadNameSupplier.get(), command).start(); } @Override @@ -130,19 +124,25 @@ public final State state() { return delegate.state(); } - /** @since 13.0 */ + /** + * @since 13.0 + */ @Override public final void addListener(Listener listener, Executor executor) { delegate.addListener(listener, executor); } - /** @since 14.0 */ + /** + * @since 14.0 + */ @Override public final Throwable failureCause() { return delegate.failureCause(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service startAsync() { @@ -150,7 +150,9 @@ public final Service startAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service stopAsync() { @@ -158,25 +160,33 @@ public final Service stopAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning() { delegate.awaitRunning(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitRunning(timeout, unit); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated() { delegate.awaitTerminated(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitTerminated(timeout, unit); diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java b/android/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java index fd5c5c911533..ac2dd03848d6 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java @@ -14,13 +14,13 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.CheckReturnValue; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.RunnableFuture; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Abstract {@link ListeningExecutorService} implementation that creates {@link ListenableFuture} @@ -33,37 +33,46 @@ * @author Chris Povirk * @since 14.0 */ -@Beta -@CanIgnoreReturnValue +@CheckReturnValue @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class AbstractListeningExecutorService extends AbstractExecutorService implements ListeningExecutorService { + /** Constructor for use by subclasses. */ + public AbstractListeningExecutorService() {} - /** @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) */ + /** + * @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) + */ + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override protected final RunnableFuture newTaskFor( Runnable runnable, @ParametricNullness T value) { return TrustedListenableFutureTask.create(runnable, value); } - /** @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) */ + /** + * @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) + */ + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override protected final RunnableFuture newTaskFor(Callable callable) { return TrustedListenableFutureTask.create(callable); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override public ListenableFuture submit(Runnable task) { return (ListenableFuture) super.submit(task); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override public ListenableFuture submit( Runnable task, @ParametricNullness T result) { return (ListenableFuture) super.submit(task, result); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override public ListenableFuture submit(Callable task) { return (ListenableFuture) super.submit(task); diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java b/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java index 7288ba53d728..f695d4d29ca0 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java @@ -17,14 +17,18 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Supplier; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.GuardedBy; import com.google.j2objc.annotations.WeakOuter; +import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -36,9 +40,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Base class for services that can implement {@link #startUp} and {@link #shutDown} but while in @@ -98,9 +100,9 @@ * @since 11.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public abstract class AbstractScheduledService implements Service { - private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + private static final LazyLogger logger = new LazyLogger(AbstractScheduledService.class); /** * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its @@ -115,6 +117,22 @@ public abstract class AbstractScheduledService implements Service { * @since 11.0 */ public abstract static class Scheduler { + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleWithFixedDelay} method. + * + * @param initialDelay the time to delay first execution + * @param delay the delay between the termination of one execution and the commencement of the + * next + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration + public static Scheduler newFixedDelaySchedule(Duration initialDelay, Duration delay) { + return newFixedDelaySchedule( + toNanosSaturated(initialDelay), toNanosSaturated(delay), NANOSECONDS); + } + /** * Returns a {@link Scheduler} that schedules the task using the {@link * ScheduledExecutorService#scheduleWithFixedDelay} method. @@ -125,8 +143,7 @@ public abstract static class Scheduler { * @param unit the time unit of the initialDelay and delay parameters */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration - public static Scheduler newFixedDelaySchedule( - final long initialDelay, final long delay, final TimeUnit unit) { + public static Scheduler newFixedDelaySchedule(long initialDelay, long delay, TimeUnit unit) { checkNotNull(unit); checkArgument(delay > 0, "delay must be > 0, found %s", delay); return new Scheduler() { @@ -139,6 +156,21 @@ public Cancellable schedule( }; } + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleAtFixedRate} method. + * + * @param initialDelay the time to delay first execution + * @param period the period between successive executions of the task + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration + public static Scheduler newFixedRateSchedule(Duration initialDelay, Duration period) { + return newFixedRateSchedule( + toNanosSaturated(initialDelay), toNanosSaturated(period), NANOSECONDS); + } + /** * Returns a {@link Scheduler} that schedules the task using the {@link * ScheduledExecutorService#scheduleAtFixedRate} method. @@ -148,8 +180,7 @@ public Cancellable schedule( * @param unit the time unit of the initialDelay and period parameters */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration - public static Scheduler newFixedRateSchedule( - final long initialDelay, final long period, final TimeUnit unit) { + public static Scheduler newFixedRateSchedule(long initialDelay, long period, TimeUnit unit) { checkNotNull(unit); checkArgument(period > 0, "period must be > 0, found %s", period); return new Scheduler() { @@ -177,8 +208,8 @@ private final class ServiceDelegate extends AbstractService { // A handle to the running task so that we can stop it when a shutdown has been requested. // These two fields are volatile because their values will be accessed from multiple threads. - @CheckForNull private volatile Cancellable runningTask; - @CheckForNull private volatile ScheduledExecutorService executorService; + private volatile @Nullable Cancellable runningTask; + private volatile @Nullable ScheduledExecutorService executorService; // This lock protects the task so we can ensure that none of the template methods (startUp, // shutDown or runOneIteration) run concurrently with one another. @@ -202,13 +233,17 @@ public void run() { } AbstractScheduledService.this.runOneIteration(); } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); try { shutDown(); } catch (Exception ignored) { - logger.log( - Level.WARNING, - "Error while attempting to shut down the service after failure.", - ignored); + restoreInterruptIfIsInterruptedException(ignored); + logger + .get() + .log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); } notifyFailed(t); // requireNonNull is safe now, just as it was above. @@ -224,32 +259,28 @@ public void run() { @Override protected final void doStart() { executorService = - MoreExecutors.renamingDecorator( - executor(), - new Supplier() { - @Override - public String get() { - return serviceName() + " " + state(); - } - }); + MoreExecutors.renamingDecorator(executor(), () -> serviceName() + " " + state()); executorService.execute( - new Runnable() { - @Override - public void run() { - lock.lock(); - try { - startUp(); - runningTask = scheduler().schedule(delegate, executorService, task); - notifyStarted(); - } catch (Throwable t) { - notifyFailed(t); - if (runningTask != null) { - // prevent the task from running if possible - runningTask.cancel(false); - } - } finally { - lock.unlock(); + () -> { + lock.lock(); + try { + startUp(); + /* + * requireNonNull is safe because executorService is never cleared after the + * assignment above. + */ + requireNonNull(executorService); + runningTask = scheduler().schedule(delegate, executorService, task); + notifyStarted(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + if (runningTask != null) { + // prevent the task from running if possible + runningTask.cancel(false); } + } finally { + lock.unlock(); } }); } @@ -261,27 +292,25 @@ protected final void doStop() { requireNonNull(executorService); runningTask.cancel(false); executorService.execute( - new Runnable() { - @Override - public void run() { + () -> { + try { + lock.lock(); try { - lock.lock(); - try { - if (state() != State.STOPPING) { - // This means that the state has changed since we were scheduled. This implies - // that an execution of runOneIteration has thrown an exception and we have - // transitioned to a failed state, also this means that shutDown has already - // been called, so we do not want to call it again. - return; - } - shutDown(); - } finally { - lock.unlock(); + if (state() != State.STOPPING) { + // This means that the state has changed since we were scheduled. This implies + // that an execution of runOneIteration has thrown an exception and we have + // transitioned to a failed state, also this means that shutDown has already + // been called, so we do not want to call it again. + return; } - notifyStopped(); - } catch (Throwable t) { - notifyFailed(t); + shutDown(); + } finally { + lock.unlock(); } + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); } }); } @@ -345,9 +374,9 @@ public Thread newThread(Runnable runnable) { return MoreExecutors.newThread(serviceName(), runnable); } } - final ScheduledExecutorService executor = + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl()); - // Add a listener to shutdown the executor after the service is stopped. This ensures that the + // Add a listener to shut down the executor after the service is stopped. This ensures that the // JVM shutdown will not be prevented from exiting after this service has stopped or failed. // Technically this listener is added after start() was called so it is a little gross, but it // is called within doStart() so we know that the service cannot terminate or fail concurrently @@ -393,19 +422,25 @@ public final State state() { return delegate.state(); } - /** @since 13.0 */ + /** + * @since 13.0 + */ @Override public final void addListener(Listener listener, Executor executor) { delegate.addListener(listener, executor); } - /** @since 14.0 */ + /** + * @since 14.0 + */ @Override public final Throwable failureCause() { return delegate.failureCause(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service startAsync() { @@ -413,7 +448,9 @@ public final Service startAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @CanIgnoreReturnValue @Override public final Service stopAsync() { @@ -421,25 +458,33 @@ public final Service stopAsync() { return this; } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning() { delegate.awaitRunning(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitRunning(timeout, unit); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated() { delegate.awaitTerminated(); } - /** @since 15.0 */ + /** + * @since 15.0 + */ @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { delegate.awaitTerminated(timeout, unit); @@ -459,6 +504,7 @@ private static final class FutureAsCancellable implements Cancellable { } @Override + @SuppressWarnings("Interruption") // We are propagating an interrupt from a caller. public void cancel(boolean mayInterruptIfRunning) { delegate.cancel(mayInterruptIfRunning); } @@ -478,6 +524,8 @@ public boolean isCancelled() { * @since 11.0 */ public abstract static class CustomScheduler extends Scheduler { + /** Constructor for use by subclasses. */ + public CustomScheduler() {} /** A callable class that can reschedule itself using a {@link CustomScheduler}. */ private final class ReschedulableCallable implements Callable<@Nullable Void> { @@ -524,8 +572,7 @@ private final class ReschedulableCallable implements Callable<@Nullable Void> { /** The future that represents the next execution of this task. */ @GuardedBy("lock") - @CheckForNull - private SupplantableFuture cancellationDelegate; + private @Nullable SupplantableFuture cancellationDelegate; ReschedulableCallable( AbstractService service, ScheduledExecutorService executor, Runnable runnable) { @@ -535,8 +582,7 @@ private final class ReschedulableCallable implements Callable<@Nullable Void> { } @Override - @CheckForNull - public Void call() throws Exception { + public @Nullable Void call() throws Exception { wrappedRunnable.run(); reschedule(); return null; @@ -553,6 +599,7 @@ public Cancellable reschedule() { try { schedule = CustomScheduler.this.getNextSchedule(); } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); service.notifyFailed(t); return new FutureAsCancellable(immediateCancelledFuture()); } @@ -566,6 +613,8 @@ public Cancellable reschedule() { try { toReturn = initializeOrUpdateCancellationDelegate(schedule); } catch (Throwable e) { + // Any Exception is either a RuntimeException or sneaky checked exception. + // // If an exception is thrown by the subclass then we need to make sure that the service // notices and transitions to the FAILED state. We do it by calling notifyFailed directly // because the service does not monitor the state of the future so if the exception is not @@ -625,6 +674,7 @@ private static final class SupplantableFuture implements Cancellable { } @Override + @SuppressWarnings("Interruption") // We are propagating an interrupt from a caller. public void cancel(boolean mayInterruptIfRunning) { /* * Lock to ensure that a task cannot be rescheduled while a cancel is ongoing. @@ -681,6 +731,16 @@ public Schedule(long delay, TimeUnit unit) { this.delay = delay; this.unit = checkNotNull(unit); } + + /** + * @param delay the time from now to delay execution + * @since 33.4.0 (but since 31.1 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration + public Schedule(Duration delay) { + this(toNanosSaturated(delay), NANOSECONDS); + } } /** diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractService.java b/android/guava/src/com/google/common/util/concurrent/AbstractService.java index 1cf31bf915a6..7ba004bb88b1 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractService.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractService.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import static com.google.common.util.concurrent.Service.State.FAILED; import static com.google.common.util.concurrent.Service.State.NEW; import static com.google.common.util.concurrent.Service.State.RUNNING; @@ -25,10 +26,10 @@ import static com.google.common.util.concurrent.Service.State.TERMINATED; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.util.concurrent.Monitor.Guard; -import com.google.common.util.concurrent.Service.State; // javadoc needs this +import com.google.common.util.concurrent.Service.State; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.ForOverride; import com.google.errorprone.annotations.concurrent.GuardedBy; @@ -36,7 +37,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Base class for implementing services that can handle {@link #doStart} and {@link #doStop} @@ -49,7 +50,7 @@ * @since 1.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public abstract class AbstractService implements Service { private static final ListenerCallQueue.Event STARTING_EVENT = new ListenerCallQueue.Event() { @@ -89,7 +90,7 @@ public String toString() { private static final ListenerCallQueue.Event TERMINATED_FROM_STOPPING_EVENT = terminatedEvent(STOPPING); - private static ListenerCallQueue.Event terminatedEvent(final State from) { + private static ListenerCallQueue.Event terminatedEvent(State from) { return new ListenerCallQueue.Event() { @Override public void call(Listener listener) { @@ -103,7 +104,7 @@ public String toString() { }; } - private static ListenerCallQueue.Event stoppingEvent(final State from) { + private static ListenerCallQueue.Event stoppingEvent(State from) { return new ListenerCallQueue.Event() { @Override public void call(Listener listener) { @@ -236,7 +237,6 @@ protected AbstractService() {} * * @since 27.0 */ - @Beta @ForOverride protected void doCancelStart() {} @@ -249,6 +249,7 @@ public final Service startAsync() { enqueueStartingEvent(); doStart(); } catch (Throwable startupFailure) { + restoreInterruptIfIsInterruptedException(startupFailure); notifyFailed(startupFailure); } finally { monitor.leave(); @@ -288,6 +289,7 @@ public final Service stopAsync() { throw new AssertionError("isStoppable is incorrectly implemented, saw: " + previous); } } catch (Throwable shutdownFailure) { + restoreInterruptIfIsInterruptedException(shutdownFailure); notifyFailed(shutdownFailure); } finally { monitor.leave(); @@ -316,7 +318,7 @@ public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutExcept monitor.leave(); } } else { - // It is possible due to races the we are currently in the expected state even though we + // It is possible due to races that we are currently in the expected state even though we // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never // even check the guard. I don't think we care too much about this use case but it could lead // to a confusing error message. @@ -343,7 +345,7 @@ public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutExc monitor.leave(); } } else { - // It is possible due to races the we are currently in the expected state even though we + // It is possible due to races that we are currently in the expected state even though we // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never // even check the guard. I don't think we care too much about this use case but it could lead // to a confusing error message. @@ -477,13 +479,17 @@ public final State state() { return snapshot.externalState(); } - /** @since 14.0 */ + /** + * @since 14.0 + */ @Override public final Throwable failureCause() { return snapshot.failureCause(); } - /** @since 13.0 */ + /** + * @since 13.0 + */ @Override public final void addListener(Listener listener, Executor executor) { listeners.addListener(listener, executor); @@ -512,7 +518,7 @@ private void enqueueRunningEvent() { listeners.enqueue(RUNNING_EVENT); } - private void enqueueStoppingEvent(final State from) { + private void enqueueStoppingEvent(State from) { if (from == State.STARTING) { listeners.enqueue(STOPPING_FROM_STARTING_EVENT); } else if (from == State.RUNNING) { @@ -522,7 +528,7 @@ private void enqueueStoppingEvent(final State from) { } } - private void enqueueTerminatedEvent(final State from) { + private void enqueueTerminatedEvent(State from) { switch (from) { case NEW: listeners.enqueue(TERMINATED_FROM_NEW_EVENT); @@ -542,7 +548,7 @@ private void enqueueTerminatedEvent(final State from) { } } - private void enqueueFailedEvent(final State from, final Throwable cause) { + private void enqueueFailedEvent(State from, Throwable cause) { // can't memoize this one due to the exception listeners.enqueue( new ListenerCallQueue.Event() { @@ -577,14 +583,14 @@ private static final class StateSnapshot { * The exception that caused this service to fail. This will be {@code null} unless the service * has failed. */ - @CheckForNull final Throwable failure; + final @Nullable Throwable failure; StateSnapshot(State internalState) { this(internalState, false, null); } StateSnapshot( - State internalState, boolean shutdownWhenStartupFinishes, @CheckForNull Throwable failure) { + State internalState, boolean shutdownWhenStartupFinishes, @Nullable Throwable failure) { checkArgument( !shutdownWhenStartupFinishes || internalState == STARTING, "shutdownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.", @@ -600,7 +606,9 @@ private static final class StateSnapshot { this.failure = failure; } - /** @see Service#state() */ + /** + * @see Service#state() + */ State externalState() { if (shutdownWhenStartupFinishes && state == STARTING) { return STOPPING; @@ -609,7 +617,9 @@ State externalState() { } } - /** @see Service#failureCause() */ + /** + * @see Service#failureCause() + */ Throwable failureCause() { checkState( state == FAILED, diff --git a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java index dd68791594ae..a455f7d24140 100644 --- a/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AbstractTransformFuture.java @@ -17,28 +17,29 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.util.concurrent.Futures.getDone; import static com.google.common.util.concurrent.MoreExecutors.rejectionPropagatingExecutor; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.errorprone.annotations.ForOverride; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Implementations of {@code Futures.transform*}. */ @GwtCompatible -@ElementTypesAreNonnullByDefault -@SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. +// Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || +@SuppressWarnings("ShortCircuitBoolean") abstract class AbstractTransformFuture< I extends @Nullable Object, O extends @Nullable Object, F, T extends @Nullable Object> extends FluentFuture.TrustedFuture implements Runnable { - static ListenableFuture create( + static ListenableFuture createAsync( ListenableFuture input, AsyncFunction function, Executor executor) { - checkNotNull(executor); AsyncTransformFuture output = new AsyncTransformFuture<>(input, function); input.addListener(output, rejectionPropagatingExecutor(executor, output)); return output; @@ -46,7 +47,6 @@ abstract class AbstractTransformFuture< static ListenableFuture create( ListenableFuture input, Function function, Executor executor) { - checkNotNull(function); TransformFuture output = new TransformFuture<>(input, function); input.addListener(output, rejectionPropagatingExecutor(executor, output)); return output; @@ -56,8 +56,8 @@ abstract class AbstractTransformFuture< * In certain circumstances, this field might theoretically not be visible to an afterDone() call * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. */ - @CheckForNull ListenableFuture inputFuture; - @CheckForNull F function; + @LazyInit @Nullable ListenableFuture inputFuture; + @LazyInit @Nullable F function; AbstractTransformFuture(ListenableFuture inputFuture, F function) { this.inputFuture = checkNotNull(inputFuture); @@ -65,9 +65,13 @@ abstract class AbstractTransformFuture< } @Override + @SuppressWarnings({ + "CatchingUnchecked", // sneaky checked exception + "nullness", // TODO(b/147136275): Remove once our checker understands & and |. + }) public final void run() { - ListenableFuture localInputFuture = inputFuture; - F localFunction = function; + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + @RetainedLocalRef F localFunction = function; if (isCancelled() | localInputFuture == null | localFunction == null) { return; } @@ -103,7 +107,7 @@ public final void run() { // Set the cause of the exception as this future's exception. setException(e.getCause()); return; - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Bug in inputFuture.get(). Propagate to the output Future so that its consumers don't hang. setException(e); return; @@ -121,6 +125,7 @@ public final void run() { try { transformResult = doTransform(localFunction, sourceResult); } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); // This exception is irrelevant in this thread, but useful for the client. setException(t); return; @@ -178,16 +183,16 @@ public final void run() { @Override protected final void afterDone() { - maybePropagateCancellationTo(inputFuture); + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + maybePropagateCancellationTo(localInputFuture); this.inputFuture = null; this.function = null; } @Override - @CheckForNull - protected String pendingToString() { - ListenableFuture localInputFuture = inputFuture; - F localFunction = function; + protected @Nullable String pendingToString() { + @RetainedLocalRef ListenableFuture localInputFuture = inputFuture; + @RetainedLocalRef F localFunction = function; String superString = super.pendingToString(); String resultString = ""; if (localInputFuture != null) { @@ -218,13 +223,13 @@ private static final class AsyncTransformFuture< ListenableFuture doTransform( AsyncFunction function, @ParametricNullness I input) throws Exception { - ListenableFuture outputFuture = function.apply(input); + ListenableFuture output = function.apply(input); checkNotNull( - outputFuture, + output, "AsyncFunction.apply returned null instead of a Future. " + "Did you mean to return immediateFuture(null)? %s", function); - return outputFuture; + return output; } @Override diff --git a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java index 6d2ed9c8e8cd..9647758bdc66 100644 --- a/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/AggregateFuture.java @@ -18,8 +18,8 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.util.concurrent.AggregateFuture.ReleaseResourcesReason.ALL_INPUT_FUTURES_PROCESSED; import static com.google.common.util.concurrent.AggregateFuture.ReleaseResourcesReason.OUTPUT_FUTURE_DONE; -import static com.google.common.util.concurrent.Futures.getDone; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; import static java.util.Objects.requireNonNull; import static java.util.logging.Level.SEVERE; @@ -27,12 +27,12 @@ import com.google.common.collect.ImmutableCollection; import com.google.errorprone.annotations.ForOverride; import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A future whose value is derived from a collection of input futures. @@ -41,10 +41,12 @@ * @param the type of the output (i.e. this) future */ @GwtCompatible -@ElementTypesAreNonnullByDefault +@SuppressWarnings( + // Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + "ShortCircuitBoolean") abstract class AggregateFuture extends AggregateFutureState { - private static final Logger logger = Logger.getLogger(AggregateFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(AggregateFuture.class); /** * The input futures. After {@link #init}, this field is read only by {@link #afterDone()} (to @@ -56,7 +58,8 @@ abstract class AggregateFuture> futures; + @LazyInit + private @Nullable ImmutableCollection> futures; private final boolean allMustSucceed; private final boolean collectsValues; @@ -72,10 +75,11 @@ abstract class AggregateFuture> localFutures = futures; + @RetainedLocalRef ImmutableCollection> localFutures = futures; releaseResources(OUTPUT_FUTURE_DONE); // nulls out `futures` if (isCancelled() & localFutures != null) { @@ -91,9 +95,8 @@ protected final void afterDone() { } @Override - @CheckForNull - protected final String pendingToString() { - ImmutableCollection> localFutures = futures; + protected final @Nullable String pendingToString() { + @RetainedLocalRef ImmutableCollection> localFutures = futures; if (localFutures != null) { return "futures=" + localFutures; } @@ -137,27 +140,12 @@ final void init() { int i = 0; for (ListenableFuture future : futures) { int index = i++; - future.addListener( - () -> { - try { - if (future.isCancelled()) { - // Clear futures prior to cancelling children. This sets our own state but lets - // the input futures keep running, as some of them may be used elsewhere. - futures = null; - cancel(false); - } else { - collectValueFromNonCancelledFuture(index, future); - } - } finally { - /* - * "null" means: There is no need to access `futures` again during - * `processCompleted` because we're reading each value during a call to - * handleOneInputDone. - */ - decrementCountAndMaybeComplete(null); - } - }, - directExecutor()); + if (future.isDone()) { + processAllMustSucceedDoneFuture(index, future); + } else { + future.addListener( + () -> processAllMustSucceedDoneFuture(index, future), directExecutor()); + } } } else { /* @@ -166,22 +154,49 @@ final void init() { * Future.get() when we don't need to (specifically, for whenAllComplete().call*()), and it * lets all futures share the same listener. * - * We store `localFutures` inside the listener because `this.futures` might be nulled out by - * the time the listener runs for the final future -- at which point we need to check all - * inputs for exceptions *if* we're collecting values. If we're not, then the listener doesn't - * need access to the futures again, so we can just pass `null`. + * We store `localFuturesOrNull` inside the listener because `this.futures` might be nulled + * out by the time the listener runs for the final future -- at which point we need to check + * all inputs for exceptions *if* we're collecting values. If we're not, then the listener + * doesn't need access to the futures again, so we can just pass `null`. * * TODO(b/112550045): Allocating a single, cheaper listener is (I think) only an optimization. * If we make some other optimizations, this one will no longer be necessary. The optimization * could actually hurt in some cases, as it forces us to keep all inputs in memory until the * final input completes. */ - ImmutableCollection> localFutures = - collectsValues ? futures : null; - Runnable listener = () -> decrementCountAndMaybeComplete(localFutures); - for (ListenableFuture future : futures) { - future.addListener(listener, directExecutor()); + @RetainedLocalRef + ImmutableCollection> localFutures = futures; + ImmutableCollection> localFuturesOrNull = + collectsValues ? localFutures : null; + Runnable listener = () -> decrementCountAndMaybeComplete(localFuturesOrNull); + for (ListenableFuture future : localFutures) { + if (future.isDone()) { + decrementCountAndMaybeComplete(localFuturesOrNull); + } else { + future.addListener(listener, directExecutor()); + } + } + } + } + + private void processAllMustSucceedDoneFuture( + int index, ListenableFuture future) { + try { + if (future.isCancelled()) { + // Clear futures prior to cancelling children. This sets our own state but lets + // the input futures keep running, as some of them may be used elsewhere. + futures = null; + cancel(false); + } else { + collectValueFromNonCancelledFuture(index, future); } + } finally { + /* + * "null" means: There is no need to access `futures` again during + * `processCompleted` because we're reading each value during a call to + * handleOneInputDone. + */ + decrementCountAndMaybeComplete(null); } } @@ -230,7 +245,7 @@ private static void log(Throwable throwable) { (throwable instanceof Error) ? "Input Future failed with Error" : "Got more than one input Future failure. Logging failures after the first"; - logger.log(SEVERE, message, throwable); + logger.get().log(SEVERE, message, throwable); } @Override @@ -265,18 +280,18 @@ final void addInitialException(Set seen) { private void collectValueFromNonCancelledFuture(int index, Future future) { try { // We get the result, even if collectOneValue is a no-op, so that we can fail fast. - collectOneValue(index, getDone(future)); + // We use getUninterruptibly over getDone as a micro-optimization, we know the future is done. + collectOneValue(index, getUninterruptibly(future)); } catch (ExecutionException e) { handleException(e.getCause()); - } catch (Throwable t) { + } catch (Throwable t) { // sneaky checked exception handleException(t); } } private void decrementCountAndMaybeComplete( - @CheckForNull - ImmutableCollection> - futuresIfNeedToCollectAtCompletion) { + @Nullable ImmutableCollection> + futuresIfNeedToCollectAtCompletion) { int newRemaining = decrementRemainingAndGet(); checkState(newRemaining >= 0, "Less than 0 remaining futures"); if (newRemaining == 0) { @@ -285,9 +300,8 @@ private void decrementCountAndMaybeComplete( } private void processCompleted( - @CheckForNull - ImmutableCollection> - futuresIfNeedToCollectAtCompletion) { + @Nullable ImmutableCollection> + futuresIfNeedToCollectAtCompletion) { if (futuresIfNeedToCollectAtCompletion != null) { int i = 0; for (Future future : futuresIfNeedToCollectAtCompletion) { @@ -357,7 +371,7 @@ private static boolean addCausalChain(Set seen, Throwable param) { * We've seen this, so we've seen its causes, too. No need to re-add them. (There's one case * where this isn't true, but we ignore it: If we record an exception, then someone calls * initCause() on it, and then we examine it again, we'll conclude that we've seen the whole - * chain before when it fact we haven't. But this should be rare.) + * chain before when in fact we haven't. But this should be rare.) */ return false; } diff --git a/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java b/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java index ee23ca021e97..67af74224860 100644 --- a/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java +++ b/android/guava/src/com/google/common/util/concurrent/AggregateFutureState.java @@ -25,9 +25,7 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A helper which does some thread-safe operations for aggregate futures, which must be implemented @@ -40,28 +38,24 @@ */ @GwtCompatible(emulated = true) @ReflectionSupport(value = ReflectionSupport.Level.FULL) -@ElementTypesAreNonnullByDefault abstract class AggregateFutureState extends AbstractFuture.TrustedFuture { // Lazily initialized the first time we see an exception; not released until all the input futures // have completed and we have processed them all. - @CheckForNull private volatile Set seenExceptions = null; + private volatile @Nullable Set seenExceptions = null; private volatile int remaining; private static final AtomicHelper ATOMIC_HELPER; - private static final Logger log = Logger.getLogger(AggregateFutureState.class.getName()); + private static final LazyLogger log = new LazyLogger(AggregateFutureState.class); static { AtomicHelper helper; Throwable thrownReflectionFailure = null; try { - helper = - new SafeAtomicHelper( - newUpdater(AggregateFutureState.class, Set.class, "seenExceptions"), - newUpdater(AggregateFutureState.class, "remaining")); - } catch (Throwable reflectionFailure) { + helper = new SafeAtomicHelper(); + } catch (Throwable reflectionFailure) { // sneaky checked exception // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // For these users fallback to a suboptimal implementation, based on synchronized. This will @@ -73,7 +67,7 @@ abstract class AggregateFutureState // Log after all static init is finished; if an installed logger uses any Futures methods, it // shouldn't break in cases where reflection is missing/broken. if (thrownReflectionFailure != null) { - log.log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); + log.get().log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); } } @@ -152,32 +146,23 @@ final void clearSeenExceptions() { private abstract static class AtomicHelper { /** Atomic compare-and-set of the {@link AggregateFutureState#seenExceptions} field. */ abstract void compareAndSetSeenExceptions( - AggregateFutureState state, @CheckForNull Set expect, Set update); + AggregateFutureState state, @Nullable Set expect, Set update); /** Atomic decrement-and-get of the {@link AggregateFutureState#remaining} field. */ abstract int decrementAndGetRemainingCount(AggregateFutureState state); } private static final class SafeAtomicHelper extends AtomicHelper { - final AtomicReferenceFieldUpdater, Set> - seenExceptionsUpdater; - - final AtomicIntegerFieldUpdater> remainingCountUpdater; - - @SuppressWarnings({"rawtypes", "unchecked"}) // Unavoidable with reflection API - SafeAtomicHelper( - AtomicReferenceFieldUpdater seenExceptionsUpdater, - AtomicIntegerFieldUpdater remainingCountUpdater) { - this.seenExceptionsUpdater = - (AtomicReferenceFieldUpdater, Set>) - seenExceptionsUpdater; - this.remainingCountUpdater = - (AtomicIntegerFieldUpdater>) remainingCountUpdater; - } + private static final AtomicReferenceFieldUpdater< + ? super AggregateFutureState, ? super @Nullable Set> + seenExceptionsUpdater = seenExceptionsUpdaterFromWithinAggregateFutureState(); + + private static final AtomicIntegerFieldUpdater> + remainingCountUpdater = remainingCountUpdaterFromWithinAggregateFutureState(); @Override void compareAndSetSeenExceptions( - AggregateFutureState state, @CheckForNull Set expect, Set update) { + AggregateFutureState state, @Nullable Set expect, Set update) { seenExceptionsUpdater.compareAndSet(state, expect, update); } @@ -190,7 +175,7 @@ int decrementAndGetRemainingCount(AggregateFutureState state) { private static final class SynchronizedAtomicHelper extends AtomicHelper { @Override void compareAndSetSeenExceptions( - AggregateFutureState state, @CheckForNull Set expect, Set update) { + AggregateFutureState state, @Nullable Set expect, Set update) { synchronized (state) { if (state.seenExceptions == expect) { state.seenExceptions = update; @@ -205,4 +190,27 @@ int decrementAndGetRemainingCount(AggregateFutureState state) { } } } + + /** + * Returns an {@link AtomicReferenceFieldUpdater} for {@link #seenExceptions}. + * + *

    The creation of the updater has to happen directly inside {@link AggregateFutureState}, as + * discussed in {@link AbstractFuture#methodHandlesLookupFromWithinAbstractFuture}. + */ + private static AtomicReferenceFieldUpdater< + ? super AggregateFutureState, ? super @Nullable Set> + seenExceptionsUpdaterFromWithinAggregateFutureState() { + return newUpdater(AggregateFutureState.class, Set.class, "seenExceptions"); + } + + /** + * Returns an {@link AtomicIntegerFieldUpdater} for {@link #remaining}. + * + *

    The creation of the updater has to happen directly inside {@link AggregateFutureState}, as + * discussed in {@link AbstractFuture#methodHandlesLookupFromWithinAbstractFuture}. + */ + private static AtomicIntegerFieldUpdater> + remainingCountUpdaterFromWithinAggregateFutureState() { + return newUpdater(AggregateFutureState.class, "remaining"); + } } diff --git a/android/guava/src/com/google/common/util/concurrent/AsyncCallable.java b/android/guava/src/com/google/common/util/concurrent/AsyncCallable.java index 7ee831e95827..3f2405a22098 100644 --- a/android/guava/src/com/google/common/util/concurrent/AsyncCallable.java +++ b/android/guava/src/com/google/common/util/concurrent/AsyncCallable.java @@ -14,10 +14,9 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.concurrent.Future; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Computes a value, possibly asynchronously. For an example usage and more information, see {@link @@ -28,9 +27,7 @@ * * @since 20.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public interface AsyncCallable { /** * Computes a result {@code Future}. The output {@code Future} need not be {@linkplain diff --git a/android/guava/src/com/google/common/util/concurrent/AsyncFunction.java b/android/guava/src/com/google/common/util/concurrent/AsyncFunction.java index cd70d20dc515..c79e1ffcf093 100644 --- a/android/guava/src/com/google/common/util/concurrent/AsyncFunction.java +++ b/android/guava/src/com/google/common/util/concurrent/AsyncFunction.java @@ -16,7 +16,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.concurrent.Future; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Transforms a value, possibly asynchronously. For an example usage and more information, see @@ -26,7 +26,6 @@ * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface AsyncFunction { /** * Returns an output {@code Future} to use in place of the given {@code input}. The output {@code diff --git a/android/guava/src/com/google/common/util/concurrent/AtomicDouble.java b/android/guava/src/com/google/common/util/concurrent/AtomicDouble.java index 161fca83a633..8a21b220a5e7 100644 --- a/android/guava/src/com/google/common/util/concurrent/AtomicDouble.java +++ b/android/guava/src/com/google/common/util/concurrent/AtomicDouble.java @@ -18,6 +18,9 @@ import static java.lang.Double.longBitsToDouble; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.concurrent.atomic.AtomicLong; /** @@ -49,8 +52,7 @@ * @author Martin Buchholz * @since 11.0 */ -@ElementTypesAreNonnullByDefault -public class AtomicDouble extends Number implements java.io.Serializable { +public class AtomicDouble extends Number { private static final long serialVersionUID = 0L; // We would use AtomicLongFieldUpdater, but it has issues on some Android devices. @@ -164,6 +166,7 @@ public final double getAndAdd(double delta) { * * @param delta the value to add * @return the updated value + * @since 31.1 */ @CanIgnoreReturnValue public final double addAndGet(double delta) { @@ -226,15 +229,14 @@ public double doubleValue() { * * @serialData The current value is emitted (a {@code double}). */ - private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeDouble(get()); } /** Reconstitutes the instance from a stream (that is, deserializes it). */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); value = new AtomicLong(); set(s.readDouble()); diff --git a/android/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java b/android/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java index 3adb84b9fd38..0ccfcf9f2be4 100644 --- a/android/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java +++ b/android/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java @@ -17,8 +17,13 @@ import static java.lang.Double.longBitsToDouble; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.ImmutableLongArray; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.concurrent.atomic.AtomicLongArray; /** @@ -44,8 +49,8 @@ * @since 11.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault -public class AtomicDoubleArray implements java.io.Serializable { +@J2ktIncompatible +public class AtomicDoubleArray implements Serializable { private static final long serialVersionUID = 0L; // Making this non-final is the lesser evil according to Effective @@ -188,6 +193,7 @@ public final double getAndAdd(int i, double delta) { * @param i the index * @param delta the value to add * @return the updated value + * @since 31.1 */ @CanIgnoreReturnValue public double addAndGet(int i, double delta) { @@ -232,7 +238,7 @@ public String toString() { * @serialData The length of the array is emitted (int), followed by all of its elements (each a * {@code double}) in the proper order. */ - private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); // Write out array length @@ -246,8 +252,7 @@ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOExceptio } /** Reconstitutes the instance from a stream (that is, deserializes it). */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int length = s.readInt(); diff --git a/android/guava/src/com/google/common/util/concurrent/AtomicLongMap.java b/android/guava/src/com/google/common/util/concurrent/AtomicLongMap.java index bac02c1436fe..3f97967be5ae 100644 --- a/android/guava/src/com/google/common/util/concurrent/AtomicLongMap.java +++ b/android/guava/src/com/google/common/util/concurrent/AtomicLongMap.java @@ -16,11 +16,12 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.collect.Maps; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; @@ -28,7 +29,7 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A map containing {@code long} values that can be atomically updated. While writes to a @@ -43,6 +44,8 @@ *

    Instances of this class may be used by multiple threads concurrently. All operations are * atomic unless otherwise noted. * + *

    Instances of this class are serializable if the keys are serializable. + * *

    Note: If your values are always positive and less than 2^31, you may wish to use a * {@link com.google.common.collect.Multiset} such as {@link * com.google.common.collect.ConcurrentHashMultiset} instead. @@ -54,7 +57,7 @@ * @since 11.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public final class AtomicLongMap implements Serializable { private final ConcurrentHashMap map; @@ -64,7 +67,7 @@ private AtomicLongMap(ConcurrentHashMap map) { /** Creates an {@code AtomicLongMap}. */ public static AtomicLongMap create() { - return new AtomicLongMap(new ConcurrentHashMap()); + return new AtomicLongMap<>(new ConcurrentHashMap<>()); } /** Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}. */ @@ -290,7 +293,6 @@ boolean remove(K key, long value) { * * @since 20.0 */ - @Beta @CanIgnoreReturnValue public boolean removeIfZero(K key) { return remove(key, 0); @@ -326,7 +328,7 @@ public long sum() { return sum; } - @CheckForNull private transient Map asMap; + @LazyInit private transient @Nullable Map asMap; /** Returns a live, read-only view of the map backing this {@code AtomicLongMap}. */ public Map asMap() { diff --git a/android/guava/src/com/google/common/util/concurrent/Atomics.java b/android/guava/src/com/google/common/util/concurrent/Atomics.java index c1b6964ec23c..99098cb96763 100644 --- a/android/guava/src/com/google/common/util/concurrent/Atomics.java +++ b/android/guava/src/com/google/common/util/concurrent/Atomics.java @@ -17,7 +17,7 @@ import com.google.common.annotations.GwtIncompatible; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceArray; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to classes in the {@code java.util.concurrent.atomic} package. @@ -26,7 +26,6 @@ * @since 10.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class Atomics { private Atomics() {} diff --git a/android/guava/src/com/google/common/util/concurrent/Callables.java b/android/guava/src/com/google/common/util/concurrent/Callables.java index 3b52c2e98217..756237020362 100644 --- a/android/guava/src/com/google/common/util/concurrent/Callables.java +++ b/android/guava/src/com/google/common/util/concurrent/Callables.java @@ -16,12 +16,12 @@ import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Supplier; import java.util.concurrent.Callable; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to the {@link Callable} interface. @@ -30,7 +30,6 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Callables { private Callables() {} @@ -47,7 +46,7 @@ private Callables() {} * * @since 20.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible public static AsyncCallable asAsyncCallable( Callable callable, ListeningExecutorService listeningExecutorService) { @@ -64,6 +63,7 @@ private Callables() {} * @param nameSupplier The supplier of thread names, {@link Supplier#get get} will be called once * for each invocation of the wrapped callable. */ + @J2ktIncompatible @GwtIncompatible // threads static Callable threadRenaming( Callable callable, Supplier nameSupplier) { @@ -91,6 +91,7 @@ private Callables() {} * @param nameSupplier The supplier of thread names, {@link Supplier#get get} will be called once * for each invocation of the wrapped callable. */ + @J2ktIncompatible @GwtIncompatible // threads static Runnable threadRenaming(Runnable task, Supplier nameSupplier) { checkNotNull(nameSupplier); @@ -110,6 +111,7 @@ static Runnable threadRenaming(Runnable task, Supplier nameSupplier) { } /** Tries to set name of the given {@link Thread}, returns true if successful. */ + @J2ktIncompatible @GwtIncompatible // threads private static boolean trySetName(String threadName, Thread currentThread) { /* diff --git a/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java b/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java index c5ce0c45e0b2..5cd27c524a9c 100644 --- a/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ClosingFuture.java @@ -32,13 +32,13 @@ import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import static java.util.logging.Level.FINER; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; -import com.google.common.annotations.Beta; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ClosingFuture.Combiner.AsyncCombiningCallable; @@ -48,7 +48,6 @@ import com.google.errorprone.annotations.DoNotMock; import com.google.j2objc.annotations.RetainedWith; import java.io.Closeable; -import java.io.IOException; import java.util.IdentityHashMap; import java.util.Map; import java.util.concurrent.Callable; @@ -59,9 +58,7 @@ import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A step in a pipeline of an asynchronous computation. When the last step in the computation is @@ -191,13 +188,12 @@ * @since 30.0 */ // TODO(dpb): Consider reusing one CloseableList for the entire pipeline, modulo combinations. -@Beta // @Beta for one release. @DoNotMock("Use ClosingFuture.from(Futures.immediate*Future)") -@ElementTypesAreNonnullByDefault +@J2ktIncompatible // TODO(dpb): GWT compatibility. public final class ClosingFuture { - private static final Logger logger = Logger.getLogger(ClosingFuture.class.getName()); + private static final LazyLogger logger = new LazyLogger(ClosingFuture.class); /** * An object that can capture objects to be closed later, when a {@link ClosingFuture} pipeline is @@ -235,8 +231,7 @@ public static final class DeferredCloser { */ @CanIgnoreReturnValue @ParametricNullness - // TODO(b/163345357): Widen bound to AutoCloseable once we require API Level 19. - public C eventuallyClose( + public C eventuallyClose( @ParametricNullness C closeable, Executor closingExecutor) { checkNotNull(closingExecutor); if (closeable != null) { @@ -255,7 +250,7 @@ public interface ClosingCallable { /** * Computes a result, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, Executor) + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but * not before this method completes), even if this method throws or the pipeline is cancelled. */ @@ -273,7 +268,7 @@ public interface AsyncClosingCallable { /** * Computes a result, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, Executor) + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but * not before this method completes), even if this method throws or the pipeline is cancelled. */ @@ -291,7 +286,7 @@ public interface ClosingFunctionAny objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, Executor) + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but * not before this method completes), even if this method throws or the pipeline is cancelled. */ @@ -309,7 +304,7 @@ public interface AsyncClosingFunctionAny objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, Executor) + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but * not before this method completes), even if this method throws or the pipeline is cancelled. */ @@ -352,7 +347,7 @@ public V get() throws ExecutionException { /** * Starts closing all closeable objects captured during the {@link ClosingFuture}'s asynchronous * operation on the {@link Executor}s specified by calls to {@link - * DeferredCloser#eventuallyClose(Closeable, Executor)}. + * DeferredCloser#eventuallyClose(Object, Executor)}. * *

    If any such calls specified {@link MoreExecutors#directExecutor()}, those objects will be * closed synchronously. @@ -385,7 +380,24 @@ public interface ValueAndCloserConsumer { */ public static ClosingFuture submit( ClosingCallable callable, Executor executor) { - return new ClosingFuture<>(callable, executor); + checkNotNull(callable); + CloseableList closeables = new CloseableList(); + TrustedListenableFutureTask task = + TrustedListenableFutureTask.create( + new Callable() { + @Override + @ParametricNullness + public V call() throws Exception { + return callable.call(closeables.closer); + } + + @Override + public String toString() { + return callable.toString(); + } + }); + executor.execute(task); + return new ClosingFuture<>(task, closeables); } /** @@ -397,7 +409,30 @@ public interface ValueAndCloserConsumer { */ public static ClosingFuture submitAsync( AsyncClosingCallable callable, Executor executor) { - return new ClosingFuture<>(callable, executor); + checkNotNull(callable); + CloseableList closeables = new CloseableList(); + TrustedListenableFutureTask task = + TrustedListenableFutureTask.create( + new AsyncCallable() { + @Override + public ListenableFuture call() throws Exception { + CloseableList newCloseables = new CloseableList(); + try { + ClosingFuture closingFuture = callable.call(newCloseables.closer); + closingFuture.becomeSubsumedInto(closeables); + return closingFuture.future; + } finally { + closeables.add(newCloseables, directExecutor()); + } + } + + @Override + public String toString() { + return callable.toString(); + } + }); + executor.execute(task); + return new ClosingFuture<>(task, closeables); } /** @@ -408,20 +443,20 @@ public interface ValueAndCloserConsumer { * when the pipeline is done, use {@link #submit(ClosingCallable, Executor)} instead. */ public static ClosingFuture from(ListenableFuture future) { - return new ClosingFuture(future); + return new ClosingFuture<>(future); } /** * Starts a {@link ClosingFuture} pipeline with a {@link ListenableFuture}. * - *

    If {@code future} succeeds, its value will be closed (using {@code closingExecutor)} when + *

    If {@code future} succeeds, its value will be closed (using {@code closingExecutor)}) when * the pipeline is done, even if the pipeline is canceled or fails. * *

    Cancelling the pipeline will not cancel {@code future}, so that the pipeline can access its * value in order to close it. * * @param future the future to create the {@code ClosingFuture} from. For discussion of the - * future's result type {@code C}, see {@link DeferredCloser#eventuallyClose(Closeable, + * future's result type {@code C}, see {@link DeferredCloser#eventuallyClose(Object, * Executor)}. * @param closingExecutor the future's result will be closed on this executor * @deprecated Creating {@link Future}s of closeable types is dangerous in general because the @@ -433,17 +468,15 @@ public interface ValueAndCloserConsumer { * ClosingFuture#from}. */ @Deprecated - // TODO(b/163345357): Widen bound to AutoCloseable once we require API Level 19. - public static - ClosingFuture eventuallyClosing( - ListenableFuture future, final Executor closingExecutor) { + public static + ClosingFuture eventuallyClosing(ListenableFuture future, Executor closingExecutor) { checkNotNull(closingExecutor); - final ClosingFuture closingFuture = new ClosingFuture<>(nonCancellationPropagating(future)); + ClosingFuture closingFuture = new ClosingFuture<>(nonCancellationPropagating(future)); Futures.addCallback( future, - new FutureCallback<@Nullable Closeable>() { + new FutureCallback<@Nullable AutoCloseable>() { @Override - public void onSuccess(@CheckForNull Closeable result) { + public void onSuccess(@Nullable AutoCloseable result) { closingFuture.closeables.closer.eventuallyClose(result, closingExecutor); } @@ -587,57 +620,16 @@ public static Combiner whenAllSucceed( } private final AtomicReference state = new AtomicReference<>(OPEN); - private final CloseableList closeables = new CloseableList(); + private final CloseableList closeables; private final FluentFuture future; private ClosingFuture(ListenableFuture future) { - this.future = FluentFuture.from(future); - } - - private ClosingFuture(final ClosingCallable callable, Executor executor) { - checkNotNull(callable); - TrustedListenableFutureTask task = - TrustedListenableFutureTask.create( - new Callable() { - @Override - @ParametricNullness - public V call() throws Exception { - return callable.call(closeables.closer); - } - - @Override - public String toString() { - return callable.toString(); - } - }); - executor.execute(task); - this.future = task; + this(future, new CloseableList()); } - private ClosingFuture(final AsyncClosingCallable callable, Executor executor) { - checkNotNull(callable); - TrustedListenableFutureTask task = - TrustedListenableFutureTask.create( - new AsyncCallable() { - @Override - public ListenableFuture call() throws Exception { - CloseableList newCloseables = new CloseableList(); - try { - ClosingFuture closingFuture = callable.call(newCloseables.closer); - closingFuture.becomeSubsumedInto(closeables); - return closingFuture.future; - } finally { - closeables.add(newCloseables, directExecutor()); - } - } - - @Override - public String toString() { - return callable.toString(); - } - }); - executor.execute(task); - this.future = task; + private ClosingFuture(ListenableFuture future, CloseableList closeables) { + this.future = FluentFuture.from(future); + this.closeables = closeables; } /** @@ -679,7 +671,7 @@ public ListenableFuture statusFuture() { * *

    After calling this method, you may not call {@link #finishToFuture()}, {@link * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on - * this {@code ClosingFuture}. + * the original {@code ClosingFuture} instance. * * @param function transforms the value of this step to the value of the derived step * @param executor executor to run the function in @@ -689,7 +681,7 @@ public ListenableFuture statusFuture() { * finished} */ public ClosingFuture transform( - final ClosingFunction function, Executor executor) { + ClosingFunction function, Executor executor) { checkNotNull(function); AsyncFunction applyFunction = new AsyncFunction() { @@ -730,7 +722,7 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #transform(ClosingFunction, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) closer.eventuallyClose()} + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. @@ -772,7 +764,7 @@ public String toString() { * *

    After calling this method, you may not call {@link #finishToFuture()}, {@link * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on - * this {@code ClosingFuture}. + * the original {@code ClosingFuture} instance. * * @param function transforms the value of this step to a {@code ClosingFuture} with the value of * the derived step @@ -783,7 +775,7 @@ public String toString() { * finished} */ public ClosingFuture transformAsync( - final AsyncClosingFunction function, Executor executor) { + AsyncClosingFunction function, Executor executor) { checkNotNull(function); AsyncFunction applyFunction = new AsyncFunction() { @@ -811,7 +803,7 @@ public String toString() { * *

      *
    • It does not need to capture any {@link Closeable} objects by calling {@link - * DeferredCloser#eventuallyClose(Closeable, Executor)}. + * DeferredCloser#eventuallyClose(Object, Executor)}. *
    • It returns a {@link ListenableFuture}. *
    * @@ -827,14 +819,9 @@ public String toString() { * ListenableFuture} with the value of a derived step */ public static - AsyncClosingFunction withoutCloser(final AsyncFunction function) { + AsyncClosingFunction withoutCloser(AsyncFunction function) { checkNotNull(function); - return new AsyncClosingFunction() { - @Override - public ClosingFuture apply(DeferredCloser closer, V input) throws Exception { - return ClosingFuture.from(function.apply(input)); - } - }; + return (closer, input) -> ClosingFuture.from(function.apply(input)); } /** @@ -863,7 +850,7 @@ public ClosingFuture apply(DeferredCloser closer, V input) throws Exception { * *

    After calling this method, you may not call {@link #finishToFuture()}, {@link * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on - * this {@code ClosingFuture}. + * the original {@code ClosingFuture} instance. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception * type is matched against this step's exception. "This step's exception" means the cause of @@ -885,7 +872,7 @@ public ClosingFuture catching( // Avoids generic type capture inconsistency problems where |? extends V| is incompatible with V. private ClosingFuture catchingMoreGeneric( - Class exceptionType, final ClosingFunction fallback, Executor executor) { + Class exceptionType, ClosingFunction fallback, Executor executor) { checkNotNull(fallback); AsyncFunction applyFallback = new AsyncFunction() { @@ -928,7 +915,7 @@ public String toString() { * {@code ClosingFuture}. If possible, prefer calling {@link #catching(Class, * ClosingFunction, Executor)} instead, with a function that returns the next value * directly. - *

  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) closer.eventuallyClose()} + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. @@ -956,7 +943,7 @@ public String toString() { * *

    After calling this method, you may not call {@link #finishToFuture()}, {@link * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on - * this {@code ClosingFuture}. + * the original {@code ClosingFuture} instance. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception * type is matched against this step's exception. "This step's exception" means the cause of @@ -982,9 +969,7 @@ public ClosingFuture catchingAsync( // Avoids generic type capture inconsistency problems where |? extends V| is incompatible with V. private ClosingFuture catchingAsyncMoreGeneric( - Class exceptionType, - final AsyncClosingFunction fallback, - Executor executor) { + Class exceptionType, AsyncClosingFunction fallback, Executor executor) { checkNotNull(fallback); AsyncFunction asyncFunction = new AsyncFunction() { @@ -1013,21 +998,18 @@ public String toString() { * *

    After calling this method, you may not call {@link * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, this method, or any other - * derivation method on this {@code ClosingFuture}. + * derivation method on the original {@code ClosingFuture} instance. * * @return a {@link Future} that represents the final value or exception of the pipeline */ public FluentFuture finishToFuture() { if (compareAndUpdateState(OPEN, WILL_CLOSE)) { - logger.log(FINER, "will close {0}", this); + logger.get().log(FINER, "will close {0}", this); future.addListener( - new Runnable() { - @Override - public void run() { - checkAndUpdateState(WILL_CLOSE, CLOSING); - close(); - checkAndUpdateState(CLOSING, CLOSED); - } + () -> { + checkAndUpdateState(WILL_CLOSE, CLOSING); + close(); + checkAndUpdateState(CLOSING, CLOSED); }, directExecutor()); } else { @@ -1058,13 +1040,13 @@ public void run() { * receiver can store the {@link ValueAndCloser} outside the receiver for later synchronous use. * *

    After calling this method, you may not call {@link #finishToFuture()}, this method again, or - * any other derivation method on this {@code ClosingFuture}. + * any other derivation method on the original {@code ClosingFuture} instance. * * @param consumer a callback whose method will be called (using {@code executor}) when this * operation is done */ public void finishToValueAndCloser( - final ValueAndCloserConsumer consumer, Executor executor) { + ValueAndCloserConsumer consumer, Executor executor) { checkNotNull(consumer); if (!compareAndUpdateState(OPEN, WILL_CREATE_VALUE_AND_CLOSER)) { switch (state.get()) { @@ -1086,14 +1068,7 @@ public void finishToValueAndCloser( } throw new AssertionError(state); } - future.addListener( - new Runnable() { - @Override - public void run() { - provideValueAndCloser(consumer, ClosingFuture.this); - } - }, - executor); + future.addListener(() -> provideValueAndCloser(consumer, ClosingFuture.this), executor); } private static void provideValueAndCloser( @@ -1118,8 +1093,9 @@ public void run() { * completed normally; {@code true} otherwise */ @CanIgnoreReturnValue + @SuppressWarnings("Interruption") // We are propagating an interrupt from a caller. public boolean cancel(boolean mayInterruptIfRunning) { - logger.log(FINER, "cancelling {0}", this); + logger.get().log(FINER, "cancelling {0}", this); boolean cancelled = future.cancel(mayInterruptIfRunning); if (cancelled) { close(); @@ -1128,7 +1104,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { } private void close() { - logger.log(FINER, "closing {0}", this); + logger.get().log(FINER, "closing {0}", this); closeables.close(); } @@ -1227,9 +1203,7 @@ private Peeker(ImmutableList> futures) { * .closing(executor); * }

  • */ - // TODO(cpovirk): Use simple name instead of fully qualified after we stop building with JDK 8. - @com.google.errorprone.annotations.DoNotMock( - "Use ClosingFuture.whenAllSucceed() or .whenAllComplete() instead.") + @DoNotMock("Use ClosingFuture.whenAllSucceed() or .whenAllComplete() instead.") public static class Combiner { private final CloseableList closeables = new CloseableList(); @@ -1243,10 +1217,10 @@ public interface CombiningCallable { /** * Computes a result, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. * * @param peeker used to get the value of any of the input futures */ @@ -1263,10 +1237,10 @@ public interface AsyncCombiningCallable { /** * Computes a {@link ClosingFuture} result, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. * * @param peeker used to get the value of any of the input futures */ @@ -1299,7 +1273,7 @@ private Combiner(boolean allMustSucceed, Iterable> in * {@code ExecutionException} will be extracted and used as the failure of the derived step. */ public ClosingFuture call( - final CombiningCallable combiningCallable, Executor executor) { + CombiningCallable combiningCallable, Executor executor) { Callable callable = new Callable() { @Override @@ -1345,9 +1319,8 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) - * closer.eventuallyClose()} for every closeable object this step creates in order to - * capture it for later closing. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. * @@ -1356,7 +1329,7 @@ public String toString() { * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. */ public ClosingFuture callAsync( - final AsyncCombiningCallable combiningCallable, Executor executor) { + AsyncCombiningCallable combiningCallable, Executor executor) { AsyncCallable asyncCallable = new AsyncCallable() { @Override @@ -1381,16 +1354,10 @@ public String toString() { : Futures.whenAllComplete(inputFutures()); } - private static final Function, FluentFuture> INNER_FUTURE = - new Function, FluentFuture>() { - @Override - public FluentFuture apply(ClosingFuture future) { - return future.future; - } - }; - private ImmutableList> inputFutures() { - return FluentIterable.from(inputs).transform(INNER_FUTURE).toList(); + return FluentIterable.from(inputs) + .>transform(future -> future.future) + .toList(); } } @@ -1419,10 +1386,10 @@ public interface ClosingFunction2< /** * Applies this function to two inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ @ParametricNullness U apply(DeferredCloser closer, @ParametricNullness V1 value1, @ParametricNullness V2 value2) @@ -1443,10 +1410,10 @@ public interface AsyncClosingFunction2< /** * Applies this function to two inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ ClosingFuture apply( DeferredCloser closer, @ParametricNullness V1 value1, @ParametricNullness V2 value2) @@ -1476,7 +1443,7 @@ private Combiner2(ClosingFuture future1, ClosingFuture future2) { * ExecutionException} will be extracted and used as the failure of the derived step. */ public ClosingFuture call( - final ClosingFunction2 function, Executor executor) { + ClosingFunction2 function, Executor executor) { return call( new CombiningCallable() { @Override @@ -1519,9 +1486,8 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) - * closer.eventuallyClose()} for every closeable object this step creates in order to - * capture it for later closing. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. * @@ -1530,7 +1496,7 @@ public String toString() { * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. */ public ClosingFuture callAsync( - final AsyncClosingFunction2 function, Executor executor) { + AsyncClosingFunction2 function, Executor executor) { return callAsync( new AsyncCombiningCallable() { @Override @@ -1576,10 +1542,10 @@ public interface ClosingFunction3< /** * Applies this function to three inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ @ParametricNullness U apply( @@ -1607,10 +1573,10 @@ public interface AsyncClosingFunction3< /** * Applies this function to three inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ ClosingFuture apply( DeferredCloser closer, @@ -1646,7 +1612,7 @@ private Combiner3( * ExecutionException} will be extracted and used as the failure of the derived step. */ public ClosingFuture call( - final ClosingFunction3 function, Executor executor) { + ClosingFunction3 function, Executor executor) { return call( new CombiningCallable() { @Override @@ -1693,9 +1659,8 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) - * closer.eventuallyClose()} for every closeable object this step creates in order to - * capture it for later closing. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. * @@ -1704,7 +1669,7 @@ public String toString() { * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. */ public ClosingFuture callAsync( - final AsyncClosingFunction3 function, Executor executor) { + AsyncClosingFunction3 function, Executor executor) { return callAsync( new AsyncCombiningCallable() { @Override @@ -1760,10 +1725,10 @@ public interface ClosingFunction4< /** * Applies this function to four inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ @ParametricNullness U apply( @@ -1795,10 +1760,10 @@ public interface AsyncClosingFunction4< /** * Applies this function to four inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ ClosingFuture apply( DeferredCloser closer, @@ -1840,7 +1805,7 @@ private Combiner4( * ExecutionException} will be extracted and used as the failure of the derived step. */ public ClosingFuture call( - final ClosingFunction4 function, Executor executor) { + ClosingFunction4 function, Executor executor) { return call( new CombiningCallable() { @Override @@ -1888,9 +1853,8 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) - * closer.eventuallyClose()} for every closeable object this step creates in order to - * capture it for later closing. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. * @@ -1899,7 +1863,7 @@ public String toString() { * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. */ public ClosingFuture callAsync( - final AsyncClosingFunction4 function, Executor executor) { + AsyncClosingFunction4 function, Executor executor) { return callAsync( new AsyncCombiningCallable() { @Override @@ -1961,10 +1925,10 @@ public interface ClosingFunction5< /** * Applies this function to five inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ @ParametricNullness U apply( @@ -1999,10 +1963,10 @@ public interface AsyncClosingFunction5< /** * Applies this function to five inputs, or throws an exception if unable to do so. * - *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Closeable, - * Executor) closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline - * is done (but not before this method completes), even if this method throws or the pipeline - * is cancelled. + *

    Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. */ ClosingFuture apply( DeferredCloser closer, @@ -2049,7 +2013,7 @@ private Combiner5( * ExecutionException} will be extracted and used as the failure of the derived step. */ public ClosingFuture call( - final ClosingFunction5 function, Executor executor) { + ClosingFunction5 function, Executor executor) { return call( new CombiningCallable() { @Override @@ -2099,9 +2063,8 @@ public String toString() { *

  • Use this method only when calling an API that returns a {@link ListenableFuture} or a * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, * Executor)} instead, with a function that returns the next value directly. - *
  • Call {@link DeferredCloser#eventuallyClose(Closeable, Executor) - * closer.eventuallyClose()} for every closeable object this step creates in order to - * capture it for later closing. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code * ClosingFuture} call {@link #from(ListenableFuture)}. * @@ -2110,7 +2073,7 @@ public String toString() { * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. */ public ClosingFuture callAsync( - final AsyncClosingFunction5 function, Executor executor) { + AsyncClosingFunction5 function, Executor executor) { return callAsync( new AsyncCombiningCallable() { @Override @@ -2139,34 +2102,45 @@ public String toString() { return toStringHelper(this).add("state", state.get()).addValue(future).toString(); } + @SuppressWarnings({"removal", "Finalize"}) // b/260137033 @Override protected void finalize() { if (state.get().equals(OPEN)) { - logger.log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); + logger.get().log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); FluentFuture unused = finishToFuture(); } } - private static void closeQuietly(@CheckForNull final Closeable closeable, Executor executor) { + private static void closeQuietly(@Nullable AutoCloseable closeable, Executor executor) { if (closeable == null) { return; } try { executor.execute( - new Runnable() { - @Override - public void run() { - try { - closeable.close(); - } catch (IOException | RuntimeException e) { - logger.log(WARNING, "thrown by close()", e); - } + () -> { + try { + closeable.close(); + } catch (Exception e) { + /* + * In guava-jre, any kind of Exception may be thrown because `closeable` has type + * `AutoCloseable`. + * + * In guava-android, the only kinds of Exception that may be thrown are + * RuntimeException and IOException because `closeable` has type `Closeable`—except + * that we have to account for sneaky checked exception. + */ + restoreInterruptIfIsInterruptedException(e); + logger.get().log(WARNING, "thrown by close()", e); } }); } catch (RejectedExecutionException e) { - if (logger.isLoggable(WARNING)) { - logger.log( - WARNING, String.format("while submitting close to %s; will close inline", executor), e); + if (logger.get().isLoggable(WARNING)) { + logger + .get() + .log( + WARNING, + String.format("while submitting close to %s; will close inline", executor), + e); } closeQuietly(closeable, directExecutor()); } @@ -2185,11 +2159,11 @@ private boolean compareAndUpdateState(State oldState, State newState) { } // TODO(dpb): Should we use a pair of ArrayLists instead of an IdentityHashMap? - private static final class CloseableList extends IdentityHashMap + private static final class CloseableList extends IdentityHashMap implements Closeable { private final DeferredCloser closer = new DeferredCloser(this); private volatile boolean closed; - @CheckForNull private volatile CountDownLatch whenClosed; + private volatile @Nullable CountDownLatch whenClosed; ListenableFuture applyClosingFunction( @@ -2230,7 +2204,7 @@ public void close() { } closed = true; } - for (Map.Entry entry : entrySet()) { + for (Map.Entry entry : entrySet()) { closeQuietly(entry.getKey(), entry.getValue()); } clear(); @@ -2239,7 +2213,7 @@ public void close() { } } - void add(@CheckForNull Closeable closeable, Executor executor) { + void add(@Nullable AutoCloseable closeable, Executor executor) { checkNotNull(executor); if (closeable == null) { return; diff --git a/android/guava/src/com/google/common/util/concurrent/CollectionFuture.java b/android/guava/src/com/google/common/util/concurrent/CollectionFuture.java index c106c157e257..3b32f0f0a47d 100644 --- a/android/guava/src/com/google/common/util/concurrent/CollectionFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/CollectionFuture.java @@ -20,23 +20,23 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.Lists; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.Collections; import java.util.List; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Aggregate future that collects (stores) results of each future. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault abstract class CollectionFuture extends AggregateFuture { /* * We access this field racily but safely. For discussion of a similar situation, see the comments - * on the fields of TimeoutFuture. This field is slightly different than the fields discussed + * on the fields of TimeoutFuture. This field is slightly different from the fields discussed * there: cancel() never reads this field, only writes to it. That makes the race here completely * harmless, rather than just 99.99% harmless. */ - @CheckForNull private List<@Nullable Present> values; + @LazyInit private @Nullable List<@Nullable Present> values; CollectionFuture( ImmutableCollection> futures, @@ -58,7 +58,7 @@ abstract class CollectionFuture> localValues = values; + @RetainedLocalRef List<@Nullable Present> localValues = values; if (localValues != null) { localValues.set(index, new Present<>(returnValue)); } @@ -66,7 +66,7 @@ final void collectOneValue(int index, @ParametricNullness V returnValue) { @Override final void handleAllCompleted() { - List<@Nullable Present> localValues = values; + @RetainedLocalRef List<@Nullable Present> localValues = values; if (localValues != null) { set(combine(localValues)); } @@ -102,9 +102,9 @@ static final class ListFuture /** The result of a successful {@code Future}. */ private static final class Present { - V value; + @ParametricNullness final V value; - Present(V value) { + Present(@ParametricNullness V value) { this.value = value; } } diff --git a/android/guava/src/com/google/common/util/concurrent/CombinedFuture.java b/android/guava/src/com/google/common/util/concurrent/CombinedFuture.java index c22211164c40..3820200d730c 100644 --- a/android/guava/src/com/google/common/util/concurrent/CombinedFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/CombinedFuture.java @@ -19,21 +19,21 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableCollection; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import com.google.j2objc.annotations.WeakOuter; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Aggregate future that computes its value by calling a callable. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class CombinedFuture extends AggregateFuture<@Nullable Object, V> { - @CheckForNull private CombinedFutureInterruptibleTask task; + @LazyInit private @Nullable CombinedFutureInterruptibleTask task; CombinedFuture( ImmutableCollection> futures, @@ -56,11 +56,11 @@ final class CombinedFuture } @Override - void collectOneValue(int index, @CheckForNull Object returnValue) {} + void collectOneValue(int index, @Nullable Object returnValue) {} @Override void handleAllCompleted() { - CombinedFutureInterruptibleTask localTask = task; + @RetainedLocalRef CombinedFutureInterruptibleTask localTask = task; if (localTask != null) { localTask.execute(); } @@ -83,7 +83,7 @@ void releaseResources(ReleaseResourcesReason reason) { @Override protected void interruptTask() { - CombinedFutureInterruptibleTask localTask = task; + @RetainedLocalRef CombinedFutureInterruptibleTask localTask = task; if (localTask != null) { localTask.interruptTask(); } diff --git a/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java index decb5f1b183d..12de35eaff01 100644 --- a/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java +++ b/android/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -17,8 +17,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -27,7 +27,6 @@ import com.google.common.collect.MapMaker; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.j2objc.annotations.Weak; import java.util.ArrayList; import java.util.Arrays; @@ -42,8 +41,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and {@link @@ -160,10 +158,8 @@ * @author Darick Tong * @since 13.0 */ -@Beta -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public class CycleDetectingLockFactory { /** @@ -173,7 +169,6 @@ public class CycleDetectingLockFactory { * * @since 13.0 */ - @Beta public interface Policy { /** @@ -193,7 +188,6 @@ public interface Policy { * * @since 13.0 */ - @Beta public enum Policies implements Policy { /** * When potential deadlock is detected, this policy results in the throwing of the {@code @@ -215,7 +209,7 @@ public void handlePotentialDeadlock(PotentialDeadlockException e) { WARN { @Override public void handlePotentialDeadlock(PotentialDeadlockException e) { - logger.log(Level.SEVERE, "Detected potential deadlock", e); + logger.get().log(Level.SEVERE, "Detected potential deadlock", e); } }, @@ -394,7 +388,6 @@ private static String getLockName(Enum rank) { * @param The Enum type representing the explicit lock ordering. * @since 13.0 */ - @Beta public static final class WithExplicitOrdering> extends CycleDetectingLockFactory { @@ -452,7 +445,7 @@ public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) { //////// Implementation ///////// - private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName()); + private static final LazyLogger logger = new LazyLogger(CycleDetectingLockFactory.class); final Policy policy; @@ -534,7 +527,6 @@ private static class ExampleStackTrace extends IllegalStateException { * * @since 13.0 */ - @Beta public static final class PotentialDeadlockException extends ExampleStackTrace { private final ExampleStackTrace conflictingStackTrace; @@ -571,10 +563,14 @@ public String getMessage() { */ private interface CycleDetectingLock { - /** @return the {@link LockGraphNode} associated with this lock. */ + /** + * @return the {@link LockGraphNode} associated with this lock. + */ LockGraphNode getLockGraphNode(); - /** @return {@code true} if the current thread has acquired this lock. */ + /** + * @return {@code true} if the current thread has acquired this lock. + */ boolean isAcquiredByCurrentThread(); } @@ -685,8 +681,7 @@ void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) { * @return If a path was found, a chained {@link ExampleStackTrace} illustrating the path to the * {@code lock}, or {@code null} if no path was found. */ - @CheckForNull - private ExampleStackTrace findPathTo(LockGraphNode node, Set seen) { + private @Nullable ExampleStackTrace findPathTo(LockGraphNode node, Set seen) { if (!seen.add(this)) { return null; // Already traversed this node. } @@ -717,7 +712,8 @@ private ExampleStackTrace findPathTo(LockGraphNode node, Set seen */ private void aboutToAcquire(CycleDetectingLock lock) { if (!lock.isAcquiredByCurrentThread()) { - ArrayList acquiredLockList = acquiredLocks.get(); + // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get + ArrayList acquiredLockList = requireNonNull(acquiredLocks.get()); LockGraphNode node = lock.getLockGraphNode(); node.checkAcquiredLocks(policy, acquiredLockList); acquiredLockList.add(node); @@ -731,7 +727,8 @@ private void aboutToAcquire(CycleDetectingLock lock) { */ private static void lockStateChanged(CycleDetectingLock lock) { if (!lock.isAcquiredByCurrentThread()) { - ArrayList acquiredLockList = acquiredLocks.get(); + // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get + ArrayList acquiredLockList = requireNonNull(acquiredLocks.get()); LockGraphNode node = lock.getLockGraphNode(); // Iterate in reverse because locks are usually locked/unlocked in a // LIFO order. diff --git a/android/guava/src/com/google/common/util/concurrent/DirectExecutor.java b/android/guava/src/com/google/common/util/concurrent/DirectExecutor.java index 0c3a46b703c3..30229714c638 100644 --- a/android/guava/src/com/google/common/util/concurrent/DirectExecutor.java +++ b/android/guava/src/com/google/common/util/concurrent/DirectExecutor.java @@ -22,7 +22,6 @@ * execute}. */ @GwtCompatible -@ElementTypesAreNonnullByDefault enum DirectExecutor implements Executor { INSTANCE; diff --git a/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java b/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java new file mode 100644 index 000000000000..8ccd000b50f1 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.concurrent.GuardedBy; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +/** See newDirectExecutorService javadoc for behavioral notes. */ +@J2ktIncompatible // Emulated +@GwtIncompatible +final class DirectExecutorService extends AbstractListeningExecutorService { + + /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ + private final Object lock = new Object(); + + /* + * Conceptually, these two variables describe the executor being in + * one of three states: + * - Active: shutdown == false + * - Shutdown: runningTasks > 0 and shutdown == true + * - Terminated: runningTasks == 0 and shutdown == true + */ + @GuardedBy("lock") + private int runningTasks = 0; + + @GuardedBy("lock") + private boolean shutdown = false; + + @Override + public void execute(Runnable command) { + startTask(); + try { + command.run(); + } finally { + endTask(); + } + } + + @Override + public boolean isShutdown() { + synchronized (lock) { + return shutdown; + } + } + + @Override + public void shutdown() { + synchronized (lock) { + shutdown = true; + if (runningTasks == 0) { + lock.notifyAll(); + } + } + } + + // See newDirectExecutorService javadoc for unusual behavior of this method. + @Override + public List shutdownNow() { + shutdown(); + return Collections.emptyList(); + } + + @Override + public boolean isTerminated() { + synchronized (lock) { + return shutdown && runningTasks == 0; + } + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); + synchronized (lock) { + while (true) { + if (shutdown && runningTasks == 0) { + return true; + } else if (nanos <= 0) { + return false; + } else { + long now = System.nanoTime(); + NANOSECONDS.timedWait(lock, nanos); + nanos -= System.nanoTime() - now; // subtract the actual time we waited + } + } + } + } + + /** + * Checks if the executor has been shut down and increments the running task count. + * + * @throws RejectedExecutionException if the executor has been previously shutdown + */ + private void startTask() { + synchronized (lock) { + if (shutdown) { + throw new RejectedExecutionException("Executor already shutdown"); + } + runningTasks++; + } + } + + /** Decrements the running task count. */ + private void endTask() { + synchronized (lock) { + int numRunning = --runningTasks; + if (numRunning == 0) { + lock.notifyAll(); + } + } + } +} diff --git a/android/guava/src/com/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index 34871255e812..000000000000 --- a/android/guava/src/com/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.util.concurrent; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/util/concurrent/ExecutionError.java b/android/guava/src/com/google/common/util/concurrent/ExecutionError.java index deaff15a426c..48c20bf81eab 100644 --- a/android/guava/src/com/google/common/util/concurrent/ExecutionError.java +++ b/android/guava/src/com/google/common/util/concurrent/ExecutionError.java @@ -15,7 +15,9 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As with {@code @@ -28,31 +30,61 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public class ExecutionError extends Error { /* - * Ideally, this class would have exposed only constructors that require a non-null cause. We - * might try to move in that direction, but there are complications. See - * https://github.com/jspecify/nullness-checker-for-checker-framework/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + * Ideally, this class would have exposed only constructors that require a non-null cause. See + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + * and https://github.com/jspecify/jspecify/issues/490. + * + * (That would also have ensured that its cause was always an Error, rather than possibly another + * kind of Throwable that was later passed to initCause. Then we could have declared the override + * `public final Error getCause()`.) */ - /** Creates a new instance with {@code null} as its detail message. */ + /** + * Creates a new instance with {@code null} as its detail message and no cause. + * + * @deprecated Prefer {@linkplain ExecutionError(Error)} a constructor that accepts a cause: Users + * of this class typically expect for instances to have a non-null cause. At the moment, you + * can usually still preserve behavior by passing an explicit {@code null} cause. Note, + * however, that passing an explicit {@code null} cause prevents anyone from calling {@link + * #initCause} later, so it is not quite equivalent to using a constructor that omits the + * cause. + */ + @Deprecated protected ExecutionError() {} - /** Creates a new instance with the given detail message. */ - protected ExecutionError(@CheckForNull String message) { + /** + * Creates a new instance with the given detail message and no cause. + * + * @deprecated Prefer {@linkplain ExecutionError(String, Error)} a constructor that accepts a + * cause: Users of this class typically expect for instances to have a non-null cause. At the + * moment, you can usually still preserve behavior by passing an explicit {@code null} + * cause. Note, however, that passing an explicit {@code null} cause prevents anyone from + * calling {@link #initCause} later, so it is not quite equivalent to using a constructor that + * omits the cause. + */ + @SuppressWarnings("InlineMeSuggester") // b/387265535 + @Deprecated + protected ExecutionError(@Nullable String message) { super(message); } - /** Creates a new instance with the given detail message and cause. */ - public ExecutionError(@CheckForNull String message, @CheckForNull Error cause) { + /** + * Creates a new instance with the given detail message and cause. Prefer to provide a + * non-nullable {@code cause}, as many users expect to find one. + */ + public ExecutionError(@Nullable String message, @Nullable Error cause) { super(message, cause); } - /** Creates a new instance with the given cause. */ - public ExecutionError(@CheckForNull Error cause) { + /** + * Creates a new instance with {@code null} as its detail message and the given cause. Prefer to + * provide a non-nullable {@code cause}, as many users expect to find one. + */ + public ExecutionError(@Nullable Error cause) { super(cause); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/util/concurrent/ExecutionList.java b/android/guava/src/com/google/common/util/concurrent/ExecutionList.java index 96fc51ca88d0..fbf2e4bcc16e 100644 --- a/android/guava/src/com/google/common/util/concurrent/ExecutionList.java +++ b/android/guava/src/com/google/common/util/concurrent/ExecutionList.java @@ -17,11 +17,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.concurrent.GuardedBy; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A support class for {@code ListenableFuture} implementations to manage their listeners. An @@ -39,19 +39,18 @@ * @author Sven Mawson * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ExecutionList { /** Logger to log exceptions caught when running runnables. */ - private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + private static final LazyLogger log = new LazyLogger(ExecutionList.class); /** * The runnable, executor pairs to execute. This acts as a stack threaded through the {@link * RunnableExecutorPair#next} field. */ @GuardedBy("this") - @CheckForNull - private RunnableExecutorPair runnables; + private @Nullable RunnableExecutorPair runnables; @GuardedBy("this") private boolean executed; @@ -138,27 +137,31 @@ public void execute() { * Submits the given runnable to the given {@link Executor} catching and logging all {@linkplain * RuntimeException runtime exceptions} thrown by the executor. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private static void executeListener(Runnable runnable, Executor executor) { try { executor.execute(runnable); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if - // we're given a bad one. We only catch RuntimeException because we want Errors to propagate - // up. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + runnable + " with executor " + executor, - e); + // we're given a bad one. We only catch Exception because we want Errors to propagate up. + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + runnable + + " with executor " + + executor, + e); } } private static final class RunnableExecutorPair { final Runnable runnable; final Executor executor; - @CheckForNull RunnableExecutorPair next; + @Nullable RunnableExecutorPair next; RunnableExecutorPair( - Runnable runnable, Executor executor, @CheckForNull RunnableExecutorPair next) { + Runnable runnable, Executor executor, @Nullable RunnableExecutorPair next) { this.runnable = runnable; this.executor = executor; this.next = next; diff --git a/android/guava/src/com/google/common/util/concurrent/ExecutionSequencer.java b/android/guava/src/com/google/common/util/concurrent/ExecutionSequencer.java index 7291064bb278..f13e5671878b 100644 --- a/android/guava/src/com/google/common/util/concurrent/ExecutionSequencer.java +++ b/android/guava/src/com/google/common/util/concurrent/ExecutionSequencer.java @@ -25,12 +25,13 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.concurrent.LazyInit; import java.util.concurrent.Callable; import java.util.concurrent.Executor; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Serializes execution of tasks, somewhat like an "asynchronous {@code synchronized} block." Each @@ -55,7 +56,7 @@ * (However, cancellation can prevent an unstarted task from running.) Therefore, the * next task will wait for any running callable (or pending {@code Future} returned by an * {@code AsyncCallable}) to complete, without interrupting it (and without calling {@code - * cancel} on the {@code Future}). So beware: Even if you cancel every precededing {@code + * cancel} on the {@code Future}). So beware: Even if you cancel every preceding {@code * Future} returned by this class, the next task may still have to wait.. *
  • Once an {@code AsyncCallable} returns a {@code Future}, this class considers that task to * be "done" as soon as that {@code Future} completes in any way. Notably, a {@code @@ -84,8 +85,7 @@ * * @since 26.0 */ -@Beta -@ElementTypesAreNonnullByDefault +@J2ktIncompatible public final class ExecutionSequencer { private ExecutionSequencer() {} @@ -99,7 +99,7 @@ public static ExecutionSequencer create() { private final AtomicReference> ref = new AtomicReference<>(immediateVoidFuture()); - private ThreadConfinedTaskQueue latestTaskQueue = new ThreadConfinedTaskQueue(); + @LazyInit private ThreadConfinedTaskQueue latestTaskQueue = new ThreadConfinedTaskQueue(); /** * This object is unsafely published, but avoids problematic races by relying exclusively on the @@ -130,11 +130,13 @@ private static final class ThreadConfinedTaskQueue { * All the states where thread != currentThread are identical for our purposes, and so even * though it's racy, we don't care which of those values we get, so no need to synchronize. */ - @CheckForNull Thread thread; + @LazyInit @Nullable Thread thread; + /** Only used by the thread associated with this object */ - @CheckForNull Runnable nextTask; + @Nullable Runnable nextTask; + /** Only used by the thread associated with this object */ - @CheckForNull Executor nextExecutor; + @Nullable Executor nextExecutor; } /** @@ -252,7 +254,7 @@ public String toString() { taskFuture.cancel(false); } }; - // Adding the listener to both futures guarantees that newFuture will aways be set. Adding to + // Adding the listener to both futures guarantees that newFuture will always be set. Adding to // taskFuture guarantees completion if the callable is invoked, and adding to outputFuture // propagates cancellation if the callable has not yet been invoked. outputFuture.addListener(listener, directExecutor()); @@ -292,22 +294,22 @@ private static final class TaskNonReentrantExecutor extends AtomicReference T newProxy( T target, Class interfaceType, long timeoutDuration, TimeUnit timeoutUnit) { @@ -48,6 +51,7 @@ public T newProxy( return target; // ha ha } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override @ParametricNullness public T callWithTimeout( @@ -59,16 +63,14 @@ public T newProxy( } catch (RuntimeException e) { throw new UncheckedExecutionException(e); } catch (Exception e) { + restoreInterruptIfIsInterruptedException(e); throw new ExecutionException(e); } catch (Error e) { throw new ExecutionError(e); - } catch (Throwable e) { - // It's a non-Error, non-Exception Throwable. Such classes are usually intended to extend - // Exception, so we'll treat it like an Exception. - throw new ExecutionException(e); } } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override @ParametricNullness public T callUninterruptiblyWithTimeout( @@ -77,19 +79,16 @@ public T newProxy( } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) { checkNotNull(runnable); checkNotNull(timeoutUnit); try { runnable.run(); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception throw new UncheckedExecutionException(e); } catch (Error e) { throw new ExecutionError(e); - } catch (Throwable e) { - // It's a non-Error, non-Exception Throwable. Such classes are usually intended to extend - // Exception, so we'll treat it like a RuntimeException. - throw new UncheckedExecutionException(e); } } diff --git a/android/guava/src/com/google/common/util/concurrent/FluentFuture.java b/android/guava/src/com/google/common/util/concurrent/FluentFuture.java index 2b6ef9f557f2..a1a71c0e7f1e 100644 --- a/android/guava/src/com/google/common/util/concurrent/FluentFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/FluentFuture.java @@ -15,19 +15,22 @@ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; +import com.google.errorprone.annotations.InlineMe; +import java.time.Duration; 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 org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link ListenableFuture} that supports fluent chains of operations. For example: @@ -70,10 +73,8 @@ * * @since 23.0 */ -@Beta @DoNotMock("Use FluentFuture.from(Futures.immediate*Future) or SettableFuture") @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public abstract class FluentFuture extends GwtFluentFutureCatchingSpecialization { @@ -141,6 +142,9 @@ public final boolean cancel(boolean mayInterruptIfRunning) { * @deprecated no need to use this * @since 28.0 */ + @InlineMe( + replacement = "checkNotNull(future)", + staticImports = "com.google.common.base.Preconditions.checkNotNull") @Deprecated public static FluentFuture from(FluentFuture future) { return checkNotNull(future); @@ -183,6 +187,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { * {@code get()} throws a different kind of exception, that exception itself. * @param executor the executor that runs {@code fallback} if the input fails */ + @J2ktIncompatible @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") public final FluentFuture catching( Class exceptionType, Function fallback, Executor executor) { @@ -247,12 +252,32 @@ public final FluentFuture catching( * {@code get()} throws a different kind of exception, that exception itself. * @param executor the executor that runs {@code fallback} if the input fails */ + @J2ktIncompatible @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") public final FluentFuture catchingAsync( Class exceptionType, AsyncFunction fallback, Executor executor) { return (FluentFuture) Futures.catchingAsync(this, exceptionType, fallback, executor); } + /** + * Returns a future that delegates to this future but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified timeout expires. + * If the timeout expires, not only will the output future finish, but also the input future + * ({@code this}) will be cancelled and interrupted. + * + * @param timeout when to time out the future + * @param scheduledExecutor The executor service to enforce the timeout. + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // ScheduledExecutorService + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public final FluentFuture withTimeout( + Duration timeout, ScheduledExecutorService scheduledExecutor) { + return withTimeout(toNanosSaturated(timeout), TimeUnit.NANOSECONDS, scheduledExecutor); + } + /** * Returns a future that delegates to this future but will finish early (via a {@link * TimeoutException} wrapped in an {@link ExecutionException}) if the specified timeout expires. @@ -263,6 +288,7 @@ public final FluentFuture catchingAsync( * @param unit the time unit of the time parameter * @param scheduledExecutor The executor service to enforce the timeout. */ + @J2ktIncompatible @GwtIncompatible // ScheduledExecutorService @SuppressWarnings("GoodTime") // should accept a java.time.Duration public final FluentFuture withTimeout( diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingDeque.java b/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingDeque.java index d0d72a815918..2cdf348d12d8 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingDeque.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingDeque.java @@ -17,11 +17,12 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ForwardingDeque; import java.util.Collection; import java.util.concurrent.BlockingDeque; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. @@ -44,8 +45,8 @@ * @author Emily Soldal * @since 21.0 (since 14.0 as {@link com.google.common.collect.ForwardingBlockingDeque}) */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingBlockingDeque extends ForwardingDeque implements BlockingDeque { @@ -91,14 +92,12 @@ public E takeLast() throws InterruptedException { } @Override - @CheckForNull - public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { return delegate().pollFirst(timeout, unit); } @Override - @CheckForNull - public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E pollLast(long timeout, TimeUnit unit) throws InterruptedException { return delegate().pollLast(timeout, unit); } @@ -118,8 +117,7 @@ public E take() throws InterruptedException { } @Override - @CheckForNull - public E poll(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(long timeout, TimeUnit unit) throws InterruptedException { return delegate().poll(timeout, unit); } diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java b/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java index dc8511d94c7c..ae52c9626ddf 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingBlockingQueue.java @@ -15,12 +15,13 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ForwardingQueue; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A {@link BlockingQueue} which forwards all its method calls to another {@link BlockingQueue}. @@ -36,9 +37,8 @@ * @param the type of elements held in this collection * @since 4.0 */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingBlockingQueue extends ForwardingQueue implements BlockingQueue { @@ -48,24 +48,27 @@ protected ForwardingBlockingQueue() {} @Override protected abstract BlockingQueue delegate(); + @CanIgnoreReturnValue @Override public int drainTo(Collection c, int maxElements) { return delegate().drainTo(c, maxElements); } + @CanIgnoreReturnValue @Override public int drainTo(Collection c) { return delegate().drainTo(c); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { return delegate().offer(e, timeout, unit); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override - @CheckForNull - public E poll(long timeout, TimeUnit unit) throws InterruptedException { + public @Nullable E poll(long timeout, TimeUnit unit) throws InterruptedException { return delegate().poll(timeout, unit); } @@ -79,6 +82,7 @@ public int remainingCapacity() { return delegate().remainingCapacity(); } + @CanIgnoreReturnValue // TODO(kak): consider removing this @Override public E take() throws InterruptedException { return delegate().take(); diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingCondition.java b/android/guava/src/com/google/common/util/concurrent/ForwardingCondition.java index d17f98cf2aa1..5393dbdb615c 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingCondition.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingCondition.java @@ -14,12 +14,13 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.J2ktIncompatible; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; /** Forwarding wrapper around a {@code Condition}. */ -@ElementTypesAreNonnullByDefault +@J2ktIncompatible abstract class ForwardingCondition implements Condition { abstract Condition delegate(); diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java b/android/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java index ed78b8685694..92a3a72fa233 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingExecutorService.java @@ -15,8 +15,10 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ForwardingObject; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.CheckReturnValue; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; @@ -25,19 +27,22 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An executor service which forwards all its method calls to another executor service. Subclasses * should override one or more methods to modify the behavior of the backing executor service as * desired per the decorator pattern. * + *

    {@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingExecutorService}. + * * @author Kurt Alfred Kluever * @since 10.0 */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingExecutorService extends ForwardingObject implements ExecutorService { /** Constructor for use by subclasses. */ @@ -46,6 +51,7 @@ protected ForwardingExecutorService() {} @Override protected abstract ExecutorService delegate(); + @CheckReturnValue @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return delegate().awaitTermination(timeout, unit); @@ -93,6 +99,7 @@ public void shutdown() { } @Override + @CanIgnoreReturnValue public List shutdownNow() { return delegate().shutdownNow(); } diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingFluentFuture.java b/android/guava/src/com/google/common/util/concurrent/ForwardingFluentFuture.java index cb779c34ed4b..52fc1b039dfa 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingFluentFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingFluentFuture.java @@ -21,7 +21,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * {@link FluentFuture} that forwards all calls to a delegate. @@ -34,7 +34,6 @@ * forwards to that future and adds the desired methods. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class ForwardingFluentFuture extends FluentFuture { private final ListenableFuture delegate; diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingFuture.java b/android/guava/src/com/google/common/util/concurrent/ForwardingFuture.java index 47002b03a31b..b8ea27ba845c 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingFuture.java @@ -22,7 +22,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link Future} which forwards all its method calls to another future. Subclasses should @@ -34,9 +34,7 @@ * @author Sven Mawson * @since 1.0 */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingFuture extends ForwardingObject implements Future { /** Constructor for use by subclasses. */ @@ -46,6 +44,7 @@ protected ForwardingFuture() {} protected abstract Future delegate(); @Override + @CanIgnoreReturnValue public boolean cancel(boolean mayInterruptIfRunning) { return delegate().cancel(mayInterruptIfRunning); } @@ -61,12 +60,14 @@ public boolean isDone() { } @Override + @CanIgnoreReturnValue @ParametricNullness public V get() throws InterruptedException, ExecutionException { return delegate().get(); } @Override + @CanIgnoreReturnValue @ParametricNullness public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java b/android/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java index 386809194b84..d204518bb048 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingListenableFuture.java @@ -16,9 +16,8 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.Executor; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link ListenableFuture} which forwards all its method calls to another future. Subclasses @@ -30,9 +29,7 @@ * @author Shardul Deo * @since 4.0 */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. @GwtCompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingListenableFuture extends ForwardingFuture implements ListenableFuture { diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java b/android/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java index fe25b867af4c..a362379426e6 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingListeningExecutorService.java @@ -15,9 +15,9 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.Callable; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A listening executor service which forwards all its method calls to another listening executor @@ -25,12 +25,15 @@ * executor service as desired per the decorator pattern. * + *

    {@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingListeningExecutorService}. + * * @author Isaac Shum * @since 10.0 */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class ForwardingListeningExecutorService extends ForwardingExecutorService implements ListeningExecutorService { /** Constructor for use by subclasses. */ diff --git a/android/guava/src/com/google/common/util/concurrent/ForwardingLock.java b/android/guava/src/com/google/common/util/concurrent/ForwardingLock.java index db2026ab414c..7ff05340ebc0 100644 --- a/android/guava/src/com/google/common/util/concurrent/ForwardingLock.java +++ b/android/guava/src/com/google/common/util/concurrent/ForwardingLock.java @@ -14,12 +14,13 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** Forwarding wrapper around a {@code Lock}. */ -@ElementTypesAreNonnullByDefault +@J2ktIncompatible abstract class ForwardingLock implements Lock { abstract Lock delegate(); diff --git a/android/guava/src/com/google/common/util/concurrent/FutureCallback.java b/android/guava/src/com/google/common/util/concurrent/FutureCallback.java index f5684e7c907a..35033faf8664 100644 --- a/android/guava/src/com/google/common/util/concurrent/FutureCallback.java +++ b/android/guava/src/com/google/common/util/concurrent/FutureCallback.java @@ -17,7 +17,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A callback for accepting the results of a {@link java.util.concurrent.Future} computation @@ -29,7 +29,6 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public interface FutureCallback { /** Invoked with the result of the {@code Future} computation when it is successful. */ void onSuccess(@ParametricNullness V result); diff --git a/android/guava/src/com/google/common/util/concurrent/Futures.java b/android/guava/src/com/google/common/util/concurrent/Futures.java index e6bf7a7c12fb..1dd80fe10bf0 100644 --- a/android/guava/src/com/google/common/util/concurrent/Futures.java +++ b/android/guava/src/com/google/common/util/concurrent/Futures.java @@ -16,13 +16,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -33,6 +34,9 @@ import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; import com.google.common.util.concurrent.internal.InternalFutures; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; +import java.time.Duration; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; @@ -45,8 +49,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Static utility methods pertaining to the {@link Future} interface. @@ -73,7 +76,6 @@ * @since 1.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Futures extends GwtFuturesCatchingSpecialization { // A note on memory visibility. @@ -104,7 +106,7 @@ public final class Futures extends GwtFuturesCatchingSpecialization { // (hypothetical) unsafe read by our caller. Note: adding 'volatile' does not fix this issue, // it would just add an edge such that if done() observed non-null, then it would also // definitely observe all earlier writes, but we still have no guarantee that done() would see - // the inital write (just stronger guarantees if it does). + // the initial write (just stronger guarantees if it does). // // See: http://cs.oswego.edu/pipermail/concurrency-interest/2015-January/013800.html // For a (long) discussion about this specific issue and the general futility of life. @@ -160,7 +162,7 @@ private Futures() {} public static ListenableFuture immediateFailedFuture( Throwable throwable) { checkNotNull(throwable); - return new ImmediateFailedFuture(throwable); + return new ImmediateFailedFuture<>(throwable); } /** @@ -169,8 +171,13 @@ private Futures() {} * * @since 14.0 */ + @SuppressWarnings("unchecked") // ImmediateCancelledFuture can work with any type public static ListenableFuture immediateCancelledFuture() { - return new ImmediateCancelledFuture(); + ListenableFuture instance = ImmediateCancelledFuture.INSTANCE; + if (instance != null) { + return (ListenableFuture) instance; + } + return new ImmediateCancelledFuture<>(); } /** @@ -179,7 +186,6 @@ private Futures() {} * @throws RejectedExecutionException if the task cannot be scheduled for execution * @since 28.2 */ - @Beta public static ListenableFuture submit( Callable callable, Executor executor) { TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); @@ -194,7 +200,6 @@ private Futures() {} * @throws RejectedExecutionException if the task cannot be scheduled for execution * @since 28.2 */ - @Beta public static ListenableFuture<@Nullable Void> submit(Runnable runnable, Executor executor) { TrustedListenableFutureTask<@Nullable Void> task = TrustedListenableFutureTask.create(runnable, null); @@ -208,7 +213,6 @@ private Futures() {} * @throws RejectedExecutionException if the task cannot be scheduled for execution * @since 23.0 */ - @Beta public static ListenableFuture submitAsync( AsyncCallable callable, Executor executor) { TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); @@ -216,31 +220,44 @@ private Futures() {} return task; } + /** + * Schedules {@code callable} on the specified {@code executor}, returning a {@code Future}. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + // TODO(cpovirk): Return ListenableScheduledFuture? + public static ListenableFuture scheduleAsync( + AsyncCallable callable, Duration delay, ScheduledExecutorService executorService) { + return scheduleAsync(callable, toNanosSaturated(delay), TimeUnit.NANOSECONDS, executorService); + } + /** * Schedules {@code callable} on the specified {@code executor}, returning a {@code Future}. * * @throws RejectedExecutionException if the task cannot be scheduled for execution * @since 23.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // java.util.concurrent.ScheduledExecutorService @SuppressWarnings("GoodTime") // should accept a java.time.Duration + // TODO(cpovirk): Return ListenableScheduledFuture? public static ListenableFuture scheduleAsync( AsyncCallable callable, long delay, TimeUnit timeUnit, ScheduledExecutorService executorService) { TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); - final Future scheduled = executorService.schedule(task, delay, timeUnit); - task.addListener( - new Runnable() { - @Override - public void run() { - // Don't want to interrupt twice - scheduled.cancel(false); - } - }, - directExecutor()); + Future scheduled = executorService.schedule(task, delay, timeUnit); + /* + * Even when the user interrupts the task, we pass `false` to `cancel` so that we don't + * interrupt a second time after the interruption performed by TrustedListenableFutureTask. + */ + task.addListener(() -> scheduled.cancel(false), directExecutor()); return task; } @@ -280,7 +297,7 @@ public void run() { * @param executor the executor that runs {@code fallback} if {@code input} fails * @since 19.0 */ - @Beta + @J2ktIncompatible @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") public static ListenableFuture catching( ListenableFuture input, @@ -345,14 +362,34 @@ public void run() { * @param executor the executor that runs {@code fallback} if {@code input} fails * @since 19.0 (similar functionality in 14.0 as {@code withFallback}) */ - @Beta + @J2ktIncompatible @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") public static ListenableFuture catchingAsync( ListenableFuture input, Class exceptionType, AsyncFunction fallback, Executor executor) { - return AbstractCatchingFuture.create(input, exceptionType, fallback, executor); + return AbstractCatchingFuture.createAsync(input, exceptionType, fallback, executor); + } + + /** + * Returns a future that delegates to another but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified duration expires. + * + *

    The delegate future is interrupted and cancelled if it times out. + * + * @param delegate The future to delegate to. + * @param time when to time out the future + * @param scheduledExecutor The executor service to enforce the timeout. + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static ListenableFuture withTimeout( + ListenableFuture delegate, Duration time, ScheduledExecutorService scheduledExecutor) { + return withTimeout(delegate, toNanosSaturated(time), TimeUnit.NANOSECONDS, scheduledExecutor); } /** @@ -362,12 +399,12 @@ public void run() { *

    The delegate future is interrupted and cancelled if it times out. * * @param delegate The future to delegate to. - * @param time when to timeout the future + * @param time when to time out the future * @param unit the time unit of the time parameter * @param scheduledExecutor The executor service to enforce the timeout. * @since 19.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // java.util.concurrent.ScheduledExecutorService @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static ListenableFuture withTimeout( @@ -413,13 +450,12 @@ public void run() { * input's failure (if not) * @since 19.0 (in 11.0 as {@code transform}) */ - @Beta public static ListenableFuture transformAsync( ListenableFuture input, AsyncFunction function, Executor executor) { - return AbstractTransformFuture.create(input, function, executor); + return AbstractTransformFuture.createAsync(input, function, executor); } /** @@ -451,7 +487,6 @@ ListenableFuture transformAsync( * @return A future that holds result of the transformation. * @since 9.0 (in 2.0 as {@code compose}) */ - @Beta public static ListenableFuture transform( ListenableFuture input, Function function, Executor executor) { @@ -478,10 +513,10 @@ ListenableFuture transform( * @return A future that returns the result of the transformation. * @since 10.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // TODO public static Future lazyTransform( - final Future input, final Function function) { + Future input, Function function) { checkNotNull(input); checkNotNull(function); return new Future() { @@ -516,6 +551,7 @@ private O applyTransformation(I input) throws ExecutionException { try { return function.apply(input); } catch (Throwable t) { + // Any Exception is either a RuntimeException or sneaky checked exception. throw new ExecutionException(t); } } @@ -538,7 +574,6 @@ private O applyTransformation(I input) throws ExecutionException { * @return a future that provides a list of the results of the component futures * @since 10.0 */ - @Beta @SafeVarargs public static ListenableFuture> allAsList( ListenableFuture... futures) { @@ -566,7 +601,6 @@ private O applyTransformation(I input) throws ExecutionException { * @return a future that provides a list of the results of the component futures * @since 10.0 */ - @Beta public static ListenableFuture> allAsList( Iterable> futures) { ListenableFuture> nullable = @@ -585,11 +619,10 @@ private O applyTransformation(I input) throws ExecutionException { * * @since 20.0 */ - @Beta @SafeVarargs public static FutureCombiner whenAllComplete( ListenableFuture... futures) { - return new FutureCombiner(false, ImmutableList.copyOf(futures)); + return new FutureCombiner<>(false, ImmutableList.copyOf(futures)); } /** @@ -600,10 +633,9 @@ private O applyTransformation(I input) throws ExecutionException { * * @since 20.0 */ - @Beta public static FutureCombiner whenAllComplete( Iterable> futures) { - return new FutureCombiner(false, ImmutableList.copyOf(futures)); + return new FutureCombiner<>(false, ImmutableList.copyOf(futures)); } /** @@ -613,11 +645,10 @@ private O applyTransformation(I input) throws ExecutionException { * * @since 20.0 */ - @Beta @SafeVarargs public static FutureCombiner whenAllSucceed( ListenableFuture... futures) { - return new FutureCombiner(true, ImmutableList.copyOf(futures)); + return new FutureCombiner<>(true, ImmutableList.copyOf(futures)); } /** @@ -627,10 +658,9 @@ private O applyTransformation(I input) throws ExecutionException { * * @since 20.0 */ - @Beta public static FutureCombiner whenAllSucceed( Iterable> futures) { - return new FutureCombiner(true, ImmutableList.copyOf(futures)); + return new FutureCombiner<>(true, ImmutableList.copyOf(futures)); } /** @@ -659,8 +689,6 @@ private O applyTransformation(I input) throws ExecutionException { * * @since 20.0 */ - @Beta - @CanIgnoreReturnValue // TODO(cpovirk): Consider removing, especially if we provide run(Runnable) @GwtCompatible public static final class FutureCombiner { private final boolean allMustSucceed; @@ -685,10 +713,16 @@ private FutureCombiner( * ExecutionException} that gets thrown by the returned combined future. * *

    Canceling this future will attempt to cancel all the component futures. + * + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even if you don't care about the value of the future, you should + * typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. */ public ListenableFuture callAsync( AsyncCallable combiner, Executor executor) { - return new CombinedFuture(futures, allMustSucceed, executor, combiner); + return new CombinedFuture<>(futures, allMustSucceed, executor, combiner); } /** @@ -704,11 +738,16 @@ private FutureCombiner( * ExecutionException} that gets thrown by the returned combined future. * *

    Canceling this future will attempt to cancel all the component futures. + * + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even if you don't care about the value of the future, you should + * typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. */ - @CanIgnoreReturnValue // TODO(cpovirk): Remove this public ListenableFuture call( Callable combiner, Executor executor) { - return new CombinedFuture(futures, allMustSucceed, executor, combiner); + return new CombinedFuture<>(futures, allMustSucceed, executor, combiner); } /** @@ -721,13 +760,17 @@ private FutureCombiner( *

    Canceling this Future will attempt to cancel all the component futures. * * @since 23.6 + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even though the future never produces a value other than {@code null}, + * you should typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. */ - public ListenableFuture run(final Runnable combiner, Executor executor) { + public ListenableFuture run(Runnable combiner, Executor executor) { return call( new Callable<@Nullable Void>() { @Override - @CheckForNull - public Void call() throws Exception { + public @Nullable Void call() throws Exception { combiner.run(); return null; } @@ -743,7 +786,6 @@ public Void call() throws Exception { * * @since 15.0 */ - @Beta public static ListenableFuture nonCancellationPropagating( ListenableFuture future) { if (future.isDone()) { @@ -757,9 +799,9 @@ public Void call() throws Exception { /** A wrapped future that does not propagate cancellation to its delegate. */ private static final class NonCancellationPropagatingFuture extends AbstractFuture.TrustedFuture implements Runnable { - @CheckForNull private ListenableFuture delegate; + @LazyInit private @Nullable ListenableFuture delegate; - NonCancellationPropagatingFuture(final ListenableFuture delegate) { + NonCancellationPropagatingFuture(ListenableFuture delegate) { this.delegate = delegate; } @@ -767,16 +809,15 @@ private static final class NonCancellationPropagatingFuture localDelegate = delegate; + @RetainedLocalRef ListenableFuture localDelegate = delegate; if (localDelegate != null) { setFuture(localDelegate); } } @Override - @CheckForNull - protected String pendingToString() { - ListenableFuture localDelegate = delegate; + protected @Nullable String pendingToString() { + @RetainedLocalRef ListenableFuture localDelegate = delegate; if (localDelegate != null) { return "delegate=[" + localDelegate + "]"; } @@ -807,22 +848,21 @@ protected void afterDone() { * @return a future that provides a list of the results of the component futures * @since 10.0 */ - /* - * Another way to express this signature would be to bound by @NonNull and accept LF. That might be better: There's currently no difference between the outputs users - * get when calling this with and calling it with <@Nullable Foo>. The only difference is - * that calling it with won't work when an input Future has a @Nullable type. So why even - * make that error possible by giving callers the choice? - * - * On the other hand, the current signature is consistent with the similar allAsList method. And - * eventually this method may go away entirely in favor of an API like - * whenAllComplete().collectSuccesses(). That API would have a signature more like the current - * one. - */ - @Beta @SafeVarargs public static ListenableFuture> successfulAsList( ListenableFuture... futures) { + /* + * Another way to express this signature would be to bound by @NonNull and accept + * LF. That might be better: There's currently no difference between the + * outputs users get when calling this with and calling it with <@Nullable Foo>. The only + * difference is that calling it with won't work when an input Future has a @Nullable + * type. So why even make that error possible by giving callers the choice? + * + * On the other hand, the current signature is consistent with the similar allAsList method. And + * eventually this method may go away entirely in favor of an API like + * whenAllComplete().collectSuccesses(). That API would have a signature more like the current + * one. + */ return new ListFuture(ImmutableList.copyOf(futures), false); } @@ -844,7 +884,6 @@ protected void afterDone() { * @return a future that provides a list of the results of the component futures * @since 10.0 */ - @Beta public static ListenableFuture> successfulAsList( Iterable> futures) { return new ListFuture(ImmutableList.copyOf(futures), false); @@ -871,28 +910,20 @@ protected void afterDone() { * * @since 17.0 */ - @Beta public static ImmutableList> inCompletionOrder( Iterable> futures) { ListenableFuture[] copy = gwtCompatibleToArray(futures); - final InCompletionOrderState state = new InCompletionOrderState<>(copy); + InCompletionOrderState state = new InCompletionOrderState<>(copy); ImmutableList.Builder> delegatesBuilder = ImmutableList.builderWithExpectedSize(copy.length); for (int i = 0; i < copy.length; i++) { delegatesBuilder.add(new InCompletionOrderFuture(state)); } - final ImmutableList> delegates = delegatesBuilder.build(); + ImmutableList> delegates = delegatesBuilder.build(); for (int i = 0; i < copy.length; i++) { - final int localI = i; - copy[i].addListener( - new Runnable() { - @Override - public void run() { - state.recordInputCompletion(delegates, localI); - } - }, - directExecutor()); + int localI = i; + copy[i].addListener(() -> state.recordInputCompletion(delegates, localI), directExecutor()); } @SuppressWarnings("unchecked") @@ -904,7 +935,7 @@ public void run() { @SuppressWarnings("unchecked") private static ListenableFuture[] gwtCompatibleToArray( Iterable> futures) { - final Collection> collection; + Collection> collection; if (futures instanceof Collection) { collection = (Collection>) futures; } else { @@ -918,7 +949,7 @@ public void run() { // cancelled. private static final class InCompletionOrderFuture extends AbstractFuture { - @CheckForNull private InCompletionOrderState state; + private @Nullable InCompletionOrderState state; private InCompletionOrderFuture(InCompletionOrderState state) { this.state = state; @@ -948,8 +979,7 @@ protected void afterDone() { } @Override - @CheckForNull - protected String pendingToString() { + protected @Nullable String pendingToString() { InCompletionOrderState localState = state; if (localState != null) { // Don't print the actual array! We don't want inCompletionOrder(list).toString() to have @@ -1013,6 +1043,7 @@ private void recordInputCompletion( delegateIndex = delegates.size(); } + @SuppressWarnings("Interruption") // We are propagating an interrupt from a caller. private void recordCompletion() { if (incompleteOutputCount.decrementAndGet() == 0 && wasCancelled) { for (ListenableFuture toCancel : inputFutures) { @@ -1066,9 +1097,7 @@ private void recordCompletion() { * @since 10.0 */ public static void addCallback( - final ListenableFuture future, - final FutureCallback callback, - Executor executor) { + ListenableFuture future, FutureCallback callback, Executor executor) { Preconditions.checkNotNull(callback); future.addListener(new CallbackListener(future, callback), executor); } @@ -1093,13 +1122,14 @@ public void run() { return; } } - final V value; + V value; try { value = getDone(future); } catch (ExecutionException e) { callback.onFailure(e.getCause()); return; - } catch (RuntimeException | Error e) { + } catch (Throwable e) { + // Any Exception is either a RuntimeException or sneaky checked exception. callback.onFailure(e); return; } @@ -1175,10 +1205,10 @@ public String toString() { * *

    Instances of {@code exceptionClass} are created by choosing an arbitrary public constructor * that accepts zero or more arguments, all of type {@code String} or {@code Throwable} - * (preferring constructors with at least one {@code String}) and calling the constructor via - * reflection. If the exception did not already have a cause, one is set by calling {@link - * Throwable#initCause(Throwable)} on it. If no such constructor exists, an {@code - * IllegalArgumentException} is thrown. + * (preferring constructors with at least one {@code String}, then preferring constructors with at + * least one {@code Throwable}) and calling the constructor via reflection. If the exception did + * not already have a cause, one is set by calling {@link Throwable#initCause(Throwable)} on it. + * If no such constructor exists, an {@code IllegalArgumentException} is thrown. * * @throws X if {@code get} throws any checked exception except for an {@code ExecutionException} * whose cause is not itself a checked exception @@ -1191,8 +1221,8 @@ public String toString() { * does not have a suitable constructor * @since 19.0 (in 10.0 as {@code get}) */ - @Beta @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // reflection @ParametricNullness public static V getChecked( @@ -1200,6 +1230,60 @@ public String toString() { return FuturesGetChecked.getChecked(future, exceptionClass); } + /** + * Returns the result of {@link Future#get(long, TimeUnit)}, converting most exceptions to a new + * instance of the given checked exception type. This reduces boilerplate for a common use of + * {@code Future} in which it is unnecessary to programmatically distinguish between exception + * types or to extract other information from the exception instance. + * + *

    Exceptions from {@code Future.get} are treated as follows: + * + *

      + *
    • Any {@link ExecutionException} has its cause wrapped in an {@code X} if the cause + * is a checked exception, an {@link UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an {@code Error}. + *
    • Any {@link InterruptedException} is wrapped in an {@code X} (after restoring the + * interrupt). + *
    • Any {@link TimeoutException} is wrapped in an {@code X}. + *
    • Any {@link CancellationException} is propagated untouched, as is any other {@link + * RuntimeException} (though {@code get} implementations are discouraged from throwing such + * exceptions). + *
    + * + *

    The overall principle is to continue to treat every checked exception as a checked + * exception, every unchecked exception as an unchecked exception, and every error as an error. In + * addition, the cause of any {@code ExecutionException} is wrapped in order to ensure that the + * new stack trace matches that of the current thread. + * + *

    Instances of {@code exceptionClass} are created by choosing an arbitrary public constructor + * that accepts zero or more arguments, all of type {@code String} or {@code Throwable} + * (preferring constructors with at least one {@code String}, then preferring constructors with at + * least one {@code Throwable}) and calling the constructor via reflection. If the exception did + * not already have a cause, one is set by calling {@link Throwable#initCause(Throwable)} on it. + * If no such constructor exists, an {@code IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code ExecutionException} + * whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code ExecutionException} with a + * {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} with an {@code + * Error} as its cause + * @throws CancellationException if {@code get} throws a {@code CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code RuntimeException} or + * does not have a suitable constructor + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @CanIgnoreReturnValue + @J2ktIncompatible + @GwtIncompatible // reflection + @ParametricNullness + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static V getChecked( + Future future, Class exceptionClass, Duration timeout) throws X { + return getChecked(future, exceptionClass, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Returns the result of {@link Future#get(long, TimeUnit)}, converting most exceptions to a new * instance of the given checked exception type. This reduces boilerplate for a common use of @@ -1243,8 +1327,8 @@ public String toString() { * does not have a suitable constructor * @since 19.0 (in 10.0 as {@code get} and with different parameter order) */ - @Beta @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // reflection @SuppressWarnings("GoodTime") // should accept a java.time.Duration @ParametricNullness @@ -1293,22 +1377,17 @@ public String toString() { checkNotNull(future); try { return getUninterruptibly(future); - } catch (ExecutionException e) { - wrapAndThrowUnchecked(e.getCause()); - throw new AssertionError(); - } - } - - private static void wrapAndThrowUnchecked(Throwable cause) { - if (cause instanceof Error) { - throw new ExecutionError((Error) cause); + } catch (ExecutionException wrapper) { + if (wrapper.getCause() instanceof Error) { + throw new ExecutionError((Error) wrapper.getCause()); + } + /* + * It's an Exception. (Or it's a non-Error, non-Exception Throwable. From my survey of such + * classes, I believe that most users intended to extend Exception, so we'll treat it like an + * Exception.) + */ + throw new UncheckedExecutionException(wrapper.getCause()); } - /* - * It's an Exception. (Or it's a non-Error, non-Exception Throwable. From my survey of such - * classes, I believe that most users intended to extend Exception, so we'll treat it like an - * Exception.) - */ - throw new UncheckedExecutionException(cause); } /* diff --git a/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java b/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java index 04564f3a0770..4f65cbda5bb0 100644 --- a/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java +++ b/android/guava/src/com/google/common/util/concurrent/FuturesGetChecked.java @@ -19,8 +19,8 @@ import static java.util.Arrays.asList; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; import com.google.common.collect.Ordering; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.ref.WeakReference; @@ -34,12 +34,11 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Static methods used to implement {@link Futures#getChecked(Future, Class)}. */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class FuturesGetChecked { @CanIgnoreReturnValue @ParametricNullness @@ -182,7 +181,7 @@ private static boolean hasConstructorUsableByGetChecked( try { Exception unused = newWithCause(exceptionClass, new Exception()); return true; - } catch (Exception e) { + } catch (Throwable t) { // sneaky checked exception return false; } } @@ -191,7 +190,7 @@ private static X newWithCause(Class exceptionClass, Thr // getConstructors() guarantees this as long as we don't modify the array. @SuppressWarnings({"unchecked", "rawtypes"}) List> constructors = (List) Arrays.asList(exceptionClass.getConstructors()); - for (Constructor constructor : preferringStrings(constructors)) { + for (Constructor constructor : preferringStringsThenThrowables(constructors)) { X instance = newFromConstructor(constructor, cause); if (instance != null) { if (instance.getCause() == null) { @@ -207,24 +206,24 @@ private static X newWithCause(Class exceptionClass, Thr cause); } - private static List> preferringStrings( + private static List> preferringStringsThenThrowables( List> constructors) { - return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); + return WITH_STRING_PARAM_THEN_WITH_THROWABLE_PARAM.sortedCopy(constructors); } - private static final Ordering> WITH_STRING_PARAM_FIRST = + // TODO: b/296487962 - Consider defining a total order over constructors. + private static final Ordering>> ORDERING_BY_CONSTRUCTOR_PARAMETER_LIST = Ordering.natural() - .onResultOf( - new Function, Boolean>() { - @Override - public Boolean apply(Constructor input) { - return asList(input.getParameterTypes()).contains(String.class); - } - }) + .onResultOf((List> params) -> params.contains(String.class)) + .compound( + Ordering.natural() + .onResultOf((List> params) -> params.contains(Throwable.class))) .reverse(); + private static final Ordering> WITH_STRING_PARAM_THEN_WITH_THROWABLE_PARAM = + ORDERING_BY_CONSTRUCTOR_PARAMETER_LIST.onResultOf( + constructor -> asList(constructor.getParameterTypes())); - @CheckForNull - private static X newFromConstructor(Constructor constructor, Throwable cause) { + private static @Nullable X newFromConstructor(Constructor constructor, Throwable cause) { Class[] paramTypes = constructor.getParameterTypes(); Object[] params = new Object[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { diff --git a/android/guava/src/com/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java b/android/guava/src/com/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java index d29a673dd854..e21f556c266b 100644 --- a/android/guava/src/com/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java +++ b/android/guava/src/com/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java @@ -15,7 +15,8 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; -import org.checkerframework.checker.nullness.qual.Nullable; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Hidden superclass of {@link FluentFuture} that provides us a place to declare special GWT @@ -23,7 +24,7 @@ * FluentFuture.catching} family of methods. Those versions have slightly different signatures. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@J2ktIncompatible // Super-sourced abstract class GwtFluentFutureCatchingSpecialization extends AbstractFuture { /* diff --git a/android/guava/src/com/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java b/android/guava/src/com/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java index 95131cec33f6..f6b210ca3466 100644 --- a/android/guava/src/com/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java +++ b/android/guava/src/com/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java @@ -15,6 +15,7 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.J2ktIncompatible; /** * Hidden superclass of {@link Futures} that provides us a place to declare special GWT versions of @@ -23,7 +24,7 @@ * different signatures. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault +@J2ktIncompatible // Super-sourced abstract class GwtFuturesCatchingSpecialization { /* * This server copy of the class is empty. The corresponding GWT copy contains alternative diff --git a/android/guava/src/com/google/common/util/concurrent/IgnoreJRERequirement.java b/android/guava/src/com/google/common/util/concurrent/IgnoreJRERequirement.java new file mode 100644 index 000000000000..b7cababe8921 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/IgnoreJRERequirement.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Target; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

    Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@interface IgnoreJRERequirement {} diff --git a/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java b/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java index 81912f5d814d..fd1aaed0f24a 100644 --- a/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ImmediateFuture.java @@ -22,17 +22,15 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.util.logging.Logger; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** Implementation of {@link Futures#immediateFuture}. */ @GwtCompatible -@ElementTypesAreNonnullByDefault // TODO(cpovirk): Make this final (but that may break Mockito spy calls). class ImmediateFuture implements ListenableFuture { static final ListenableFuture NULL = new ImmediateFuture<@Nullable Object>(null); - private static final Logger log = Logger.getLogger(ImmediateFuture.class.getName()); + private static final LazyLogger log = new LazyLogger(ImmediateFuture.class); @ParametricNullness private final V value; @@ -41,18 +39,23 @@ class ImmediateFuture implements ListenableFuture } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public void addListener(Runnable listener, Executor executor) { checkNotNull(listener, "Runnable was null."); checkNotNull(executor, "Executor was null."); try { executor.execute(listener); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // ListenableFuture's contract is that it will not throw unchecked exceptions, so log the bad // runnable and/or executor and swallow it. - log.log( - Level.SEVERE, - "RuntimeException while executing runnable " + listener + " with executor " + executor, - e); + log.get() + .log( + Level.SEVERE, + "RuntimeException while executing runnable " + + listener + + " with executor " + + executor, + e); } } @@ -98,6 +101,9 @@ static final class ImmediateFailedFuture extends Tru } static final class ImmediateCancelledFuture extends TrustedFuture { + static final @Nullable ImmediateCancelledFuture INSTANCE = + AbstractFuture.GENERATE_CANCELLATION_CAUSES ? null : new ImmediateCancelledFuture<>(); + ImmediateCancelledFuture() { cancel(false); } diff --git a/android/guava/src/com/google/common/util/concurrent/Internal.java b/android/guava/src/com/google/common/util/concurrent/Internal.java new file mode 100644 index 000000000000..782a20410920 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/Internal.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import java.time.Duration; + +/** This class is for {@code com.google.common.util.concurrent} use only! */ +@J2ktIncompatible +@GwtIncompatible // java.time.Duration +@SuppressWarnings("Java7ApiChecker") +@IgnoreJRERequirement // We use this method only from within APIs that require a Duration. +final class Internal { + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

    Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + static long toNanosSaturated(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } + + private Internal() {} +} diff --git a/android/guava/src/com/google/common/util/concurrent/InterruptibleTask.java b/android/guava/src/com/google/common/util/concurrent/InterruptibleTask.java index bcec007303bd..9191436088ab 100644 --- a/android/guava/src/com/google/common/util/concurrent/InterruptibleTask.java +++ b/android/guava/src/com/google/common/util/concurrent/InterruptibleTask.java @@ -15,6 +15,7 @@ package com.google.common.util.concurrent; import static com.google.common.util.concurrent.NullnessCasts.uncheckedCastNullableTToT; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; @@ -22,11 +23,10 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.AbstractOwnableSynchronizer; import java.util.concurrent.locks.LockSupport; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; @GwtCompatible(emulated = true) @ReflectionSupport(value = ReflectionSupport.Level.FULL) -@ElementTypesAreNonnullByDefault // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. // Since this class only needs CAS on one field, we can avoid this bug by extending AtomicReference @@ -45,6 +45,7 @@ private static final class DoNothingRunnable implements Runnable { @Override public void run() {} } + // The thread executing the task publishes itself to the superclass' reference and the thread // interrupting sets DONE when it has finished interrupting. private static final Runnable DONE = new DoNothingRunnable(); @@ -52,7 +53,6 @@ public void run() {} // Why 1000? WHY NOT! private static final int MAX_BUSY_WAIT_SPINS = 1000; - @SuppressWarnings("ThreadPriorityCheck") // The cow told me to @Override public final void run() { /* @@ -74,6 +74,7 @@ public final void run() { result = runInterruptibly(); } } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); error = t; } finally { // Attempt to set the task as done so that further attempts to interrupt will fail. @@ -91,6 +92,10 @@ public final void run() { } } + @SuppressWarnings({ + "Interruption", // We are restoring an interrupt on this thread. + "ThreadPriorityCheck", // TODO: b/175898629 - Consider onSpinWait. + }) private void waitForInterrupt(Thread currentThread) { /* * If someone called cancel(true), it is possible that the interrupted bit hasn't been set yet. @@ -186,6 +191,7 @@ private void waitForInterrupt(Thread currentThread) { * Interrupts the running task. Because this internally calls {@link Thread#interrupt()} which can * in turn invoke arbitrary code it is not safe to call while holding a lock. */ + @SuppressWarnings("Interruption") // We are implementing a user-requested interrupt. final void interruptTask() { // Since the Thread is replaced by DONE before run() invokes listeners or returns, if we succeed // in this CAS, there's no risk of interrupting the wrong thread or interrupting a thread that @@ -231,6 +237,11 @@ private void setOwner(Thread thread) { super.setExclusiveOwnerThread(thread); } + @VisibleForTesting + @Nullable Thread getOwner() { + return super.getExclusiveOwnerThread(); + } + @Override public String toString() { return task.toString(); diff --git a/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java b/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java index 0b0db4573ae2..611c91e6bf20 100644 --- a/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java +++ b/android/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -17,26 +17,28 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Utilities necessary for working with libraries that supply plain {@link Future} instances. Note * that, whenever possible, it is strongly preferred to modify those libraries to return {@code * ListenableFuture} directly. * + *

    For interoperability between {@code ListenableFuture} and {@code CompletableFuture}, + * consider Future Converter. + * * @author Sven Mawson * @since 10.0 (replacing {@code Futures.makeListenable}, which existed in 1.0) */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class JdkFutureAdapters { /** * Assigns a thread to the given {@link Future} to provide {@link ListenableFuture} functionality. @@ -158,9 +160,11 @@ public void addListener(Runnable listener, Executor exec) { * to return a proper ListenableFuture instead of using listenInPoolThread. */ getUninterruptibly(delegate); - } catch (Throwable e) { - // ExecutionException / CancellationException / RuntimeException / Error + } catch (Throwable t) { + // (including CancellationException and sneaky checked exception) // The task is presumably done, run the listeners. + // TODO(cpovirk): Do *something* in case of Error (and maybe + // non-CancellationException, non-ExecutionException exceptions)? } executionList.execute(); }); diff --git a/android/guava/src/com/google/common/util/concurrent/LazyLogger.java b/android/guava/src/com/google/common/util/concurrent/LazyLogger.java new file mode 100644 index 000000000000..0b79ff43d068 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/LazyLogger.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; + +/** A holder for a {@link Logger} that is initialized only when requested. */ +@GwtCompatible +final class LazyLogger { + private final Object lock = new Object(); + + private final String loggerName; + private volatile @Nullable Logger logger; + + LazyLogger(Class ownerOfLogger) { + this.loggerName = ownerOfLogger.getName(); + } + + Logger get() { + /* + * We use double-checked locking. We could the try racy single-check idiom, but that would + * depend on Logger to not contain mutable state. + * + * We could use Suppliers.memoizingSupplier here, but I micro-optimized to this implementation + * to avoid the extra class for the lambda (and maybe more for memoizingSupplier itself) and the + * indirection. + * + * One thing to *avoid* is a change to make each Logger user use memoizingSupplier directly: + * That may introduce an extra class for each lambda (currently a dozen). + */ + Logger local = logger; + if (local != null) { + return local; + } + synchronized (lock) { + local = logger; + if (local != null) { + return local; + } + return logger = Logger.getLogger(loggerName); + } + } +} diff --git a/android/guava/src/com/google/common/util/concurrent/ListenableFuture.java b/android/guava/src/com/google/common/util/concurrent/ListenableFuture.java index a8d9dd4de5f9..c3d84c6200f7 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListenableFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ListenableFuture.java @@ -18,7 +18,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A {@link Future} that accepts completion listeners. Each listener has an associated executor, and @@ -36,10 +37,11 @@ * *

    The main purpose of {@code ListenableFuture} is to help you chain together a graph of * asynchronous operations. You can chain them together manually with calls to methods like {@link - * Futures#transform(ListenableFuture, com.google.common.base.Function, Executor) - * Futures.transform}, but you will often find it easier to use a framework. Frameworks automate the - * process, often adding features like monitoring, debugging, and cancellation. Examples of - * frameworks include: + * Futures#transform(ListenableFuture, com.google.common.base.Function, Executor) Futures.transform} + * (or {@link FluentFuture#transform(com.google.common.base.Function, Executor) + * FluentFuture.transform}), but you will often find it easier to use a framework. Frameworks + * automate the process, often adding features like monitoring, debugging, and cancellation. + * Examples of frameworks include: * *

      *
    • Dagger Producers @@ -116,7 +118,7 @@ * put in a special hack for us: https://issuetracker.google.com/issues/131431257) */ @DoNotMock("Use the methods in Futures (like immediateFuture) or SettableFuture") -@ElementTypesAreNonnullByDefault +@NullMarked public interface ListenableFuture extends Future { /** * Registers a listener to be {@linkplain Executor#execute(Runnable) run} on the given executor. diff --git a/android/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java b/android/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java index 678a6c671a46..02bcd1c0745e 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java +++ b/android/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java @@ -18,6 +18,7 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -25,7 +26,7 @@ import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code @@ -40,8 +41,8 @@ * @author Sven Mawson * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public class ListenableFutureTask extends FutureTask implements ListenableFuture { // TODO(cpovirk): explore ways of making ListenableFutureTask final. There are some valid reasons diff --git a/android/guava/src/com/google/common/util/concurrent/ListenableScheduledFuture.java b/android/guava/src/com/google/common/util/concurrent/ListenableScheduledFuture.java index e5aa5e302e53..73a1c2008d43 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListenableScheduledFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/ListenableScheduledFuture.java @@ -14,10 +14,9 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.concurrent.ScheduledFuture; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Helper interface to implement both {@link ListenableFuture} and {@link ScheduledFuture}. @@ -25,8 +24,6 @@ * @author Anthony Zana * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public interface ListenableScheduledFuture extends ScheduledFuture, ListenableFuture {} diff --git a/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java b/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java index ed8c9065ac93..4370bf23b767 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java +++ b/android/guava/src/com/google/common/util/concurrent/ListenerCallQueue.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import com.google.common.collect.Queues; import com.google.errorprone.annotations.concurrent.GuardedBy; @@ -26,7 +27,6 @@ import java.util.Queue; import java.util.concurrent.Executor; import java.util.logging.Level; -import java.util.logging.Logger; /** * A list of listeners for implementing a concurrency friendly observable object. @@ -52,11 +52,11 @@ * the listeners can be delayed slightly so that locks can be dropped. Also, because {@link * #dispatch} is expected to be called concurrently, it is idempotent. */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class ListenerCallQueue { // TODO(cpovirk): consider using the logger associated with listener.getClass(). - private static final Logger logger = Logger.getLogger(ListenerCallQueue.class.getName()); + private static final LazyLogger logger = new LazyLogger(ListenerCallQueue.class); // TODO(chrisn): promote AppendOnlyCollection for use here. private final List> listeners = @@ -147,7 +147,7 @@ private static final class PerListenerQueue implements Runnable { this.executor = checkNotNull(executor); } - /** Enqueues a event to be run. */ + /** Enqueues an event to be run. */ synchronized void add(ListenerCallQueue.Event event, Object label) { waitQueue.add(event); labelQueue.add(label); @@ -157,6 +157,7 @@ synchronized void add(ListenerCallQueue.Event event, Object label) { * Dispatches all listeners {@linkplain #enqueue enqueued} prior to this call, serially and in * order. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception void dispatch() { boolean scheduleEventRunner = false; synchronized (this) { @@ -168,22 +169,25 @@ void dispatch() { if (scheduleEventRunner) { try { executor.execute(this); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // reset state in case of an error so that later dispatch calls will actually do something synchronized (this) { isThreadScheduled = false; } // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while running callbacks for " + listener + " on " + executor, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while running callbacks for " + listener + " on " + executor, + e); throw e; } } } @Override + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception public void run() { boolean stillRunning = true; try { @@ -204,12 +208,14 @@ public void run() { // Always run while _not_ holding the lock, to avoid deadlocks. try { nextToRun.call(listener); - } catch (RuntimeException e) { + } catch (Exception e) { // sneaky checked exception // Log it and keep going. - logger.log( - Level.SEVERE, - "Exception while executing callback: " + listener + " " + nextLabel, - e); + logger + .get() + .log( + Level.SEVERE, + "Exception while executing callback: " + listener + " " + nextLabel, + e); } } } finally { diff --git a/android/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java b/android/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java index 83ea759f8e74..2e00ecfeece4 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/ListeningExecutorService.java @@ -23,7 +23,7 @@ import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An {@link ExecutorService} that returns {@link ListenableFuture} instances. To create an instance @@ -37,7 +37,6 @@ "Use TestingExecutors.sameThreadScheduledExecutor, or wrap a real Executor from " + "java.util.concurrent.Executors with MoreExecutors.listeningDecorator") @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface ListeningExecutorService extends ExecutorService { /** * @return a {@code ListenableFuture} representing pending completion of the task diff --git a/android/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java b/android/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java index a6be949e574f..c0ffec3a5f9b 100644 --- a/android/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/ListeningScheduledExecutorService.java @@ -18,7 +18,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link ScheduledExecutorService} that returns {@link ListenableFuture} instances from its @@ -30,25 +30,32 @@ * @since 10.0 */ @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface ListeningScheduledExecutorService extends ScheduledExecutorService, ListeningExecutorService { - /** @since 15.0 (previously returned ScheduledFuture) */ + /** + * @since 15.0 (previously returned ScheduledFuture) + */ @Override ListenableScheduledFuture schedule(Runnable command, long delay, TimeUnit unit); - /** @since 15.0 (previously returned ScheduledFuture) */ + /** + * @since 15.0 (previously returned ScheduledFuture) + */ @Override ListenableScheduledFuture schedule( Callable callable, long delay, TimeUnit unit); - /** @since 15.0 (previously returned ScheduledFuture) */ + /** + * @since 15.0 (previously returned ScheduledFuture) + */ @Override ListenableScheduledFuture scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit); - /** @since 15.0 (previously returned ScheduledFuture) */ + /** + * @since 15.0 (previously returned ScheduledFuture) + */ @Override ListenableScheduledFuture scheduleWithFixedDelay( Runnable command, long initialDelay, long delay, TimeUnit unit); diff --git a/android/guava/src/com/google/common/util/concurrent/Monitor.java b/android/guava/src/com/google/common/util/concurrent/Monitor.java index d88a8bcb2f06..f9244317e118 100644 --- a/android/guava/src/com/google/common/util/concurrent/Monitor.java +++ b/android/guava/src/com/google/common/util/concurrent/Monitor.java @@ -15,16 +15,19 @@ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.primitives.Longs; import com.google.errorprone.annotations.concurrent.GuardedBy; import com.google.j2objc.annotations.Weak; +import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import javax.annotation.CheckForNull; +import java.util.function.BooleanSupplier; +import org.jspecify.annotations.Nullable; /** * A synchronization abstraction supporting waiting on arbitrary boolean conditions. @@ -197,10 +200,9 @@ * @author Martin Buchholz * @since 10.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible @SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. -@ElementTypesAreNonnullByDefault public final class Monitor { // TODO(user): Use raw LockSupport or AbstractQueuedSynchronizer instead of ReentrantLock. // TODO(user): "Port" jsr166 tests for ReentrantLock. @@ -301,7 +303,6 @@ public final class Monitor { * * @since 10.0 */ - @Beta public abstract static class Guard { @Weak final Monitor monitor; @@ -312,8 +313,7 @@ public abstract static class Guard { /** The next active guard */ @GuardedBy("monitor.lock") - @CheckForNull - Guard next; + @Nullable Guard next; protected Guard(Monitor monitor) { this.monitor = checkNotNull(monitor, "monitor"); @@ -339,8 +339,7 @@ protected Guard(Monitor monitor) { * A linked list threaded through the Guard.next field. */ @GuardedBy("lock") - @CheckForNull - private Guard activeGuards = null; + private @Nullable Guard activeGuards = null; /** * Creates a monitor with a non-fair (but fast) ordering policy. Equivalent to {@code @@ -361,11 +360,43 @@ public Monitor(boolean fair) { this.lock = new ReentrantLock(fair); } + /** + * Creates a new {@linkplain Guard guard} for this monitor. + * + * @param isSatisfied the new guard's boolean condition (see {@link Guard#isSatisfied + * isSatisfied()}) + * @since 33.4.0 (but since 21.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + // We have to rely on users not to call this, as NewApi won't flag BooleanSupplier creation. + @IgnoreJRERequirement + public Guard newGuard(BooleanSupplier isSatisfied) { + checkNotNull(isSatisfied, "isSatisfied"); + return new Guard(this) { + @Override + public boolean isSatisfied() { + return isSatisfied.getAsBoolean(); + } + }; + } + /** Enters this monitor. Blocks indefinitely. */ public void enter() { lock.lock(); } + /** + * Enters this monitor. Blocks at most the given time. + * + * @return whether the monitor was entered + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enter(Duration time) { + return enter(toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Enters this monitor. Blocks at most the given time. * @@ -373,14 +404,14 @@ public void enter() { */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration public boolean enter(long time, TimeUnit unit) { - final long timeoutNanos = toSafeNanos(time, unit); - final ReentrantLock lock = this.lock; + long timeoutNanos = toSafeNanos(time, unit); + ReentrantLock lock = this.lock; if (!fair && lock.tryLock()) { return true; } boolean interrupted = Thread.interrupted(); try { - final long startTime = System.nanoTime(); + long startTime = System.nanoTime(); for (long remainingNanos = timeoutNanos; ; ) { try { return lock.tryLock(remainingNanos, TimeUnit.NANOSECONDS); @@ -405,6 +436,19 @@ public void enterInterruptibly() throws InterruptedException { lock.lockInterruptibly(); } + /** + * Enters this monitor. Blocks at most the given time, and may be interrupted. + * + * @return whether the monitor was entered + * @throws InterruptedException if interrupted while waiting + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enterInterruptibly(Duration time) throws InterruptedException { + return enterInterruptibly(toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Enters this monitor. Blocks at most the given time, and may be interrupted. * @@ -436,7 +480,7 @@ public void enterWhen(Guard guard) throws InterruptedException { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); lock.lockInterruptibly(); @@ -460,14 +504,32 @@ public void enterWhen(Guard guard) throws InterruptedException { * * @return whether the monitor was entered, which guarantees that the guard is now satisfied * @throws InterruptedException if interrupted while waiting + * @since 33.4.0 (but since 28.0 in the JRE flavor) */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enterWhen(Guard guard, Duration time) throws InterruptedException { + return enterWhen(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied, and may be + * interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + */ + @SuppressWarnings({ + "GoodTime", // should accept a java.time.Duration + "LabelledBreakTarget", // TODO(b/345814817): Maybe fix. + }) public boolean enterWhen(Guard guard, long time, TimeUnit unit) throws InterruptedException { - final long timeoutNanos = toSafeNanos(time, unit); + long timeoutNanos = toSafeNanos(time, unit); if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; boolean reentrant = lock.isHeldByCurrentThread(); long startTime = 0L; @@ -518,7 +580,7 @@ public void enterWhenUninterruptibly(Guard guard) { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); lock.lock(); @@ -535,6 +597,19 @@ public void enterWhenUninterruptibly(Guard guard) { } } + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enterWhenUninterruptibly(Guard guard, Duration time) { + return enterWhenUninterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both * the time to acquire the lock and the time to wait for the guard to be satisfied. @@ -543,11 +618,11 @@ public void enterWhenUninterruptibly(Guard guard) { */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration public boolean enterWhenUninterruptibly(Guard guard, long time, TimeUnit unit) { - final long timeoutNanos = toSafeNanos(time, unit); + long timeoutNanos = toSafeNanos(time, unit); if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; long startTime = 0L; boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); boolean interrupted = Thread.interrupted(); @@ -575,7 +650,7 @@ public boolean enterWhenUninterruptibly(Guard guard, long time, TimeUnit unit) { if (guard.isSatisfied()) { satisfied = true; } else { - final long remainingNanos; + long remainingNanos; if (startTime == 0L) { startTime = initNanoTime(timeoutNanos); remainingNanos = timeoutNanos; @@ -612,7 +687,7 @@ public boolean enterIf(Guard guard) { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; lock.lock(); boolean satisfied = false; @@ -625,6 +700,19 @@ public boolean enterIf(Guard guard) { } } + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enterIf(Guard guard, Duration time) { + return enterIf(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the * lock, but does not wait for the guard to be satisfied. @@ -661,7 +749,7 @@ public boolean enterIfInterruptibly(Guard guard) throws InterruptedException { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; lock.lockInterruptibly(); boolean satisfied = false; @@ -674,6 +762,19 @@ public boolean enterIfInterruptibly(Guard guard) throws InterruptedException { } } + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean enterIfInterruptibly(Guard guard, Duration time) throws InterruptedException { + return enterIfInterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the * lock, but does not wait for the guard to be satisfied, and may be interrupted. @@ -686,7 +787,7 @@ public boolean enterIfInterruptibly(Guard guard, long time, TimeUnit unit) if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; if (!lock.tryLock(time, unit)) { return false; } @@ -713,7 +814,7 @@ public boolean tryEnterIf(Guard guard) { if (guard.monitor != this) { throw new IllegalMonitorStateException(); } - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; if (!lock.tryLock()) { return false; } @@ -735,7 +836,7 @@ public boolean tryEnterIf(Guard guard) { * @throws InterruptedException if interrupted while waiting */ public void waitFor(Guard guard) throws InterruptedException { - if (!((guard.monitor == this) & lock.isHeldByCurrentThread())) { + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { throw new IllegalMonitorStateException(); } if (!guard.isSatisfied()) { @@ -743,6 +844,20 @@ public void waitFor(Guard guard) throws InterruptedException { } } + /** + * Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May + * be called only by a thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean waitFor(Guard guard, Duration time) throws InterruptedException { + return waitFor(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May * be called only by a thread currently occupying this monitor. @@ -752,8 +867,8 @@ public void waitFor(Guard guard) throws InterruptedException { */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration public boolean waitFor(Guard guard, long time, TimeUnit unit) throws InterruptedException { - final long timeoutNanos = toSafeNanos(time, unit); - if (!((guard.monitor == this) & lock.isHeldByCurrentThread())) { + long timeoutNanos = toSafeNanos(time, unit); + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { throw new IllegalMonitorStateException(); } if (guard.isSatisfied()) { @@ -770,7 +885,7 @@ public boolean waitFor(Guard guard, long time, TimeUnit unit) throws Interrupted * currently occupying this monitor. */ public void waitForUninterruptibly(Guard guard) { - if (!((guard.monitor == this) & lock.isHeldByCurrentThread())) { + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { throw new IllegalMonitorStateException(); } if (!guard.isSatisfied()) { @@ -778,6 +893,19 @@ public void waitForUninterruptibly(Guard guard) { } } + /** + * Waits for the guard to be satisfied. Waits at most the given time. May be called only by a + * thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean waitForUninterruptibly(Guard guard, Duration time) { + return waitForUninterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + /** * Waits for the guard to be satisfied. Waits at most the given time. May be called only by a * thread currently occupying this monitor. @@ -786,15 +914,15 @@ public void waitForUninterruptibly(Guard guard) { */ @SuppressWarnings("GoodTime") // should accept a java.time.Duration public boolean waitForUninterruptibly(Guard guard, long time, TimeUnit unit) { - final long timeoutNanos = toSafeNanos(time, unit); - if (!((guard.monitor == this) & lock.isHeldByCurrentThread())) { + long timeoutNanos = toSafeNanos(time, unit); + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { throw new IllegalMonitorStateException(); } if (guard.isSatisfied()) { return true; } boolean signalBeforeWaiting = true; - final long startTime = initNanoTime(timeoutNanos); + long startTime = initNanoTime(timeoutNanos); boolean interrupted = Thread.interrupted(); try { for (long remainingNanos = timeoutNanos; ; ) { @@ -818,7 +946,7 @@ public boolean waitForUninterruptibly(Guard guard, long time, TimeUnit unit) { /** Leaves this monitor. May be called only by a thread currently occupying this monitor. */ public void leave() { - final ReentrantLock lock = this.lock; + ReentrantLock lock = this.lock; try { // No need to signal if we will still be holding the lock when we return if (lock.getHoldCount() == 1) { @@ -1015,6 +1143,7 @@ private boolean isSatisfied(Guard guard) { try { return guard.isSatisfied(); } catch (Throwable throwable) { + // Any Exception is either a RuntimeException or sneaky checked exception. signalAllWaiters(); throw throwable; } diff --git a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java index 3711d07a3473..2760478d576c 100644 --- a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -16,21 +16,23 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Callables.threadRenaming; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; +import static com.google.common.util.concurrent.SneakyThrows.sneakyThrow; +import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Supplier; -import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Queues; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.concurrent.GuardedBy; import java.lang.reflect.InvocationTargetException; +import java.time.Duration; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -49,7 +51,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, @@ -61,10 +63,32 @@ * @since 3.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class MoreExecutors { private MoreExecutors() {} + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits when the application + * is complete. It does so by using daemon threads and adding a shutdown hook to wait for their + * completion. + * + *

      This is mainly for fixed thread pools. See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @return an unmodifiable version of the input which will not hang the JVM + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static ExecutorService getExitingExecutorService( + ThreadPoolExecutor executor, Duration terminationTimeout) { + return getExitingExecutorService( + executor, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + /** * Converts the given ThreadPoolExecutor into an ExecutorService that exits when the application * is complete. It does so by using daemon threads and adding a shutdown hook to wait for their @@ -78,7 +102,7 @@ private MoreExecutors() {} * @param timeUnit unit of time for the time parameter * @return an unmodifiable version of the input which will not hang the JVM */ - @Beta + @J2ktIncompatible @GwtIncompatible // TODO @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static ExecutorService getExitingExecutorService( @@ -99,12 +123,35 @@ public static ExecutorService getExitingExecutorService( * @param executor the executor to modify to make sure it exits when the application is finished * @return an unmodifiable version of the input which will not hang the JVM */ - @Beta + @J2ktIncompatible @GwtIncompatible // concurrency public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { return new Application().getExitingExecutorService(executor); } + /** + * Converts the given ScheduledThreadPoolExecutor into a ScheduledExecutorService that exits when + * the application is complete. It does so by using daemon threads and adding a shutdown hook to + * wait for their completion. + * + *

      This is mainly for fixed thread pools. See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @return an unmodifiable version of the input which will not hang the JVM + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor, Duration terminationTimeout) { + return getExitingScheduledExecutorService( + executor, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + /** * Converts the given ScheduledThreadPoolExecutor into a ScheduledExecutorService that exits when * the application is complete. It does so by using daemon threads and adding a shutdown hook to @@ -118,7 +165,7 @@ public static ExecutorService getExitingExecutorService(ThreadPoolExecutor execu * @param timeUnit unit of time for the time parameter * @return an unmodifiable version of the input which will not hang the JVM */ - @Beta + @J2ktIncompatible @GwtIncompatible // TODO @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static ScheduledExecutorService getExitingScheduledExecutorService( @@ -140,13 +187,32 @@ public static ScheduledExecutorService getExitingScheduledExecutorService( * @param executor the executor to modify to make sure it exits when the application is finished * @return an unmodifiable version of the input which will not hang the JVM */ - @Beta + @J2ktIncompatible @GwtIncompatible // TODO public static ScheduledExecutorService getExitingScheduledExecutorService( ScheduledThreadPoolExecutor executor) { return new Application().getExitingScheduledExecutorService(executor); } + /** + * Add a shutdown hook to wait for thread completion in the given {@link ExecutorService service}. + * This is useful if the given service uses daemon threads, and we want to keep the JVM from + * exiting immediately on shutdown, instead giving these daemon threads a chance to terminate + * normally. + * + * @param service ExecutorService which uses daemon threads + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static void addDelayedShutdownHook(ExecutorService service, Duration terminationTimeout) { + addDelayedShutdownHook(service, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + /** * Add a shutdown hook to wait for thread completion in the given {@link ExecutorService service}. * This is useful if the given service uses daemon threads, and we want to keep the JVM from @@ -158,7 +224,7 @@ public static ScheduledExecutorService getExitingScheduledExecutorService( * JVM * @param timeUnit unit of time for the time parameter */ - @Beta + @J2ktIncompatible @GwtIncompatible // TODO @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static void addDelayedShutdownHook( @@ -167,6 +233,7 @@ public static void addDelayedShutdownHook( } /** Represents the current application to register shutdown hooks. */ + @J2ktIncompatible @GwtIncompatible // TODO @VisibleForTesting static class Application { @@ -197,26 +264,23 @@ final ScheduledExecutorService getExitingScheduledExecutorService( } final void addDelayedShutdownHook( - final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { + ExecutorService service, long terminationTimeout, TimeUnit timeUnit) { checkNotNull(service); checkNotNull(timeUnit); addShutdownHook( - MoreExecutors.newThread( + newThread( "DelayedShutdownHook-for-" + service, - new Runnable() { - @Override - public void run() { - try { - // We'd like to log progress and failures that may arise in the - // following code, but unfortunately the behavior of logging - // is undefined in shutdown hooks. - // This is because the logging code installs a shutdown hook of its - // own. See Cleaner class inside {@link LogManager}. - service.shutdown(); - service.awaitTermination(terminationTimeout, timeUnit); - } catch (InterruptedException ignored) { - // We're shutting down anyway, so just ignore. - } + () -> { + service.shutdown(); + try { + // We'd like to log progress and failures that may arise in the + // following code, but unfortunately the behavior of logging + // is undefined in shutdown hooks. + // This is because the logging code installs a shutdown hook of its + // own. See Cleaner class inside {@link LogManager}. + service.awaitTermination(terminationTimeout, timeUnit); + } catch (InterruptedException ignored) { + // We're shutting down anyway, so just ignore. } })); } @@ -227,6 +291,7 @@ void addShutdownHook(Thread hook) { } } + @J2ktIncompatible @GwtIncompatible // TODO private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { executor.setThreadFactory( @@ -236,109 +301,6 @@ private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { .build()); } - // See newDirectExecutorService javadoc for behavioral notes. - @GwtIncompatible // TODO - private static final class DirectExecutorService extends AbstractListeningExecutorService { - /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ - private final Object lock = new Object(); - - /* - * Conceptually, these two variables describe the executor being in - * one of three states: - * - Active: shutdown == false - * - Shutdown: runningTasks > 0 and shutdown == true - * - Terminated: runningTasks == 0 and shutdown == true - */ - @GuardedBy("lock") - private int runningTasks = 0; - - @GuardedBy("lock") - private boolean shutdown = false; - - @Override - public void execute(Runnable command) { - startTask(); - try { - command.run(); - } finally { - endTask(); - } - } - - @Override - public boolean isShutdown() { - synchronized (lock) { - return shutdown; - } - } - - @Override - public void shutdown() { - synchronized (lock) { - shutdown = true; - if (runningTasks == 0) { - lock.notifyAll(); - } - } - } - - // See newDirectExecutorService javadoc for unusual behavior of this method. - @Override - public List shutdownNow() { - shutdown(); - return Collections.emptyList(); - } - - @Override - public boolean isTerminated() { - synchronized (lock) { - return shutdown && runningTasks == 0; - } - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - synchronized (lock) { - while (true) { - if (shutdown && runningTasks == 0) { - return true; - } else if (nanos <= 0) { - return false; - } else { - long now = System.nanoTime(); - TimeUnit.NANOSECONDS.timedWait(lock, nanos); - nanos -= System.nanoTime() - now; // subtract the actual time we waited - } - } - } - } - - /** - * Checks if the executor has been shut down and increments the running task count. - * - * @throws RejectedExecutionException if the executor has been previously shutdown - */ - private void startTask() { - synchronized (lock) { - if (shutdown) { - throw new RejectedExecutionException("Executor already shutdown"); - } - runningTasks++; - } - } - - /** Decrements the running task count. */ - private void endTask() { - synchronized (lock) { - int numRunning = --runningTasks; - if (numRunning == 0) { - lock.notifyAll(); - } - } - } - } - /** * Creates an executor service that runs each task in the thread that invokes {@code * execute/submit}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. This applies both to @@ -379,14 +341,22 @@ public static ListeningExecutorService newDirectExecutorService() { * difficult to reproduce because they depend on timing. For example: * *

        - *
      • A call like {@code future.transform(function, directExecutor())} may execute the function - * immediately in the thread that is calling {@code transform}. (This specific case happens - * if the future is already completed.) If {@code transform} call was made from a UI thread - * or other latency-sensitive thread, a heavyweight function can harm responsiveness. - *
      • If the task will be executed later, consider which thread will trigger the execution -- - * since that thread will execute the task inline. If the thread is a shared system thread - * like an RPC network thread, a heavyweight task can stall progress of the whole system or - * even deadlock it. + *
      • When a {@code ListenableFuture} listener is registered to run under {@code + * directExecutor}, the listener can execute in any of three possible threads: + *
          + *
        1. When a thread attaches a listener to a {@code ListenableFuture} that's already + * complete, the listener runs immediately in that thread. + *
        2. When a thread attaches a listener to a {@code ListenableFuture} that's + * incomplete and the {@code ListenableFuture} later completes normally, the + * listener runs in the thread that completes the {@code ListenableFuture}. + *
        3. When a listener is attached to a {@code ListenableFuture} and the {@code + * ListenableFuture} gets cancelled, the listener runs immediately in the thread that + * cancelled the {@code Future}. + *
        + * Given all these possibilities, it is frequently possible for listeners to execute in UI + * threads, RPC network threads, or other latency-sensitive threads. In those cases, slow + * listeners can harm responsiveness, slow the system as a whole, or worse. (See also the + * note about locking below.) *
      • If many tasks will be triggered by the same event, one heavyweight task may delay other * tasks -- even tasks that are not themselves {@code directExecutor} tasks. *
      • If many such tasks are chained together (such as with {@code @@ -402,9 +372,11 @@ public static ListeningExecutorService newDirectExecutorService() { * terminate whichever thread happens to trigger the execution. *
      * - * Additionally, beware of executing tasks with {@code directExecutor} while holding a lock. Since - * the task you submit to the executor (or any other arbitrary work the executor does) may do slow - * work or acquire other locks, you risk deadlocks. + * A specific warning about locking: Code that executes user-supplied tasks, such as {@code + * ListenableFuture} listeners, should take care not to do so while holding a lock. Additionally, + * as a further line of defense, prefer not to perform any locking inside a task that will be run + * under {@code directExecutor}: Not only might the wait for a lock be long, but if the running + * thread was holding a lock, the listener may deadlock or break lock isolation. * *

      This instance is equivalent to: * @@ -431,7 +403,7 @@ public static Executor directExecutor() { * *

      {@linkplain Executor#execute executed} tasks have a happens-before order as defined in the * Java Language Specification. Tasks execute with the same happens-before order that the function - * calls to {@link Executor#execute `execute()`} that submitted those tasks had. + * calls to {@link Executor#execute execute()} that submitted those tasks had. * *

      The executor uses {@code delegate} in order to {@link Executor#execute execute} each task in * turn, and does not create any threads of its own. @@ -469,7 +441,6 @@ public static Executor directExecutor() { * * @since 23.3 (since 23.1 as {@code sequentialExecutor}) */ - @Beta @GwtIncompatible public static Executor newSequentialExecutor(Executor delegate) { return new SequentialExecutor(delegate); @@ -591,7 +562,7 @@ public ListenableScheduledFuture schedule(Runnable command, long delay, TimeU Callable callable, long delay, TimeUnit unit) { TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); ScheduledFuture scheduled = delegate.schedule(task, delay, unit); - return new ListenableScheduledTask(task, scheduled); + return new ListenableScheduledTask<>(task, scheduled); } @Override @@ -659,8 +630,9 @@ public void run() { try { delegate.run(); } catch (Throwable t) { + // Any Exception is either a RuntimeException or sneaky checked exception. setException(t); - throw Throwables.propagate(t); + throw t; } } @@ -686,7 +658,31 @@ protected String pendingToString() { * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} * implementations. */ - @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @J2ktIncompatible + @GwtIncompatible + @ParametricNullness + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + static T invokeAnyImpl( + ListeningExecutorService executorService, + Collection> tasks, + boolean timed, + Duration timeout) + throws InterruptedException, ExecutionException, TimeoutException { + return invokeAnyImpl( + executorService, tasks, timed, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} + * implementations. + */ + @SuppressWarnings({ + "GoodTime", // should accept a java.time.Duration + "CatchingUnchecked", // sneaky checked exception + "Interruption", // We copy AbstractExecutorService.invokeAny. Maybe we shouldn't: b/227335009. + }) + @J2ktIncompatible @GwtIncompatible @ParametricNullness static T invokeAnyImpl( @@ -748,7 +744,9 @@ protected String pendingToString() { return f.get(); } catch (ExecutionException eex) { ee = eex; - } catch (RuntimeException rex) { + } catch (InterruptedException iex) { + throw iex; + } catch (Exception rex) { // sneaky checked exception ee = new ExecutionException(rex); } } @@ -768,20 +766,12 @@ protected String pendingToString() { /** * Submits the task and adds a listener that adds the future to {@code queue} when it completes. */ + @J2ktIncompatible @GwtIncompatible // TODO private static ListenableFuture submitAndAddQueueListener( - ListeningExecutorService executorService, - Callable task, - final BlockingQueue> queue) { - final ListenableFuture future = executorService.submit(task); - future.addListener( - new Runnable() { - @Override - public void run() { - queue.add(future); - } - }, - directExecutor()); + ListeningExecutorService executorService, Callable task, BlockingQueue> queue) { + ListenableFuture future = executorService.submit(task); + future.addListener(() -> queue.add(future), directExecutor()); return future; } @@ -795,7 +785,7 @@ public void run() { * * @since 14.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // concurrency public static ThreadFactory platformThreadFactory() { if (!isAppEngineWithApiClasses()) { @@ -806,22 +796,15 @@ public static ThreadFactory platformThreadFactory() { Class.forName("com.google.appengine.api.ThreadManager") .getMethod("currentRequestThreadFactory") .invoke(null); - /* - * Do not merge the 3 catch blocks below. javac would infer a type of - * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of Android - * don't *seem* to mind, but there might be edge cases of which we're unaware.) - */ - } catch (IllegalAccessException e) { - throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); - } catch (ClassNotFoundException e) { - throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); - } catch (NoSuchMethodException e) { + } catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) { throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); } catch (InvocationTargetException e) { - throw Throwables.propagate(e.getCause()); + // `currentRequestThreadFactory` has no `throws` clause. + throw sneakyThrow(e.getCause()); } } + @J2ktIncompatible @GwtIncompatible // TODO private static boolean isAppEngineWithApiClasses() { if (System.getProperty("com.google.appengine.runtime.environment") == null) { @@ -857,11 +840,13 @@ private static boolean isAppEngineWithApiClasses() { * Creates a thread using {@link #platformThreadFactory}, and sets its name to {@code name} unless * changing the name is forbidden by the security manager. */ + @J2ktIncompatible @GwtIncompatible // concurrency static Thread newThread(String name, Runnable runnable) { checkNotNull(name); checkNotNull(runnable); - Thread result = platformThreadFactory().newThread(runnable); + // TODO(b/139726489): Confirm that null is impossible here. + Thread result = requireNonNull(platformThreadFactory().newThread(runnable)); try { result.setName(name); } catch (SecurityException e) { @@ -884,16 +869,12 @@ static Thread newThread(String name, Runnable runnable) { * @param executor The executor to decorate * @param nameSupplier The source of names for each task */ + @J2ktIncompatible @GwtIncompatible // concurrency - static Executor renamingDecorator(final Executor executor, final Supplier nameSupplier) { + static Executor renamingDecorator(Executor executor, Supplier nameSupplier) { checkNotNull(executor); checkNotNull(nameSupplier); - return new Executor() { - @Override - public void execute(Runnable command) { - executor.execute(Callables.threadRenaming(command, nameSupplier)); - } - }; + return command -> executor.execute(threadRenaming(command, nameSupplier)); } /** @@ -907,20 +888,20 @@ public void execute(Runnable command) { * @param service The executor to decorate * @param nameSupplier The source of names for each task */ + @J2ktIncompatible @GwtIncompatible // concurrency - static ExecutorService renamingDecorator( - final ExecutorService service, final Supplier nameSupplier) { + static ExecutorService renamingDecorator(ExecutorService service, Supplier nameSupplier) { checkNotNull(service); checkNotNull(nameSupplier); return new WrappingExecutorService(service) { @Override protected Callable wrapTask(Callable callable) { - return Callables.threadRenaming(callable, nameSupplier); + return threadRenaming(callable, nameSupplier); } @Override protected Runnable wrapTask(Runnable command) { - return Callables.threadRenaming(command, nameSupplier); + return threadRenaming(command, nameSupplier); } }; } @@ -936,20 +917,21 @@ protected Runnable wrapTask(Runnable command) { * @param service The executor to decorate * @param nameSupplier The source of names for each task */ + @J2ktIncompatible @GwtIncompatible // concurrency static ScheduledExecutorService renamingDecorator( - final ScheduledExecutorService service, final Supplier nameSupplier) { + ScheduledExecutorService service, Supplier nameSupplier) { checkNotNull(service); checkNotNull(nameSupplier); return new WrappingScheduledExecutorService(service) { @Override protected Callable wrapTask(Callable callable) { - return Callables.threadRenaming(callable, nameSupplier); + return threadRenaming(callable, nameSupplier); } @Override protected Runnable wrapTask(Runnable command) { - return Callables.threadRenaming(command, nameSupplier); + return threadRenaming(command, nameSupplier); } }; } @@ -971,6 +953,44 @@ protected Runnable wrapTask(Runnable command) { *

      If, at any step of the process, the calling thread is interrupted, the method calls {@link * ExecutorService#shutdownNow()} and returns. * + *

      For a version of this method that waits indefinitely, use {@link + * ExecutorService#close}. + * + * @param service the {@code ExecutorService} to shut down + * @param timeout the maximum time to wait for the {@code ExecutorService} to terminate + * @return {@code true} if the {@code ExecutorService} was terminated successfully, {@code false} + * if the call timed out or was interrupted + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @CanIgnoreReturnValue + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean shutdownAndAwaitTermination(ExecutorService service, Duration timeout) { + return shutdownAndAwaitTermination(service, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Shuts down the given executor service gradually, first disabling new submissions and later, if + * necessary, cancelling remaining tasks. + * + *

      The method takes the following steps: + * + *

        + *
      1. calls {@link ExecutorService#shutdown()}, disabling acceptance of new submitted tasks. + *
      2. awaits executor service termination for half of the specified timeout. + *
      3. if the timeout expires, it calls {@link ExecutorService#shutdownNow()}, cancelling + * pending tasks and interrupting running tasks. + *
      4. awaits executor service termination for the other half of the specified timeout. + *
      + * + *

      If, at any step of the process, the calling thread is interrupted, the method calls {@link + * ExecutorService#shutdownNow()} and returns. + * + *

      For a version of this method that waits indefinitely, use {@link + * ExecutorService#close}. + * * @param service the {@code ExecutorService} to shut down * @param timeout the maximum time to wait for the {@code ExecutorService} to terminate * @param unit the time unit of the timeout argument @@ -978,8 +998,8 @@ protected Runnable wrapTask(Runnable command) { * if the call timed out or was interrupted * @since 17.0 */ - @Beta @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean shutdownAndAwaitTermination( @@ -1010,22 +1030,18 @@ public static boolean shutdownAndAwaitTermination( * *

      Note, the returned executor can only be used once. */ - static Executor rejectionPropagatingExecutor( - final Executor delegate, final AbstractFuture future) { + static Executor rejectionPropagatingExecutor(Executor delegate, AbstractFuture future) { checkNotNull(delegate); checkNotNull(future); if (delegate == directExecutor()) { // directExecutor() cannot throw RejectedExecutionException return delegate; } - return new Executor() { - @Override - public void execute(Runnable command) { - try { - delegate.execute(command); - } catch (RejectedExecutionException e) { - future.setException(e); - } + return command -> { + try { + delegate.execute(command); + } catch (RejectedExecutionException e) { + future.setException(e); } }; } diff --git a/android/guava/src/com/google/common/util/concurrent/NullnessCasts.java b/android/guava/src/com/google/common/util/concurrent/NullnessCasts.java index 0a0d719ef20b..7ef866d26f1e 100644 --- a/android/guava/src/com/google/common/util/concurrent/NullnessCasts.java +++ b/android/guava/src/com/google/common/util/concurrent/NullnessCasts.java @@ -15,12 +15,10 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class NullnessCasts { /** * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that @@ -52,7 +50,7 @@ final class NullnessCasts { */ @SuppressWarnings("nullness") @ParametricNullness - static T uncheckedCastNullableTToT(@CheckForNull T t) { + static T uncheckedCastNullableTToT(@Nullable T t) { return t; } @@ -65,7 +63,8 @@ final class NullnessCasts { * return to a caller, the code needs to a way to return {@code null} from a method that returns * "plain {@code T}." This API provides that. */ - @SuppressWarnings("nullness") + @SuppressWarnings({"nullness", "TypeParameterUnusedInFormals", "ReturnMissingNullable"}) + // The warnings are legitimate. Each time we use this method, we document why. @ParametricNullness static T uncheckedNull() { return null; diff --git a/android/guava/src/com/google/common/util/concurrent/OverflowAvoidingLockSupport.java b/android/guava/src/com/google/common/util/concurrent/OverflowAvoidingLockSupport.java index 3038ab7bd004..d62a18ffa526 100644 --- a/android/guava/src/com/google/common/util/concurrent/OverflowAvoidingLockSupport.java +++ b/android/guava/src/com/google/common/util/concurrent/OverflowAvoidingLockSupport.java @@ -16,21 +16,22 @@ import static java.lang.Math.min; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.locks.LockSupport; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Works around an android bug, where parking for more than INT_MAX seconds can produce an abort * signal on 32 bit devices running Android Q. */ -@ElementTypesAreNonnullByDefault +@J2ktIncompatible final class OverflowAvoidingLockSupport { // Represents the max nanoseconds representable on a linux timespec with a 32 bit tv_sec static final long MAX_NANOSECONDS_THRESHOLD = (1L + Integer.MAX_VALUE) * 1_000_000_000L - 1L; private OverflowAvoidingLockSupport() {} - static void parkNanos(@CheckForNull Object blocker, long nanos) { + static void parkNanos(@Nullable Object blocker, long nanos) { // Even in the extremely unlikely event that a thread unblocks itself early after only 68 years, // this is indistinguishable from a spurious wakeup, which LockSupport allows. LockSupport.parkNanos(blocker, min(nanos, MAX_NANOSECONDS_THRESHOLD)); diff --git a/android/guava/src/com/google/common/util/concurrent/ParametricNullness.java b/android/guava/src/com/google/common/util/concurrent/ParametricNullness.java index a745bf7bb4b8..fcc986685545 100644 --- a/android/guava/src/com/google/common/util/concurrent/ParametricNullness.java +++ b/android/guava/src/com/google/common/util/concurrent/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

        + *
      • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
      • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
      + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
        + *
      • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
      • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
      * *

      Consumers of this annotation include: * *

        - *
      • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
      • J2ObjC - *
      • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
      • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
      • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
      * + *

      This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/util/concurrent/Partially.java b/android/guava/src/com/google/common/util/concurrent/Partially.java index 7aa16e776fd5..c40ab4e007ee 100644 --- a/android/guava/src/com/google/common/util/concurrent/Partially.java +++ b/android/guava/src/com/google/common/util/concurrent/Partially.java @@ -32,7 +32,6 @@ * version. */ @GwtCompatible -@ElementTypesAreNonnullByDefault final class Partially { /** * The presence of this annotation on an API indicates that the method may be used with the diff --git a/android/guava/src/com/google/common/util/concurrent/Platform.java b/android/guava/src/com/google/common/util/concurrent/Platform.java index cfb96a0ad788..d3dbb8ae6d17 100644 --- a/android/guava/src/com/google/common/util/concurrent/Platform.java +++ b/android/guava/src/com/google/common/util/concurrent/Platform.java @@ -14,17 +14,50 @@ package com.google.common.util.concurrent; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.lang.Thread.currentThread; + import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.jspecify.annotations.Nullable; /** Methods factored out so that they can be emulated differently in GWT. */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault final class Platform { static boolean isInstanceOfThrowableClass( - @CheckForNull Throwable t, Class expectedClass) { + @Nullable Throwable t, Class expectedClass) { return expectedClass.isInstance(t); } + static void restoreInterruptIfIsInterruptedException(Throwable t) { + checkNotNull(t); // to satisfy NullPointerTester + if (t instanceof InterruptedException) { + currentThread().interrupt(); + } + } + + static void interruptCurrentThread() { + Thread.currentThread().interrupt(); + } + + static void rethrowIfErrorOtherThanStackOverflow(Throwable t) { + checkNotNull(t); + if (t instanceof Error && !(t instanceof StackOverflowError)) { + throw (Error) t; + } + } + + static V get(AbstractFuture future) + throws InterruptedException, ExecutionException { + return future.blockingGet(); + } + + static V get(AbstractFuture future, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return future.blockingGet(timeout, unit); + } + private Platform() {} } diff --git a/android/guava/src/com/google/common/util/concurrent/RateLimiter.java b/android/guava/src/com/google/common/util/concurrent/RateLimiter.java index 9bcf118160d5..2329fe43194a 100644 --- a/android/guava/src/com/google/common/util/concurrent/RateLimiter.java +++ b/android/guava/src/com/google/common/util/concurrent/RateLimiter.java @@ -16,20 +16,23 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static java.lang.Math.max; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.SmoothRateLimiter.SmoothBursty; import com.google.common.util.concurrent.SmoothRateLimiter.SmoothWarmingUp; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.Locale; import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A rate limiter. Conceptually, a rate limiter distributes permits at a configurable rate. Each @@ -91,8 +94,8 @@ // TODO(user): switch to nano precision. A natural unit of cost is "bytes", and a micro precision // would mean a maximum rate of "1MB/s", which might be small in some cases. @Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class RateLimiter { /** * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per @@ -135,6 +138,36 @@ static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) return rateLimiter; } + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per + * second" (commonly referred to as QPS, queries per second), and a warmup period, + * during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum + * rate at the end of the period (as long as there are enough requests to saturate it). Similarly, + * if the {@code RateLimiter} is left unused for a duration of {@code warmupPeriod}, it + * will gradually return to its "cold" state, i.e. it will go through the same warming up process + * as when it was first created. + * + *

      The returned {@code RateLimiter} is intended for cases where the resource that actually + * fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being + * immediately accessed at the stable (maximum) rate. + * + *

      The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will + * follow), and if it is left unused for long enough, it will return to that state. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many + * permits become available per second + * @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate, + * before reaching its stable (maximum) rate + * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code + * warmupPeriod} is negative + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static RateLimiter create(double permitsPerSecond, Duration warmupPeriod) { + return create(permitsPerSecond, toNanosSaturated(warmupPeriod), TimeUnit.NANOSECONDS); + } + /** * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per * second" (commonly referred to as QPS, queries per second), and a warmup period, @@ -185,7 +218,7 @@ static RateLimiter create( private final SleepingStopwatch stopwatch; // Can't be initialized in the constructor because mocks don't call the constructor. - @CheckForNull private volatile Object mutexDoNotUseDirectly; + private volatile @Nullable Object mutexDoNotUseDirectly; private Object mutex() { Object mutex = mutexDoNotUseDirectly; @@ -223,8 +256,7 @@ private Object mutex() { * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero */ public final void setRate(double permitsPerSecond) { - checkArgument( - permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive"); + checkArgument(permitsPerSecond > 0.0, "rate must be positive"); synchronized (mutex()) { doSetRate(permitsPerSecond, stopwatch.readMicros()); } @@ -289,6 +321,24 @@ final long reserve(int permits) { } } + /** + * Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the + * specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit + * would not have been granted before the timeout expired. + * + *

      This method is equivalent to {@code tryAcquire(1, timeout)}. + * + * @param timeout the maximum time to wait for the permit. Negative values are treated as zero. + * @return {@code true} if the permit was acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean tryAcquire(Duration timeout) { + return tryAcquire(1, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the * specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit @@ -333,6 +383,23 @@ public boolean tryAcquire() { return tryAcquire(1, 0, MICROSECONDS); } + /** + * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained + * without exceeding the specified {@code timeout}, or returns {@code false} immediately (without + * waiting) if the permits would not have been granted before the timeout expired. + * + * @param permits the number of permits to acquire + * @param timeout the maximum time to wait for the permits. Negative values are treated as zero. + * @return {@code true} if the permits were acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public boolean tryAcquire(int permits, Duration timeout) { + return tryAcquire(permits, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained * without exceeding the specified {@code timeout}, or returns {@code false} immediately (without diff --git a/android/guava/src/com/google/common/util/concurrent/Runnables.java b/android/guava/src/com/google/common/util/concurrent/Runnables.java index 5503f68ae545..35a3873366cb 100644 --- a/android/guava/src/com/google/common/util/concurrent/Runnables.java +++ b/android/guava/src/com/google/common/util/concurrent/Runnables.java @@ -14,7 +14,6 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -22,16 +21,16 @@ * * @since 16.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public final class Runnables { - - private static final Runnable EMPTY_RUNNABLE = - new Runnable() { - @Override - public void run() {} - }; + /* + * If we inline this, it's not longer a singleton under Android (at least under the Lollipop + * version that we're testing under) or J2CL. + * + * That's not necessarily a real-world problem, but it does break our tests. + */ + @SuppressWarnings({"InlineLambdaConstant", "UnnecessaryLambda"}) + private static final Runnable EMPTY_RUNNABLE = () -> {}; /** Returns a {@link Runnable} instance that does nothing when run. */ public static Runnable doNothing() { diff --git a/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java b/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java index d0b600be840e..3bff3b628a26 100644 --- a/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java +++ b/android/guava/src/com/google/common/util/concurrent/SequentialExecutor.java @@ -22,16 +22,17 @@ import static java.lang.System.identityHashCode; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.concurrent.GuardedBy; +import com.google.errorprone.annotations.concurrent.LazyInit; import com.google.j2objc.annotations.RetainedWith; import java.util.ArrayDeque; import java.util.Deque; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * Executor ensuring that all Runnables submitted are executed in order, using the provided @@ -47,10 +48,10 @@ * If an {@code Error} is thrown, the error will propagate and execution will stop until it is * restarted by a call to {@link #execute}. */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class SequentialExecutor implements Executor { - private static final Logger log = Logger.getLogger(SequentialExecutor.class.getName()); + private static final LazyLogger log = new LazyLogger(SequentialExecutor.class); enum WorkerRunningState { /** Runnable is not running and not queued for execution */ @@ -69,6 +70,7 @@ enum WorkerRunningState { private final Deque queue = new ArrayDeque<>(); /** see {@link WorkerRunningState} */ + @LazyInit @GuardedBy("queue") private WorkerRunningState workerRunningState = IDLE; @@ -134,7 +136,8 @@ public String toString() { try { executor.execute(worker); - } catch (RuntimeException | Error t) { + } catch (Throwable t) { + // Any Exception is either a RuntimeException or sneaky checked exception. synchronized (queue) { boolean removed = (workerRunningState == IDLE || workerRunningState == QUEUING) @@ -172,7 +175,7 @@ public String toString() { /** Worker that runs tasks from {@link #queue} until it is empty. */ private final class QueueWorker implements Runnable { - @CheckForNull Runnable task; + @Nullable Runnable task; @Override public void run() { @@ -200,6 +203,7 @@ public void run() { * will still be present. If the composed Executor is an ExecutorService, it can respond to * shutdown() by returning tasks queued on that Thread after {@link #worker} drains the queue. */ + @SuppressWarnings("CatchingUnchecked") // sneaky checked exception private void workOnQueue() { boolean interruptedDuringTask = false; boolean hasSetRunning = false; @@ -233,8 +237,8 @@ private void workOnQueue() { interruptedDuringTask |= Thread.interrupted(); try { task.run(); - } catch (RuntimeException e) { - log.log(Level.SEVERE, "Exception while executing runnable " + task, e); + } catch (Exception e) { // sneaky checked exception + log.get().log(Level.SEVERE, "Exception while executing runnable " + task, e); } finally { task = null; } diff --git a/android/guava/src/com/google/common/util/concurrent/Service.java b/android/guava/src/com/google/common/util/concurrent/Service.java index 0999a30ae5f4..4c03738ec598 100644 --- a/android/guava/src/com/google/common/util/concurrent/Service.java +++ b/android/guava/src/com/google/common/util/concurrent/Service.java @@ -15,6 +15,7 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; import java.util.concurrent.Executor; @@ -52,8 +53,8 @@ * @since 9.0 (in 1.0 as {@code com.google.common.base.Service}) */ @DoNotMock("Create an AbstractIdleService") +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public interface Service { /** * If the service state is {@link State#NEW}, this initiates service startup and returns @@ -209,6 +210,9 @@ enum State { * @since 15.0 (present as an interface in 13.0) */ abstract class Listener { + /** Constructor for use by subclasses. */ + public Listener() {} + /** * Called when the service transitions from {@linkplain State#NEW NEW} to {@linkplain * State#STARTING STARTING}. This occurs when {@link Service#startAsync} is called the first diff --git a/android/guava/src/com/google/common/util/concurrent/ServiceManager.java b/android/guava/src/com/google/common/util/concurrent/ServiceManager.java index 2e1c21a1fbc0..8384d2e84dfe 100644 --- a/android/guava/src/com/google/common/util/concurrent/ServiceManager.java +++ b/android/guava/src/com/google/common/util/concurrent/ServiceManager.java @@ -21,6 +21,7 @@ import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.instanceOf; import static com.google.common.base.Predicates.not; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.Service.State.FAILED; import static com.google.common.util.concurrent.Service.State.NEW; @@ -28,10 +29,11 @@ import static com.google.common.util.concurrent.Service.State.STARTING; import static com.google.common.util.concurrent.Service.State.STOPPING; import static com.google.common.util.concurrent.Service.State.TERMINATED; +import static java.util.Collections.sort; import static java.util.concurrent.TimeUnit.MILLISECONDS; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Function; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.MoreObjects; import com.google.common.base.Stopwatch; import com.google.common.collect.Collections2; @@ -50,18 +52,18 @@ import com.google.common.util.concurrent.Service.State; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.concurrent.GuardedBy; +import com.google.j2objc.annotations.J2ObjCIncompatible; import com.google.j2objc.annotations.WeakOuter; import java.lang.ref.WeakReference; -import java.util.Collections; +import java.time.Duration; import java.util.EnumSet; +import java.util.IdentityHashMap; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; -import java.util.logging.Logger; /** * A manager for monitoring and controlling a set of {@linkplain Service services}. This class @@ -117,10 +119,10 @@ * @author Luke Sandberg * @since 14.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ServiceManager implements ServiceManagerBridge { - private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); + private static final LazyLogger logger = new LazyLogger(ServiceManager.class); private static final ListenerCallQueue.Event HEALTHY_EVENT = new ListenerCallQueue.Event() { @Override @@ -156,6 +158,9 @@ public String toString() { * @since 15.0 (present as an interface in 14.0) */ public abstract static class Listener { + /** Constructor for use by subclasses. */ + public Listener() {} + /** * Called when the service initially becomes healthy. * @@ -203,10 +208,13 @@ public ServiceManager(Iterable services) { if (copy.isEmpty()) { // Having no services causes the manager to behave strangely. Notably, listeners are never // fired. To avoid this we substitute a placeholder service. - logger.log( - Level.WARNING, - "ServiceManager configured with no services. Is your application configured properly?", - new EmptyServiceManagerWarning()); + logger + .get() + .log( + Level.WARNING, + "ServiceManager configured with no services. Is your application configured" + + " properly?", + new EmptyServiceManagerWarning()); copy = ImmutableList.of(new NoOpService()); } this.state = new ServiceManagerState(copy); @@ -262,8 +270,7 @@ public void addListener(Listener listener, Executor executor) { @CanIgnoreReturnValue public ServiceManager startAsync() { for (Service service : services) { - State state = service.state(); - checkState(state == NEW, "Service %s is %s, cannot start it.", service, state); + checkState(service.state() == NEW, "Not all services are NEW, cannot start %s", this); } for (Service service : services) { try { @@ -274,7 +281,7 @@ public ServiceManager startAsync() { // service or listener). Our contract says it is safe to call this method if // all services were NEW when it was called, and this has already been verified above, so we // don't propagate the exception. - logger.log(Level.WARNING, "Unable to start Service " + service, e); + logger.get().log(Level.WARNING, "Unable to start Service " + service, e); } } return this; @@ -292,6 +299,23 @@ public void awaitHealthy() { state.awaitHealthy(); } + /** + * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more + * than the given time. The manager will become healthy after all the component services have + * reached the {@linkplain State#RUNNING running} state. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if not all of the services have finished starting within the deadline + * @throws IllegalStateException if the service manager reaches a state from which it cannot + * become {@linkplain #isHealthy() healthy}. + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public void awaitHealthy(Duration timeout) throws TimeoutException { + awaitHealthy(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more * than the given time. The manager will become healthy after all the component services have @@ -331,6 +355,21 @@ public void awaitStopped() { state.awaitStopped(); } + /** + * Waits for the all the services to reach a terminal state for no more than the given time. After + * this method returns all services will either be {@linkplain Service.State#TERMINATED + * terminated} or {@linkplain Service.State#FAILED failed}. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if not all of the services have stopped within the deadline + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public void awaitStopped(Duration timeout) throws TimeoutException { + awaitStopped(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Waits for the all the services to reach a terminal state for no more than the given time. After * this method returns all services will either be {@linkplain Service.State#TERMINATED @@ -384,6 +423,23 @@ public ImmutableMap startupTimes() { return state.startupTimes(); } + /** + * Returns the service load times. This value will only return startup times for services that + * have finished starting. + * + * @return Map of services and their corresponding startup time, the map entries will be ordered + * by startup time. + * @since 33.4.0 (but since 31.0 in the JRE flavor) + */ + @J2ObjCIncompatible + @SuppressWarnings("Java7ApiChecker") + // If users use this when they shouldn't, we hope that NewApi will catch subsequent Duration calls + @IgnoreJRERequirement + public ImmutableMap startupDurations() { + return ImmutableMap.copyOf( + Maps.transformValues(startupTimes(), Duration::ofMillis)); + } + @Override public String toString() { return MoreObjects.toStringHelper(ServiceManager.class) @@ -406,7 +462,7 @@ private static final class ServiceManagerState { final Multiset states = servicesByState.keys(); @GuardedBy("monitor") - final Map startupTimers = Maps.newIdentityHashMap(); + final IdentityHashMap startupTimers = new IdentityHashMap<>(); /** * These two booleans are used to mark the state as ready to start. @@ -604,16 +660,7 @@ ImmutableMap startupTimes() { } finally { monitor.leave(); } - Collections.sort( - loadTimes, - Ordering.natural() - .onResultOf( - new Function, Long>() { - @Override - public Long apply(Entry input) { - return input.getValue(); - } - })); + sort(loadTimes, Ordering.natural().onResultOf(Entry::getValue)); return ImmutableMap.copyOf(loadTimes); } @@ -629,7 +676,7 @@ public Long apply(Entry input) { *

    • Run the listeners (outside of the lock) * */ - void transitionService(final Service service, State from, State to) { + void transitionService(Service service, State from, State to) { checkNotNull(service); checkArgument(from != to); monitor.enter(); @@ -660,7 +707,7 @@ void transitionService(final Service service, State from, State to) { // N.B. if we miss the STARTING event then we may never record a startup time. stopwatch.stop(); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); + logger.get().log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); } } // Queue our listeners @@ -692,7 +739,7 @@ void enqueueHealthyEvent() { listeners.enqueue(HEALTHY_EVENT); } - void enqueueFailedEvent(final Service service) { + void enqueueFailedEvent(Service service) { listeners.enqueue( new ListenerCallQueue.Event() { @Override @@ -722,6 +769,9 @@ void checkHealthy() { new IllegalStateException( "Expected to be healthy after starting. The following services are not running: " + Multimaps.filterKeys(servicesByState, not(equalTo(RUNNING)))); + for (Service service : servicesByState.get(State.FAILED)) { + exception.addSuppressed(new FailedService(service)); + } throw exception; } } @@ -749,7 +799,7 @@ public void starting() { if (state != null) { state.transitionService(service, NEW, STARTING); if (!(service instanceof NoOpService)) { - logger.log(Level.FINE, "Starting {0}.", service); + logger.get().log(Level.FINE, "Starting {0}.", service); } } } @@ -775,10 +825,12 @@ public void terminated(State from) { ServiceManagerState state = this.state.get(); if (state != null) { if (!(service instanceof NoOpService)) { - logger.log( - Level.FINE, - "Service {0} has terminated. Previous state was: {1}", - new Object[] {service, from}); + logger + .get() + .log( + Level.FINE, + "Service {0} has terminated. Previous state was: {1}", + new Object[] {service, from}); } state.transitionService(service, from, TERMINATED); } @@ -791,11 +843,18 @@ public void failed(State from, Throwable failure) { // Log before the transition, so that if the process exits in response to server failure, // there is a higher likelihood that the cause will be in the logs. boolean log = !(service instanceof NoOpService); + /* + * We have already exposed startup exceptions to the user in the form of suppressed + * exceptions. We don't need to log those exceptions again. + */ + log &= from != State.STARTING; if (log) { - logger.log( - Level.SEVERE, - "Service " + service + " has failed in the " + from + " state.", - failure); + logger + .get() + .log( + Level.SEVERE, + "Service " + service + " has failed in the " + from + " state.", + failure); } state.transitionService(service, from, FAILED); } @@ -824,4 +883,14 @@ protected void doStop() { /** This is never thrown but only used for logging. */ private static final class EmptyServiceManagerWarning extends Throwable {} + + private static final class FailedService extends Throwable { + FailedService(Service service) { + super( + service.toString(), + service.failureCause(), + false /* don't enable suppression */, + false /* don't calculate a stack trace. */); + } + } } diff --git a/android/guava/src/com/google/common/util/concurrent/ServiceManagerBridge.java b/android/guava/src/com/google/common/util/concurrent/ServiceManagerBridge.java index 7ae430e3d251..5f959acceb37 100644 --- a/android/guava/src/com/google/common/util/concurrent/ServiceManagerBridge.java +++ b/android/guava/src/com/google/common/util/concurrent/ServiceManagerBridge.java @@ -17,6 +17,7 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableMultimap; import com.google.common.util.concurrent.Service.State; @@ -25,8 +26,8 @@ * servicesByState()}, to ensure binary compatibility with older Guava versions that specified * {@code servicesByState()} to return {@code ImmutableMultimap}. */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault interface ServiceManagerBridge { ImmutableMultimap servicesByState(); } diff --git a/android/guava/src/com/google/common/util/concurrent/SettableFuture.java b/android/guava/src/com/google/common/util/concurrent/SettableFuture.java index 0a9e194657f8..cc0714ecc7c1 100644 --- a/android/guava/src/com/google/common/util/concurrent/SettableFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/SettableFuture.java @@ -16,7 +16,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link ListenableFuture} whose result can be set by a {@link #set(Object)}, {@link @@ -33,7 +33,6 @@ * @since 9.0 (in 1.0 as {@code ValueFuture}) */ @GwtCompatible -@ElementTypesAreNonnullByDefault public final class SettableFuture extends AbstractFuture.TrustedFuture { /** diff --git a/android/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java b/android/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java index c6ade6a3a0a5..378dc966f660 100644 --- a/android/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java +++ b/android/guava/src/com/google/common/util/concurrent/SimpleTimeLimiter.java @@ -16,9 +16,10 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ObjectArrays; import com.google.common.collect.Sets; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -34,8 +35,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A TimeLimiter that runs method calls in the background using an {@link ExecutorService}. If the @@ -45,9 +45,10 @@ * @author Jens Nyman * @since 1.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault +// TODO: b/227335009 - Maybe change interruption behavior, but it requires thought. +@SuppressWarnings("Interruption") public final class SimpleTimeLimiter implements TimeLimiter { private final ExecutorService executor; @@ -83,22 +84,17 @@ public T newProxy( Set interruptibleMethods = findInterruptibleMethods(interfaceType); InvocationHandler handler = - new InvocationHandler() { - @Override - @CheckForNull - public Object invoke(Object obj, Method method, @CheckForNull @Nullable Object[] args) - throws Throwable { - Callable<@Nullable Object> callable = - () -> { - try { - return method.invoke(target, args); - } catch (InvocationTargetException e) { - throw throwCause(e, false /* combineStackTraces */); - } - }; - return callWithTimeout( - callable, timeoutDuration, timeoutUnit, interruptibleMethods.contains(method)); - } + (obj, method, args) -> { + Callable<@Nullable Object> callable = + () -> { + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throw throwCause(e, /* combineStackTraces= */ false); + } + }; + return callWithTimeout( + callable, timeoutDuration, timeoutUnit, interruptibleMethods.contains(method)); }; return newProxy(interfaceType, handler); } @@ -111,6 +107,7 @@ private static T newProxy(Class interfaceType, InvocationHandler handler) return interfaceType.cast(object); } + @ParametricNullness private T callWithTimeout( Callable callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { @@ -121,16 +118,12 @@ private static T newProxy(Class interfaceType, InvocationHandler handler) Future future = executor.submit(callable); try { - if (amInterruptible) { - try { - return future.get(timeoutDuration, timeoutUnit); - } catch (InterruptedException e) { - future.cancel(true); - throw e; - } - } else { - return Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); - } + return amInterruptible + ? future.get(timeoutDuration, timeoutUnit) + : getUninterruptibly(future, timeoutDuration, timeoutUnit); + } catch (InterruptedException e) { + future.cancel(true); + throw e; } catch (ExecutionException e) { throw throwCause(e, true /* combineStackTraces */); } catch (TimeoutException e) { @@ -141,6 +134,7 @@ private static T newProxy(Class interfaceType, InvocationHandler handler) @CanIgnoreReturnValue @Override + @ParametricNullness public T callWithTimeout( Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException, InterruptedException, ExecutionException { @@ -163,6 +157,7 @@ private static T newProxy(Class interfaceType, InvocationHandler handler) @CanIgnoreReturnValue @Override + @ParametricNullness public T callUninterruptiblyWithTimeout( Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException, ExecutionException { @@ -173,7 +168,7 @@ private static T newProxy(Class interfaceType, InvocationHandler handler) Future future = executor.submit(callable); try { - return Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); + return getUninterruptibly(future, timeoutDuration, timeoutUnit); } catch (TimeoutException e) { future.cancel(true /* mayInterruptIfRunning */); throw e; @@ -213,7 +208,7 @@ public void runUninterruptiblyWithTimeout( Future future = executor.submit(runnable); try { - Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); + getUninterruptibly(future, timeoutDuration, timeoutUnit); } catch (TimeoutException e) { future.cancel(true /* mayInterruptIfRunning */); throw e; diff --git a/android/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java b/android/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java index ca78905908ed..24fcf5d489dd 100644 --- a/android/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java +++ b/android/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java @@ -18,11 +18,12 @@ import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.math.LongMath; import java.util.concurrent.TimeUnit; +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class SmoothRateLimiter extends RateLimiter { /* * How is the RateLimiter designed, and why? @@ -204,6 +205,7 @@ abstract class SmoothRateLimiter extends RateLimiter { */ static final class SmoothWarmingUp extends SmoothRateLimiter { private final long warmupPeriodMicros; + /** * The slope of the line from the stable interval (when permits == 0), to the cold interval * (when permits == maxPermits) diff --git a/android/guava/src/com/google/common/util/concurrent/SneakyThrows.java b/android/guava/src/com/google/common/util/concurrent/SneakyThrows.java new file mode 100644 index 000000000000..d0c97b62affd --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/SneakyThrows.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +/** Static utility method for unchecked throwing of any {@link Throwable}. */ +@GwtCompatible +final class SneakyThrows { + /** + * Throws {@code t} as if it were an unchecked {@link Throwable}. + * + *

      This method is useful primarily when we make a reflective call to a method with no {@code + * throws} clause: Java forces us to handle an arbitrary {@link Throwable} from that method, + * rather than just the {@link RuntimeException} or {@link Error} that should be possible. (And in + * fact the static type of {@link Throwable} is occasionally justified even for a method with no + * {@code throws} clause: Some such methods can in fact throw a checked exception (e.g., by + * calling code written in Kotlin).) Typically, we want to let a {@link Throwable} from such a + * method propagate untouched, just as we'd typically let it do for a non-reflective call. + * However, we can't usually write {@code throw t;} when {@code t} has a static type of {@link + * Throwable}. But we can write {@code sneakyThrow(t);}. + * + *

      We sometimes also use {@code sneakyThrow} for testing how our code responds to + * sneaky checked exception. + * + * @return never; this method declares a return type of {@link Error} only so that callers can + * write {@code throw sneakyThrow(t);} to convince the compiler that the statement will always + * throw. + */ + @CanIgnoreReturnValue + static Error sneakyThrow(Throwable t) { + throw new SneakyThrows().throwIt(t); + } + + @SuppressWarnings("unchecked") // not really safe, but that's the point + private Error throwIt(Throwable t) throws T { + throw (T) t; + } + + private SneakyThrows() {} +} diff --git a/android/guava/src/com/google/common/util/concurrent/Striped.java b/android/guava/src/com/google/common/util/concurrent/Striped.java index 2ea61cb3248a..3391e209b3eb 100644 --- a/android/guava/src/com/google/common/util/concurrent/Striped.java +++ b/android/guava/src/com/google/common/util/concurrent/Striped.java @@ -16,8 +16,8 @@ import static com.google.common.collect.Lists.newArrayList; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -41,7 +41,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A striped {@code Lock/Semaphore/ReadWriteLock}. This offers the underlying lock striping similar @@ -82,9 +82,8 @@ * @author Dimitris Andreou * @since 13.0 */ -@Beta +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public abstract class Striped { /** * If there are at least this many stripes, we assume the memory usage of a ConcurrentMap will be @@ -217,10 +216,18 @@ public static Striped lock(int stripes) { * @return a new {@code Striped} */ public static Striped lazyWeakLock(int stripes) { - return lazy(stripes, () -> new ReentrantLock(false)); + return lazyWeakCustom(stripes, () -> new ReentrantLock(false)); } - private static Striped lazy(int stripes, Supplier supplier) { + /** + * Creates a {@code Striped} with lazily initialized, weakly referenced locks. Every lock is + * obtained from the passed supplier. + * + * @param stripes the minimum number of stripes (locks) required + * @param supplier a {@code Supplier} object to obtain locks from + * @return a new {@code Striped} + */ + static Striped lazyWeakCustom(int stripes, Supplier supplier) { return stripes < LARGE_LAZY_CUTOFF ? new SmallLazyStriped(stripes, supplier) : new LargeLazyStriped(stripes, supplier); @@ -247,7 +254,7 @@ public static Striped semaphore(int stripes, int permits) { * @return a new {@code Striped} */ public static Striped lazyWeakSemaphore(int stripes, int permits) { - return lazy(stripes, () -> new Semaphore(permits, false)); + return lazyWeakCustom(stripes, () -> new Semaphore(permits, false)); } /** @@ -269,8 +276,9 @@ public static Striped readWriteLock(int stripes) { * @return a new {@code Striped} */ public static Striped lazyWeakReadWriteLock(int stripes) { - return lazy(stripes, WeakSafeReadWriteLock::new); + return lazyWeakCustom(stripes, WeakSafeReadWriteLock::new); } + /** * ReadWriteLock implementation whose read and write locks retain a reference back to this lock. * Otherwise, a reference to just the read lock or just the write lock would not suffice to ensure diff --git a/android/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java b/android/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java index 091f56e799c0..1241ed07457c 100644 --- a/android/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java +++ b/android/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java @@ -19,14 +19,14 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.CheckReturnValue; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Locale; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.CheckForNull; +import org.jspecify.annotations.Nullable; /** * A ThreadFactory builder, providing any combination of these features: @@ -42,18 +42,21 @@ *

      If no backing thread factory is provided, a default backing thread factory is used as if by * calling {@code setThreadFactory(}{@link Executors#defaultThreadFactory()}{@code )}. * + *

      Java 21+ users: consider using the {@code Thread.Builder} interface instead. E.g., + * instead of {@code new ThreadFactoryBuilder().setPriority(priority).setDaemon(false).build()}, use + * {@code Thread.ofPlatform().priority(priority).daemon(false).factory()}. + * * @author Kurt Alfred Kluever * @since 4.0 */ -@CanIgnoreReturnValue +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class ThreadFactoryBuilder { - @CheckForNull private String nameFormat = null; - @CheckForNull private Boolean daemon = null; - @CheckForNull private Integer priority = null; - @CheckForNull private UncaughtExceptionHandler uncaughtExceptionHandler = null; - @CheckForNull private ThreadFactory backingThreadFactory = null; + private @Nullable String nameFormat = null; + private @Nullable Boolean daemon = null; + private @Nullable Integer priority = null; + private @Nullable UncaughtExceptionHandler uncaughtExceptionHandler = null; + private @Nullable ThreadFactory backingThreadFactory = null; /** Creates a new {@link ThreadFactory} builder. */ public ThreadFactoryBuilder() {} @@ -69,6 +72,7 @@ public ThreadFactoryBuilder() {} * "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. * @return this for the builder pattern */ + @CanIgnoreReturnValue public ThreadFactoryBuilder setNameFormat(String nameFormat) { String unused = format(nameFormat, 0); // fail fast if the format is bad or null this.nameFormat = nameFormat; @@ -81,6 +85,7 @@ public ThreadFactoryBuilder setNameFormat(String nameFormat) { * @param daemon whether or not new Threads created with this ThreadFactory will be daemon threads * @return this for the builder pattern */ + @CanIgnoreReturnValue public ThreadFactoryBuilder setDaemon(boolean daemon) { this.daemon = daemon; return this; @@ -89,9 +94,13 @@ public ThreadFactoryBuilder setDaemon(boolean daemon) { /** * Sets the priority for new threads created with this ThreadFactory. * + *

      Warning: relying on the thread scheduler is discouraged. + * * @param priority the priority for new Threads created with this ThreadFactory * @return this for the builder pattern */ + @CanIgnoreReturnValue public ThreadFactoryBuilder setPriority(int priority) { // Thread#setPriority() already checks for validity. These error messages // are nicer though and will fail-fast. @@ -116,6 +125,7 @@ public ThreadFactoryBuilder setPriority(int priority) { * this ThreadFactory * @return this for the builder pattern */ + @CanIgnoreReturnValue public ThreadFactoryBuilder setUncaughtExceptionHandler( UncaughtExceptionHandler uncaughtExceptionHandler) { this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler); @@ -131,6 +141,7 @@ public ThreadFactoryBuilder setUncaughtExceptionHandler( * @return this for the builder pattern * @see MoreExecutors */ + @CanIgnoreReturnValue public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) { this.backingThreadFactory = checkNotNull(backingThreadFactory); return this; @@ -143,7 +154,6 @@ public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) * * @return the fully constructed {@link ThreadFactory} */ - @CheckReturnValue public ThreadFactory build() { return doBuild(this); } @@ -164,6 +174,8 @@ private static ThreadFactory doBuild(ThreadFactoryBuilder builder) { @Override public Thread newThread(Runnable runnable) { Thread thread = backingThreadFactory.newThread(runnable); + // TODO(b/139735208): Figure out what to do when the factory returns null. + requireNonNull(thread); if (nameFormat != null) { // requireNonNull is safe because we create `count` if (and only if) we have a nameFormat. thread.setName(format(nameFormat, requireNonNull(count).getAndIncrement())); diff --git a/android/guava/src/com/google/common/util/concurrent/TimeLimiter.java b/android/guava/src/com/google/common/util/concurrent/TimeLimiter.java index 0883bccee0c1..788907a69073 100644 --- a/android/guava/src/com/google/common/util/concurrent/TimeLimiter.java +++ b/android/guava/src/com/google/common/util/concurrent/TimeLimiter.java @@ -14,15 +14,15 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.DoNotMock; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Imposes a time limit on method calls. @@ -31,11 +31,10 @@ * @author Jens Nyman * @since 1.0 */ -@Beta @DoNotMock("Use FakeTimeLimiter") +@J2ktIncompatible @GwtIncompatible @SuppressWarnings("GoodTime") // should have java.time.Duration overloads -@ElementTypesAreNonnullByDefault public interface TimeLimiter { /** @@ -99,6 +98,7 @@ public interface TimeLimiter { * @since 22.0 */ @CanIgnoreReturnValue + @ParametricNullness T callWithTimeout( Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException, InterruptedException, ExecutionException; @@ -123,6 +123,7 @@ public interface TimeLimiter { * @since 22.0 */ @CanIgnoreReturnValue + @ParametricNullness T callUninterruptiblyWithTimeout( Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException, ExecutionException; diff --git a/android/guava/src/com/google/common/util/concurrent/TimeoutFuture.java b/android/guava/src/com/google/common/util/concurrent/TimeoutFuture.java index ed8a7bfdbe70..b914a2fac915 100644 --- a/android/guava/src/com/google/common/util/concurrent/TimeoutFuture.java +++ b/android/guava/src/com/google/common/util/concurrent/TimeoutFuture.java @@ -15,17 +15,20 @@ package com.google.common.util.concurrent; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.google.j2objc.annotations.RetainedLocalRef; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Implementation of {@code Futures#withTimeout}. @@ -34,8 +37,8 @@ * in an {@link ExecutionException}) if the specified duration expires. The delegate future is * interrupted and cancelled if it times out. */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault final class TimeoutFuture extends FluentFuture.TrustedFuture { static ListenableFuture create( ListenableFuture delegate, @@ -73,8 +76,8 @@ final class TimeoutFuture extends FluentFuture.Trust * write-barriers). */ - @CheckForNull private ListenableFuture delegateRef; - @CheckForNull private ScheduledFuture timer; + @LazyInit private @Nullable ListenableFuture delegateRef; + @LazyInit private @Nullable ScheduledFuture timer; private TimeoutFuture(ListenableFuture delegate) { this.delegateRef = Preconditions.checkNotNull(delegate); @@ -82,13 +85,15 @@ private TimeoutFuture(ListenableFuture delegate) { /** A runnable that is called when the delegate or the timer completes. */ private static final class Fire implements Runnable { - @CheckForNull TimeoutFuture timeoutFutureRef; + @LazyInit @Nullable TimeoutFuture timeoutFutureRef; Fire(TimeoutFuture timeoutFuture) { this.timeoutFutureRef = timeoutFuture; } @Override + // TODO: b/227335009 - Maybe change interruption behavior, but it requires thought. + @SuppressWarnings("Interruption") public void run() { // If either of these reads return null then we must be after a successful cancel or another // call to this method. @@ -96,7 +101,7 @@ public void run() { if (timeoutFuture == null) { return; } - ListenableFuture delegate = timeoutFuture.delegateRef; + @RetainedLocalRef ListenableFuture delegate = timeoutFuture.delegateRef; if (delegate == null) { return; } @@ -118,14 +123,14 @@ public void run() { timeoutFuture.setFuture(delegate); } else { try { - ScheduledFuture timer = timeoutFuture.timer; + @RetainedLocalRef ScheduledFuture timer = timeoutFuture.timer; timeoutFuture.timer = null; // Don't include already elapsed delay in delegate.toString() String message = "Timed out"; // This try-finally block ensures that we complete the timeout future, even if attempting // to produce the message throws (probably StackOverflowError from delegate.toString()) try { if (timer != null) { - long overDelayMs = Math.abs(timer.getDelay(TimeUnit.MILLISECONDS)); + long overDelayMs = Math.abs(timer.getDelay(MILLISECONDS)); if (overDelayMs > 10) { // Not all timing drift is worth reporting message += " (timeout delayed by " + overDelayMs + " ms after scheduled time)"; } @@ -154,14 +159,13 @@ public synchronized Throwable fillInStackTrace() { } @Override - @CheckForNull - protected String pendingToString() { - ListenableFuture localInputFuture = delegateRef; - ScheduledFuture localTimer = timer; + protected @Nullable String pendingToString() { + @RetainedLocalRef ListenableFuture localInputFuture = delegateRef; + @RetainedLocalRef ScheduledFuture localTimer = timer; if (localInputFuture != null) { String message = "inputFuture=[" + localInputFuture + "]"; if (localTimer != null) { - long delay = localTimer.getDelay(TimeUnit.MILLISECONDS); + long delay = localTimer.getDelay(MILLISECONDS); // Negative delays look confusing in an error message if (delay > 0) { message += ", remaining delay=[" + delay + " ms]"; @@ -174,9 +178,10 @@ protected String pendingToString() { @Override protected void afterDone() { - maybePropagateCancellationTo(delegateRef); + @RetainedLocalRef ListenableFuture delegate = delegateRef; + maybePropagateCancellationTo(delegate); - Future localTimer = timer; + @RetainedLocalRef Future localTimer = timer; // Try to cancel the timer as an optimization. // timer may be null if this call to run was by the timer task since there is no happens-before // edge between the assignment to timer and an execution of the timer task. diff --git a/android/guava/src/com/google/common/util/concurrent/TrustedListenableFutureTask.java b/android/guava/src/com/google/common/util/concurrent/TrustedListenableFutureTask.java index 929c9fb1ea23..4549eda39940 100644 --- a/android/guava/src/com/google/common/util/concurrent/TrustedListenableFutureTask.java +++ b/android/guava/src/com/google/common/util/concurrent/TrustedListenableFutureTask.java @@ -21,8 +21,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.RunnableFuture; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * A {@link RunnableFuture} that also implements the {@link ListenableFuture} interface. @@ -31,7 +30,6 @@ * performance reasons. */ @GwtCompatible -@ElementTypesAreNonnullByDefault class TrustedListenableFutureTask extends FluentFuture.TrustedFuture implements RunnableFuture { @@ -65,7 +63,7 @@ class TrustedListenableFutureTask extends FluentFutu *

      {@code volatile} is required for j2objc transpiling: * https://developers.google.com/j2objc/guides/j2objc-memory-model#atomicity */ - @CheckForNull private volatile InterruptibleTask task; + private volatile @Nullable InterruptibleTask task; TrustedListenableFutureTask(Callable callable) { this.task = new TrustedFutureInterruptibleTask(callable); @@ -103,8 +101,7 @@ protected void afterDone() { } @Override - @CheckForNull - protected String pendingToString() { + protected @Nullable String pendingToString() { InterruptibleTask localTask = task; if (localTask != null) { return "task=[" + localTask + "]"; diff --git a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java index a1add8bcbb30..e342016e75ac 100644 --- a/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java +++ b/android/guava/src/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -17,10 +17,10 @@ import static java.util.logging.Level.SEVERE; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.annotations.VisibleForTesting; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Locale; -import java.util.logging.Logger; /** * Factories for {@link UncaughtExceptionHandler} instances. @@ -28,8 +28,8 @@ * @author Gregory Kick * @since 8.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public final class UncaughtExceptionHandlers { private UncaughtExceptionHandlers() {} @@ -50,25 +50,34 @@ private UncaughtExceptionHandlers() {} * process with an exit status of 1, indicating abnormal termination. */ public static UncaughtExceptionHandler systemExit() { - return new Exiter(Runtime.getRuntime()); + return new Exiter(Runtime.getRuntime()::exit); + } + + @VisibleForTesting + interface RuntimeWrapper { + void exit(int status); } @VisibleForTesting static final class Exiter implements UncaughtExceptionHandler { - private static final Logger logger = Logger.getLogger(Exiter.class.getName()); + private static final LazyLogger logger = new LazyLogger(Exiter.class); - private final Runtime runtime; + private final RuntimeWrapper runtime; - Exiter(Runtime runtime) { + Exiter(RuntimeWrapper runtime) { this.runtime = runtime; } @Override public void uncaughtException(Thread t, Throwable e) { try { - logger.log( - SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); - } catch (Throwable errorInLogging) { + logger + .get() + .log( + SEVERE, + String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), + e); + } catch (Throwable errorInLogging) { // sneaky checked exception // If logging fails, e.g. due to missing memory, at least try to log the // message and the cause for the failed logging. System.err.println(e.getMessage()); diff --git a/android/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java b/android/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java index 713f4a69e78d..93424486c0d5 100644 --- a/android/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java +++ b/android/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java @@ -15,7 +15,9 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with {@code @@ -33,31 +35,61 @@ * @since 10.0 */ @GwtCompatible -@ElementTypesAreNonnullByDefault public class UncheckedExecutionException extends RuntimeException { /* - * Ideally, this class would have exposed only constructors that require a non-null cause. We - * might try to move in that direction, but there are complications. See - * https://github.com/jspecify/nullness-checker-for-checker-framework/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + * Ideally, this class would have exposed only constructors that require a non-null cause. See + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + * and https://github.com/jspecify/jspecify/issues/490. + * + * (Perhaps it should also have required that its cause was a RuntimeException. However, that + * would have required that we throw a different kind of exception for wrapping *checked* + * exceptions in methods like Futures.getUnchecked and LoadingCache.get.) */ - /** Creates a new instance with {@code null} as its detail message. */ + /** + * Creates a new instance with {@code null} as its detail message and no cause. + * + * @deprecated Prefer {@linkplain UncheckedExecutionException(Throwable)} a constructor that + * accepts a cause: Users of this class typically expect for instances to have a non-null + * cause. At the moment, you can usually still preserve behavior by passing an explicit + * {@code null} cause. Note, however, that passing an explicit {@code null} cause prevents + * anyone from calling {@link #initCause} later, so it is not quite equivalent to using a + * constructor that omits the cause. + */ + @Deprecated protected UncheckedExecutionException() {} - /** Creates a new instance with the given detail message. */ - protected UncheckedExecutionException(@CheckForNull String message) { + /** + * Creates a new instance with the given detail message and no cause. + * + * @deprecated Prefer {@linkplain UncheckedExecutionException(String, Throwable)} a constructor + * that accepts a cause: Users of this class typically expect for instances to have a non-null + * cause. At the moment, you can usually still preserve behavior by passing an explicit + * {@code null} cause. Note, however, that passing an explicit {@code null} cause prevents + * anyone from calling {@link #initCause} later, so it is not quite equivalent to using a + * constructor that omits the cause. + */ + @SuppressWarnings("InlineMeSuggester") // b/387265535 + @Deprecated + protected UncheckedExecutionException(@Nullable String message) { super(message); } - /** Creates a new instance with the given detail message and cause. */ - public UncheckedExecutionException(@CheckForNull String message, @CheckForNull Throwable cause) { + /** + * Creates a new instance with the given detail message and cause. Prefer to provide a + * non-nullable {@code cause}, as many users expect to find one. + */ + public UncheckedExecutionException(@Nullable String message, @Nullable Throwable cause) { super(message, cause); } - /** Creates a new instance with the given cause. */ - public UncheckedExecutionException(@CheckForNull Throwable cause) { + /** + * Creates a new instance with {@code null} as its detail message and the given cause. Prefer to + * provide a non-nullable {@code cause}, as many users expect to find one. + */ + public UncheckedExecutionException(@Nullable Throwable cause) { super(cause); } - private static final long serialVersionUID = 0; + @GwtIncompatible @J2ktIncompatible private static final long serialVersionUID = 0; } diff --git a/android/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java b/android/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java index 8e30fca37fd1..375b712028ee 100644 --- a/android/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java +++ b/android/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java @@ -15,7 +15,8 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; -import javax.annotation.CheckForNull; +import com.google.common.annotations.J2ktIncompatible; +import org.jspecify.annotations.Nullable; /** * Unchecked version of {@link java.util.concurrent.TimeoutException}. @@ -23,20 +24,20 @@ * @author Kevin Bourrillion * @since 1.0 */ +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault public class UncheckedTimeoutException extends RuntimeException { public UncheckedTimeoutException() {} - public UncheckedTimeoutException(@CheckForNull String message) { + public UncheckedTimeoutException(@Nullable String message) { super(message); } - public UncheckedTimeoutException(@CheckForNull Throwable cause) { + public UncheckedTimeoutException(@Nullable Throwable cause) { super(cause); } - public UncheckedTimeoutException(@CheckForNull String message, @CheckForNull Throwable cause) { + public UncheckedTimeoutException(@Nullable String message, @Nullable Throwable cause) { super(message, cause); } diff --git a/android/guava/src/com/google/common/util/concurrent/Uninterruptibles.java b/android/guava/src/com/google/common/util/concurrent/Uninterruptibles.java index a33ba8286df1..89e6b0710618 100644 --- a/android/guava/src/com/google/common/util/concurrent/Uninterruptibles.java +++ b/android/guava/src/com/google/common/util/concurrent/Uninterruptibles.java @@ -15,13 +15,15 @@ package com.google.common.util.concurrent; import static com.google.common.base.Verify.verify; +import static com.google.common.util.concurrent.Internal.toNanosSaturated; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.base.Preconditions; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.time.Duration; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; @@ -33,7 +35,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is @@ -44,13 +46,13 @@ * @since 10.0 */ @GwtCompatible(emulated = true) -@ElementTypesAreNonnullByDefault public final class Uninterruptibles { // Implementation Note: As of 3-7-11, the logic for each blocking/timeout // methods is identical, save for method being invoked. /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */ + @J2ktIncompatible @GwtIncompatible // concurrency public static void awaitUninterruptibly(CountDownLatch latch) { boolean interrupted = false; @@ -73,8 +75,22 @@ public static void awaitUninterruptibly(CountDownLatch latch) { /** * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} * uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) */ - @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean awaitUninterruptibly(CountDownLatch latch, Duration timeout) { + return awaitUninterruptibly(latch, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) { @@ -99,12 +115,27 @@ public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, T } } + /** + * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean awaitUninterruptibly(Condition condition, Duration timeout) { + return awaitUninterruptibly(condition, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)} * uninterruptibly. * * @since 23.6 */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) { @@ -129,6 +160,7 @@ public static boolean awaitUninterruptibly(Condition condition, long timeout, Ti } /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */ + @J2ktIncompatible @GwtIncompatible // concurrency public static void joinUninterruptibly(Thread toJoin) { boolean interrupted = false; @@ -148,10 +180,25 @@ public static void joinUninterruptibly(Thread toJoin) { } } + /** + * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)} + * uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static void joinUninterruptibly(Thread toJoin, Duration timeout) { + joinUninterruptibly(toJoin, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)} * uninterruptibly. */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) { @@ -214,6 +261,36 @@ public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit uni } } + /** + * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly. + * + *

      Similar methods: + * + *

        + *
      • To retrieve a result from a {@code Future} that is already done, use {@link + * Futures#getDone Futures.getDone}. + *
      • To treat {@link InterruptedException} uniformly with other exceptions, use {@link + * Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}. + *
      • To get uninterruptibility and remove checked exceptions, use {@link + * Futures#getUnchecked}. + *
      + * + * @throws ExecutionException if the computation threw an exception + * @throws CancellationException if the computation was cancelled + * @throws TimeoutException if the wait timed out + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @CanIgnoreReturnValue + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @ParametricNullness + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static V getUninterruptibly( + Future future, Duration timeout) throws ExecutionException, TimeoutException { + return getUninterruptibly(future, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly. * @@ -233,6 +310,7 @@ public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit uni * @throws TimeoutException if the wait timed out */ @CanIgnoreReturnValue + @J2ktIncompatible @GwtIncompatible // TODO @SuppressWarnings("GoodTime") // should accept a java.time.Duration @ParametricNullness @@ -260,6 +338,7 @@ public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit uni } /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */ + @J2ktIncompatible @GwtIncompatible // concurrency public static E takeUninterruptibly(BlockingQueue queue) { boolean interrupted = false; @@ -286,6 +365,7 @@ public static E takeUninterruptibly(BlockingQueue queue) { * @throws IllegalArgumentException if some property of the specified element prevents it from * being added to the given queue */ + @J2ktIncompatible @GwtIncompatible // concurrency public static void putUninterruptibly(BlockingQueue queue, E element) { boolean interrupted = false; @@ -305,8 +385,23 @@ public static void putUninterruptibly(BlockingQueue queue, E element) { } } + // TODO(user): Support Sleeper somehow (wrapper or interface method)? + /** + * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static void sleepUninterruptibly(Duration sleepFor) { + sleepUninterruptibly(toNanosSaturated(sleepFor), TimeUnit.NANOSECONDS); + } + // TODO(user): Support Sleeper somehow (wrapper or interface method)? /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { @@ -331,12 +426,27 @@ public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { } } + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1, + * timeout, unit)} uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean tryAcquireUninterruptibly(Semaphore semaphore, Duration timeout) { + return tryAcquireUninterruptibly(semaphore, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1, * timeout, unit)} uninterruptibly. * * @since 18.0 */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean tryAcquireUninterruptibly( @@ -344,12 +454,29 @@ public static boolean tryAcquireUninterruptibly( return tryAcquireUninterruptibly(semaphore, 1, timeout, unit); } + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits, + * timeout, unit)} uninterruptibly. + * + * @since 33.4.0 (but since 28.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean tryAcquireUninterruptibly( + Semaphore semaphore, int permits, Duration timeout) { + return tryAcquireUninterruptibly( + semaphore, permits, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits, * timeout, unit)} uninterruptibly. * * @since 18.0 */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean tryAcquireUninterruptibly( @@ -375,12 +502,27 @@ public static boolean tryAcquireUninterruptibly( } } + /** + * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)} + * uninterruptibly. + * + * @since 33.4.0 (but since 30.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean tryLockUninterruptibly(Lock lock, Duration timeout) { + return tryLockUninterruptibly(lock, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + /** * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)} * uninterruptibly. * * @since 30.0 */ + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") // should accept a java.time.Duration public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit unit) { @@ -410,20 +552,35 @@ public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit u * * @since 30.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // concurrency public static void awaitTerminationUninterruptibly(ExecutorService executor) { // TODO(cpovirk): We could optimize this to avoid calling nanoTime() at all. verify(awaitTerminationUninterruptibly(executor, Long.MAX_VALUE, NANOSECONDS)); } + /** + * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit) + * awaitTermination(long, TimeUnit)} uninterruptibly. + * + * @since 33.4.0 (but since 30.0 in the JRE flavor) + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("Java7ApiChecker") + @IgnoreJRERequirement // Users will use this only if they're already using Duration. + public static boolean awaitTerminationUninterruptibly( + ExecutorService executor, Duration timeout) { + return awaitTerminationUninterruptibly(executor, toNanosSaturated(timeout), NANOSECONDS); + } + /** * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit) * awaitTermination(long, TimeUnit)} uninterruptibly. * * @since 30.0 */ - @Beta + @J2ktIncompatible @GwtIncompatible // concurrency @SuppressWarnings("GoodTime") public static boolean awaitTerminationUninterruptibly( diff --git a/android/guava/src/com/google/common/util/concurrent/WrappingExecutorService.java b/android/guava/src/com/google/common/util/concurrent/WrappingExecutorService.java index 5a3c3927a6b9..0bfcf66189ae 100644 --- a/android/guava/src/com/google/common/util/concurrent/WrappingExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/WrappingExecutorService.java @@ -16,8 +16,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Throwables.throwIfUnchecked; +import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Collection; @@ -29,7 +31,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An abstract {@code ExecutorService} that allows subclasses to {@linkplain #wrapTask(Callable) @@ -41,9 +43,8 @@ * * @author Chris Nokleberg */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class WrappingExecutorService implements ExecutorService { private final ExecutorService delegate; @@ -67,6 +68,7 @@ protected Runnable wrapTask(Runnable command) { try { wrapped.call(); } catch (Exception e) { + restoreInterruptIfIsInterruptedException(e); throwIfUnchecked(e); throw new RuntimeException(e); } @@ -143,6 +145,7 @@ public final void shutdown() { } @Override + @CanIgnoreReturnValue public final List shutdownNow() { return delegate.shutdownNow(); } diff --git a/android/guava/src/com/google/common/util/concurrent/WrappingScheduledExecutorService.java b/android/guava/src/com/google/common/util/concurrent/WrappingScheduledExecutorService.java index 48f23c12c7cb..4df26ddc6280 100644 --- a/android/guava/src/com/google/common/util/concurrent/WrappingScheduledExecutorService.java +++ b/android/guava/src/com/google/common/util/concurrent/WrappingScheduledExecutorService.java @@ -15,12 +15,12 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtIncompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.common.annotations.J2ktIncompatible; import java.util.concurrent.Callable; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.Nullable; /** * An abstract {@code ScheduledExecutorService} that allows subclasses to {@linkplain @@ -30,9 +30,8 @@ * * @author Luke Sandberg */ -@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict. +@J2ktIncompatible @GwtIncompatible -@ElementTypesAreNonnullByDefault abstract class WrappingScheduledExecutorService extends WrappingExecutorService implements ScheduledExecutorService { final ScheduledExecutorService delegate; diff --git a/android/guava/src/com/google/common/util/concurrent/package-info.java b/android/guava/src/com/google/common/util/concurrent/package-info.java index a2533c1fc8e9..c5b199b07292 100644 --- a/android/guava/src/com/google/common/util/concurrent/package-info.java +++ b/android/guava/src/com/google/common/util/concurrent/package-info.java @@ -15,19 +15,18 @@ /** * Concurrency utilities. * - *

      Commonly used types include {@link com.google.common.util.concurrent.ListenableFuture} and - * {@link com.google.common.util.concurrent.Service}. + *

      Commonly used types include {@link ClosingFuture}, {@link ListenableFuture}, and {@link + * Service}. * - *

      Commonly used utilities include {@link com.google.common.util.concurrent.Futures}, {@link - * com.google.common.util.concurrent.MoreExecutors}, and {@link - * com.google.common.util.concurrent.ThreadFactoryBuilder}. + *

      Commonly used utilities include {@link Futures}, {@link MoreExecutors}, {@link + * ThreadFactoryBuilder}, and {@link Uninterruptibles}. * - *

      This package is a part of the open-source Guava + *

      This package is a part of the open-source Guava * library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.util.concurrent; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/common/xml/ElementTypesAreNonnullByDefault.java b/android/guava/src/com/google/common/xml/ElementTypesAreNonnullByDefault.java deleted file mode 100644 index b4fb4e3fe470..000000000000 --- a/android/guava/src/com/google/common/xml/ElementTypesAreNonnullByDefault.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Guava Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.common.xml; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import com.google.common.annotations.GwtCompatible; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import javax.annotation.Nonnull; -import javax.annotation.meta.TypeQualifierDefault; - -/** - * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this - * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to - * "undo" it as best we can. - */ -@GwtCompatible -@Retention(RUNTIME) -@Target(TYPE) -@TypeQualifierDefault({FIELD, METHOD, PARAMETER}) -@Nonnull -@interface ElementTypesAreNonnullByDefault {} diff --git a/android/guava/src/com/google/common/xml/ParametricNullness.java b/android/guava/src/com/google/common/xml/ParametricNullness.java index e4e5d12f0e3b..e8af7cb05d02 100644 --- a/android/guava/src/com/google/common/xml/ParametricNullness.java +++ b/android/guava/src/com/google/common/xml/ParametricNullness.java @@ -19,34 +19,54 @@ import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.CLASS; import com.google.common.annotations.GwtCompatible; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * Marks a "top-level" type-variable usage as the closest we can get to "non-nullable when - * non-nullable; nullable when nullable" (like the Android {@code - * NullFromTypeParam}). + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

        + *
      • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
      • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
      + * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
        + *
      • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
      • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
      * *

      Consumers of this annotation include: * *

        - *
      • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type - * argument is non-nullable and (b) nullable when the type argument is nullable. We use this - * to "undo" {@link ElementTypesAreNonnullByDefault}. - *
      • J2ObjC - *
      • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations - * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
      • NullAway, which treats it + * identically to {@code Nullable} as of version 0.9.9. + *
      • J2ObjC, maybe: It might no longer be + * necessary there, since we have stopped using the {@code @ParametersAreNonnullByDefault} + * annotations that {@code ParametricNullness} was counteracting. *
      * + *

      This annotation is a temporary hack. We will remove it after tools no longer need + * it. */ @GwtCompatible -@Retention(RUNTIME) +@Retention(CLASS) @Target({FIELD, METHOD, PARAMETER}) -@javax.annotation.meta.TypeQualifierNickname -@javax.annotation.Nonnull(when = javax.annotation.meta.When.UNKNOWN) @interface ParametricNullness {} diff --git a/android/guava/src/com/google/common/xml/XmlEscapers.java b/android/guava/src/com/google/common/xml/XmlEscapers.java index a1c637c355ba..9868923e8975 100644 --- a/android/guava/src/com/google/common/xml/XmlEscapers.java +++ b/android/guava/src/com/google/common/xml/XmlEscapers.java @@ -14,7 +14,6 @@ package com.google.common.xml; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.escape.Escaper; import com.google.common.escape.Escapers; @@ -38,9 +37,7 @@ * @author David Beaumont * @since 15.0 */ -@Beta @GwtCompatible -@ElementTypesAreNonnullByDefault public class XmlEscapers { private XmlEscapers() {} @@ -96,6 +93,7 @@ public static Escaper xmlContentEscaper() { *

      This escaper does not treat surrogate pairs specially and does not perform Unicode * validation on its input. */ + @SuppressWarnings("EscapedEntity") // We do mean for the user to see " etc. public static Escaper xmlAttributeEscaper() { return XML_ATTRIBUTE_ESCAPER; } diff --git a/android/guava/src/com/google/common/xml/package-info.java b/android/guava/src/com/google/common/xml/package-info.java index bd4c952162f3..a263f55f44c8 100644 --- a/android/guava/src/com/google/common/xml/package-info.java +++ b/android/guava/src/com/google/common/xml/package-info.java @@ -17,12 +17,12 @@ * for * XML. * - *

      This package is a part of the open-source Guava + *

      This package is a part of the open-source Guava * library. */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package com.google.common.xml; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/android/guava/src/com/google/thirdparty/publicsuffix/PublicSuffixPatterns.java b/android/guava/src/com/google/thirdparty/publicsuffix/PublicSuffixPatterns.java index 74196a99d05c..c1718f89bffd 100644 --- a/android/guava/src/com/google/thirdparty/publicsuffix/PublicSuffixPatterns.java +++ b/android/guava/src/com/google/thirdparty/publicsuffix/PublicSuffixPatterns.java @@ -42,7 +42,13 @@ private PublicSuffixPatterns() {} /** If a hostname is contained as a key in this map, it is a public suffix. */ public static final ImmutableMap EXACT = TrieParser.parseTrie( - "a&0&0trk9--nx?27qjf--nx?e9ebgn--nx?nbb0c7abgm--nx??1&2oa08--nx?apg6qpcbgm--nx?hbbgm--nx?rdceqa08--nx??2&8ugbgm--nx?eyh3la2ckx--nx?qbd9--nx??3&2wqq1--nx?60a0y8--nx??4x1d77xrck--nx?6&1f4a3abgm--nx?2yqyn--nx?3np8lv81qo3--nx?5b06t--nx?axq--nx?ec7q--nx?lbgw--nx??883xnn--nx?9d2c24--nx?a&a?it??b!.&gro?lim?moc?sr,t&en?opsgolb,?ude?vog??abila?c?ihsot?m?n??c!.&b&a?m?n??c&b?g?q??ep?fn?k&s?y??ln?no?oc,p&i-on,ohsdaerpsym,?sn?t&n?opsgolb,?un?ysrab,?i&ma?r&emarp?fa??sroc??naiva?s??d&ats?n&eit?oh??om?sa?tl??eg?f&c?ob??g!emo?naripi?oy??hskihs?i&cnal?dem?hs?k!on??sa!.snduolc,??jnin?k&aso?dov?ede?usto??l!.&c,gro?moc?ofni?r&ep?nb,?t&en?ni??ude?vog??irgnahs?le&nisiuc?rbmuder???m!.&ca?gro?oc?sserp?ten?vog??ahokoy?e00sf7vqn--nx?m??n!.&ac?cc?eman?gro?ibom?loohcs?moc?ni?o&c?fni?rp??r&d?o??s&u?w??vt?xm??av?is?olecrab?tea??p!.&bog?ca?d&em?ls??g&ni?ro??mo&c?n??oba?ten?ude??c?g7hyabgm--nx?ra!.&461e?6pi?iru?nru?rdda-ni?siri???s??q!.&eman?gro?hcs?lim?moc?t&en?opsgolb,?ude?vog???r&az?emac?f4a3abgm--nx?n!d5uhf8le58r4w--nx??u&kas?tan???s!.&bup?dem?gro?hcs?moc?ten?ude?vog??ac!.uban.iu,?iv??t&ad?elhta?led?oyot??u!.&a&cinniv?emirc?i&hzhziropaz?stynniv??s&edo?sedo??tlay?vatlop??bs?cc,d&argovorik?o!roghzu??tl,?e&hzhziropaz?nvir?t??f&i?ni,?g&l?ro??hk?i&stvinrehc?ykstynlemhk??k&c?m?s&nagul?t&enod?ul??v&iknarf-onavi?orteporp&end?ind?????l&iponret?opotsa&bes?ves??p??m&k?oc?s?yrk??n&c?d?i?osrehk?v?ylov??o&c,nvor??p&d?p,z??r&c?imotihz?k?ymotyhz??sk?t&en?l?z??ude?v:c?e&alokin?ik??i&alokym?hinrehc?krahk?vl?yk??k?l?o&g!inrehc??krahk??r?,xc,y&ikstinlemhk?mus?s&akrehc?sakrehc?tvonrehc???z&ib,u????v!aj?bb?et?iv??waniko?x&a?iacal??yogan?z&.&bew?c&a?i&n?rga???gro?l&im?oohcs??m&on?t??o&c!.topsgolb,?gn??radnorg?sin?t&en?la??ude?vog?wal??zip???b&00ave5a9iabgm--nx?1&25qhx--nx?68quv--nx?e2kc1--nx??2xtbgm--nx?3&b2kcc--nx?jca1d--nx??4&6&1rfz--nx?qif--nx??96rzc--nx??7w9u16qlj--nx?88uvor--nx?a&0dc4xbgm--nx?c?her?n?ra?t??b!.&erots?gro?moc?o&c?fni??ten?ude?v&og?t??zib??a??c&j?s??d&hesa08--nx?mi??g?l!.&gro?moc?ten?ude?vog??m??s!.&gro?moc?ten?ude?vog???tc-retarebsnegmrev--nx?u&lc!.&elej,snduolc,y&nop,srab,??smas??p!.ysrab,??wp-gnutarebsnegmrev--nx??c&1&1q54--nx?hbgw--nx??2e9c2czf--nx?4&4ub1km--nx?a1e--nx?byj9q--nx?erd5a9b1kcb--nx??8&4xx2g--nx?c9jrb2h--nx??9jr&b&2h--nx?54--nx?9s--nx??c&eg--nx?h3--nx?s2--nx???a!.&gro?lim?moc?rrd,ten?ude?vog??3a09--nx!.&ca1o--nx?gva1c--nx?h&ca1o--nx?za09--nx??ta1d--nx?ua08--nx???da??b&a?b?ci?f76a0c7ylqbgm--nx?sh??c!.&eugaelysatnaf,gnipparcs,liamwt,nwaps.secnatsni,revres-emag,s&nduolc,otohpym,seccaptf,?xsc,?0atf7b45--nx?a1l--nx??e!.&21k?bog?dem?gro?lim?moc?nif?o&fni?rp??ten?ude?vog??beuq?n?smoc??fdh?i&l&buperananab?ohtac??n&agro?ilc?osanap??sum?tic??l!.&gro?moc?oc?ten?ude?vog?yo,?l??m!.&mt?ossa??p1akcq--nx??n!.&mon?ossa??i?p??relcel?s!.&gro?moc?ten?ude?vog??c??t!.&e&m,w,?hc,?s?w??v!.&e0,gro?lim?moc?ten?ude?v&g:.d,,og???q??wp?yn??d&2urzc--nx?3&1wrpk--nx?c&4b11--nx?9jrcpf--nx???5xq55--nx?697uto--nx?75yrpk--nx?9ctdvkce--nx?a!.mon?d?er?olnwod??b2babgm--nx?c!.vog?g9a2g2b0ae0chclc--nx??e&m!bulc??r!k??sopxe?timil?w??fc?g!.&ude?vog???h&d3tbgm--nx?p?t??i!.&ased?bew?ca?etrof,golbw,hcs?lim?o&c!.topsgolb,?g??palf,r&egolb,o??sepnop?ten?ym?zib??ar?b?ordna?p?rdam??l&iub?og?row??m!.&ed,ot,pj,t&a,opsgolb,???n&a&b?l!.citats:.&setis,ved,?,raas???ob?uf??o&of?rp??r&a&c&tiderc?yalcrab??ugnav??ef506w4b--nx?k!.&oc,ude,?jh3a1habgm--nx??of??s!.&dem?gro?moc?ofni?ten?ude?v&og?t???m!kcrem???t!.topsgolb,excwkcc--nx?l??uolc!.&a&bura-vnej.&1ti,abura.rue.1ti,?tcepsrep,xo:.&ku,nt,?,?bewilek:.sc,,citsalej.piv,drayknil,elej,gnitsohdnert.&ed,hc,?letemirp:.ku,,m&edaid,ialcer.&ac,ku,su,??n&evueluk,woru,?r&epolroov,o&pav,tnemele,??tenraxa.1-se,ululetoj,xelpciffart,??za5cbgn--nx??e&1&53wlf--nx?7a1hbbgm--nx?ta3kg--nx??2a6a1b6b1i--nx?3ma0e1cvr--nx?418txh--nx?707b0e3--nx?a!.&ca?gro?hcs?lim?oc?t&en?opsgolb,?vog??09--nx??b!.&ca?gnitsohbew,nevueluk.yxorpze,pohsdaerpsym,snoitulostsohretni.duolc,topsgolb,?ortal?ut!uoy???c&0krbd4--nx?a&lp!.oc,?ps!.&lla4sx,rebu,tsafym,?artxe??sla??i!ffo??n&a&d?iler?nif?rusni!efil?srelevart???eics!.oby,??rofria??d!.&1sndnyd,42pi-nyd,7erauqs,amil4,b&ow-nrefeilgitsng--nx,rb-ni,vz-nelletsebgitsng--nx,?decalpb,e&daregtmueart,mohsnd,nihcamyek,?hcierebsnoissuksid,keegnietsi,lsd-ni,moc,n&-i-g-o-l,aw-ym,e&lletsebgitsnüg,sgnutiel,?i&emtsi,lreb-n&i,yd,??norblieh-sh.ti.segap,oitatsksid-ygolonys,pv&-n&i,yd,?nyd,?refeilgitsnüg,?orp-ytinummoc,p&h21,iog:ol,,ohsdaerpsym,?r&e&ntrapdeeps.remotsuc,su&-lautriv,lautriv,?t&adpusnd,tub-ni,uor-ym,?vres&-e&bucl,mohym,?bew-emoh:.nyd,,luhcs,??ogiv-&niem,ym,??s&d-&onys,ygolonys,?nd&-&dd,nufiat,sehcsimanyd,tenretni,yard,?isoc.nyd,ps,yard,?oper-&nvs,tig,?sndd:.&nyd,sndnyd,?,?topsgolb,vresi-&niem,tset,?xi2,y&awetag-&llawerif,ym,?srab,tic-amil,?zten&mitbel,sadtretteuf,??a&lg?rt!.oby,??i&sdoow?ug??nil?on--nx??e!.&bil?dem?eif?gro?irp?kiir?moc!.topsgolb,?pia?ude?vog??ei?ffoc?gg?r&f?ged???f&a&c?s??il??g!.&gro?lim?moc?t&en?vp??ude?vog??a&f?gtrom?p!.&3xlh,kselp,sndp,tengam,xlh,ycvrp,??rots?yov??elloc?na&hcxe?ro??roeg?ug??i!.&pohsdaerpsym,topsgolb,vog??tilop?v&bba?om???j!.&fo,gro?oc?ten???k!.&c&a?s??e&m?n??ibom?o&c!.topsgolb,?fni?g??ro??i&b?l?n???l&a&dmrif?s!.rof,rof???b&a?i&b?dua???c&aro?ric??dnik?g!oog??i&bom?ms??l&asal?erauqa??ppa?uhcs?yts!efil???m!.&4&32i,p&ct,v,??66c,ailisarb,b&dnevar,g-raegelif,?ca?duolcsd,e&d-raegelif,i&-raegelif,lpad:.tsohlacol,,?pcm,?g&ro?s-raegelif,?hctilg,k&catsegde,uoc,?noitatsksid,o&bmoy,c!ku,?t&nigol,poh,??p&ion,j-raegelif,ohbew,?qbw,r&aegelif,idcm,ofsnd,?s&dym,ndd,ti!bt,?umhol,?t&en?s&acdnuos,ohon,??u&a-raegelif,de??v&irp?og??y&golonys,olpedew,srab,??a&g?n!.&reh.togrof,sih.togrof,???em?i&rp?twohs??orhc?w??n!goloc?i&lno!.&egats-oree,oree,ysrab,??w??o!.&derno:.gnigats,,ecivres,knilemoh,r&ednu,of,??hp?latipac?ts&der?e&gdirb?rif???z!.&66duolc,amil,sh,???ruoblem??om?p!.&bog?gro?lim?mo&c?n??t&en?opsgolb,?ude??irg?yks??r!.&mo&c?n??ossa?topsgolb,?a&c!htlaeh??pmoc?wtfos??bc?eh?if?ots!.&e&rawpohs,saberots,?yflles,??taeht?u&ces?sni?t&inruf?necca??za???s!.&a!bap.us,?b!ibnal?rofmok??c!a??d!b?n&arb?ubroflanummok???e?f!noc,?g!ro??h!f??i!trap??k!shf??l?m!oc,t??n!mygskurbrutan??o?p!ohsdaerpsym,p??r!owebdluocti,?s!serp?yspoi,?t!opsgolb,?u?vhf?w?x!uvmok??y?z??a&c?el?hc??i&er?urc??nesemoh?roh?uoh??t&a&d?ts&e!laer??lla???is!.&e&lej,nilnigol,r&etnim,ocevon,?winmo,?k&rowtenoilof,wnf,?laicosnepo,n&eyb,oyc,?spvtsaf,thrs,xulel,ysrab,?bew??ov?ra?t&ioled?ol??utitsni??u&lb?qi&nilc?tuob???v!.&21e?b&ew?ib?og??ce&r?t??erots?gro?lim?m&o&c?n??rif??o&c?fni??rar?stra?t&en?ni??ude?vog??as?e3gerb2h--nx?i&l!.xlh,?rd?ssergorp??ol??w&kct--nx?r??xul?y!.&gro?lim?moc?ten?ude?vog????f&0f3rkcg--nx?198xim--nx?280xim--nx?7vqn--nx?a!.&gro?moc?ten?ude?vog???b!.vog?wa9bgm--nx??c!.topsgolb,a1p--nx!.&a14--nx,b8lea1j--nx,c&avc0aaa08--nx,ma09--nx,?f&a1a09--nx,ea1j--nx,?gva1c--nx,nha1h--nx,pda1j--nx,zila1h--nx,??ns??ea1j--nx?fo?g?iam?l&a1d--nx?og??n!.&bew?cer?erots?m&oc?rif??ofni?re&hto?p??stra?ten???orp?p!.&gro?moc?ude???rus?t!w??vd7ckaabgm--nx?w??g&2&4wq55--nx?8zrf6--nx??3&44sd3--nx?91w6j--nx!.&a5wqmg--nx?d&22svcw--nx?5xq55--nx??gla0do--nx?m1qtxm--nx?vta0cu--nx????455ses--nx?5mzt5--nx?69vqhr--nx?7&8a4d5a4prebgm--nx?rb2c--nx??a!.&gro?mo&c?n??oc?ten??vd??b!.&0?1?2?3?4?5?6?7?8?9?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t!opsgolb,?u?v?w?x?y!srab,?z???c!b?za9a0cbgm--nx??e!.&eman?gro?ics?lim?moc!.topsgolb,?nue?ten?ude?vog??a??g!.&ayc,gro?lenap:.nomead,,oc?saak,ten???i&a?v??k!.&g&olb,ro??ku,lim?moc?oi,pj,su,ten?ude?v&og?t,???m!.&drp?gro?lim?m&o&c?n??t??oc?ude?vog??pk??n!.&dtl,eman?gro?hcs?i!bom??l&im?oc,?m&oc!.topsgolb,?rif,?neg,ogn,ten?ude?vog??aw?i!b!mulp??car?d&art?dew??h&sif?tolc??k&iv?oo&b?c???ls?n&aelc?iart??p!pohs??re&enigne?tac??t&ad?ekram!.&htiw,morf,??hgil?lusnoc?neg?ov?soh!.tfarcnepo,??vi&g?l???o!s??u&rehcisrev?smas?tarebsnegömrev???o&d?lb?og!.&duolc,etalsnart,???r&2n084qlj--nx?ebmoolb?o!.&77ndc.c:sr,,a&remacytirucesym,t&neimip,sivretla,?z,?bew-llams,d&ab-yrev-si,e&sufnocsim,vas-si,?nuof-si,oog-yrev-si,uolc&arfniarodef,mw,??e&a,cin-yrev-si,grof&loot,peh,?l&as-4-ffuts,poeparodef,?m&-morf,agevres,ohruoyslles,?n&ozdop,uma.elet,?r&ehwongniogyldlob,iwym,uces-77ndc.nigiro.lss,?t&adidnac-a-si,is&-ybboh,golb,???fehc-a-si,golbymdaer,k&eeg-a&-si,si,?h,nut,?l&i&amwt,ve-yrev-si,?lawerif&-ym,ym,?sd-ni,?m&acssecca,edom-elbac,?n&af&blm,cfu,egelloc,lfn,s&citlec-a-si,niurb-a-si,tap-a-si,?xos-a-si,?ibptth,o&itatsksid,rviop,?pv-ni,?o&jodsnd,tp&az,oh,??p&i&-on,fles,?o&hbew,tksedeerf,?tf&e&moh,vres,?ym,??r&e&gatop,ppepteews,su-xunil-a-si,?gmtrec,vdmac,?s&a&ila&nyd,snd,?nymsd,?b&alfmw,bevres,?dylimaf,eirfotatophcuoc,j,koob-daer,ltbup,nd&-won,deerf,emoh,golb,kcud,mood,nyd:.&emoh,og,?,ps,rvd,tog,uolc,?s&a-skcik,ndd,?tnemhcattaomb,u,?t&ce&jorparodef.&duolc,gts.so.ppa,so.ppa,?riderbew,?e&ews-yrev-si,nretni&ehtfodne,fodne,??hgink-a-si,igude,oi-allizom,s&ixetn&od,seod,?o&h-emag,l-si,?rifyam,??ue:.&a&-q,c,?cm,dc,e&b,d,e,i,m,s,?g&b,n,?hc,i&f,s,?k&d,m,s,u,?l&a,i,n,p,?n&c,i,?o&n,r,ssa,?pj,r&f,g,h,k,t,?s&e,i:rap,,u,?t&a,en,i,l,m,ni,p,?u&a,de,h,l,r,?vl,y&c,m,?z&c,n,??,vresnyd,x&inuemoh,unilemoh,?y&limafxut,srab,???ub&mah?oj???s!.&delacsne,gro?moc?rep?t&en?opsgolb,?ude?vog??gb639j43us5--nx??t?u!.&c&a?s??en?gro?moc?o&c?g??ro?topsgolb,??v!.ta,a1c--nx??wsa08--nx??h&0ee5a3ld2ckx--nx?4wc3o--nx!.&a&2xyc3o--nx?3j0hc3m--nx?ve4b3c0oc21--nx??id1kzuc3h--nx?l8bxi8ifc21--nx?rb0ef1c21--nx???8&8yvfe--nx?a7maabgm--nx??b!.&gro?moc?ten?ude?vog??mg??c!.&7erauqs,amil4,duolc-drayknil,gniksnd,p&h21,ohsdaerpsym,?sndtog,topsgolb,wolf.e&a.1pla,nigneppa,?xi2,ytic-amil,?aoc?et?ir!euz??r&aes!errecnac??uhc??sob?taw!s???d0sbgp--nx?f&2lpbgm--nx?k??g!.&gro?lim?moc?ude?vog???m!a1j--nx??ocir?p!.&gro?i?lim?moc?ogn?ten?ude?vog???s!.&g&nabhsah,ro??l&im?xv,?m&oc?roftalp.&cb,su,tne,ue,??pib,ten?vog?won,yolpedew,?a&c?nom??i&d?f?ri???t!.&ca?enilno,im?ni?o&c?g??pohs,ro?ten??iaf!.oby,?laeh!.arh,?orxer?ra&ba?e???vo!.lopdren,?zb??i&3tupk--nx?7a0oi--nx?a!.&ffo?gro?moc?ten?uwu,?1p--nx?bud?dnuyh?tnihc??b!.&gro?moc?oc?ro?ude??ahduba?o!m!.&duolcsd,ysrab,???s??c!.&ayb-tropora--nx?ca?d&e?m??esserp?gro?ln,moc?nif,o&c?g?ssa??ro?t&en?ni?roporéa??ude?vuog??cug?t??d&dk?ua??e&bhf--nx?piat??f!.&aw5-nenikkh--nx,dnala?iki,mroftalpduolc.if,nenikkäh,pohsdaerpsym,retnecatad.&omed,saap,?topsgolb,yd,?onas??g!.&d&om?tl??gro?moc?ude?vog???h&c&atih?ra??s&abodoy?ibustim???juohs?k!.&gro?moc?ofni?ten?ude?vog?zib??b4gc--nx?iw?nisleh?s?uzus??l!.&aac,topsgolb,?drahcir?iamsi??maim?n!.&b&ew?og??ca?gro?lim?mo&c?n??ni?o&c?fni??pp?t&en?ni??ude?zib??airpic?i&hgrobmal?m??re??om?rarref?s!.&egaptig,ppatig,topsgolb,?ed??t&aresam?i&c?nifni??rahb?tagub??ut?v!.&21k?gro?moc?oc?ten???wik?xa&rp?t??yf??j&6pqgza9iabgm--nx?8da1tabbgl--nx?b!.&ossa?topsgolb,uaerrab?vuog???d?f!.&ca?eman?gro?lim?moc?o&fni?rp??ten?vog?zib???nj?s?t!.&bew?c&a?in??eman?gro?lim?moc?o&c?g??t&en?ni?set??ude?vog?zib???yqx94qit--nx??k&8uxp3--nx?924tcf--nx?arfel?c&a&bdeef?lb??ebdnul?ilc?reme?ud??d!.&erots,ger,mrif,oc,pohsdaerpsym,topsgolb,zib,?t??e&es?samet??h!.&a&4ya0cu--nx?5wqmg--nx??b3qa0do--nx?cni,d&2&2svcw--nx?3rvcl--nx??5xq55--nx?tl,?g&a0nt--nx?la0do--nx?ro??i&050qmg--nx?7a0oi--nx?xa0km--nx??m&1qtxm--nx?oc??npqic--nx?saaces,t&en?opsgolb,?ude?v&di?og?ta0cu--nx??xva0fz--nx?人&个?個?箇??司公?府政?絡&網?网??織&組?组??织&組?组??络&網?网??育&敎?教???n??i&tsob?vdnas??l!.&bew?c&a?os??dtl?gro?hcs?letoh?moc?nssa?ogn?prg?t&en?ni??ude?vog??at?cd?is??m!.&eman?fni?gro?moc?t&en?opsgolb,?ude?vog???n&ab!cfdh?etats?mmoc?t&en?fos??u??i!l!.&noyc,pepym,??p???oob?p!.&b&ew?og??gro?kog?m&af?oc??nog?ofni?pog?sog?ten?ude?vog?zib???row!.&morf,ot,?ten!.&htumiza,nolt,o&c,vra,??doof???s!.topsgolb,?t?u!.&c&a?lp??d&om?tl??e&cilop?m??gro!.&gul:g,,sgul,yr&ettoly&lkeew,tiniffa,?tneelffar,???nnoc,o&c!.&bunsorter.tsuc,e&lddiwg,n&ilnoysrab,ozgniebllew,??krametyb.&hd,mv,?omida,p&i-on,ohsdaerpsym,?t&fihsreyal.j,opsgolb,?vres-hn,ysrab,??rpoc,?psoh,shn?t&en?nmyp,?vog!.eci&ffoemoh,vres,??ysrab,???l&04sr4w--nx?a!.&gro?lim?moc?t&en?opsgolb,?ude?vog??bolg?c?ed?g!el??i&c&nanif!.oc,lpl??os??romem?tnedurp??n&if?oitanretni??t&i&gid!.sppaduolc:.nodnol,,?p&ac?soh???ned?ot??utum!nretsewhtron???c!.&bog?lim?oc?topsgolb,vog???dil?e&datic?n&ahc?nahc!gnikooc?levart?rehtaew???t!ria?tam??vart??f&8f&pbgo--nx?tbgm--nx??a?n??g!.&gro?moc?oc?ten?ude?xx,zib,??h&d?op??i!.&21k?ca?fdi?gro?inum?oc!.&egapvar,redrotibat,topsgolb,??ten?vog??a&f?m&e?g?toh???m?r?xil??l&a&b&esab?t&eksab!.&sua,zn,??oof???c?mt??e&d?hs??ihmailliw?j??m!.&esserp?gro?moc?ten?ude?v&og?uog????n!.&n&iemodleeutriv,o&med,rtsic,??oc,pohsdaerpsym,retsulc-gnitsoh,topsgolb,wsma,yalphk,?o??o&a?btuf?l!.gmo,?o&c!.&ed,rotnemele,??hcs??rit?u??p!.&a&cin&diws?gel??d&g,ortso?urawon??i&dem?mraw?nydg,?k&elo&guld?rtso??slopolam?tsu?ytsyrut??l&ip?o&kzs?w&-awolats?oksnok????n&erapohs,img?zcel,?rog&-ai&bab?nelej??j?z??syn?tsaim?w&a&l&eib?i?o??zsraw??o&namil?tainop,??z&eiwolaib?mol???c&e&iw&alselob?o&nsos?rtso???le&im?zrogz???orw,p??d&em,ia?ragrats?uolc&inu,sds,??e&c&i&lrog?w&ilg,o&hc&arats?orp??klop?tak????yzreibok??i&csjuoniws?ksromop?saldop??l&ahdop?opo??napokaz,tatselaer?z&romop?swozam???g&alble?ezrbo&lok?nrat??ro??hcyzrblaw?i&csomohcurein?grat?klawus??k&e&rut?walcolw??in&byr?diws,sark,?le?o&nas?tsylaib??rob&el?lam??s&als?jazel?nadg,puls?rowezrp???l&colw?e&r?vart??i&am?m???m&o&c?dar?n?tyb??s&g?iruot??t!a???n&a&gaz?nzop,?i&bul?cezczs?lbul,molow?nok?zd&eb?obeiws???uleiw?y&tzslo?z&rtek?seic????o&c,fni?k&celo?zdolk??lkan?n&leim?pek?t&uk?yzczs??z&copo?eing?rowaj???rga?tua?w&ejarg?ogarm???p&e&eb,lks!emoh,??klwwortso?ohs!-ecremmoce,daerpsym,??romophcaz?sos?t&aiwop?en?opos,ra,sezc??ude?v&irp?og!.&a&p?s!w???bni&p?w??ci?dtiw?essp?fiw?g&imu?u??hiiw?m&igu?rio?u!o???nds?o&ks?p!pu??s?wtsorats??p&a?sp!mk?pk?wk??u&m?p??wk?z??r&ksw?s??s&i?oiw?u?zu??talusnok?w&gzr?i&p?rg?w??m?opu?u!imzw???zouw????w&a&l&corw?sizdow??w??o&golg?k&ark,ul?zsurp??r&az?gew??t&rabul,sugua??z&coks?sezr????xes?y&buzsak?d&azczseib?ikseb??hcyt?n&jes?lod-zreimizak??pal?r&ogt?uzam??walup?zutrak??z&am-awar?c&aprak?iwol?zsogdyb??dalezc?ib?s&i&lak?p??uklo????l??r&as?f?s??s!.&gro?moc?ten?ude?vog???t!.vog??ubnatsi?x3b689qq6--nx?yc5rb54--nx??m&00tsb3--nx?1qtxm--nx?981rvj--nx?a!.&aayn,enummoc?gro?moc?o&c?idar,ken,?t&en?opsgolb,??c!bew??dretsma?e&rts?t!.&citsalej,esruocsid,???fma?xq--nx??b!.&gro?moc?ten?ude?vog??i??c!.&moc?oc?ten?vog???d!.&gro?moc?ten?ude?vog???f!.&gro?moc?oidar,ten?ude??i??g!vu96d8syzf--nx??h?i!.&ca?gro?moc?o&c!.&clp?dtl???r,?t&en?t??vt??k?rbg4--nx??k!.&drp?e&rianiretev?sserp??gro?lim?m&o&c?n??t??nicedem?ossa?pooc?s&eriaton?neicamrahp?sa??ude?v&og?uog????l&if?ohkcots??o!.&dem?gro?m&oc?uesum??o&c?rp??ten?ude?vog??b?c!.&2aq,3pmevres,5sndd,a&c&-morf,ir&bafno,fa,??g&-morf,oy-sehcaet,?i-morf,m&-morf,all&-a-si,amai,??p&-morf,c-a-si,?r&emacytirucesym,odih,?s,tadtsudgniht,v-morf,w-morf,z,?b&dnevarym,ew&-sndnyd,draiw.segap,ottad,?g,ildts.ipa,?c&amytirucesemoh,d-morf,esyrcs,itsalej.omed,n&-morf,vym,?p&kroweht,ytirucesemoh,?q,rievres,s-morf,?d&aerotffuts,e&calpb,ifitrec-&si,ton-si,?llortnocduolc,rewopenignepw:.sj,,tsohecapsppa,?i&-morf,rgevissam.saap,?m-morf,n&-morf,abeht-htiw-si,?s-morf,uolc&-noitatsyalp,hr,iafaw.&d&ej,yr,?nol,?meaeboda,panqym:-&ahpla,ved,?,smetsystuo,tekcilc,ved&j,pw,??vreser,wetomer,?e&butuoyhtiw,c&iffo-sndnyd,navdamrcnaiculle,?d:-morf,o&celgoog,n&il.srebmem,neve.&1-&su,ue,?2-&su,ue,?3-&su,ue,?4-&su,ue,????,erf&-sndnyd,sndd,?filflahevres,gnahcxeevres,i&hcet-a-si,p-sekil,?k&auqevres,irtsretnuocevres,?l&bitpa-no,googhtiw,?m&agevres,ina-otni-si,oh-&sndnyd,ta-sndnyd,??n&-morf,ilno&-evreser,ysrab,?og-si,?r&alfduolcyrt,ehwynanohtyp:.ue,,ihcec,?s&ivdamrcnaiculle,run-a-si,?t&i&nuarepo,s&-ybboh,aloy,tipohs,xiw,??omer-sndnyd,upmocsma,ysgolb,?v&als-elcibuc-a-si,i&lsndd,tavresnoc-a-si,??z&amkcar,eelg,iig,??fehc-a-si,g&ni&gats-&raeghtua,swennwot,?ksndd,robsikrow,?o&fgp,lb&-sndnyd,pawodni,sihtsetirw,???h&n-morf,o-morf,?i&fiwehtno,h-morf,kiw-sndnyd,m-morf,pdetsoh,r-morf,w-morf,z&ihcppa,nilppa,??jn-morf,k&a&-morf,erfocsic,?cils-si,eeg&-a&-si,si,?sndd,?h,latsnaebcitsale:.&1-&htuos-pa,lartnec-&ac,ue,?ts&ae&-&as,su,?ht&ron-pa,uos-pa,??ew-&su,ue,vog-su,???2-ts&ae&-su,ht&ron-pa,uos-pa,??ew-&su,ue,??3-ts&aehtron-pa,ew-ue,??,o-morf,r&adhtiwtliub,ow&-&sndnyd,ta-sndnyd,?ten-orehkcats,??u,?l&a&-morf,colottad,rebil-a-si,?f-morf,i&-morf,am&-sndnyd,detsohpw,??l&ecelffaw,uf-ytnuob:.a&hpla,teb,?,?ppmswa,ru-&elpmis,taen,?ssukoreh,xegap,?m&n-morf,pml.ppa,rofererac-htlaeh,sacrasevres,uirarret-yltsaf,?n&a&cilbuper-a-si,f&-sllub-a-si,racsan-a-si,?i&cisum-a-si,ratrebil-a-si,??c,dc&hsums,umpw,xirtrepmi,?eerg-a-si,i-morf,m-morf,o&ehtnaptog,isam-al-a-tse,r&italik,tap-el-tse,?s&iam-al-a-tse,replausunu,??pj,t-morf,?o&bordym,c,hce-namtsop,jodsnd,m&-morf,ed-baltlow,?n:iloxip,,ttadym,?p&2pevres,aelutym,i&-sndnyd,fles,ogol,ruoy&esol,hctid,?ym&eerf,teg,??ohsdaerpsym,pa&-rettalp,anis:piv,,esaberif,k1,lortnocduolc,oifilauq,r&aegyks,oetem:.ue,,?tnorfegap,ukoreh,?t&fevres,thevres,??r&a:-morf,tskcor-a-si,,b,e&d&iv&erp-yb-detsoh.saap,orpnwo,?ner&.ppa,no,??e&bevres,nigne-na-si,?ggolb&-a-si,ndi,?h&caet-a-si,pargotohp-a-si,?krow-drah-a-si,n&gised-a-si,ia&rtlanosrep-a-si,tretne-na-si,??p&acsdnal-a-si,eekkoob-a-si,?retac-a-si,subq,tn&ecysrab,iap-a-si,uh-a-si,?vres&-&ki.&cpj-rev-duolcj,duolcj,?s&ndnyd,pvtsaf,??inim,nmad,sak,?y&alp-a-si,wal-a-si,?zilibomdeepsegap,?g,ituob,k,mgrp.nex,o&-morf,sivdalaicnanif-a-si,t&areleccalabolgswa,c&a-na-si,od-a-si,?susaym,??p-morf,u&as-o-nyd,e&tsoh.&duolc-gar,hc-duolc-gar,?ugolb-nom-tse,?omuhevres,??s&a&apod,ila&nyd,snd,?nymsd,vnacremarf,?bbevres,ci&p&-sndnyd,evres,?tcatytiruces,?dylimaf,e&cived-anelab,itilitu3,lahw-eht-sevas,mag-otni-si,t&isro,yskciuq,??i&ht2tniop,pa&elgoog,tneltneg,??jfac,k&-morf,aerf-ten,colb&egrof,pohsym,??m&-morf,cxolb,?n&d&-pmet,dyard,golb,mood,tog,?kselp,nyd,ootrac-otni-si,?o&-xobeerf,xobeerf,?ppa&raeghtua,tneg,?r&ac-otni-si,e&ntrap-paelut,tsohmaerd,??s&e&l-rof-slles,rtca-na-si,?ibodym,?tsaeb-cihtym.&a&llicno,zno,?ilay,lacarac,re&gitnef,motsuc,?sv,toleco,x:n&ihps,yl,?,?u,wanozama.&1-&htuos-pa&-3s,.&3s,etisbew-3s,kcatslaud.3s,??la&nretxe-3s,rtnec-&ac&-3s,.&3s,etisbew-3s,kcatslaud.3s,??ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,????ts&ae&-&as&-&3s,etisbew-3s,?.kcatslaud.3s,?su:-etisbew-3s,.kcatslaud.3s,,?ht&ron-pa&-&3s,etisbew-3s,?.kcatslaud.3s,?uos-pa&-&3s,etisbew-3s,?.kcatslaud.3s,???ew-&su-&3s,etisbew-3s,?ue&-&3s,etisbew-3s,?.kcatslaud.3s,?vog-su-&3s,spif-3s,????2-ts&ae&-su&-3s,.&3s,etisbew-3s,kcatslaud.3s,??ht&ron-pa&-3s,.&3s,etisbew-3s,kcatslaud.3s,??uos-pa&-&3s,etisbew-3s,?.kcatslaud.3s,???ew-&su-&3s,etisbew-3s,?ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,????3&-tsew-ue&-3s,.&3s,etisbew-3s,kcatslaud.3s,??s,???t&arcomed-a-si,c&-morf,etedatad.&ecnatsni,omed,??eel&-si,rebu-si,?hgilfhtiwletoh,iurcermrcnaiculle,m-morf,n&atnuocca-na-si,e&duts-a-si,r-ot-ecaps,tnocresu&buhtig,e&capsppa,lbavresbo.citats,?pl,???ops&edoc,golb,ppa,?s&i&hcrana-&a-si,na-si,?laicos-a-si,pareht-a-si,tra-na-si,xetn&od,seod,??oh&piym,sfn,??u&-morf,nyekcoh-asi,?v-morf,?u&-rof-slles,4,e,h,oynahtretramssi,r:ug-a-si,,?v&n-morf,w-morf,?w&o&lpwons-yrt,zok,?ww100,?x&bsbf.sppa,em,i&nuemoh,rtrepmi,?obaniateb,t-morf,unilemoh,?y&a&bnx:.&2u,lacol-2u,?,l&erottad,pezam,?wetag-llawerif,?dnacsekil,fipohsym,k&-morf,niksisnd,?rotceridevitcaym,u:goo,,w-morf,x&alagkeeg,orphsilbup,???inu??m!.&dna,rof,??or?tsla??p!.nwo,?raf!.jrots,etats??s?t!.&gro?lim?mo&c?n??oc?ten?ude?vog???u&esum!.&a&92chg-seacinumocelet-e-soierroc--nx?atnav?c&i&aduj?rfatsae??rollam??d&anac?enomaledasac?irolf??e&raaihpledalihp?srednu??g&hannavas?oonattahc??hamo?i&auhsu?bmuloc!hsitirb??dem?groeg?hpledalihp?l&artsua?etalif??n&igriv?rofilac??ssur?tsonod??ksa&la?rben??l&lojal?q-snl--nx?uossim!trof???m&a&bala?nap??enic?o&m?r???n&a&cirema?idni??edasap?ilorachtuos?olecrab??r&abrabatnas?ezzivs??su?t&nalta?osennim??zalp??c&dnotgnihsaw?ebeuq?i&depolcycne?ficap?hpargonaeco?lbup?sum?t&carporihc?lec?naltadim??vu??yn??d&a&dhgab?etsmraf?m?orliar??i&rdam?ulegnedleeb??leif?n&a!l&gne?nif?ragyduj?t&ocs?rop??yram???u&brofsdgybmeh?osdnaegami???r&augria?ofxo???e&c&a&l&ap?phtrib??ps??n&a&lubma?tsiser??e&fedlatsaoc?gilletni?ics!foyrotsih????pein?rof??d&nukneklov?revasem??e&rt?tsurt??f&atnas?ildliw??g&a&lliv?tireh!lanoitan???dirbmac?rog??i&cnum?nollaw??koorbrehs?l&ab?bib?cycrotom?i&ssim?txet??oks?tsac??m&affollah?it!iram??utsoc??n&golos?ilno?recul??r&a&uqs?waled!foetats???i&hs&acnal?kroy?pmahwen??otsih??omitlab?ut&an?cetihcra?inruf?luc!irga?su???vuol??s&abatad?iacnarf?sius?uoh!lum???t&a&locohc?rak?ts!e!yrtnuoc!su?????imesoy?tevroc??u&qihpargonaeco?velleb??vit&caretni?omotua???f&iuj?ohgrub??g&n&i&dliub?ginerevmuesum?kiv?lahw?nim?peekemit?vil??ulmmastsnuk??orf?r&ebnrats?u&b&ierf?le?m&ah?uan??ram?s&mailliw!lainoloc??naitsirhc?retepts??zlas??ob&irf?mexul?????h&atu?c&raeser?sirotsih?uot??g&ea1h--nx?rubsttip??si&tirb?wej??t&laeh?ro&n?wtrof??uo&mnom?y????i&d6glbhbd9--nx?iawah?k&nisleh?s??lad!rodavlas??sissa?tannicnic??k&c&nivleeg?olc!-dna-hctaw?dnahctaw???fj?inebis?l&is?ofron??na&rfenna?t??oorbnarc?r&am&ned?reiets??oy!wen????l&a&ci&dem?golo&eahcra?meg?oz??natob?rotsih??ertnom?iromem?noita&cude?n??oc?rutluc?trop?utriv?van??e&nurb?s&ab?surb??utriv??i&artnogero?sarb??l&a&besab?hsnoegrus??e&hs?rdnevle??i&b?m!dniw????o&bup?ohcs?tsirb???m&a&dretsma?ets?h&netlehc?rud???ct?elas!urej??l&if?ohkcots?u??raf?silanruoj?u&esumyrotsihlarutan?ira&tenalp?uqa??terobra???n&a&c!irema!evitan???gihcim?i&dni?tpyge??mfoelsi?wehctaksas??e&d&alokohcs?ews?rag!cinatob?lacinatob?s&nerdlihc?u????gahnepoc?hcneum?laftsew?ppahcsnetewruutan?r&dlihc?ednaalv?hu!dnutamieh???sseig??gised!dn&atra?utsnuk???h&ab!nesie??ojts??i&lreb?tsua??l&eok?ocnil??n&ob?urbneohcs??o&dnol?gero?i&s&iv&dnadnuos?elet??nam??t&a&c&inummoc?ude!tra???dnuof?erc?i&cossa?va??kinummokelet?nissassa?r&belectsevrah?oproc?tsulli??silivic?t&nalp?s??vres&erp?noclatnemnorivne??zilivic??c&elloc?if-ecneics??ibihxe???ri?s&dnah?imaj?reffej?sral??t&erbepac?nilc?sob???r&e&b?dom?tsew?uab?zul??obredap??vahnebeok?wot??o&2a6v-seacinumoc--nx?ablib?c&edtra?ixemwen?sicnarfnas??elap?g&a&cihc?to??eidnas??i&cadnuf?diserp?ratno??llecitnom?mitiram?nirot?r&htna?ienajedoir???pohskrow?qari?r&aw!dloc?livic??dd?e&b&ma?yc??irrac?llimsiwel?naksiznarf?papswen?t&aeht?exe?nec!ecneics?larutluc?muesum?tra??s&ehc&nam?or??neum??upmoc???ia!nepo??obal?u&asonid?obal?takirak???s&a&l&g?l&ad?eh???xet??di&k?pardnarg??e&cneics!larutan??dnal?hcsi&deuj?rotsih!nizidem?rutan??selhcs??itinamuh?l&aw?egnasol?l&e&rutansecneics?xurb??iasrev???r&e&em?ugif??tsac??suohcirotsih?u&en?q&adac?itna!nacirema?su????õçacinumoc!elet-e-soierroc???gnirpsmlap?htab?i&lopanaidni?rap?uoltnias?xa??l&essurb?lod??mraeriflanoitan?n&a&blats?l??erdlihc?oi&snam?tacinummoc!elet-dna-stsop???äl??re&dnalf?lttes?mraf?nim?tnececneics??s&alg?erp??t&farc!dnastra??nalp?olip?ra!e&nif?vitaroced!su???su?xuaeb???u&b!muloc??cric???t&agilltrop?cejorp?dats?e&esum?kramnaidni??iorted?ne&m&elttes?norivne?piuqemraf??vnoc??oped?r&a!drib?enif?gttuts?hsiwej?kcor?n&acirema?ootrac??tamsa?yraropmetnoc??op&aes?snart?wen??ufknarf??s&a&cdaorb?octsae??ewhtuos?ilayol?nuk?r&ohnemled?uhlyram??urt???u&a&bgreb?etalpodaroloc??rmyc??w&ocsom?rn??x&esse?ineohp?nam?tas??y&a&bekaepasehc?w&etag?liar???camrahp?doc?e&hsub?l&ekreb?l&av!eniwydnarb??ort???n&dys?om??rrus?s&nreug?rejwen???golo&e&ahcra?g??motne?nh&cet?te??oz?po&rhtna?t??roh??hpargotohp?l&etalihp?imaf??m&edaca?onortsa??n&atob?yn??ps?r&a&ropmetnoc?tilim??e&diorbme?llag!tra??vocsid??lewej?nosameerf?otsih!dnaecneics?ecneics?gnivil!su??la&col?rutan??retupmoc?su??tsudnidnaecneics??spelipe?t&eicos!lacirotsih??i&nummoc?srevinu??nuoc???z&arg?iewhcs?nil?ojadab?urcatnas??моки?םילשורי???rof??z!.&ca?gro?hcs?lim?moc?o&c?fni??ten?ude?vog?zib????n&315rmi--nx?a&brud?cilbuper?f?grompj?hkaga?idraug?m?ol?ssin?u&hix?qna??varac?yalo??b!.&gro?moc?oc,ten?ude?vog??c??c!.&ah?bh?c&a?s??d&5xq55--nx?g?s?uolctnatsni,?eh?g&la0do--nx?ro??h&a?q?s??i&7a0oi--nx?h??j&b?f?t?x?z??kh?l&h?im?j??m&n?oc!.swanozama.&1-htron-nc.3s,be.1-&htron-nc,tsewhtron-nc,????n&h?l?s?y??om?qc?s&g?j??ten?ude?vog?wt?x&g?j?n?s??z&g?x??司公?絡網?络网??b??d&g!.ypnc,?ka??e&drag?erg?fuak?gawsklov?hctik?i&libommi?w??m!.r&iaper,of,??po?r!ednaalv??sier?ves??g!.&ca?gro?moc?ten?ude?vog??is&ed!.ssb,?irev???h!.&bog?cc,gro?lim?moc?ten?ude???i!.&bew,c&a?in??dni?esabapus,gro?lim?mrif?neg?oc?s&er?nduolc,?t&en?opsgolb,?ude?vog?ysrab,?elknivlac?griv?ks?lreb?p?v?w!.taht,?x??k!.&gro?ten?ude?vog???l&eok?ocnil??m!.&cyn,gro?ude?vog???o&dnol!.&fo,ni,??i&hsaf!.fo,?n&o?utiderc??siv!orue??t&a&cude!.oc,?dnuof?tsyalp??c&etorp?u&a?rtsnoc?????kin?las?mrom?nac?p&q?uoc??s&iam?nhojcs?pe?scire??t&ron?sob??zama??p!.&gro?oc?ten?ude?vog??k??r&e&c?yab??op!.eidni,??s!.&gro?moc?osrep?t&opsgolb,ra??ude?v&inu?uog????t!.&d&ni?uolcegnaro,?gro?ltni?m&oc!nim??siruot??nif?o&fni?srep??sne?t&an?en??vog??m??u&f?r!.&bdnevar,lper,s&h,revres,?tnempoleved,??stad?xamay?y??v!.&ca?eman?gro?htlaeh?moc?o&fni?rp??t&en?ni?opsgolb,?ude?vog?zib???wo&rc?t!epac????o&76i4orfy--nx?a!.&bp?de?go?oc?ti?vg??boat??b!.&a&ci&sum?tilop??i&c&arcomed?neic??golo&ce?ncet??m&edaca?onoce??rt&ap?sudni??vilob??n&egidni?icidem??serpme?tsiver?vitarepooc??b&ew?og??dulas?e&rbmon?tr&a?op&ed?snart????g&olb?ro??ikiw?l&a&noi&canirulp?seforp??rutan??im??moc?o&fni?lbeup?rga?tneimivom??saiciton?t&askt?en?ni??ude?vt??h?iew?olg??c!.&bew?cer?dr&c,rac,?esabapus,gro?ipym,l&im?per:.di,,?m&o&c!.topsgolb,?n??rif?udon,?ofni?s&egap&dael,l,?tra??t&4n,en?ni??ude?vog??a?e?in?mara?s&edarb?ic???d!.&b&ew?og??dls?gro?lim?moc?t&en?ra??ude?vog??agoba?if?zd7acbgm--nx??e&c?d&iv?or??morafla??f!ni!.&e&g&delwonk-fo-l&errab,lerrab,?ellocevoli,?ht-skorg,rom-rof-ereh,tadpusn:d,,?llatiswonk,macrvd,ofni-v,p&i&-on,fles,?ohbew,?ruo-rof,s&iht-skorg,nd&-cimanyd,nyd,uolc,??tsrifyam,ysrab,zmurof,???g&el?n!am?ib???hwsohw?i!.&35nyd,8302,a&minifed,tad-b,?b&altig,uhtig,?czh,d&in,u&olc&iaznab.ppa,ropav,?rd,??e&c&apsinu.1rf-duolc,ivedniser,?donppad.sndnyd,egipa,lej,nilnigol,sufxob,t&i&beulb,snoehtnap,?newtu,ybeeb.saap,??gni&gatsniser.secived,tsohytsoh,?k&coregrof.di,orgn,ramytefasresworb,?m&oc?udon,?n&mtsp:.kcom,,yded,?ot&oq,pyrctfihs,?p&opilol,pa&-arusah,e&nalpkcab,tybeeb.1dkes,???r&e&tsneum-hf,vres&cisab,lautriv,??ial.sppa,?s&codehtdaer,gnihtbew,nemeis-om,pparevelc,tacdnas,?t&e&kcubtib,notorp,?i&belet,detfihs,kecaps,?raedon.egats,s&ohg,udgniht.&cersid.&dvreser,tsuc,?dorp.tsuc,gnitset.&dvreser,tsuc,?ved.&dvreser,tsuc,????vgib.0ku,whs,x&bslprbv.g,cq,rotide,?y&olpedew,srab,??b?d&ar?u&a?ts???j?r?syhp??j!.&eman?gro?hcs?lim?moc?ten?ude?vog???ll&ag?o??m!.&gro?moc?ten?ude?vog??g?il?mi?orp??n!.&a&0&b-ekhgnark--nx?c-iehsrgev--nx?g-lksedlig--nx?k-negnanvk--nx??1&p-nedragy--nx?q-&asierrs--nx?grebsnt--nx?lado-rs--nx?n&egnidl--nx?orf-rs--nx??regnayh--nx?ssofenh--nx??r-datsgrt--nx?s-ladrjts--nx?v-y&senner--nx?vrejks--nx???3g-datsobegh--nx?4&5-&dnaleprj--nx?goksnerl--nx?tednalyh--nx??6-neladnjm--nx?s-&antouvachb--nx?impouvtalm--nx??y-&agrjnevvad--nx?ikhvlaraeb--nx???7k-antouvacchb--nx?8&k-rekie-erv--nx?l-ladrua-rs--nx?m-darehsdrk--nx??a!.sg??bct-eimeuvejsemn--nx?d&do?iisevvad?lov?narts?uas??f&1-&l--nx?s--nx??2-h--nx??g&10aq0-ineve--nx?av?ev?lot?r&ajn&evvad?u??ájn&evvad?u????h?iz-lf--nx?j&ddadab?sel??k&el?hoj&sarak?šárák??iiv&ag&na&el?g??ŋ&ael?ág???ran???l&f?lahrevo?o&ms?s??sennev?t-&ilm--nx?tom--nx??u&-edr--nx?s??øms??muar?n&0-tsr--nx?2-dob--nx?5-&asir--nx?tals--nx??a&r!-i-om?f?t??t??douvsatvid?kiv?m&os?øs??n&od?ød??ra?sen?t&aouvatheig?ouv&a&c&ch&ab?áb??h&ab?áb???n??i&ag?ág??sa&mo?ttvid??án???z-rey--nx?ær&f?t???o&p-&ladr--nx?sens--nx??q-nagv--nx?r-asns--nx?s-kjks--nx?v-murb--nx?w-&anr&f--nx?t--nx??ublk--nx???ppol?q&0-t&baol--nx?soum--nx?veib--nx??x-&ipphl--nx?r&embh--nx?imph--nx???y-tinks--nx??r&f-atsr--nx?g-&an&ms--nx?nd--nx??e&drf--nx?ngs--nx??murs--nx?netl--nx?olmb--nx?sorr--nx??h-&a&lms--nx?yrf--nx??emjt--nx??i&-&lboh--nx?rsir--nx?y&d&ar--nx?na--nx??ksa--nx?lem--nx?r&ul--nx?yd--nx????stu??j-&drav--nx?rolf--nx?sdav--nx??kua?l-&drojf--nx?lares--nx??m-tlohr--nx?n-esans--nx?olf?p-sdnil--nx?s-ladrl--nx?tih?v-rvsyt--nx??s&a&ns?ons??i&ar?er&dron?r&os?øs???ár??la&g?h??mor!t??sir?uf?åns??t&koulo&nka?ŋká??la?p-raddjb--nx?r-agrjnu--nx?s&aefr&ammah?ámmáh??orf?r&o?ø???u-vreiks--nx??u&h-dnusel--nx?i-&drojfk--nx?vleslm--nx??j-ekerom--nx?k-rekrem--nx?u-&dnalr--nx?goksr--nx?sensk--nx??v-nekyr--nx?w-&k&abrd--nx?ivjg--nx??oryso--nx??y-y&dnas--nx?mrak--nx?n&art--nx?nif--nx??reva--nx??z-smort--nx??v!.sg?ledatskork?reiks??wh-antouvn--nx?x&9-dlofts--nx.aoq-relv--nx?d-nmaherk--nx?f-dnalnks--nx?h-neltloh--nx?i-drgeppo--nx?j-gve&gnal--nx?lreb--nx??m-negnilr--nx?n-drojfvk--nx??y&7-ujdaehal--nx?8-antouvig--nx?b-&dlofrs--nx?goksmr--nx?kivryr--nx?retslj--nx??e-nejsom--nx?f-y&krajb--nx?re&dni--nx?tso--nx??stivk--nx??g-regark--nx?orf?ørf??z9-drojfstb--nx??b&25-akiivagael--nx?53ay7-olousech--nx?a&iy-gv--nx?le-tl&b--nx?s--nx??n0-ydr--nx??c&0-dnal-erdns--nx?z-netot-erts--nx??g&g-regnarav-rs--nx?o-nejssendnas--nx??ju-erdils-ertsy--nx?nj-dnalh-goksrua--nx?q&q-ladsmor-go-erm--nx.&ari-yreh--nx?ednas??s-neslahsladrjts--nx???ca&4s-atsaefrmmh--nx?8m-dnusynnrb--nx?il-tl--nx?le-slg--nx?n5-rdib--nx?op-drgl--nx?uw-ynnrb--nx??d&a&qx-tggrv--nx?reh!nnivk?sd&ork?ørk??uas??ts&e&bi?kkar?llyh?nnan??g&ort?ørt??k&alf?irderf??levev?mirg?obeg&ah?æh??r&ah?ejg????barm-jdddb--nx?ie!rah?s&etivk?ladman???lof&r&os?øs??ts&ev.ednas?o.relav?ø.relåv???n&a&l&-erd&n&os?øs??ron??adroh.so?dron.&a&g5-b--nx?ri-yreh--nx??ob?y&oreh?øreh??øb??e&m!lejh??pr&oj?øj??vi??gyb?n&aks?åks??o&h-goksrua?rf??r&o?ua?ø??tros?øh-goksrua??rts!e&devt?lab?mloh???s&ellil?naitsirk?rof???u&l!os??s!d&im?lejt??e&guah?l&a?å???kkoh?lavk?naitsirk?r&af?eg&e?ie???tef?y&onnorb?ønnørb?????r&a&blavs!.sg??g&eppo?la???o&j&f&a!dniv?k?vk??die?e&dnas?kkelf??llins?r&iel?ots??s&lab?t&ab?åb??yt??å!k??ævk??les??ts??åg&eppo?lå???ureksub.sen??e&ayb-yrettn--nx?d&ar?lom?r&of?øf??år??g&gyr?nats??i&meuv&ejsem&aan?åån??sekaal??rjea??j&d&ef?oks??les??k&er&aom?åom??hgna&ark?årk??iregnir?kot!s??s&ig?uaf???l&bmab?kyb?l&av?ehtats??oh??m&it?ojt?øjt??n&arg?g&os?øs??meh?reil?te?ummok?yrb??r&dils-erts&ev?y&o?ø???ua?vod??sa&ans?åns??t&robraa?spaav??urg??f&62ats-ugsrop--nx?a&10-ujvrekkhr--nx?7k-tajjrv-attm--nx??o!.sg?h??s!.sg??v!.sg???g&5aly-yr&n--nx?v--nx??a&llor?ve&gnal?lreb???n&av!snellu??org??oks&die?m&or?ør??ner&ol?øl??r&o?ø???r&eb!adnar?edyps?s&die?elf?gnok?n&ot?øt????obspras??uahatsla?åve&gnal?lreb???h&0alu-ysm--nx?7&4ay8-akiivagg--nx?5ay7-atkoulok--nx??a!.sg???i&e&hsr&agev?ågev??rf??k&h&avlaraeb?ávlaraeb??s??lm&a?å??mpouvtal&am?ám??pph&al?ál??rrounaddleid?ssaneve?ššáneve??j&0aoq-ysgv--nx?94bawh-akhojrk--nx??k&a&b&ord?ørd??jks?lleis??iv!aklejps?l&am?evs?u??mag?nel?ojg?r&a&l?n??epok?iel?y&or?ør???s&ah?kel?om??øjg??kabene?ojsarak?ram&deh.&aoq-relv--nx?rel&av?åv??so??e&let.&ag5-b--nx?ob?øb??ra???åjks??l&a!d&anrus?d&numurb?ron??e&gnard?nte?s&meh?sin??ttin??g&is?nyl??kro?l&em?l&ejfttah?of??u&ag-ertdim?s???n&am?era?gos?i&b?nroh?r??kos?nus?oj??o-&dron?r&os?øs???ppo?r&a!l?nram??e&gne?l?v??is?o&jts?ts??u&a-&dron?r&os?øs???h??å?æl?øjts??s&e&jg?nivk?ryf??kav?mor-go-er&om.&ednas?yoreh??øm.&ednas?yøreh???uag??t&las?rajh?suan??v&l&a?e-rots??u-go-eron??yt??ksedlig?res&a?å???bib&eklof?seklyf??es!dah??h!.sg??i&m?syrt??l&ejf?ov&etsua?gnit?ksa?sdie???n!.sg??o!.sg?boh?g?h??r!.sg??å!ksedlig??øboh??m&a&rah?vk??f!.sg??h!.sg??i&e&h&dnort?rtsua?ssej??rkrejb??ksa??ol?t!.sg??u&dom?esum?r&ab?drejg?evle?os?uh?æb?øs??ttals???n&a&g&av?okssman?åv??jlis?or?r&g?rev???e&d&do&sen?ton??lah?r&agy&o?ø??ojfsam???g&iets?n&a&l&as?lab??n&avk?ævk??t&arg?ddosen??v&al?essov???i&d&ol?øl??l&ar?ær???yl??reb??iks?k&srot?y&or?ør???l&a&d&gnos?n&er?ojm?øjm??om??tloh??ug?åtloh??mmard?ojs&om?sendnas??ppolg?s&lahsladr&ojts?øjts??o??t&o&l?t-erts&ev?o?ø???roh?øl??vly&kkys?nav??yam-naj!.sg??øjs&om?sendnas???g&orf?ujb??i&dnaort?vnarg??kob?ladendua?maherk&a?å??n&it?urgsrop??orf-&dron?r&os?øs???r&aieb?evats??sfev?uaks?yrts??o&6axi-ygvtsev--nx?c,d&ob?rav??ievs?kssouf?l&m&ob?øb??ous&adna?ech&ac?áč???so!.sg???msdeks?niekotuak?r&egark?olf?y&oso?øso???s&dav?mort???p&ed?ohsdaerpsym,p&akdron?elk???r&a&d&dj&ab?áb??iab??jtif?luag?mah?vsyt??e&gn&a&k&iel?ro??merb?n&at?mas??rav-r&os?øs??srop?talf?v&ats?el??y&oh?øh???ivsgnok??il?jkniets?k&a&nvej?rem?s&gnir?nellu???ie-er&den?v&o?ø???ram?sa?årem??la&jf?vh??m&b&ah?áh??mahellil??nnul?ts&l&oj?øj??ul??y&o?ø???imp&ah?áh??m!.sg??osir?t!.sg??ádiáb?ævsyt?øsir??s&adnil?en&dnas?e&dga?k&ri&b?k??som??ve??me&h?jg??nroh-go-ejve?s&a?ednil?k&o?ø??of?yt?å??tsev??gv?hf?igaval?o&r&or?ør??sman??so&fen&oh?øh??m?v??uh&lem?sreka.sen??å!dnil???t&a&baol?g&aov?grav??jjr&av-attam?áv-attám??l&a&b?s??ás??soum?ts?v&eib?our???e&dnaly&oh?øh??f?s&nyt?rokomsdeks?sen??vtpiks??in&aks?áks??loh&ar?år??n!.sg??o&m&a?å??psgolb,?s!.sg?efremmah?or?ør??terdi?á&baol?ggráv?lá&b?s??soum?veib???u&b!.sg?alk?e&dna?gnir?nner??les?ælk??dra&b?eb??g&nasrop?vi?ŋásrop??j&daehal&a?á??jedub?v&arekkhar?árekkhár???ksiouf?n&diaegadvoug?taed???v&irp?lesl&am?åm???y&b&essen?nart?sebel?tsev??o&d&ar?na!s??or??gavtsev?k&rajb?sa??lem?mrak?n&art?n&if?orb???r&a&mah?n?v??e&dni?t&so?ton??va??ul?yd??s&am?enner?gav?lrak?tivk??vrejks??ø&d&ar?na!s??ør??gåvtsev?k&rajb?sa??lem?mrak?n&art?n&if?ørb???r&e&dni?t&so?tøn??va??ul?yd?æ&n?v???s&enner?gåv?tivk?åm??vrejks???á&slág?tlá?vreiks??å&gåv?h?jddådåb?lf??ø&d&ob?rav??r&egark?olf??s&dav?mort????aki?i&sac?tal??u??o&b?f?g?hay?o?ttat??r!.&cer?erots?gro?m&o&c?n??rif?t??o&c,fni??pohs,stra?t&n?opsgolb,?www?ysrab,?e&a!.&a&ac?cgd?idem??bulc!orea??ci&ffartria?taborea??e&cn&a&l&lievrus-ria?ubma??netniam?rusni??erefnoc??gnahcxe?mordorea?ni&gne?lria?zagam??rawtfos??gni&d&art?ilg!arap?gnah???l&dnahdnuorg?ledom??noollab?retac?sael?t&lusnoc?uhcarap??vidyks??hcraeser?l&anruoj?euf?icnuoc?ortnoc!-ciffart-ria???n&gised?oi&nu?t&a&cifitrec?ercer?gi&tsevni-tnedicca?van??i&cossa!-regnessap??valivic??redef??cudorp?neverp-tnedicca????ograc?p&ihsnoipmahc?uorg!gnikrow???r&e&dart?enigne?korb?niart?trahc??o&htua?tacude???s&citsigol?e&civres?r??krow?serp!xe??tnega??t&farcr&ia?otor??hgil&f?orcim??liubemoh?n&atlusnoc?e&duts?m&esuma?n&iatretne?revog??piuqe????olip?ropria?si&lanruoj?tneics???w&erc?ohs??y&cnegreme?dobper?tefas????rref?z??p!.&a&aa?ca?pc??dem?ecartsnd.icb,gne?r&ab?uj??snduolc,t&acova?cca?hcer??wal?ysrab,???s!.&em?gro?hcs,moc?ten?ude?vog???t!.&116,ayo,gro?lim?moc?nayn,sulpnpv,t&cennockciuq.tcerid,en??ude?v&dr,og???o&hp?m?v?yk??tol?ua??v&iv?lov??xas?ykot??p&a&ehc?g?m?s??eej?g!.&gro?ibom?moc?ossa?ppa,ten?ude???i&r!.nalc,?v?z??j!.&a&3&5xq6f--nx?xqi0ostn--nx??5wtb6--nx?85uwuu--nx?9xtlk--nx?bihc!.&a&bihciakoy?don?ma&him?ye&ragan?tat???r&a&bom?gan?hihci??u&agedos?kas?ustak???s&os?ufomihs??t&amihcay?iran??w&a&g&im&anah?o??omak??kihci?zustum??ihsak??y&agamak?imonihci???e&akas?nagot??i&azni?esohc?h&asa?s&abanuf?ohc???ka&to?zok??musi?orihs?r&akihabihsokoy?o&dim?tak??ukujuk??usihs??nano&hc?yk??o&d&iakustoy?ustam??hsonhot?k&a&rihs?t??iba??nihsaran?sobimanim?tas&arihsimao?imot??uhc?yihcay??u&kujno?s&ayaru?t&imik?tuf???zarasik????g&as!.&a&gas?m&a&tamah?yik??ihsak??rat?t&a&gatik?hatik??ira!ihsin????e&kaira?nimimak??i&akneg?g&aruyk?o??h&c&amo?uo??siorihs??kaznak?modukuf?ra&gonihsoy?mi???nezih?u&k&at?ohuok??s&ot?tarak?????ihs!.&a&kok?m&a&hagan?yirom??ihsakat??rabiam?wagoton??e&miharot?nokih??houyr?i&azaihsin?esok?kustakat?moihsagih??na&mihcahimo?nok??o&hsia?mag?t&asoyot?ok?tir???us&ay?t&asuk?o??????k&aso!.&a&d&awihsik?eki??k&a&noyot?s&akaayahihc?oihsagih???oadat?uziak??m&ayas!akaso??odak??r&a&bustam?wihsak??ediijuf??t&akarih?i&k?us???wag&ayen?odoyihsagih???e&son?tawanojihs??honim?i&akas?h&cugirom?s&ayabadnot?i&a&kat?t??n??oyimusihsagih???k&a&rabi?sim??ustakat??muzi?r&ijat?otamuk???nan&ak?n&ah?es???o&ay?n&a&ganihcawak?simuzi?tak??eba?ikibah?oyot??t&anim?iad?omamihs??uhc??ust&oimuzi?tes????ou&kuf!.&a&d&amay?eos??g&no?ok?usak??hiku?k&awayim?uzii??ma&kan?y&asih?im???rawak?t&a&gon?ka&h?num?t???umo??wa&g&a&kan?nay?t??ias??ko!rih???y&ihsa?usak???e&m&ay?uruk??taruk?us??i&a&nohs?raihcat??goruk?h&cukuf?s&a&gih?hukuy??in???k&a&gako?muzim??iust?o?ustani??m&anim?otihsoynihs?u??r&ogo?ugasas??usu??ne&siek?zu&b?kihc???o&gukihc?h&ak?ot?ukihc??j&ono?ukihc??kayim?nihsukihc?to?uhc??u&fiazad?gnihs?stoyot????zihs!.&a&bmetog?d&amihs?eijuf?ihsoy?omihs??kouzihs?mihsim?ra&biah?honikam??tawi?wa&g&ekak?ukik??kijuf??yimonijuf??i&a&ra?sok??hcamirom?juf?kaz&eamo?ustam??ma&nnak?ta??nukonuzi?orukuf??nohenawak?o&nosus?ti??u&stamamah?z&a&mun?wak??i!ay?i&hs&agih?in??manim??mihs????????m&a&tias!.&a&d&ihsoy?ot?usah??k&a&dih?sa??o&arihs?s???m&a&tias?y&as?o&rom?tah??ustamihsagih???i&hsagurust?jawak??uri??ni?wa&g&e&ko?man??ikot?o??k&ara?i&hsoy?mak???ru?zorokot??y&a&g&amuk?ihsok?otah??kuf??imo??ziin??e&bakusak?ogawak?sogo?ttas?zokoy??i&baraw?h&cugawak?s&oyim?ubustam???iroy?k&ato?ihs?u&k?stawi???m&akoyr?i&hsoy?juf??uziimak???naznar?o&dakas?ihsay?jnoh?n&a&go?nim??imijuf?nah?oy??r&ihsayim?otagan??t&asim!ak??igus?omatik??zak??u&bihcihc!ihsagih??sonuok?ynah????y&ak&aw!.&a&d&ira?notimak??kadih?ma&h&arihs?im??y&a&kaw?tik??oduk???ru&ustakihcan?y??sauy?wa&g&a&dira?zok??orih??konik??yok?zok??e&banat?dawi??i&garustak?jiat?mani??naniak?o&bog?nimik?t&asim?omihs&ah?uk????ugnihs???o!.&a&jos?koasak?m&ay&ako?ust??ihsayah??r&abi?ukawaihsin??wi&aka?nam???e&gakay?kaw??i&gan?h&cu&kasa?otes??sahakat??k&asim?ihsaruk??miin??n&anemuk?ezib??o&hsotas?jnihs?n&amat?imagak??ohs?uhcibik?????ot!.&a&damay?got?koakat?may&etat?ot??nahoj?riat?waki&inakan?reman???eb&ayo?oruk??i&h&asa?ciimak?sahanuf??kuzanu?m&an&i?ot??ih???nezuyn?otnan?u&hcuf?stimukuf?z&imi?ou???????ihs&o&gak!.&a&m&ayuok?ihsogak??si?yonak??e&banawak?n&at&akan?imanim??uka??tomoonihsin??i&adnesamustas?k&azarukam?oih??m&ama?uzi??usuy??nesi?o&knik?os?tomustam??uzimurat???rih!.&a&ka&n?s??m&ayukuf?i&hsorihihsagih?j&ate?imakikaso????r&a&bohs?h&ekat?im???es??tiak?wiad??e&kato?ruk??i&h&ci&akustah?mono?nihs??s&inares?oyim???manimasa?uk??negokikesnij?o&gnoh?namuk??uhcuf????uk&ot!.&a&bihci?mi&hsu&kot?stamok??m??wagakan??egihsustam?i&gum?h&coganas?soyim??kijaw?m&anim?uzia??ukihsihs??nan&a?iak??o&nati?turan????uf!.&a&batuf?m&a&to?y&enak?irok???ihs&im?ukuf??os?uko??r&aboihsatik?uganat??ta&katik?mawak?rih??w&a&g&akus?emas?uy??k&a&mat?rihs?sa??ihsi??nah??ohs???e&gnabuzia?iman?ta&d?tii???i&adnab?enet?hs&agih?iimagak??k&a&wi?zimuzi??ubay??minuk?r&ook?ustamay???nihsiat?o&g&etomo?ihsin?nan?omihs??no!duruf?rih??rihsawani?ta&may?simuzia???u&rahim?stamakawuzia?zia&ihsin?nay???????nug!.&a&bawak?doyihc?k&anna?oi&hsoy?juf?mot???m&ayakat?ustagaihsagih??n&ihsatak?nak??r&ahonagan?nak?o?u&kati?mamat???t&amun?inomihs?o??w&akubihs?iem?ohs???i&hsa&beam?yabetat??kas&akat?esi??m&akanim?uzio??ogamust?rodim??o&jonakan?n&eu?oyikust??tnihs??u&komnan?stasuk?yrik?????ran!.&a&bihsak?d&akatotamay?u!o???guraki?m&ay&atik&imak?omihs??irokotamay??oki??ra&hihsak?n??wa&geson?knet???e&kayim?ozamay?sog?ustim??i&a&rukas?wak??garustak?h&ciomihs?sinawak??jo?ka&mnak?toruk??makawak?nos?r&net?otakat?ugeh???o&d&na?oyo??gnas?jnihs?nihsoy!ihsagih??tomarawat?yrok????t&ag&amay!.&a&dihsio?k&atarihs?ourust??may&a&kan?rum??enak?onimak??rukho?ta&ga&may?nuf??hakat?kas??wa&g&ekas?orumam??ki&hsin?m??z&anabo?enoy?ot???zuy??e&agas?bonamay?dii?nihsagih?o??i&a&gan?nohs??h&asa?sinawak??nugo??o&dnet?jnihs?ynan??ukohak???iin!.&a&ga?k&ium?oagan??munou!imanim??t&a&bihs?giin??ioy??w&a&gioti?kikes?zuy??irak??yijo??e&kustim?mabust??i&aniat?hcamakot?kaz&awihsak?omuzi??m&a&gat?karum??o???n&anust?esog??o&das?ihcot?jnas?k&ihay?oym??mak?naga?ries??u&ories?steoj?????i&ka!.&a&go?k&asok?oimak??t&ago!rihcah??ika!atik???w&aki?oyk???e&mojog?natim?suranihsagih?t&ado?okoy???i&hsoyirom?magatak?naokimak??nesiad?o&hakin?jnoh!iruy??nuzak?rihson?tasi&juf?m??yjnoh??u&kobmes?oppah????o!.&a&dakatognub?m&asah?ihsemih??su?t&ekat?i&h?o????e&onokok?ustimak??i&jih?k&asinuk?ias?usu??mukust??onoognub?u&fuy?juk?ppeb?suk??????wa&ga&k!.&a&mihsoan?rihotok?waga&kihsagih?ya???emaguram?i&j&nonak?ustnez??kunas?monihcu??o&hsonot?nnam?yotim??u&st&amakat?odat??zatu????nak!.&a&dustam?kus&okoy?tarih??maz?nibe?r&a&gihsaimanim?h&esi?imagas??wa&do?guy???u&im?kamak???tikamay?wa&k&ia?oyik?umas??sijuf??yimonin??e&nokah?saya??i&akan?esiak?gusta?hsuz?kasagihc?o?ukust??o&nadah?sio?tamay?????kihsi!.&a&danihcu?gak?kihs?mijaw?t&abust?ikawak??wazanak??i&gurust?hcionon?mon?ukah??nasukah?o&anan?ton!akan???u&kohak?stamok?z&imana?us?????niko!.&a&han?m&arat?ijemuk?uru??n&e&dak?zi??no??ra&hihsin?rih??wa&kihsi?niko??yehi?zonig??e&osaru?seay??i&hsagih?jomihs?k&a&gihsi?not??ihsakot??m&a&ginuk?kihsug?maz??igo?otekat??nuga!noy???n&a&moti?timoy?wonig??i&jikan?k???o&gan?jnan?tiad&atik?imanim???u&botom?kusug&akan!atik??imot??rab&anoy?eah???????c&204ugv--nx?462a0t7--nx?678z7vq5d--nx?94ptr5--nx?a??d&17sql1--nx?3thr--nx?5&20xbz--nx?40sj5--nx??7&87tlk--nx?ptlk--nx??861ti4--nx?a?e??e&16thr--nx?5&1a4m2--nx?9ny7k--nx??im!.&a&bot?k&asustam?uzus??m&a&him?y&emak?im???ihs??nawuk?wi&em?k???e&bani?ogawak?si!imanim???i&arataw?gusim?h&asa?ciakkoy??k&a&mat?sosik?t??iat??raban??o&dat?hik?n&amuk?ihseru?o&du?mok????ust???mihe!.&a&m&a&h&ataway?iin??yustam??ij&awu?imak???taki!man???ebot?i&anoh?kasam?rabami??n&ania?egokamuk?oot??o&jias?kihcu?nustam?uhcukokihs?yi!es???u&kohik?zo????n!.&nriheg,teniesa.resu,?amihs!.&a&d&amah?ho?usam??kustay?m&a?ihsoni&hsin?ko???wakih??e&namihs?ustam??i&g&aka?usay??konikak?mikih??nannu?o&mu&kay?zi!ihsagih?uko???nawust?tasim??u&stog?yamat?????tawi!.&a&bahay?d&amay?on??koirom?t&a&honat?katnezukir??imus??w&as&ijuf?uzim??ihs???e&hon&i&hci?n??uk??tawi??i&a&duf?murak?wak??h&custo?si&amak?ukuzihs???j&oboj?uk??k&a&m&anah?uzuk??sagenak??esonihci??m&akatik?uzia&rih?wi????o&kayim?no&rih?t??tanufo??uhso????g&3zsiu--nx?71qstn--nx?l??h&03pv23--nx?13ynr--nx?22tsiu--nx?61qqle--nx??i&54urkm--nx?g&ayim!.&a&dukak?m&a&goihs?kihs??ihsustam!ihsagih??unawi??r&awago?iho??ta&bihs?rum??w&a&gano?kuruf??iat??y&imot?ukaw???e&mot?nimes??i&hsiorihs?ka&monihsi?s&awak?o???mak?r&ataw?o&muram?tan????o&az?jagat?t&asim?omamay???u&fir?k&irnasimanim?uhsakihcihs?????ihcot!.&a&g&a&h?kihsa??ust??kom?m&ay&o?usarak??unak??r&a&boihsusan?watho??iho?ukas??t&akihsin?iay??wa&konimak?zenakat??y&imonustu?oihs???e&iiju?kustomihs?nufawi??i&akihci?g&etom?ihcot?on???o&k&ihsam?kin??nas?sioruk?tab??u&bim?san?????h&c&ia!.&a&dnah?m&a!h&akat?im??yuni??ihs&ibot?ust???r&a&hat?tihs??ik?u&ihsagih?kawi???t&ihc?o&k?yot???wa&koyot?zani??yi&monihci?rak???e&inak?k&aoyot?usa??manokot?noyot??i&a&gusak?kot?sia??eot?h&asairawo?cugo?s&ahoyot?oyim???k&a&mok?zako??ihssi??motay?rogamag??n&an&ikeh?ok??ihssin??o&got?ihsin?jna?rihsnihs?suf?tes??u&bo?raho?s&oyik?takihs??yrihc?zah????ok!.&a&dusay?kadih?mayotom?r&ah&im?usuy??umakan??sot!ihsin??wa&g&atik?odoyin??k&as?o????i&esieg?hco!k??jamu?k&a!sus??usto??ma&gak?k??rahan??o&mukus?n&i?ust!ihsagih???torum?yot!o???u&koknan?zimihsasot????ugamay!.&a&m&ayukot?ihso??toyot??e&bu?subat??i&gah?kesonomihs?nukawi?rakih??nanuhs?otagan?u&ba?foh?otim?stamaduk?uy?????sanamay!.&a&dihsoyijuf?mayabat?r&ahoneu?ustakihsin??w&a&k&ayah?ijuf??suran??ohs???egusok?i&ak?h&cimakan?s&anamay?od???k&asarin?u&feuf?sto????o&k&akanamay?ihcugawakijuf??nihso?t&asimawakihci?ukoh??uhc??spla-imanim?u&b&nan?onim??fok?hsok?rust?????ka&rabi!.&a&bukust?gok?kan!ihcatih??m&a&sak?timo?wi??ihsak?ustomihs??ni?r&a&hihcu?way??u&agimusak?ihcust???t&ag&amay?eman??oihcatih??w&ag&arukas?o??os??yi&moihcatih?rom???e&bomot?dirot?not?tadomihs??i&a&k&as?ot??rao??esukihc?gahakat?h&asa?catih??k&a&rabi?saguyr??ihsani?uy??ma?rukustamat??o&dnab?giad?him?kati?rihsijuf?soj?t&asorihs?im??yihcay??u&fius?kihsu?simak????sagan!.&a&m&abo?ihsust??natawak?r&abamihs?u&mo?ustam???wijihc?yahasi??i&akias?hies?k&asagan?i??masah??neznu?o&besas?darih?t&eso?og!imaknihs????ust&igot?onihcuk?uf????zayim!.&a&biihs?guyh?k&oebon?ustorom??mihsuk?r&emihsin?uatik??ta&katik?mim??wag&atik?odak??ya??e&banakat?sakog??i&hsayabok?kaza&kat?yim??m&animawak?ot&inuk?nihs????nanihcin?o&j&ik?onokayim??n&ibe?ust??tias??urahakat????ro&moa!.&a&dawot?turust?wasim??e&hon&ihc&ah?ihs??nas?og?ukor??sario??i&anarih?ganayati?hsioruk?jehon?kasorih?makihsah?nawo?r&amodakan?omoa???o&gnihs?kkat??u&ragust?stum????ttot!.&a&r&ahawak?uotok??sa&kaw?sim???egok?irottot?nanihcin?o&ganoy?nih?tanimiakas??u&bnan?z&ay?ihc??????ukuf!.&a&deki?gurust?ma&bo?h&akat?im??yustak??sakaw??eabas?i&akas?ho?jiehie?ukuf??nezihce!imanim??ono????k&26rtl8--nx?4&3qtr5--nx?ytjd--nx??522tin--nx?797ti4--nx??l33ussp--nx?m&11tqqq--nx?41s3c--nx??n&30sql1--nx?65zqhe--nx?n7p7qrt0--nx??o&131rot--nx?7qrbk--nx?c?diakkoh!.&a&deki?gakihset?hcebihs?k&adih?u&fib?narihs???m&ayiruk?hot?ihs&orihatik?ukuf??oras?usta??r&ib&a!ka??o?uruf??ozo?u&gakihsagih?oyot???sakim?ta&gikust?mun??w&a&ga&k&an?uf??nus!imak???k&aru?i&h&asa?sagih??kat?mak??omihs?um??zimawi??ine?oyk??yot??e&a&mustam?nan??b&a&kihs?yak??o&noroh?to???ian?k&ihsam?ufoto??nakami?ppoko!ihsin??sotihc?tad!okah??uonikat??i&a&bib?mokamot?n&a&k&kaw?oroh??wi??eomak?ihsatu?okik?usta&moruk?sakan????eib?h&c&ioy?u&bmek?irihs???s&ase?ekka?oknar?uesom???jufirihsir?k&amamihs?i&at?n???m&atik?otoyot??oa&kihs?rihs??r&a&hs?kihsi?mot??ihs&aba?ir??otarib???n&a&hctuk?rorum?se?tokahs??uber??o&kayot?m&ire?ukay??naruf!ima&k?nim???orih?r&ih&ibo?suk??o&bah?h&i&b?hsimak??sa??pnan?yan??umen??t&asoyik?eko?ukoh???u&bassa?kotnihs?m&assaw?uo??pp&akiin?en&ioto?nuk??ip??rato?s&akat?t&eb&e?i&a?hs!a??robon??m&e?o&m?takan???no&h?tamah??o&mik?s?t??u&kir?ppihc?st???onihsnihs?ufuras??uaru??yru!koh??zimihs!ok?????g!oyh!.&a&bmat?dnas?gusak?k&at?o&oyot?y??uzarakat??m&ayasas?irah??wa&g&ani?okak??k&i&hci?mak??oy???yi&hsa?monihsin???i&asak?hs&aka?i&at?nawak???j&awa!imanim??emih??k&a&goa?s&agama?ukuf??wihsin??i&hsog?m???mati?oia?rogimak??n&annas?esnonihs??o&gasa!kat??ka?n&ikat?o?ustat??rihsay?sihs?tomus?yas??u&bay?gnihs?????nagan!.&a&bukah?d&a&w?yim??e&ki?u??ii??k&a&s&ay?uki??zus??ihsoo?ousay??m&ay&akat?ii??i&hsukufosik?jii??ukihc??n&i!hsetat??uzii??r&ah?ugot??saim?t&agamay?oyim??w&a&g&a&kan?n??o??kustam?ziurak??onim!imanim??u&koo?s!omihs????ya&ko?rih???e&akas?nagamok?subo??i&gakat?h&asa?c&a!mo!nanihs???uonamay??sukagot??k&a&kas?mimanim?to??ia&atik?imanim??oa?uzihcom??m&akawak?ijuf?o!t???r&ato?ijoihs?omakat???n&ana?esnoawazon??o&hukas?n&a&gan?kan??i&hc?muza??ustat??romok?si&gan?k??tomustam??u&k&as?ohukihc??stamega????to&mamuk!.&a&gamay?rahihsin?sukama!imak??tamanim??enufim?i&hcukik?k&ihsam?u??nugo!imanim??romakat??o&ara?rihsustay?sa?t&amay?om&amuk?us??u!koyg???yohc??u&sagan?zo????yk!.&a&bmatoyk?k&ies?oemak?uzaw??mayi&h&cukuf?sagih??muk??nihsamay?rawatiju?t&away?ik???e&ba&nat!oyk??ya??di?ni??i&ju?kazamayo?manim??natnan?o&gnatoyk?kum?mak?rihsamayimanim?y&gakan?ka&koagan?s??oj???u&ruziam?z&ayim?ik??????wtc1--nx?ykot!.&a&d&i&hcam?mus??oyihc??k&atim?ihsustak??m&a&t!uko??yarumihsa&gih?sum???i&hs&agoa?ika?o!t??uzuok??ren???r&a&honih?wasago??iadok?umah??ssuf?t&ik?o??wa&g&anihs?ode??k&ara?ihcat???y&agates?ubihs???e&amok?donih?m&o?urukihsagih??soyik??i&enagok?gani?h&ca&da?tinuk??sabati??j&nubukok?oihcah??manigus??o&huzim?jihcah?n&akan?ih!sasum??urika??rugem?t&a&mayihsagih?nim??iat?ok??uhc?yknub??u&fohc?hcuf?kujnihs?????r&2xro6--nx?g?o??s&9nvfe--nx?xvp4--nx??t&netnocresu,opsgolb,?u&4rvp8--nx?fig!.&a&d&eki?ih??kimot?m&ayakat?ihsah??ne?raha&gi&kes?makak??sak??taga&may?tik??wa&g&ibi?ustakan??karihs!ihsagih????e&katim?uawak??i&gohakas?hc&apna?uonaw??k&ago?es?ot??m&anuzim?ijat??nak?urat??nanig?o&dog?jug?makonim?nim?roy?sihcih??u&fig?s&otom?t&amasak?oay???????x5ytlk--nx?yu6d27srjd--nx?z72thr--nx?井福?京東?分大?取鳥?口山?城&宮?茨??媛愛?山&富?岡?歌和??岡&福?静??島&児鹿?広?徳?福??崎&宮?長??川&奈神?石?香??庫兵?形山?手岩?木栃?本熊?根島?梨山?森青?潟新?玉埼?田秋?知&愛?高??縄沖?良奈?葉千?賀&佐?滋??道海北?都京?重三?野長?阜岐?阪大?馬群???k!.&art?gro?moc?per?ude?vog???l&eh?l??m!.uj,ac?j??nd?o&g?h&pih?s!.ysrab,??lnud?oc?t!.&lldtn,snd-won,???pa!.&0mroftalp,arusah,bew:erif,,e&gatskrelc,niln&igol,okoob,?tupmocegde,?krelc,lecrev,n&aecolatigidno,ur:.a,,?poon,remarf,t&ibelet,xenw,?yfilten,??ra&a?hs??u&ekam?llag?org!.esruocsid,cts?kouk?nayalo???vsr?xece4ibgm--nx??q&a!3a9y--nx??g?i!.&gro?lim?moc?ten?ude?vog???m?se??r&a!.&a&cisum?sanes??bog?gro?l&autum?im??moc!.topsgolb,?pooc?rut?t&e&b?n??ni??ude?vog??4d5a4prebgm--nx?b?c?eydoog?los?t&at?s!uen???ugaj??b!.&21g?a&b&a&coros?iuc??itiruc??cnogoas?dicerapa?gniram?i&naiog?ramatnas??n&erom?irdnol??op?p&acam?irolf?ma&j?s???rief?tsivaob??b!aj?ib?mi?sb??c&ba?e&r?t??js?sp?t!e???d&em?mb?n&f?i??rt??e&dnarganipmac?ficer?ht?llivnioj?rdnaotnas??f&dj?ed?gg?n&e?i???g&e&l!.&a&b,m,p,?bp,c&a,s,?e&c,p,s,?fd,gm,ip,jr,la,ma,nr,o&g,r,t,?p&a,s,?r&p,r,?s&e,m,r,?tm,??s??l&s?z??n&c?e?o??ol!b?f?v??pp?ro??hvp?i&du?kiw?nana?oretin?r&c?eurab??sp?te?xat??l&at&an?rof??el?im?sq??m&a?da?e&gatnoc?leb??f?ic?oc!.&duolclautriv.elacs.sresu,topsgolb,???nce?o&ariebir?c&e?narboir?saso??d&o?ranreboas??e&g?t??i&b?dar?ecam?r??rp?t&a?erpoir???p&er?m!e?t??ooc?pa?se??qra?r&af?ga?o&davlas?j??tn?ut??s&a&ixac?mlap?nipmac??ed?u&anam?j?m???t&am?e&d?n?v??nc?o&f?n??ra?sf??u&caug9?de?ja?rg??v&da?ed?og!.&a&b?m?p??bp?c&a?s??e&c?p?s??fd?gm?ip?jr?la?ma?nr?o&g?r?t??p&a?s??r&p?r??s&e?m?r??tm???rs?t??xiv?z&hb?ls?o&c?f?????c!.&as?ca?de?if?o&c?g??ro???e&bew?ccos?dnik?e&b?n&igne?oip??rac??gni&arg?rheob??h&cor?sok?t&aew?orb???itnorf?k&col?o&p?rb???l&aed?ffeahcs??mal?nes?pinuj?t&a&eht?rebsnegömrev??law?nec?s&acnal?nom?ubkcolb??upmoc??v&o&csid?rdnal??resbo??wulksretlow?ywal?zifp??f!.&aterg?bew-no,drp?e&c&itsuj-reissiuh?narf-ne-setsitned-sneigrurihc,?lipuog,rianiretev??hny,i&cc?rgabmahc??m&o&c?n??t??n&eicamrahp?icedem??ossa?pohsdaerpsym,s&e&lbatpmoc-strepxe?riaton?tsitned-sneigrurihc?uova??o&-x&bf,obeerf,?x&bf,obeerf,???t&acova?o&or-ne,psgolb,?r&epxe-ertemoeg?op!orea????vuog??avc7ylqbgm--nx?s??g!.&gro?moc?t&en?opsgolb,?ude?vog???h!.&e&erf,man??mo&c?rf??topsgolb,zi??ur??i!.&a&61f4a3abgm--nx?rf4a3abgm--nx??ca?di?gro?hcs?oc?ten?vog?نار&يا?یا???a&h?per??ew?lf??k!.&c&a?s??e&n?p?r??gk?iggnoeyg?kub&gn&oeyg?uhc??noej??l&im?uoes??man&gn&oeyg?uhc??noej??n&as&lu?ub??o&e&hcni?jead??wgnag???o&c?g??ro?s&e?h?m??topsgolb,u&gead?j&ej?gnawg????cilf??l!.&gro?moc?ten?ude?vog???m!.&topsgolb,vog???n!.&gro?moc?ofni?ten?ude?vog?zib???o&htua?odtnorf?t&c&a?od??laer???p!.&alsi?ca?eman?forp?gro?moc?o&fni?rp??t&en?se??ude?vog?zib???s?t!.&21k?bew?cn!.vog??eman?gro?kst?l&e&b?t??im?op??moc!.topsgolb,?neg?ofni?pek?rd?sbb?ten?ude?v&a?og?t??zib??f?m??ubad?vd??s&8sqif--nx?9zqif--nx?a!.vog?birappnb?gev?lliv?mtsirhc?s??b!.&ew,gro?moc?ten?ude?vog??c?oj?s?u??c&i&hparg?p?t&sigolyrrek?ylana???od??d&a?d?ik?l?n&iwriaf?omaid??oogemoh?rac??e!.&bog?gro?mo&c!.topsgolb,?n??pohsdaerpsym,ude??civres!.enilnigol,?d&d2bgm--nx?oc??h&ctaw?guh??i&lppus?rtsudni?treporp!yrrek???jaiv?l&aw?cycrotom?etoh?gnis?pats??m&ag?oh?reh??nut?ohs?picer?r&it?ut&cip!.7331,?nev???s!i&rpretne?urc??ruoc??taicossa?vig??g!nidloh??h5c822qif--nx?i!.&ekacpuc,gro?moc?t&en?ni?opsgolb,?ude?vog??a09--nx?nnet?rap?targ??k&c&or!.&ecapsbew,snddym,ytic-amil,??us??hxda08--nx?row??l!.&c&a?s??ed,gro?o&c?fni??ten?ude?vog?zib??a&ed?tner??e&ssurb?toh!yrrek???lahsram?m?oot??m!.&bal,etisinim,gro?moc?ten?ude?vog??b?etsys!.tniopthgink,?ialc??n&a&f?gorf?ol??egassap?i&a&grab?mod??giro??o&it&acav?cudorp?ulos??puoc???o&dnoc?geuj?leuv?ppaz?t&ohp?ua???p!.&ces?gro?moc?olp?ten?ude?vog??i&hsralohcs?lihp?t??u??r!.&au,ca?gro?ni?oc?topsgolb,ude?vog?xo,yldnerb.pohs,?a&c?p?tiug??c?e&dliub!.etisduolc,?erac?gor?levart?mraf?n&niw?trap??wolf??ot&cartnoc?omatat??pj?uot??s!.&em?gro?hcs?moc?ten?ude?vog?zib??alg?e&n&isub!.oc,?tif??rp!xe!nacirema???xnal??iws??t&a&e&b?ytic??ob??ek&cit?ram??fig?h&cay?gilf??n&atnuocca?e&mt&rapa?sevni??ve!.oc,???rap??u!.&a&c!.&21k?bil?cc???g!.&21k?bil?cc???i!.&21k?bil?cc???l!.&21k?bil?cc???m!.&21k!.&hcorap?rthc?tvp???bil?cc???p!.&21k?bil?cc???si?v!.&21k?bil?cc???w!.&21k?bil?cc????c&d!.&21k?bil?cc???n!.&21k?bil?cc???s!.&21k?bil?cc????d&e&f?lacsne.xhp,?i!.&21k?bil?cc???m!.&21k?bil?cc???n!.&bil?cc???s!.&bil?cc???u&olcrim,rd,??e&d!.&21k?bil,cc???las-4-&dnal,ffuts,?m!.&21k?bil?cc???n!.&21k?bil?cc????h&n!.&21k?bil?cc???o!.&21k?bil?cc????i&h!.&bil?cc???m!.&21k?bil?c&c?et??goc?n&eg?otae??robra-nna?sum?tsd?wanethsaw???nd?r!.&bil?cc???v!.&21k?bil?cc???w!.&21k?bil?cc????jn!.&21k?bil?cc???k&a!.&21k?bil?cc???o!.&21k?bil?cc????l&a!.&21k?bil?cc???f!.&21k?bil?cc???i!.&21k?bil?cc????mn!.&21k?bil?cc???n&afflog,i!.&21k?bil?cc???m!.&21k?bil?cc???sn?t!.&21k?bil?cc????o&c!.&21k?bil?cc???m!.&21k?bil?cc???ttniop,?p&ion,rettalp,?r&a!.&21k?bil?cc???o!.&21k?bil?cc???p!.&21k?bil?cc????s&a!.&21k?bil?cc???dik?k!.&21k?bil?cc???m!.&21k?bil?cc???nd&deerf,uolc,??t&c!.&21k?bil?cc???m!.&21k?bil?cc???u!.&21k?bil?cc???v!.&21k?bil?cc????ug!.&21k?bil?cc???v&n!.&21k?bil?cc???w!.cc???x&ohparg,t!.&21k?bil?cc????y&b-si,k!.&21k?bil?cc???n!.&21k?bil?cc???w!.&21k?bil?cc????za!.&21k?bil?cc????ah!uab??bria?col?e!.ytrap.resu,?ineserf?lp?xe&l?n???vt?w!.&66duolc,gro?moc?s&ndnyd,tepym,?ten?ude?vog??a?e&iver?n!.elbaeciton,??odniw??y&alcrab?cam?ot???t&0srzc--nx?a!.&amil4,ca!.hts??gni&liamerutuf,tsoherutuf,?o&c!.topsgolb,?fni,?p&h21,ohsdaerpsym,?r&euefknuf.neiw,o??v&g?irp,?xi2,ytic-amil,zib,?c?e!s??hc?if?l!asite??mami?rcomed??b!.&gro?moc?ten?ude?vog??b?gl??c&atnoc?e&les?rid!txen????dimhcs?e!.&eman?gro?moc?ofni?ten?ude?vog?zib??b?em?grat?id?k&circ?ram??n!.&0rab,1rab,2rab,5inu,6vnyd,7&7ndc.r,erauqs,?a&l&-morf,moob,?minifed,remacytirucesym,tadsyawla,z,?b&boi,g,lyltsaf:.pam,,?c&inagro-gnitae,paidemym,?d&ecalpb,irgevissam.saap.&1-&gs,nol,rf,yn,?2-&nol,yn,??nab-eht-ni,uolc&meaeboda,nievas.c&di-etsedron,itsalej,?xednay:.e&garots,tisbew,?,??e&c&narusnihtlaehezitavirp,rofelacs.j,?gdirbtib,ht-no-eciffo,l&acsnoom,ibom-eruza,?m&ecnuob,ohtanyd,tcerider,?n&ilno-evreser,ozdop,?rehurht,s:abapus,,tis-repparcs,zamkcar,?f&aeletis,crs.&cos,resu,?ehc-a-si,?g&ni&reesnes,sirkcilc,tsohnnylf,?olb&evres,tsaf,??k&catsvano,eeg-a&-si,si,?u,?l&acolottad,iamwt,s&d-ni,s-77ndc,??m&ac&asac,ih,?urofniem,?n&a&f&agp,lhn,?i&bed,llerk,??dcduabkcalb,i,pv-ni,?o&c-morf,duppa,jodsnd,rp-ytinummoc,ttadym,?p&i&-&etsef,on,?emoh,fles,nwo,?j,mac-dnab-ta,o&-oidar-mah,h&bew,sdaerpsym,??pa&duolc,egde,?tfe&moh,vres,?usnd,?r&e&tsulcyduolc,vres-xnk,?vdslennahc:.u,,?s&a&ila&nyd,snd,?nymsd,?bbevres,dylimaf,e&gde-ndc,suohsyub,t&isbeweruza,ys,??k&catstsaf,ekokohcs,?n&d&-won,d,golb,npv,?oitcnufduolc,?ppacitatseruza:.&2suts&ae,ew,?aisatsae,eporuetsew,sulartnec,?,s&a-skcik,ecca&-citats,duolc,???t&adies,ce&ffeym,jorprot:.segap,,?e&nretnifodne,smem,?farcenimevres,i-&ekorb,s&eod,lles,teg,??n&essidym,orfduolc,?r0p3l3t,s&ixetnod,oh&-spv:.citsalej.&cir,lta,sjn,?,gnik,???u&h,nyd,r:eakust.citsalej,,?ved-naissalta.dorp.ndc,x&inuemoh,spym,tsale.&1ots-slj,2ots-slj,3ots-slj,?unilemoh,?y&awetag-llawerif,ffijduolc:.&ed-1arf,su-1tsew,?,ltsaf.&dorp.&a,labolg,?lss.&a,b,labolg,?pam,slteerf,?n&-morf,ofipi,?srab,?z&a-morf,tirfym,???p?tcip?v??f&ig?o&l?sorcim???g!.&bog?dni?ed,g&olb,ro??lim?moc?ot,ten?ude???h!.&dem?gro?l&er?op??m&oc?rif??o&fni?rp?s&rep?sa???po&hs?oc??t&en?luda?ra??ude?vuog???i!.&a&2n-loritds--nx?7e-etsoaellav--nx?8&c-aneseclrof--nx?i-lrofanesec--nx??at?b?c!cul??dv?i&blo&-oipmet?oipmet??cserb?drabmol?g&gof?urep??l&gup?i&cis?me&-oigger?oigger???uig&-&aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf???aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf????n&a&brev?cul?pmac?tac??idras?obrac&-saiselgi?saiselgi??resi??otsip?r&b&alac!-oigger?oigger??mu??dna&-&attelrab-inart?inart-attelrab??attelrabinart?inartattelrab?ssela??epmi?ugil??tnelav&-obiv?obiv??vap?z&e&nev?ps&-al?al???irog???l&iuqa!l??leib??m&or?rap??n!acsot?e&dom?is?sec&-&ilrof?ìlrof??ilrof?ìlrof???g&amor&-ailime?ailime??edras?olob??i&ssem?tal??ne!var??o&cna?merc?rev?vas???oneg?p?r!a&csep?rr&ac&-assam?assam??ef??von??etam?tsailgo!-lled?lled???s!ip?sam&-ararrac?ararrac??u&caris?gar???t!a&cilisab?recam??resac?soa!-&d&-&ellav?lav??ellav?lav??ellav??d&-&ellav?lav??ellav?lav??ellav??te&lrab&-&airdna-inart?inart-airdna??airdnainart?inartairdna??ssinatlac???udap?v!o&dap?neg?tnam???zn&airb&-a&lled-e-aznom?znom??a&lledeaznom?znom??eaznom??e&c&aip?iv??soc?top??om???b&-&23,46,61,?3c-lorit-ds-onitnert--nx?be-etsoa&-ellav--nx?dellav--nx??c!f-anesec-lrof--nx?m-lrof-anesec--nx??he-etsoa-d-ellav--nx?m!u??o2-loritds-nezob--nx?sn-loritds&-nasl&ab--nx?ub--nx??nitnert--nx??v!6-lorit-dsnitnert--nx?7-loritds&-nitnert--nx?onitnert--nx???z&r-lorit-ds&-nitnert--nx?onitnert--nx??s-loritds-onitnert--nx???c&f?is?l?m?p?r?v??d&p?u!olcnys,??e&c!cel?inev?nerolf??f?g!ida&-&a&-onitnert?onitnert??otla!-onitnert?onitnert???a&-onitnert?onitnert??otla!-on&azlob?itnert??onitnert????hcram?l?m!or??n&idu?o&n&edrop?isorf??torc???p?r?s&erav?ilom??t!nomeip?s&eirt?oa!-&d-e&ellav?éllav??e&ellav?éllav???de&ellav?éllav??e&ellav?éllav?????v?znerif??g&a?b?f?il?o?p?r?up?vf??hc?i&b?c?dol?f?l!lecrev?opan?rof&-anesec?anesec???m?n&a&part?rt&-attelrab-airdna?attelrabairdna???imir?ret??p?r!a&b?ilgac?ssas???s!idnirb??t&ei&hc?r??sa??v??l&a!c??b?c?o&m?rit&-&d&eus&-&nitnert?onitnert??nitnert?onitnert??us&-&nitnert?onitnert??nitnert?onitnert??üs&-&nitnert?onitnert??nitnert?onitnert???s&-onitnert?onitnert???d&eus!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??us&-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??üs!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert???s&-onitnert?onitnert?????m&ac?f?i!t.nepo.citsalej.duolc,?ol?r??n&a!lim?sl&ab?ub???b?c?e!en.cj,v?zob??irut?m!p??p?r?t??o&a!v??b!retiv??c!cel??enuc?g!ivor??i&dem&-onadipmac?onadipmac??pmet&-aiblo?aiblo??rdnos?zal??l?m!a&greb?ret??oc?re&f?lap???n!a&dipmac&-oidem?oidem??lim?tsiro?zlob??ecip&-ilocsa?ilocsa??i&bru&-orasep?orasep??lleva?rot?tnert??r&elas?ovil??ulleb??p?r!a&sep&-onibru?onibru??znatac??oun??s!ivert?sabopmac??t!arp?e&nev?ssorg??n&arat?e&girga?rt?veneb????zz&era?urba???p&a?ohsdaerpsym,s?t??qa?r&a!m?s??b!a??c?f?g?k?me?o?p?s?t?v??s&a&b?iselgi&-ainobrac?ainobrac???b?c?elpan?i?m?ot?s?t?v??t&a?b?c?l?m?nomdeip?o!psgolb,?p?v??u&de?l?n?p??v&a?og?p?s?t?v??y&drabmol?ellav&-atsoa?atsoa??licis?nacsut??z&al?b?c?p??ìlrof&-anesec?anesec???derc?er?f?m?utni??je3a3abgm--nx?kh?l!.&topsgolb,vog??uda??m!.&gro?moc!.topsgolb,?ten?ude???n&a&morockivdnas?ruatser?tnuocca??e&g?m&eganam!.retuor,?piuqe??r??i!.ue?m?opdlog??opud?uocsid??o&b?cs!.&ude,vog:.ecivres,,??d?g?h?j?oferab?p&edemoh?s???p!.&emon?gro?lbup?moc?t&en?ni?opsgolb,?ude?vog???r&a!m&law?s???epxe?op&er?pus!.ysrab,?s???s!.&adaxiabme?e&motoas?picnirp?rots??gro?lim?moc?o&c?dalusnoc?hon,?ten?ude??a&cmoc?f??e&b?padub?r?uq??i!rolf?tned??o&h!.&duolc&p,rim,?e&lej,tiseerf,?flah,lrupmet,s&pvtsaf,seccaduolc,?tsafym,vedumpw,??p!sua???urt??t!.&eman?gro?ibom?levart?m&oc?uesum??o&c?fni?r&ea?p???pooc?sboj?t&en?ni??ude?vog?zib??ayh?n?o!bba?irram???uognah?xen?y!.gro,?ztej??u&2&5te9--nx?yssp--nx??a!.&a&s?w??civ?d&i?lq??fnoc?gro?moc!.&pohsdaerpsym,stelduolc.lem,topsgolb,??nsa?ofni?sat?t&ca?en?n??ude!.&a&s?w??ci&lohtac?v??dlq?sat?t&ca?n??wsn!.sloohcs????vog!.&a&s?w??civ?dlq?sat???wsn?zo??ti??c!.&fni?gro?moc?ten?ude?vog??i??d&e!.tir.segap-tig,?iab??e!.&dcym,enozgniebllew,noitatsksid,odagod.citsalej,snd&ps,uolc,?ysrab,??g!.&bew?gro?m&aug?oc??ofni?ten?ude?vog???h!.&0002?a&citore?idem?kitore??edszot?gro?ilus?letoh?m&alker?lif?t?urof??naltagni?o&c?ediv?fni?levynok?nisac??pohs?rarga?s&a&kal?zatu??emag?wen??t&lob?opsgolb,rops??virp?xe&s?zs??ytic?zsagoj??os?sut??l!.topsgolb,?m!.&ca?gro?moc?oc?ro?ten?vog???n!.&duolcesirpretne,eni&esrem,m,?tenkcahs,?em!.ysrab,??o&ggnaw?y!c???r!.&3kl,a&i&kymlak,rikhsab,vodrom,?yegyda,?bps,ca,duolcrim,e&niram,rpcm,?g&bc,nitsohurger.citsalej,ro,?ianatsuk,k&ihclan,s&m,rogitayp,??li&amdlc.bh,m,?moc,natsegad,onijym,pp,ri&b,d&cm:.spv,,orue,?midalv,?s&ar,itym,?t&en,ni,opsgolb,set,?u&4an,de,?vo&g,n,?ynzorg,zakvakidalv,?myc?p?ug??s!.&a&d&golov,nagarak,?gulak,i&groeg,kymlak,lerak,nemra,rikhsab,ssakahk,vodrom,zahkba,?lut,rahkub,vut,yegyda,znep,?bps,da&baghsa,rgonilest,?gunel,i&anatsuk,hcos,ovan,ttailgot,?k&alhsygnam,ihclan,s&legnahkra,m,n&a&mrum,yrb,?i&buytka,nbo,??tiort,vorkop,??l&ocarak,ybmaj,?na&gruk,jiabreza,ts&egad,hkazak-&htron,tsae,???ovonavi,r&adonsark,imidalv,?t&enxe,nek&hsat,mihc,??vo&hsalab,n,?ynzorg,z&akvakidalv,emret,??t&amok?i&juf?masih????v!.&em,g&olb,ro??moc?nc,ten?ude?ved,??ykuyr??v&b?c!.topsgolb,?ed!.&enilnigol,gnigats-oned,hcetaidem,lecrev,o&ned,tpyrctfihs,?ppa-rettalp,s&egap,rekrow,?vr&esi,uc,?weiverpbuhtig,ylf,??ih?l!.&di?fnoc?gro?lim?moc?nsa?ten?ude?vog???m!.&eman?gro?lim?m&oc?uesum??o&fni?r&ea?p???pooc?t&en?ni??ude?vog?zib???o&g?m??rt?s!.&bog?der?gro?moc?ude???t!.&bew-eht-no,naht-&esrow,retteb,?sndnyd,?d?gh?i?won??uqhv--nx??w&a!.moc?hs?l??b!.&gro?oc???c!.&gro?moc?ten?ude??cp??e&iver!.oby,?n?s??g?k!.&bme?dni?gro?moc?ten?ude?vog???m!.&ca?gro?m&oc?uesum??oc?pooc?t&en?ni??ude?vog?zib??b??o&csom?h!s??n?w??p!.&344x,de?en?o&c?g??ro?snduolc,ualeb???r!.&ca?gro?lim?oc?pooc?ten?vog??n??t!.&a46oa0fz--nx?b&82wrzc--nx?ulc??emag?gro?l&im?ru,?moc!.reliamym,?t&en?opsgolb,?ude?v&di?og?ta0cu--nx??zibe?業商?織組?路網???z!.&ca?gro?lim?oc?vog????x&a!.&cm,eb,gg,s&e,u,?tac,ue,yx,?t??c!.&hta,ofni,vog???e&d&ef?nay??ma!nab??rof?s??ilften?jt?m!.&bog?gro?moc?t&en?opsgolb,?ude??g?ma2ibgy--nx??o&b!x??f?rex??rbgn--nx?s!.vog??x&am&jt?kt??x???y&4punu--nx?7rr03--nx?a&d!i&loh?rfkcalb??ot!.emyfilauqerp,??g?lp?p!ila??rot?ssin?wdaorb??b!.&duolcym,fo?hcetaidem,lim?moc!.topsgolb,?vog??ab?gur??c!.&ca?dtl?eman?gro?m&oc!.&ecrofelacs.j,topsgolb,??t??orp?s&egolke?serp??t&en?nemailrap??vog?zib??amrahp?nega??d&dadog?uts??e&kcoh?ltneb?n&dys?om?rotta??snikcm??g!.&eb,gro?moc?oc?ten?ude?vog??olonhcet!.oc,?rene??hpargotohp?id?k!.&gro?moc?ten?ude?vog??s??l!.&clp?d&em?i??gro?hcs?moc?ten?ude?vog??f?imaf!nacirema??l&a?il??ppus??m!.&eman?gro?lim?moc?t&en?opsgolb,?ude?vog?zib??edaca!.laiciffo,?ra??n&a&ffit?pmoc!ylimafa???os??o&j?s??p!.&gro?lim?moc?pooc?ten?ude?vog???r&e&corg?grus?llag?viled??lewej?otcerid?tnuoc?uxul??s!.&gro?lim?moc?ten?ude?vog??pil??t&efas?i&c?ledif?n&ifx?ummoc!.&bdnevar,gon,murofym,???r&ahc?uces??srevinu??laer?r&ap!.oby,?eporp??uaeb??u!.&bug?gro?lim?moc!.topsgolb,?ten?ude??b!tseb???van!dlo??xes??z&a!.&eman?gro?lim?moc?o&fni?rp??pp?t&en?ni??ude?vog?zib???b!.&az,gro?jsg,moc?ten?ude?vog???c!.&4e,inum.duolc.&rsu,tlf,?m&laer,urtnecatem.motsuc,?oc,topsgolb,??d!.&cos?gro?lop?m&oc?t??ossa?t&en?ra??ude?vog???ib!.&duolcsd,e&ht-rof,mos-rof,rom-rof,?izoj,nafamm,p&i&-on,fles,?ohbew,tfym,?retteb-rof,snd&nyd,uolc,?xro,?g??k!.&duolcj,gro?lim?moc?t&en?ropeletzak.saapu,?ude?vog???m!.&ca?gro?lim?oc?ten?ude?v&da?og????n!.&asq-irom--nx?ca?gro?htlaeh?i&r&c?o&am?ām???wi!k???keeg?l&im?oohcs??neg?oc!.topsgolb,?t&en?nemailrap?vog???a!niflla???rawhcs?s!.&ca?gro?oc???t!.&c&a?s??e&m?n??ibom?l&etoh?im??o&c?fni?g??ro?vt???u!.&gro?moc?oc?ten??rwon??yx!.&e&nozlacol,tisgolb,?gnitfarc,otpaz,??zub??λε?υε?авксом?брс!.&гро?до?ка?р&бо?п!у?????г&б?ро??дкм?зақ?итед?килотак?леб?мок?н&йално?ом??рку?сур!.&арамас,бпс,гро,зиб,ичос,ксм,м&ок,ырк,?рим,я,??тйас?фр?юе?յահ?לארשי?םוק?اي&روس?سيلم?ناتيروم??بر&ع?غملا??ة&كبش?ي&دوعسلا?روس??یدوعسلا??ت&ا&راما?لاصتا??را&ب?ڀ?ھب???ر&ئازجلا?ازاب?صم?طق??سنوت?عقوم?قارع?ك&تيب?يلوثاك??موك?ن&ا&تس&كاپ?کاپ??دوس?ر&يا?یا??مع?يلعلا??درالا?ميلا?ي&رحبلا?طسلف???ه&ارمه?يدوعسلا??وكمارا?يبظوبا?ۃیدوعسلا?टेन?त&राभ?ोराभ??नठगंस?मॉक?्मतराभ?ত&রাভ?ৰাভ??ালংাব?ਤਰਾਭ?તરાભ?ତରାଭ?ாயித்நஇ?ைக்ஙலஇ?்ரூப்பக்ஙிச?్తరాభ?ತರಾಭ?ംതരാഭ?ාකංල?มอค?ยทไ!.&จิกรุธ?ต็นเ?ร&ก์คงอ?าหท??ลาบฐัร?าษกึศ???ວາລ?ეგ?なんみ?アトス?トンイポ?ドウラク?ムコ?ル&グーグ?ーセ??ン&ゾマア?ョシッァフ??业企?东广?乐娱?亚基诺?你爱我?信中?务政?动移?博微?卦八?厅餐?司公?品食?善慈?团集?国中?國中?址网?坡加新?城商?尚时?山佛?店&商?网?酒大里嘉??府政?康健?息信?戏游?拉里格香?拿大?教主天?机手?构机!织组??标商?歌谷?浦利飞?港香!.&人個?司公?府政?絡網?織組?育教???湾台?灣&台?臺??物购?界世?益公?看点?科盈訊電?站网?籍書?线在?络网?网文中?聘招?販通?车汽众大?逊马亚?通联?里嘉?锡马淡?門澳?门澳?闻新?電家?국한?넷닷?성삼?컴닷??"); + "a&0&0trk9--nx?27qjf--nx?e9ebgn--nx?nbb0c7abgm--nx??1&2oa08--nx?apg6qpcbgm--nx?hbbgm--nx?rdceqa08--nx??2&8ugbgm--nx?eyh3la2ckx--nx?qbd9--nx??3&2wqq1--nx?60a0y8--nx??4x1d77xrck--nx?6&1f4a3abgm--nx?2yqyn--nx?5b06t--nx?axq--nx?ec7q--nx?lbgw--nx??883xnn--nx?9d2c24--nx?a&a?it??b!.&gro?lim?moc?sr,ten?ude?vog??abila?c?ihsot?m?n??c!.&b&a?m?n??c&b?g?q??ep?fn?k&s?y??ln?no?oc,p&i-on,ohsdaerpsym,?sn?tn?un?xob,ysrab,?i&ma?r&emarp?fa??sroc???d&ats?n&eit?oh??om?sa?tl??eg?f&c?ob??g!emo?naripi?oy??hskihs?i&dem!.remarf,?hs?k!on??sa!.&nomead,snduolc,xid,???jnin?k&aso?dov?ede?usto??l!.&gro?moc?ofni?r&ep?nb,?t&en?ni??ude?vog??irgnahs?le&nisiuc?rbmuder???m!.&ca?gro?oc?sserp?ten?vog??ahokoy?e00sf7vqn--nx?m??n!.&gro?moc?oc?t&en?la??vog??av?is?olecrab?tea??p!.&bog?ca?d&em?ls??g&ni?ro??mo&c?n??oba?ten?ude??c?g7hyabgm--nx?ra!.&461e?6pi?emoh?iru?nru?rdda-ni?siri???s??q!.&eman?gro?hcs?lim?moc?ten?ude?vog???r&az?emac?f4a3abgm--nx?n!d5uhf8le58r4w--nx??ukas??s!.&bup?dem?gro?hcs?moc?ten?ude?vog??ac!.uban.iu,?iv??t&ad?elhta?led?oyot??u!.&a&cinniv?emirc?i&hzhziropaz?stynniv?ttaprakaz??s&edo?sedo??tlay?vatlop??bs?cc,d&argovorik?o!ro&ghzu?hhzu???tl,?e&hzhziropaz?i,nvir?t??f&i?ni,?g&l?ro??hk?i&stvinrehc?ykstyn&lemhk?vypork???k&c?m?s&na&gul?hul??t&enod?ul??v&iknarf-onavi?orteporp&end?ind?????l&iponret?opotsa&bes?ves??p??m&k?oc?s?yrk??n&c?d?i?osrehk?v?ylov??o&c,nvor??p&d?p,z??r&c?imotihz?k?ymotyhz??sk?t&en?l?z??ude?v:c?e&alokin?ik??i&alokym?hinrehc?krahk?vl?yk??k?l?o&g!inrehc??krahk??r?,xc,y&ikstinlemhk?mus?s&akrehc?sakrehc?tvonrehc???z&ib,u????v!aj?bb?et?iv??waniko?x&a?iacal??yogan?z&.&bew?c&a?i&n?rga???gro?l&im?oohcs??m&on?t??o&c?gn??radnorg?sin?t&en?la??ude?vog?wal??zip!.korgn,???b&00ave5a9iabgm--nx?1&25qhx--nx?68quv--nx?e2kc1--nx??2xtbgm--nx?3&b2kcc--nx?jca1d--nx??4&6&1rfz--nx?qif--nx??96rzc--nx??88uvor--nx?a&0dc4xbgm--nx?c?her?n?ra?t??b!.&erots?gro?moc?o&c?fni??ten?ude?v&og?t??zib??a??c&j?s??d&hesa08--nx?mi??g?l!.&gro?moc?ten?ude?vog??m??s!.&gro?moc?ten?ude?vog???tc-retarebsnegmrev--nx?u&lc!.&elej,snduolc,ysrab,?smas??p!.ysrab,??wp-gnutarebsnegmrev--nx??c&1&1q54--nx?hbgw--nx??2e9c2czf--nx?4&4ub1km--nx?a1e--nx?byj9q--nx?erd5a9b1kcb--nx??8&4xx2g--nx?c9jrb2h--nx??9jr&b&2h--nx?54--nx?9s--nx??c&eg--nx?h3--nx?s2--nx???a!.&gro?kcabdeef,lim?moc?rrd,smrof,ten?ude?vog??3a09--nx!.&ca1o--nx?gva1c--nx?h&ca1o--nx?za09--nx??ta1d--nx?ua08--nx????b&a?b?ci?f76a0c7ylqbgm--nx?sh??c!.&eugaelysatnaf,gnipparcs,liamwt,nwaps.secnatsni,revres-emag,s&nduolc,otohpym,pparevelc,seccaptf,?xsc,?0atf7b45--nx?a1l--nx??e!.&21k?bog?dem?esab,gro?l&aiciffo,im??moc?nif?o&fni?rp??ten?ude?vog??beuq?n?smoc??fdh?i&lohtac?n&agro?ilc?osanap??sum?tic??l!.&gro?moc?oc?ten?ude?vog?yo,?l??m!.&mt?ossa??p1akcq--nx??n!.&mon?ossa??i?p??relcel?s!.&gro?moc?ten?ude?vog???t!s?w??v!.&gro?lim?moc?sndym,ten?ude?v&g:.d,,og????wp?yn??d&2urzc--nx?3&1wrpk--nx?c&4b11--nx?9jrcpf--nx???5xq55--nx?697uto--nx?75yrpk--nx?9ctdvkce--nx?a!d?er?olnwod??b2babgm--nx?c!.vog?g9a2g2b0ae0chclc--nx??e&m!bulc??r!k??sopxe?timil?w??fc?g!.&ude?vog???h&d3tbgm--nx?p?t??i!.&ased?bew?ca?hcs?lim?o&c?g??ro?sepnop?ten?ym?zib??b?ordna?p?rdam??l&iub!.0v,?og?row??m!.ri,?n&a&b?l!raas???ob?uf??o&of?rp??r&a&c&tiderc?yalcrab??ugnav??ef506w4b--nx?k!.&oc,ude,?jh3a1habgm--nx??of??s!.&dem?gro?moc?ofni?ten?ude?v&og?t???m!kcrem???t!excwkcc--nx?l??uolc!.&a&bura-vnej.&1ti,abura.rue.1ti,?tcepsrep,xo:.&ku,nt,?,?b&altam,dnevar,ewilek:.sc,,?citsalej.piv,drayknil,elej,gnitsohdnert.&ed,hc,?letemirp:.ku,,m&edaid,ialcer.&ac,ku,su,??n&evueluk,woru,?paz,qhelbavresbo,r&epolroov,o&pav,tnemele,??secivres-nosinu,t&enraxa.1-se,ikcatsno.snur,lobevres,?ululetoj,wcs.&gnilebaltrams,koobelacs,latemerab.&1-&rap-rf,sma-ln,?2-rap-rf,?rap-rf.&3s,cnf:.snoitcnuf,,etisbew-3s,mhw,s8k:.sedon,,tipkcoc,?s&8k,ecnatsni.&bup,virp,?ma-ln.&3s,etisbew-3s,mhw,s8k:.sedon,,tipkcoc,??waw-lp.&3s,etisbew-3s,s8k:.sedon,,tipkcoc,??xelpciffart,yawocne.ue,??za5cbgn--nx??e&1&53wlf--nx?7a1hbbgm--nx?ta3kg--nx??2a6a1b6b1i--nx?3ma0e1cvr--nx?418txh--nx?707b0e3--nx?a!.&ca?gro?hcs?lim?oc?ten?vog??09--nx??b!.&ca?etisbew321,gnitsohbew,nevueluk.yxorpze,pohsdaerpsym,sn&duolc,oitulostsohretni.duolc,??ortal?ut!uoy???c&0krbd4--nx!.&a2qbd8--nx?b8adbeh--nx?c6ytdgbd4--nx?d8lhbd5--nx???a&lp!.oc,?ps!.&fh:.citats,,lla4sx,rebu,sonoi-ppa,t&cejorp,safym,?uyieh,?artxe??sla??i!ffo??n&a&d?iler?nif?rusni!efil?srelevart???eics??rofria??d!.&1sndnyd,42pi-nyd,7erauqs,amil4,b&ow-nrefeilgitsng--nx,rb-ni,ur,vz-nelletsebgitsng--nx,?decalpb,e&daregtmueart,luhcsvresi,mohsnd,nihcamyek,tiesbew321,?gifnocecapsbew,hcierebsnoissuksid,keegnietsi,lsd-ni,m&oc,rofttalpluhcs,uhcob-inu-rhur:.con.oi,,?n&-i-g-o-l,aw-ym,e&lletsebgitsnüg,sgnutiel,?i&emtsi,lreb-n&i,yd,??norblieh-sh.ti.&hcraeser-segap,segap,?oitatsksid-ygolonys,pv&-n&i,yd,?nyd,?refeilgitsnüg,?orp-ytinummoc,p&h21,iog:ol,,ohsdaerpsym,?r&e&ntrapdeeps.remotsuc,su&-lautriv,lautriv,?t&adpusnd,tub-ni,uor-ym,?vres&-e&bucl,mohym,?bew-emoh:.nyd,,luhcs,??ogiv-&niem,ym,??s&d-&onys,ygolonys,?nd&-&dd,nufiat,sehcsimanyd,tenretni,yard,?isoc.nyd,ps,yard,?oper-&nvs,tig,?sndd:.&nyd,sndnyd,?,?vresi-&niem,tset,?xi2,y&awetag-&llawerif,ym,?srab,tic-amil,?zten&mitbel,sadtretteuf,??art?i&sdoow?ug??on--nx??e!.&bil?dem?eif?gro?irp?kiir?moc?pia?ude?vog??ei?ffoc?gg?r&f?ged???f&a&c?s??il??g!.&gro?loohcs?moc?t&en?vp??ude?vog??a&f?gtrom?p!.&3xlh,detalsnart,grebedoc,kselp,mea,sndp,wolfyeh,xlh,y&cvrp,kcor,???rots?yov??elloc?na&hcxe?ro!.hcet,??roeg?ug??i!.&pohsdaerpsym,vog??tilop?v&bba?om???j!.&fo,gro?oc?ten???k!.&c&a?s??e&m?n??ibom?o&c?fni?g??ro??i&b?l?n???l&a&dmrif?s!rof???b&a?i&b?dua???c&aro?ric??dnik?g!oog??i&bom?ms??l&asal?erauqa??ppa?uhcs?yts!efil???m!.&4&32i,p&ct,v,??66c,ailisarb,ca?duolcsd,g&ro?s-raegelif,?hctilg,kcatsegde,noitatsksid,o&c?t&nigol,poh,??p&i&on,snart.etis,?ohbew,?r&aegelif,idcm,ofsnd,?s&dym,ndd,ti?umhol,?t&en?farc,s&acdnuos,ohon,??ude?v&irp?og??y&golonys,srab,??a&g?n!.&reh.togrof,sih.togrof,???em?irp?orhc?w??n!goloc?i&lno!.&egats-oree,oree,redliubetisbew,ysrab,??w??o!.ecivres,hp?latipac?ts&der?e&gdirb?rif???z!.&66duolc,amil,tikcats,???ruoblem??om?p!.&bog?gro?lim?mo&c?n??ten?ude??irg?yks??r!.&bilten,moc?nac,ossa??a&c!htlaeh??pmoc?wtfos??bc?eh?if?ots!.&e&rawpohs,saberots,?y&flles,srab,???taeht?u&ces?sni?t&inruf?necca??za???s!.&a!disnim321,?b!ibnal?rofmok??c!a??d!b?n&arb?ubroflanummok???e?f?g!ro??h!f??i!trap??k!shf??l?m!oc,t??n!mygskurbrutan??o?p!ohsdaerpsym,p??r!owebdluocti,?s!serp?yspoi,?t?u?vhf?w?x!uvmok??y?z??a&c?el?hc??i&er?urc??nesemoh?roh?uoh??t&a&d?ts&e!laer??lla???is!.&bewwuoj,derauqspw,e&lej,nilnigol,rocevon,winmo,?laicosnepo,n&eyb,o&iton,yc,??s&ihtedam,pvtsaf,?thrs,wolfyeh,xevnoc,ysrab,?bew!.remarf,??ov?ra?t&ioled?ol??utitsni??u&lb?qi&nilc?tuob???v!.&21e?b&ew?ib?og??ce&r?t??erots?gro?lim?m&o&c?n??rif??o&c?fni??rar?stra?t&en?ni??ude?vog??as?e3gerb2h--nx?i&l!.&mea,xlh,??rd?ssergorp??ol??w&kct--nx?r??xul?y!.&gro?lim?moc?ten?ude?vog????f&0f3rkcg--nx?198xim--nx?280xim--nx?7vqn--nx?a!.&gro?moc?ten?ude?vog???b!.vog?wa9bgm--nx??c!a1p--nx!.&a14--nx,b8lea1j--nx,c&avc0aaa08--nx,ma09--nx,?f&a1a09--nx,ea1j--nx,?gva1c--nx,nha1h--nx,pda1j--nx,zila1h--nx,??ns??ea1j--nx?g?iam?l&a1d--nx?og??n!.&bew?cer?erots?m&oc?rif??ofni?re&hto?p??stra?ten???orp?p!.&gro?moc?ude???rus?t!.hcs,w??w!.&hcs,zib,???g&2&4wq55--nx?8zrf6--nx??3&44sd3--nx?91w6j--nx!.&a5wqmg--nx?d&22svcw--nx?5xq55--nx??gla0do--nx?m1qtxm--nx?vta0cu--nx????455ses--nx?5mzt5--nx?69vqhr--nx?7&8a4d5a4prebgm--nx?rb2c--nx??a!.&gro?mo&c?n??oc?ten??vd??b!.&0?1?2?3?4?5?6?7?8?9?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?x?y!srab,?z???c!b?za9a0cbgm--nx??e!.&ca?em!an??gro?ics?lim?moc?nue?ofni?t&en?rops??ude?v&og?t???a??g!.&gro?hsadtob,lenap:.nomead,,oc?saak,t&en?ikcats,???i&a?v??k!.&gro?lim?moc?su,ten?ude?vog???m!.&drp?gro?lim?mo&c?n??oc?ude?vog??pk??n!.&clp,dtl,eman?gro?hcs?i!bom??l&im?oc,?m&oc?rif,?neg,ogn,ten?ude?vog?zib:.&gl,ld,no,o&c,g,??,?aw?i!b!mulp??car?d&art?dew??h&sif?tolc??k&iv?oo&b?c???ls?n&aelc?iart??p!pohs??re&enigne?tac??t&ad?ekram?hgil?lusnoc?neg?ov?soh!.tfarcnepo,??vi&g?l???o!.lbo,s??u&rehcisrev?smas?tarebsnegömrev???o&d?lb?og!.&duolc,etalsnart,???r&2n084qlj--nx?ebmoolb?o!.&77ndc.c:sr,,a&remacytirucesym,t&neimip,sivretla,?z,?bew-llams,cimanyd-pi,d&ab-yrev-si,e&sufnocsim,vas-si,?nuof-si,oog-yrev-si,uolc&arfniarodef,ehtgnituor,mw,??e&a,cin-yrev-si,grof&loot,peh,?l&as-4-ffuts,poeparodef,?m&-morf,agevres,ohruoyslles,?nozdop,r&ehwongniogyldlob,iwym,uces-77ndc.nigiro.lss,?t&adidnac-a-si,is&-ybboh,golb,???fehc-a-si,golbymdaer,k&eeg-a&-si,si,?h,nut,?l&acol-si,i&amwt,ve-yrev-si,?lawerif&-ym,ym,?sd-ni,?m&acssecca,edom-elbac,?n&af&blm,cfu,egelloc,lfn,s&citlec-a-si,niurb-a-si,tap-a-si,?xos-a-si,?ibptth,o&itatsksid,rviop,?p&j,v-ni,??o&jodsnd,tp&az,oh,??p&i&-on,fles,?o&hbew,tksedeerf,?tf&e&moh,vres,?ym,??r&e&gatop,ppepteews,su-xunil-a-si,?vdmac,?s&a&ila&nyd,snd,?nymsd,?b&alfmw,bevres,?d&ikcet.3s,ylimaf,?eirfotatophcuoc,j,koob-daer,ltbup,nd&-won,deerf,emoh,golb,kcud,mood,nyd:.&emoh,og,?,ps,rvd,tog,uolc,?s&a-skcik,ndd,?tnemhcattaomb,u,?t&ce&jorparodef.&duolc,gts.so.ppa,so.ppa,?riderbew,?e&ews-yrev-si,nretni&ehtfodne,fodne,??hgink-a-si,s&ixetn&od,seod,?o&h-emag,l-si,?rifyam,??ue:.&ac,dc,e&b,d,e,i,m,s,?g&b,n,?hc,i&f,s,?k&d,m,s,u,?l&a,i,n,p,?n&c,i,?o&n,r,ssa,?pj,r&f,g,h,k,t,?s&e,i,u,?t&a,en,i,l,m,ni,p,?u&a,de,h,l,r,?vl,y&c,m,?z&c,n,??,vresnyd,x&inuemoh,unilemoh,?y&limafxut,raidanetah,srab,???ub&mah?oj???s!.&delacsne,gro?moc?ten?ude?vog??gb639j43us5--nx??t?u!.&c&a?s??en?gro?moc?o&c?g??ro???v!.ude?a1c--nx??wsa08--nx??h&0ee5a3ld2ckx--nx?4wc3o--nx!.&a&2xyc3o--nx?3j0hc3m--nx?ve4b3c0oc21--nx??id1kzuc3h--nx?l8bxi8ifc21--nx?rb0ef1c21--nx???8&8yvfe--nx?a7maabgm--nx??b!.&gro?moc?ten?ude?vog??mg??c!.&7erauqs,amil4,duolc-drayknil,e&lacsduolc.&amr.stcejbo,gpl.stcejbo,tsuc,?tisbew321,?gniksnd,p&h21,ohsdaerpsym,?snd&tog,uolc,?wolf.e&a.1pla,nigneppa,?xi2,ytic-amil,?aoc?et!.spparevelc,?ir!euz??r&aes?uhc??sob?taw!s???d0sbgp--nx?f&2lpbgm--nx?k??g!.&gro?lim?moc?ude?vog???m!a1j--nx??ocir?p!.&gro?i?lim?moc?ogn?snduolc,ten?ude?vog???s!.&adtob,g&nabhsah,ro??lim?m&oc?roftalp.&su,tne,ue,??ten?vog?won,?a&c?nom??i&d?f?ri???t!.&ca?enilno,im?ni?o&c?g??pohs,ro?ten??iaf?laeh!.arh,?orxer?rae??v", + "o!.lopdren,?zb??i&3tupk--nx?7a0oi--nx?a!.&ffo?gro?moc?remarf,ten?uwu,?1p--nx?bud?dnuyh?tnihc??b!.&gro?moc?oc?ro?ude??ahduba?o!m!.&duolcsd,ysrab,???s??c!.&ayb-tropora--nx?ca?de?gro?moc?o&c?g?ssa??ro?t&en?ni?roporéa??ude?vuog??cug?t??d&dk?ua??e&bhf--nx?piat??f!.&aw5-nenikkh--nx,dnala?i&ki,spak,?mroftalpduolc.if,nenikkäh,pohsdaerpsym,retnecatad.&omed,saap,?uvisitok321,yd,?onas??g!.&d&om?tl??gro?moc?ude?vog???h&c&atih?ra??s&abodoy?ibustim???juohs?k!.&gro?moc?ofni?ten?ude?vog?zib??b4gc--nx?iw!.remarf,?nisleh?s?uzus??l!drahcir?iamsi??maim?n!.&b&ew?og??ca?gro?lim?mo&c?n??ni?o&c?fni??pp?t&en?ni??ude?zib??airpic?i&hgrobmal?m??re??om?rarref?s!.&5f,egaptig,ppatig,?ed??t&i&c?nifni??rahb??ut?v!.&21k?gro?moc?oc?ten???wik?xa&rp?t??yf??j&6pqgza9iabgm--nx?8da1tabbgl--nx?b!.&acirfa?eto?gro?m&oc?siruot??o&c!e??fni?noce?rga?tser??russa?s&etcetihcra?risiol?tacova??t&en?naruatser??ude?vinu?yenom???d?f!.&ca?eman?gro?lim?moc?o&fni?rp??ten?vog?zib???nj?s?t!.&bew?c&a?in??eman?gro?lim?moc?o&c?g??t&en?ni?set??ude?vog?zib???yqx94qit--nx??k&8uxp3--nx?924tcf--nx?arfel?c&a&bdeef?lb??ebdnul?ilc?rem!e???d!.&e&disemmejh321,rots,?ger,mrif,oc,pohsdaerpsym,zib,?t??e&es?samet??h!.&a&4ya0cu--nx?5wqmg--nx??b3qa0do--nx?cni,d&2&2svcw--nx?3rvcl--nx??5xq55--nx?tl,?g&a0nt--nx?la0do--nx?ro??i&050qmg--nx?7a0oi--nx?xa0km--nx??m&1qtxm--nx?oc??npqic--nx?ten?ude?v&di?og?ta0cu--nx??xva0fz--nx?人&个?個?箇??司公?府政?絡&網?网??織&組?组??织&組?组??络&網?网??育&敎?教???n??i&tsob?vdnas??l!.&bew?c&a?os??dtl?gro?hcs?letoh?moc?nssa?ogn?prg?t&en?ni??ude?vog??at?cd?is??m!.&eman?fni?gro?moc?ten?ude?vog???n&ab!cfdh?etats?mmoc?t&en?fos??u??i!l!.&egarotstfn.sfpi,noyc,pepym,ztirfym,??p???oob?p!.&b&ew?og??ca?g&og?ro??kog?m&af?oc??p&kg?og??sog?ten?ude?vog?zib???row!ten!.&htumiza,nolt,o&c,vra,????s?t?u!.&c&a?lp??dtl?e&cilop?m?tismin,?gro!.&gul:g,,sgul,yr&ettoly&lkeew,tiniffa,?tneelffar,???lenap-tnednepedni,n&noc,oissimmoc-&layor,tnednepedni,??o&c!.&bunsorter.tsuc,enilnoysrab,krametyb.&hd,mv,?omida,p&i-on,ohsdaerpsym,?tfihsreyal.j,vres-hn,ysrab,??rpoc,?psoh,shn?t&en?nmyp,seuqni-tnednepedni,?vog!.&ecivres,ipa,ngiapmac,??weiver-tnednepedni,y&riuqni-&cilbup,tnednepedni,?srab,????l&04sr4w--nx?a!.&gro?lim?moc?ten?ude?vog??bolg?c?ed?g!el??i&c&nanif!.oc,lpl??os??romem?tnedurp??n&if?oitanretni??t&i&gid!.sppaduolc:.nodnol,,?p&ac?soh???ned?ot???c!.&bog?lim?oc?snduolc,vog???dil?e&datic?n&ahc?nahc!rehtaew???t!ria?tam??vart??f&8f&pbgo--nx?tbgm--nx??a?n??g!.&gro?moc?oc?ten?ude?zib,??h&d?op??i!.&21k?ca?fdi?gro?inum?oc!.&egapvar,redrotibat,tibatym,??ten?vog??a&f?m&e!.&kwat.p,otkwat.p,psirc.no,??g?toh???m?r??l&a&b&esab?t&eksab!.&sua,zn,??oof???c?mt??e&d?hs??ihmailliw?j??m!.&esserp?gro?moc?ten?ude?v&og?uog????n!.&etisbew321,no&med,rtsic,?oc,pohsdaerpsym,retsulc-gnitsoh,vog,yalphk,?o??o&a?btuf?l!.gmo,?o&c!.&ed,rotnemele,??hcs??rit?u??p!.&2uoy,a&cin&diws?gel??d&g,ortso?urawon??i&dem?mraw?nydg,?k&elo&guld?rtso??slopolam?tsu?ytsyrut??l&ip?o&kzs?w&-awolats?oksnok????mrifd,n&erapohs,img?zcel,?rog&-ai&bab?nelej??j?z??syn?tsaim?w&a&l&eib?i?o??zsraw??o&namil?tainop,??z&eiwolaib?mol???c&e&iw&alselob?o&nsos?rtso???le&im?zrogz???orw,p??d&em,ia?ragrats?uolc&inu,sds,??e&c&i&lrog?naibap,w&einreiks,ilg,o&hc&arats?orp??klop?tak????yzreibok??i&csjuoniws?ksromop?saldop??l&ahdop?opo??napokaz,t&atselaer?iselpmis,?z&romop?swozam???g&alble?ezrbo&lok?nrat??ro??hcyzrblaw?i&csomohcurein?grat?klawus??k&colp,e&rut?walcolw??in&byr?diws,sark,?le?o&nas?tsylaib??rob&el?lam??s&als?jazel?nadg,puls?rowezrp???l&colw?e&r?vart??i&am?m???m&o&c?dar?n?tyb??s&g?iruot??t!a???n&a&gaz?nzop,?i&bul?cezczs?lbul,molow?nok?zd&eb?obeiws???u&leiw?rot,?y&tzslo?z&rtek?seic????o&c,fni?k&celo?sleib,zdolk??lkan?n&leim?pek?t&uk?yzczs??z&copo?eing?rowaj???rga?t&nokd,ua??w&ejarg?ogarm???p&e&eb,lks!emoh,??klwwortso?ohs!-ecremmoce,daerpsym,??romophcaz?s&klofc,os??t&aiwop?en?opos,ra,sezc??ude?v&irp?og!.&a&io?p?s!w???bni&p?w??ci?dtiw?e&ko?ss&p?w???fiw?g&imu?u??hiiw?m&igu?rio?u!o???nds!ipz??o&ks?p!pu??s?wtsorats??p&a?sp!mk?pk?wk??u&m?p??wk?z??r&hcso?ksw?p?s??s&i?oiw?u?zu??talusnok?w&gzr?i&p?rg?w??m?o&o?pu??u!imzw???z&kw?ouw?????w&a&l&corw?sizdow??w??o&golg?k&ark,ul?zsurp??r&az?gew??t&rabul,sugua??z&coks?sezr????xes?y&buzsak?d&azczseib?ikseb??hcyt?n&jes?lod-zreimizak??pal?r&ogt?uzam??walup?zutrak??z&am-awar?c&aprak?iwol?zsogdyb??d&a&lezc?reis,?ol,?ib?reigz,s&i&lak?p??uklo????l??r&as?f?s??s!.&gro?moc?ten?ude?vog???t!.vog??ubnatsi?x3b689qq6--nx?yc5rb54--nx??m&00tsb3--nx?1qtxm--nx?981rvj--nx?a!.&enummoc?gro?moc?o&c?idar,?ten??c!bew??dretsma?e&rts?t!.&citsalej,esruocsid,???fma?xq--nx??b!.&gro?moc?ten?ude?vog??i??c!.&moc?oc?ten?vog???d!.&gro?moc?oc?ten?ude?vog???f!.&gro?moc?oidar,ten?ude??i??g!vu96d8syzf--nx??h?i!.&ca?gro?moc?oc!.&clp?dtl???t&en?t??vt??k?rbg4--nx??k!.&drp?e&rianiretev?sserp??gro?lim?m&o&c?n??t??nicedem?ossa?pooc?s&eriaton?neicamrahp?sa??ude?v&og?uog????l&if?ohkcots??o!.&dem?gro?m&oc?uesum??o&c?rp??ten?ude?vog??b?c!.&0x,1&21sesaila,dna1-sppa,?2aq,3pmevres,a&c&-morf,ir&bafno,fa,??g&-morf,oy-sehcaet,?i-morf,m&-morf,all&-a-si,amai,??p&-morf,c-a-si,?remacytirucesym,s,t&adtsudgniht,emta,?v-morf,w-morf,z,?b&ew&-sndnyd,arukas,draiw.segap,ottad,?ildts.ipa,?c&amytirucesemoh,d-morf,esyrcs,itsalej.omed,n&-morf,vym,?p&kroweht,ytirucesemoh,?rievres,s-morf,?d&aerotffuts,e&calpb,ifitrec-&si,ton-si,?rewopenignepw:.sj,,tsoh&-scodehtdaer,2a,ecapsppa,??i&-morf,parpc,rgevissam.saap,?m-morf,n&-morf,abeht-htiw-si,?orpnonduolcwm,s-morf,uolc&-noitatsyalp,ehtgnituor,hr,iafaw.d&ej,yr,?meaeboda,nevia,panqym:-&ahpla,ved,?,s&anym,metsystuo,?ved&j,pw,??vreser,wetomer,?e&butuoyhtiw,c&apsylop,iffo-sndnyd,?d:-morf,o&celgoog,n&il.srebmem,neve.&1-&su,ue,?2-&su,ue,?3-&su,ue,?4-&su,ue,????,erf&-sndnyd,sndd,?filflahevres,g&arots-77ndc,de-yltsaf,nahcxeevres,?i&hcet-a-si,p-sekil,?k&auqevres,irtsretnuocevres,?l&bitpa-no,googhtiw,?m&agevres,ina-otni-si,oh-&sndnyd,ta-sndnyd,??n&-morf,ilno&-evreser,ysrab,?og-si,?pacsledom,r&alfduolcyrt,ehwynanohtyp:.ue,,ihcec,?srun-a-si,t&i&nuarepo,s&-ybboh,aloy,bew-evil,elpmis,rodabew,tipohs,xiw,??omer-sndnyd,ysgolb,?v&als-elcibuc-a-si,itavresnoc-a-si,?z&amkcar,eelg,iig,??fehc-a-si,g&ni&gats-&raeghtua,swennwot,?ksndd,robsikrow,tsoh-bt.etis,?o&fgp,lb&-sndnyd,anetah,sihtsetirw,???h&n-morf,o-morf,?i&fiwehtno,h-morf,kiw-sndnyd,m-morf,p&aerocne,detsoh,?r-morf,w-morf,z&ihcppa,nilppa,??jn-morf,k&a&-morf,erfocsic,?cils-si,eeg&-a&-si,si,?sndd,?h,latsnaebcitsale:.&1-&ht&ron-ue,uos-&em,fa,pa,ue,??lartnec-&ac,li,ue,?ts&ae&-&as,pa,su,vog-su,?ht&ron-pa,uos-pa,??ew-&su,ue,vog-su,???2-ts&ae&-su,ht&ron-pa,uos-pa,??ew-&su,ue,??3-ts&aeht&ron-pa,uos-pa,?ew-ue,??,nil-kaerts,o-morf,r&adhtiwtliub,ow&-&sndnyd,ta-sndnyd,?ten-orehkcats,??sedal,u,?l&a&-morf,colottad,rebil-a-si,?f-morf,i&-morf,am&-sndnyd,detsohpw,??lecelffaw,ru-&elpmis,taen,?xegap,?m&n-morf,rofe&pyt.orp,rerac-htlaeh,?sacrasevres,uirarret-yltsaf,?n&a&cilbuper-a-si,f&-sllub-a-si,racsan-a-si,?i&cisum-a-si,ratrebil-a-si,?tarukas,?c,dc&hsums,umpw,?eerg-a-si,i&-morf,jod,?m-morf,o&ehtnaptog,isam-al-a-tse,rtap-el-tse,s&iam-al-a-tse,replausunu,??pj,t-morf,?o&bordym,c,hce-namtsop,idutsxiw,jodsnd,m&-morf,ed-baltlow,?niloxip,t&ingocnozama.&1-&ht&ron-ue.htua,uos-&em.htua,fa.htua,pa.htua,ue.htua,??lartnec-&ac.htua,em.htua,li.htua,ue.htua,?ts&ae&-&as.htua,pa.htua,su.&htua,spif-htua,??ht&ron-pa.htua,uos-pa.htua,??ew-&ac.htua,su.&htua,spif-htua,?ue.htua,vog-su.spif-htua,???2-&htuos-&pa.htua,ue.htua,?lartnec-ue.htua,ts&ae&-su.&htua,spif-htua,?ht&ron-pa.htua,uos-pa.htua,??ew-&su.&htua,spif-htua,?ue.htua,???3-ts&aeht&ron-pa.htua,uos-pa.htua,?ew-ue.htua,?4-tsaehtuos-pa.htua,?tadym,??p&2pevres,aelutym,i&-sndnyd,fles,ogol,ruoy&esol,hctid,?ym&eerf,teg,??ohsdaerpsym,pa&anis:piv,,esaberif,iparts:.aidem,,k1,nuspu,oifilauq,r&aegyks,oetem:.ue,,?tilmaerts,ukoreh,yfilpma,?t&fevres,thevres,??r&081,a&-morf,tskcor-a-si,?b:asnd,,e&d&iv&erp-yb-detsoh.saap,orpnwo,?ner&.ppa,no,??e&bevres,nigne-na-si,?ggolb-a-si,h&caet-a-si,pargotohp-a-si,?krow-drah-a-si,n&gised-a-si,ia&rtlanosrep-a-si,tretne-na-si,??p&acsdnal-a-si,eekkoob-a-si,?retac-a-si,subq,tn&ecysrab,iap-a-si,uh-a-si,?vres&-&ki.&cpj-rev-duolcj,duolcj,?s&ndnyd,pvtsaf,??bdym,inim,nmad,pc,sak,?y&alp-a-si,wal-a-si,?zilibomdeepsegap,?g,ituob,mgrp.nex,o&-morf,sivdalaicnanif-a-si,t&areleccalabolgswa,c&a-na-si,od-a-si,?susaym,??p-morf,u&as-o-nyd,e&tsoh.&duolc-gar,hc-duolc-gar,?ugolb-nom-tse,?omuhevres,??s&a&apod,ila&nyd,snd,?n&duolcym,ymsd,?vnacremarf,?bbevres,ci&p&-sndnyd,evres,?tcatytiruces,?dylimaf,e&cived-anelab,itilitu3,lahw-eht-sevas,mag-otni-si,t&i&iis,sro,?yskciuq,?ugaelyajyarg,?fpi-&eralfduolc,fc,?i&ht2tniop,murud,pa&elgoog,tneltneg,??jfac,k&-morf,aerf-ten,colb&egrof,pohsym,?nilkaerts,?m-morf,n&d&-pmet,d&-pi,yard,?golb,htiwssem,mood,tog,?kselp,nyd,ootrac-otni-si,?o&-xobeerf,xobeerf,?ppa&-avnac,raeghtua,swa,t&ikria,neg,??r&ac-otni-si,e&ntrap-paelut,tsohmaerd,??s&e&l-rof-slles,rtca-na-si,?ibodym,?tsaeb-cihtym.&a&llicno,zno,?ilay,lacarac,re&gitnef,motsuc,?sv,toleco,x:n&ihps,yl,?,?u,wanozama.&1-&3s,ht&ron-ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,tniopssecca-3s,?tniopssecca-3s,??uos-&em&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,tniopssecca-3s,?tniopssecca-3s,??fa.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??ue.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,???la&nretxe-3s,rtnec-&ac&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcats", + "laud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,??em.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?li.&3s,9duolc&-swa.stessa-weivbew,.sfv,?adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,????ts&ae&-&as&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,??pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,tniopssecca-3s,?tniopssecca-3s,??su:-etisbew-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,d&etacerped-3s,orp-&iupparme,oidutsrme,skoobetonrme,??etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,?,vog-su&-&3s,spif-3s,?.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,???ht&ron-pa&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??uos-pa&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,????ew-&ac.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,?su&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,??ue&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,d&etacerped-3s,orp-&iupparme,oidutsrme,skoobetonrme,??etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??vog-su&-&3s,etisbew-3s,spif-3s,?.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,?????2-&htuos-&pa.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?ue.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,??lartnec-ue.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?ts&ae&-su&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,d&etacerped-3s,orp-&iupparme,oidutsrme,skoobetonrme,??etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??ht&ron-pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??uos-pa&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,????ew-&su&-&3s,etisbew-3s,?.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,d&etacerped-3s,orp-&iupparme,oidutsrme,skoobetonrme,??etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,spif-&3s,tniopssecca-3s,?tniopssecca-3s,?spif-&3s,tniopssecca-3s,?tniopssecca-3s,yawetag-scitylana,??ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,tniopssecca-3s,?tniopssecca-3s,?????3&-ts&aeht&ron-pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,??uos-pa.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,??ew-ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,???s,?4-tsaehtuos-pa.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?5-tsaehtuos-pa.&3s,adbmal-tcejbo-3s,detacerped-3s,etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?labolg-3s.tniopssecca.parm,?yasdrocsid,?t&arcomed-a-si,c&-morf,etedatad.&ecnatsni,omed,??eel&-si,rebu-si,?hgilfhtiwletoh,i:batym,,m-morf,n&atnuocca-na-si,e&duts-a-si,r-ot-ecaps,tnocresu&buhtig,e&capsppa,donil.pi,lbavresbo.citats,vat,?kaerts,pl,???ops&edoc,golb,ppa,?s&i&hcrana-&a-si,na-si,?laicos-a-si,pareht-a-si,tra-na-si,xetn&od,seod,??oh&piym,sfn,??u&-morf,nyekcoh-asi,?v-morf,?u&-rof-slles,4,a-sppatikria,e,oynahtretramssi,r:ug-a-si,,?v&n-morf,rdlf,w-morf,?wo&lpwons-yrt,zok,?x&bsbf.sppa,em,inuemoh,t-morf,unilemoh,?y&a&bnx:.&2u,lacol-2u,?,l&erottad,pezam,?p-csbus,wetag-llawerif,?dnacsekil,fipohsym,k&-morf,niksisnd,?r&aidanetah,otceridevitcaym,?ugoo,w-morf,x&alagkeeg,orpmapson.duolc:.563o,,??z&esdrocsid,tilbcitats-&proc-w,ssellaitnederc-w,w,???inu??m!.dni,?or?tsla??p!.&eman,nwo,??raf!.jrots,etats??s?t!.&gro?lim?mo&c?n??oc?ten?ude?vog???u&esum?rof??z!.&ca?gro?hcs?lim?moc?o&c?fni??ten?ude?vog?zib????n&315rmi--nx?a&brud?cilbuper?f?grompj?hkaga?m?ol?ssin?u&hix?qna??varac?yalo??b!.&gro?moc?oc,ten?ude?vog??c??c!.&ah?bh?c&a?s??d&5xq55--nx?g?s?uolcpanqym,?eh?g&la0do--nx?ro??h&a?q?s!.sa,??i&7a0oi--nx?h??j&b?f?t?x?z??kh?l&h?im?j??m&n?oc!.&rekamegas.1-&htron-nc.&koobeton,oiduts,?tsewhtron-nc.&koobeton,oiduts,??swanozama.&1-&htron-nc.&3s,adbmal-tcejbo-3s,d&etacerped-3s,orp-&iupparme,oidutsrme,skoobetonrme,??etisbew-3s,ipa-etucexe,kcatslaud.&3s,etisbew-3s,tniopssecca-3s,?tniopssecca-3s,?tsewhtron-nc.&3s,adbmal-tcejbo-3s,dorp-&iupparme,oidutsrme,skoobetonrme,?etisbew-3s,ipa-etucexe,kcatslaud.&3s,tniopssecca-3s,?tniopssecca-3s,??be.1-&htron-nc,tsewhtron-nc,?????n&h?l?s?y??om?qc?s&g?j?ppa-avnac,?t&cennockciuq.tcerid,en??ude?vog?wt?x&g?j?n?s??z&g?x??司公?絡網?络网??b??d&g!.ypnc,?ka??e&drag?erg?fuak?hctik?i&libommi?w??m?po?r!ednaalv??sier?ves??g!.&ca?gro?moc?ten?ude?vog??is&ed!.&cihparg,ssb,??irev???h!.&bog?gro?lim?moc?ten?ude???i!.&ac?bew,c&a?in??dni?e&m?sabapus,?g&5?6?p?ro??i&a?hled??ku?l&evart?im??m&a?oc?rif??n&c?eg??o&c?fni?i?rp??p&ooc?u??r&ahib?d?e??s&c?er?nduolc,senisub?u??t&arajug?en!retni??ni?sop??ude?v&og?t??ysrab,zib??elknivlac?griv?ks?lreb?p?v?w?x??k!.&gro?ten?ude?vog???l&eok?ocnil??m!.&cyn,gro?ude?vog???o&dnol?i&hsaf?n&o?utiderc??siv!orue??t&a&cude!.oc,?dnuof?tsyalp??c&etorp?u&a?rtsnoc?????kin?las?mrom?nac?p&q?uoc??s&iam?pe?scire??t&ron?sob??zama??p!.&gro?oc?ten?ude?vog??k??r&e&c?yab??op??s!.&gro?moc?osrep?tra?ude?v&inu?uog????t!.&d&ni?uolcegnaro,?gro?ltni?m&oc!nim??siruot??nif?o&fni?srep??sne?t&an?en??vog??m??u&f?r!.&arail:.nari,,bdnevar,l&av.&bew,sserpxe,?per,?retropno,srevres,t&ikcats,nempoleved,?xiw,??stad?xamay?y??v!.&a&lnos?ohhnah&k?t???c&a?ouhphnib?uhphniv??di?e&man?rtneb?uhneihtauht??g&n&a&boac?ig&ah?cab?n&a?ei&k?t???uah??nad?rtcos?uqneyut??o&dmal?hpiah?lhniv?nkad?ud&hnib?iah????ro??h&ni&b&aoh?gnauq?hnin?iaht??d&hnib?man??mihcohohphnaht?n&cab?gnauq?yat??tah?vart??tlaeh??i&a!bney?coal?gngnauq?laig?ngnod??onah?rtgnauq??kalkad?m&an&ah?gnauq??oc?utnok??n&a&ehgn?gnol?kcab?uhthni&b?n???e&ibneid?y&gnuh?u&gniaht?hp????osgnal??o&fni?ht&nac?uhp??i?rp??pahtgnod?t&en?ni??u&a&hcial?mac?tgnuv-airab??de?eilcab??vog?zib???wo&rc?t!epac????o&76i4orfy--nx?a!.&bp?de?g&o?ro??oc?ti?ude?v&g?og???boat??b!.&a&ci&sum?tilop??i&c&arcomed?neic??golo&ce?ncet??m&edaca?onoce??rt&ap?sudni??vilob??n&egidni?icidem??serpme?tsiver?vitarepooc??b&ew?og??dulas?e&rbmon?tr&a?op&ed?snart????g&olb?ro??ikiw?l&a&noi&canirulp?seforp??rutan??im??moc?o&fni?lbeup?rga?tneimivom??saiciton?t&askt?en?ni??ude?vt??h?iew?olg??c!.&dr&c,rac,?esabapus,gro?ipym,l&im?per:.di,,?mo&c?n??segap&dael,l,?t&en?ilperdellawerif:.di,,?ude?vog??a?e?in?mara?s&edarb?ic???d!.&b&ew?og??dls?gro?lim?moc?t&en?ra??ude?vog??agoba?if?zd7acbgm--nx??e&c?d&iv?or???f!ni!.&dlawttim,e&g&delwonk-fo-l&errab,lerrab,?ellocevoli,?ht-skorg,rom-rof-ereh,tadpusn:d,,?llatiswonk,macrvd,ofni-v,p&i&-on,fles,?ohbew,?r&evres&3opyt,dlawttim,?uo-rof,?s&iht-skorg,nd&-cimanyd,nyd,uolc,??tsrifyam,ysrab,zmurof,???g&el?n!am?ib???hwsohw?i!.&8302,a&minifed,tad-b,?b&altig,uhtig,?czh,d&etpyrcs.tneilc,raobelgaeb,uolcropav,?e&civedniser,don&ppad.sndnyd,repyh,?egipa,lej,nilnigol,sufxob,t&i&beulb,snoehtnap,?newtu,ybeeb.saap,??g&n&alkrad,i&gatsniser.secived,tsoh&-br.etis,ytsoh,???ro??k&coregrof.di,orgn:.&as,ni,p&a,j,?su,u&a,e,??,ramytefasresworb,?lim?mo&c?n??n&aicisum,mtsp:.kcom,,yded,?o&c?idutsxiw,toq,?p&opilol,pa&-arusah,etybeeb.1dkes,??r&ddaym,e&tsneum-hf,vres&cisab,lautriv,??ial.sppa,?s&codehtdaer,gnihtbew,nemeis-om,pparevelc,t&acdnas,ekcit,??t&e&kcubtib,n!otorp,??i&belet,gude,?netnocresuseebduolc,raedon.egats,s&etwolfbew,udgniht.&cersid.&dvreser,tsuc,?dorp.tsuc,gnitset.&dv", + "reser,tsuc,?ved.&dvreser,tsuc,????ude?v&gib.0ku,og??w&hs,olfbew,?x&bdrym,cq,rotide,?ysrab,?b?d&ar?u&a?ts???j?r?syhp??j!.&dhp?g&ne?ro??hcs?i&a?rga??lim?m&f?oc??rep?ten?ude?v&og?t????ll&ag?o??m!.&gro?moc?ten?ude?vog??g?il?mi?orp??n!.&a&0&b-ekhgnark--nx?c-iehsrgev--nx?g-lksedlig--nx?k-negnanvk--nx??1&p-nedragy--nx?q-&asierrs--nx?grebsnt--nx?lado-rs--nx?n&egnidl--nx?orf-rs--nx??regnayh--nx?ssofenh--nx??r-datsgrt--nx?s-ladrjts--nx?v-y&senner--nx?vrejks--nx???3g-datsobegh--nx?4&5-&dnaleprj--nx?goksnerl--nx?tednalyh--nx??6-neladnjm--nx?s-&antouvachb--nx?impouvtalm--nx??y-&agrjnevvad--nx?ikhvlaraeb--nx???7k-antouvacchb--nx?8&k-rekie-erv--nx?l-ladrua-rs--nx?m-darehsdrk--nx??a!.sg??bct-eimeuvejsemn--nx?d&do?iisevvad?lov?narts?uas??f&1-&l--nx?s--nx??2-h--nx??g&10aq0-ineve--nx?av?ev?lot?r&ajn&evvad?u??ájn&evvad?u????h?iz-lf--nx?j&ddadab?sel??k&el?hoj&sarak?šárák??iiv&ag&na&el?g??ŋ&ael?ág???ran???l&f?lahrevo?o&ms?s??sennev?t-&ilm--nx?tom--nx??u&-edr--nx?s??øms??muar?n&0-tsr--nx?2-dob--nx?5-&asir--nx?tals--nx??a&r!-i-om?f?t??t??douvsatvid?kiv?m&os?øs??n&od?ød??ra?sen?t&aouvatheig?ouv&a&c&ch&ab?áb??h&ab?áb???n??i&ag?ág??sa&mo?ttvid??án???z-rey--nx?ær&f?t???o&p-&ladr--nx?sens--nx??q-nagv--nx?r-asns--nx?s-kjks--nx?v-murb--nx?w-&anr&f--nx?t--nx??ublk--nx???ppol?q&0-t&baol--nx?soum--nx?veib--nx??x-&ipphl--nx?r&embh--nx?imph--nx???y-tinks--nx??r&f-atsr--nx?g-&an&ms--nx?nd--nx??e&drf--nx?ngs--nx??murs--nx?netl--nx?olmb--nx?sorr--nx??h-&a&lms--nx?yrf--nx??emjt--nx??i&-&lboh--nx?rsir--nx?y&d&ar--nx?na--nx??ksa--nx?lem--nx?r&ul--nx?yd--nx????stu??j-&drav--nx?rolf--nx?sdav--nx??kua?l-&drojf--nx?lares--nx??m-tlohr--nx?n-esans--nx?olf?p-sdnil--nx?s-ladrl--nx?tih?v-rvsyt--nx??s&a&ns?ons??i&ar?er&dron?r&os?øs???ár??la&g?h??mor!t??sir?uf?åns??t&koulo&nka?ŋká??la?p-raddjb--nx?r-agrjnu--nx?s&aefr&ammah?ámmáh??orf?r&o?ø???u-vreiks--nx??u&h-dnusel--nx?i-&drojfk--nx?vleslm--nx??j-ekerom--nx?k-rekrem--nx?u-&dnalr--nx?goksr--nx?sensk--nx??v-nekyr--nx?w-&k&abrd--nx?ivjg--nx??oryso--nx??y-y&dnas--nx?mrak--nx?n&art--nx?nif--nx??reva--nx??z-smort--nx??v!.sg?ledatskork?reiks??wh-antouvn--nx?x&9-dlofts--nx.aoq-relv--nx?d-nmaherk--nx?f-dnalnks--nx?h-neltloh--nx?i-drgeppo--nx?j-gve&gnal--nx?lreb--nx??m-negnilr--nx?n-drojfvk--nx??y&7-ujdaehal--nx?8-antouvig--nx?b-&dlofrs--nx?goksmr--nx?kivryr--nx?retslj--nx??e-nejsom--nx?f-y&krajb--nx?re&dni--nx?tso--nx??stivk--nx??g-regark--nx?orf?ørf??z9-drojfstb--nx??b&25-akiivagael--nx?53ay7-olousech--nx?a&iy-gv--nx?le-tl&b--nx?s--nx??n0-ydr--nx??c&0-dnal-erdns--nx?z-netot-erts--nx??g&g-regnarav-rs--nx?o-nejssendnas--nx??ju-erdils-ertsy--nx?nj-dnalh-goksrua--nx?q&q-ladsmor-go-erm--nx.&ari-yreh--nx?ednas??s-neslahsladrjts--nx???ca&4s-atsaefrmmh--nx?8m-dnusynnrb--nx?il-tl--nx?le-slg--nx?n5-rdib--nx?op-drgl--nx?uw-ynnrb--nx??d&a&qx-tggrv--nx?reh!nnivk?sd&ork?ørk??uas??ts&e&bi?kkar?llyh?nnan??g&ort?ørt??k&alf?irderf??levev?mirg?obeg&ah?æh??r&ah?ejg????barm-jdddb--nx?ie!rah?s&etivk?ladman???lof&r&os?øs??ts&ev.ednas?o.relav?ø.relåv???n&a&l&-erd&n&os?øs??ron??adroh.so?dron.&a&g5-b--nx?ri-yreh--nx??ob?y&oreh?øreh??øb??e&m!lejh??pr&oj?øj??vi??gyb?n&aks?åks??o&h-goksrua?rf??r&o?ua?ø??tros?øh-goksrua??rts!e&devt?lab?mloh???s&ellil?naitsirk?rof???u&l!os??s!d&im?lejt??e&guah?l&a?å???kkoh?lavk?naitsirk?r&af?eg&e?ie???tef?y&onnorb?ønnørb?????r&a&blavs!.sg??g&eppo?la???o&j&f&a!dniv?k?vk??die?e&dnas?kkelf??llins?r&iel?ots??s&lab?t&ab?åb??yt??å!k??ævk??les??ts??åg&eppo?lå???ureksub.sen??e&ayb-yrettn--nx?d&ar?isemmejh321,lom?r&of?øf??år??g&gyr?nats??i&meuv&ejsem&aan?åån??sekaal??rjea??j&d&ef?oks??les??k&er&aom?åom??hgna&ark?årk??iregnir?kot!s??s&ig?uaf???l&bmab?kyb?l&av?ehtats??oh??m&it?ojt?øjt??n&arg?g&os?øs??meh?reil?te?ummok?yrb??r&dils-erts&ev?y&o?ø???ua?vod??sa&ans?åns??t&robraa?spaav??urg??f&62ats-ugsrop--nx?a&10-ujvrekkhr--nx?7k-tajjrv-attm--nx??o!.sg?h??s!.sg??v!.sg???g&5aly-yr&n--nx?v--nx??a&llor?ve&gnal?lreb???n&av!snellu??org??oks&die?m&or?ør??ner&ol?øl??r&o?ø???r&eb!adnar?edyps?s&die?elf?gnok?n&ot?øt????obspras??uahatsla?åve&gnal?lreb???h&0alu-ysm--nx?7&4ay8-akiivagg--nx?5ay7-atkoulok--nx??a!.sg???i&e&hsr&agev?ågev??rf??k&h&avlaraeb?ávlaraeb??s??lm&a?å??mpouvtal&am?ám??pph&al?ál??rrounaddleid?ssaneve?ššáneve??j&0aoq-ysgv--nx?94bawh-akhojrk--nx??k&a&b&ord?ørd??jks?lleis??iv!aklejps?l&am?evs?u??mag?nel?ojg?r&a&l?n??epok?iel?y&or?ør???s&ah?kel?om??øjg??kabene?ojsarak?ram&deh.&aoq-relv--nx?rel&av?åv??so??e&let.&ag5-b--nx?ob?øb??ra???åjks??l&a!d&anrus?d&numurb?ron??e&gnard?nte?s&meh?sin??ttin??g&is?nyl??kro?l&em?l&ejfttah?of??u&ag-ertdim?s???n&am?era?gos?i&b?nroh?r??kos?nus?oj??o-&dron?r&os?øs???ppo?r&a!l?nram??e&gne?l?v??is?o&jts?ts??u&a-&dron?r&os?øs???h??å?æl?øjts??s&e&jg?nivk?ryf??kav?mor-go-er&om.&ednas?yoreh??øm.&ednas?yøreh???uag??t&las?rajh?suan??v&l&a?e-rots??u-go-eron??yt??ksedlig?res&a?å???bib&eklof?seklyf??es!dah??h!.sg??i&m?syrt??l&ejf?ov&etsua?gnit?ksa?sdie???n!.sg??o!.sg?boh?g?h??r!.sg??å!ksedlig??øboh??m&a&rah?vk??f!.sg??h!.sg??i&e&h&dnort?rtsua?ssej??rkrejb??ksa??ol?t!.sg??u&dom?esum?r&ab?drejg?evle?os?uh?æb?øs??ttals???n&a&g&av?okssman?åv??jlis?or?r&g?rev???e&d&do&sen?ton??lah?r&agy&o?ø??ojfsam???g&iets?n&a&l&as?lab??n&avk?ævk??t&arg?ddosen??v&al?essov???i&d&ol?øl??l&ar?ær???yl??reb??iks?k&srot?y&or?ør???l&a&d&gnos?n&er?ojm?øjm??om??tloh??ug?åtloh??mmard?ojs&om?sendnas??ppolg?s&lahsladr&ojts?øjts??o??t&o&l?t-erts&ev?o?ø???roh?øl??vly&kkys?nav??yam-naj!.sg??øjs&om?sendnas???g&orf?ujb??i&dnaort?vnarg??kob?ladendua?maherk&a?å??n&it?urgsrop??orf-&dron?r&os?øs???r&aieb?evats??sfev?uaks?yrts??o&6axi-ygvtsev--nx?c,d&ob?rav??ievs?kssouf?l&m&ob?øb??ous&adna?ech&ac?áč???so!.sg???msdeks?niekotuak?r&egark?olf?y&oso?øso???s&dav?mort???p&ed?ohsdaerpsym,p&akdron?elk???r&a&d&dj&ab?áb??iab??jtif?luag?mah?vsyt??e&gn&a&k&iel?ro??merb?n&at?mas??rav-r&os?øs??srop?talf?v&ats?el??y&oh?øh???ivsgnok??il?jkniets?k&a&nvej?rem?s&gnir?nellu???ie-er&den?v&o?ø???ram?sa?årem??la&jf?vh??m&b&ah?áh??mahellil??nnul?ts&l&oj?øj??ul??y&o?ø???imp&ah?áh??m!.sg??osir?t!.sg??ádiáb?ævsyt?øsir??s&adnil?en&dnas?e&dga?k&ri&b?k??som??ve??me&h?jg??nroh-go-ejve?s&a?ednil?k&o?ø??of?yt?å??tsev??gv?hf?igaval?o&r&or?ør??sman??so&fen&oh?øh??m?v??uh&lem?sreka.sen??å!dnil???t&a&baol?g&aov?grav??jjr&av-attam?áv-attám??l&a&b?s??ás??soum?ts?v&eib?our???e&dnaly&oh?øh??f?s&nyt?rokomsdeks?sen??vtpiks??in&aks?áks??loh&ar?år??n!.sg??om&a?å??s!.sg?efremmah?or?ør??terdi?á&baol?ggráv?lá&b?s??soum?veib???u&b!.sg?alk?e&dna?gnir?nner??les?ælk??dra&b?eb??g&nasrop?vi?ŋásrop??j&daehal&a?á??jedub?v&arekkhar?árekkhár???ksiouf?n&diaegadvoug?taed???v&irp?lesl&am?åm???y&b&essen?nart?sebel?tsev??o&d&ar?na!s??or??gavtsev?k&rajb?sa??lem?mrak?n&art?n&if?orb???r&a&mah?n?v??e&dni?t&so?ton??va??ul?yd??s&am?enner?gav?lrak?tivk??vrejks??ø&d&ar?na!s??ør??gåvtsev?k&rajb?sa??lem?mrak?n&art?n&if?ørb???r&e&dni?t&so?tøn??va??ul?yd?æ&n?v???s&enner?gåv?tivk?åm??vrejks???á&slág?tlá?vreiks??å&gåv?h?jddådåb?lf??ø&d&ob?rav??r&egark?olf??s&dav?mort????aki?i&sac?tal??u??o&b?f?g?hay?o?ttat??r!.&cer?erots?gro?m&o&c?n??rif?t??o&c,fni??pohs,stra?tn?www?ysrab,?e&a!.&a&ac?cgd?idem??bulc!orea??ci&ffartria?taborea??e&c&alptekram?n&a&l&lievrus-ria?ubma??netniam?rusni??erefnoc???gnahcxe?mordorea?ni&gne?lria?zagam??rawtfos??gni&d&art?ilg!arap?gnah???l&dnahdnuorg?ledom??noollab?retac?sael?t&lusnoc?uhcarap??vidyks??hcraeser?ixat?l&anruoj?euf?icnuoc?ortnoc!-ciffart-ria???n&gised?oi&nu?t&a&cifitrec?ercer?gi&tsevni-tnedicca?van??i&cossa!-regnessap??valivic??redef??cudorp?neverp-tnedicca????ograc?p&ihsnoipmahc?uorg!gnikrow???r&e&dart?enigne?korb?niart?trahc??o&htua?tacude???s&citsigol?e&civres?r??krow?serp!xe??tnega??t&farcr&ia?otor??hgi&erf?l&f?orcim???liubemoh?n&atlusnoc?e&duts?m&n&iatretne?revog??piuqe????olip?ropria?si&lanruoj?tneics???w&erc?ohs??y&cnegreme?dobper?tefas????rref?z??p!.&a&aa?ca?pc??dem?gne?korgn,r&ab?uj??s&nduolc,rahc21,?t&acova?cca?hcer??wal?ysrab,???s!.&em?gro?moc?syevrus,ten?ude?vog???t!.&0x,116,ayo,gro?lim?moc?sulpnpv,t&cennockciuq.tcerid,en??ude?vog??o&hp?m?v?yk??tol?ua??v&iv?lov??xas?ykot??p&a&ehc?g?m?s??eej?g!.&gro?ibom?moc?ossa?ten?ude???i&r!.nalc,?v?z??j!.&0&g0,j0,o0o,t0,?a&3&5xq6f--nx?xqi0ostn--nx??5wtb6--nx?85uwuu--nx?9xtlk--nx?ad,b&ats,ihc!.&a&bihciakoy?don?ma&him?ye&ragan?tat???r&a&bom?gan?hihci??u&agedos?kas?ustak???s&os?ufomihs??t&amihcay?iran??w&a&g&im&anah?o??omak??kihci?zustum??ihsak??y&agamak?imonihci???e&akas?nagot??i&azni?esohc?h&asa?s&abanuf?ohc???ka&to?zok??musi?orihs?r&akihabihsokoy?o&dim?tak??ukujuk??usihs??nano&hc?yk??o&d&iakustoy?ustam??hsonhot?k&a&rihs?t??iba??nihsaran?sobimanim?tas&arihsimao?imot??uhc?yihcay??u&kujno?s&ayaru?t&imik?tuf???zarasik?????c&cah,ed,?g&as!.&a&gas?m&a&tamah?yik??ihsak??rat?t&a&gatik?hatik??ira!ihsin????e&kaira?nimimak??i&akneg?g&aruyk?o??h&c&amo?uo??siorihs??kaznak?modukuf?ra&gonihsoy?mi???nezih?u&k&at?ohuok??s&ot?tarak?????ihs!.&a&kok?m&a&hagan?yirom??ihsakat??rabiam?wagoton??e&miharot?nokih??houyr?i&azaihsin?esok?kustakat?moihsagih??na&mihcahimo?nok??o&hsia?mag?t&asoyot?ok?tir???us&ay?t&asuk?o??????k&aso!.&a&d&awihsik?eki??k&a&noyot?s&akaayahihc?oihsagih???oadat?uziak??m&ayas!akaso??odak??r&a&bustam?wihsak??ediijuf??t&akarih?i&k?us???wag&ayen?odoyihsagih???e&son?tawanojihs??honim?i&akas?h&cugirom?s&ayabadnot?i&a&kat?t??n??oyimusihsagih???k&a&rabi?sim??ustakat??muzi?r&ijat?otamuk???nan&ak?n&ah?es???o&ay?n&a&ganihcawak?simuzi?tak??eba?ikibah?oyot??t&anim?iad?omamihs??uhc??ust&oimuzi?tes????ou&kuf!.&a&d&amay?eos??g&no?ok?usak??hiku?k&awayim?uzii??ma&kan?y&asih?im???rawak?t&a&gon?ka&h?num?t???umo??wa&g&a&kan?nay?t??ias??ko!rih???y&ihsa?usak???e&m&ay?uruk??taruk?us??i&a&nohs?raihcat??goruk?h&cukuf?s&a&gih?hukuy??in???k&a&gako?muzim??iust?o?ustani??m&anim?otihsoynihs?u??r&ogo?ugasas??usu??ne&siek?zu&b?kihc???o&gukihc?h&ak?ot?ukihc??j&ono?ukihc??kayim?nihsukihc?to?uhc??u&fiazad?gnihs?stoyot????zihs!.&a&bmetog?d&amihs?eijuf?ihsoy?omihs??kouzihs?mihsim?ra&biah?honikam??tawi?wa&g&ekak?ukik??kijuf??yimonijuf??i&a&ra?sok??hcamirom?juf?kaz&eamo?ustam??ma&nnak?ta??nukonuzi?orukuf??nohenawak?o&nosus?ti??u&stamamah?z&a&mun?wak??i!ay?i&hs&agih?in??manim??mihs????????m&a&", + "tias!.&a&d&ihsoy?ot?usah??k&a&dih?sa??o&arihs?s???m&a&tias?y&as?o&rom?tah??ustamihsagih???i&hsagurust?jawak??uri??ni?wa&g&e&ko?man??ikot?o??k&ara?i&hsoy?mak???ru?zorokot??y&a&g&amuk?ihsok?otah??kuf??imo??ziin??e&bakusak?ogawak?sogo?ttas?zokoy??i&baraw?h&cugawak?s&oyim?ubustam???iroy?k&ato?ihs?u&k?stawi???m&akoyr?i&hsoy?juf??uziimak???naznar?o&dakas?ihsay?jnoh?n&a&go?nim??imijuf?nah?oy??r&ihsayim?otagan??t&asim!ak??igus?omatik??zak??u&bihcihc!ihsagih??sonuok?ynah????y&ak&aw!.&a&d&ira?notimak??kadih?ma&h&arihs?im??y&a&kaw?tik??oduk???ru&ustakihcan?y??sauy?wa&g&a&dira?zok??orih??konik??yok?zok??e&banat?dawi??i&garustak?jiat?mani??naniak?o&bog?nimik?t&asim?omihs&ah?uk????ugnihs???o!.&a&jos?koasak?m&ay&ako?ust??ihsayah??r&abi?ukawaihsin??wi&aka?nam???e&gakay?kaw??i&gan?h&cu&kasa?otes??sahakat??k&asim?ihsaruk??miin??n&anemuk?ezib??o&hsotas?jnihs?n&amat?imagak??ohs?uhcibik?????ot!.&a&damay?got?koakat?may&etat?ot??nahoj?riat?waki&inakan?reman???eb&ayo?oruk??i&h&asa?ciimak?sahanuf??kuzanu?m&an&i?ot??ih???nezuyn?otnan?u&hcuf?stimukuf?z&imi?ou???????ihs&o&gak!.&a&m&ayuok?ihsogak??si?yonak??e&banawak?n&at&akan?imanim??uka??tomoonihsin??i&adnesamustas?k&azarukam?oih??m&ama?uzi??usuy??nesi?o&knik?os?tomustam??uzimurat???rih!.&a&ka&n?s??m&ayukuf?i&hsorihihsagih?j&ate?imakikaso????r&a&bohs?h&ekat?im???es??tiak?wiad??e&kato?ruk??i&h&ci&akustah?mono?nihs??s&inares?oyim???manimasa?uk??negokikesnij?o&gnoh?namuk??uhcuf????uk&ot!.&a&bihci?mi&hsu&kot?stamok??m??wagakan??egihsustam?i&gum?h&coganas?soyim??kijaw?m&anim?uzia??ukihsihs??nan&a?iak??o&nati?turan????uf!.&a&batuf?m&a&to?y&enak?irok???ihs&im?ukuf??os?uko??r&aboihsatik?uganat??ta&katik?mawak?rih??w&a&g&akus?emas?uy??k&a&mat?rihs?sa??ihsi??nah??ohs???e&gnabuzia?iman?ta&d?tii???i&adnab?enet?hs&agih?iimagak??k&a&wi?zimuzi??ubay??minuk?r&ook?ustamay???nihsiat?o&g&etomo?ihsin?nan?omihs??no!duruf?rih??rihsawani?ta&may?simuzia???u&rahim?stamakawuzia?zia&ihsin?nay???????nug!.&a&bawak?doyihc?k&anna?oi&hsoy?juf?mot???m&ayakat?ustagaihsagih??n&ihsatak?nak??r&ahonagan?nak?o?u&kati?mamat???t&amun?inomihs?o??w&akubihs?iem?ohs???i&hsa&beam?yabetat??kas&akat?esi??m&akanim?uzio??ogamust?rodim??o&jonakan?n&eu?oyikust??tnihs??u&komnan?stasuk?yrik????rep,?n&ibmab,nog,ob,?ppacihc,ra&n!.&a&bihsak?d&akatotamay?u!o???guraki?m&ay&atik&imak?omihs??irokotamay??oki??ra&hihsak?n??wa&geson?knet???e&kayim?ozamay?sog?ustim??i&a&rukas?wak??garustak?h&ciomihs?sinawak??jo?ka&mnak?toruk??makawak?nos?r&net?otakat?ugeh???o&d&na?oyo??gnas?jnihs?nihsoy!ihsagih??tomarawat?yrok????rikik,?t&ag&amay!.&a&dihsio?k&atarihs?ourust??may&a&kan?rum??enak?onimak??rukho?ta&ga&may?nuf??hakat?kas??wa&g&ekas?orumam??ki&hsin?m??z&anabo?enoy?ot???zuy??e&agas?bonamay?dii?nihsagih?o??i&a&gan?nohs??h&asa?sinawak??nugo??o&dnet?jnihs?ynan??ukohak???iin!.&a&ga?k&ium?oagan??munou!imanim??t&a&bihs?giin??ioy??w&a&gioti?kikes?zuy??irak??yijo??e&kustim?mabust??i&aniat?hcamakot?kaz&awihsak?omuzi??m&a&gat?karum??o???n&anust?esog??o&das?ihcot?jnas?k&ihay?oym??mak?naga?ries??u&ories?steoj?????i&k&a!.&a&go?k&asok?oimak??t&ago!rihcah??ika!atik???w&aki?oyk???e&mojog?natim?suranihsagih?t&ado?okoy???i&hsoyirom?magatak?naokimak??nesiad?o&hakin?jnoh!iruy??nuzak?rihson?tasi&juf?m??yjnoh??u&kobmes?oppah????in,?o!.&a&dakatognub?m&asah?ihsemih??su?t&ekat?i&h?o????e&onokok?ustimak??i&jih?k&asinuk?ias?usu??mukust??onoognub?u&fuy?juk?ppeb?suk?????nayn,?wa&ga&k!.&a&mihsoan?rihotok?waga&kihsagih?ya???emaguram?i&j&nonak?ustnez??kunas?monihcu??o&hsonot?nnam?yotim??u&st&amakat?odat??zatu????nak!.&a&dustam?kus&okoy?tarih??maz?nibe?r&a&gihsaimanim?h&esi?imagas??wa&do?guy???u&im?kamak???tikamay?wa&k&ia?oyik?umas??sijuf??yimonin??e&nokah?saya??i&akan?esiak?gusta?hsuz?kasagihc?o?ukust??o&nadah?sio?tamay?????kihsi!.&a&danihcu?gak?kihs?mijaw?t&abust?ikawak??wazanak??i&gurust?hcionon?mon?ukah??nasukah?o&anan?ton!akan???u&kohak?stamok?z&imana?us?????niko!.&a&han?m&arat?ijemuk?uru??n&e&dak?zi??no??ra&hihsin?rih??wa&kihsi?niko??yehi?zonig??e&osaru?seay??i&hsagih?jomihs?k&a&gihsi?not??ihsakot??m&a&ginuk?kihsug?maz??igo?otekat??nuga!noy???n&a&moti?timoy?wonig??i&jikan?k???o&gan?jnan?tiad&atik?imanim???u&botom?kusug&akan!atik??imot??rab&anoy?eah??????yp,zomim,?bus,c&204ugv--nx?462a0t7--nx?678z7vq5d--nx?94ptr5--nx?a?mpopilol,?d&-2,17sql1--nx?3thr--nx?5&20xbz--nx?40sj5--nx??7&87tlk--nx?ptlk--nx??861ti4--nx?a?e!tfarcdnah,?n&eirf&lrig,yob,?om,?ooftac,?e&16thr--nx?5&1a4m2--nx?9ny7k--nx??damydaer,eweep,garotsarukas.&10ksi.3s,20ksi.3s,?i&bmoz,m!.&a&bot?k&asustam?uzus??m&a&him?y&emak?im???ihs??nawuk?wi&em?k???e&bani?ogawak?si!imanim???i&arataw?gusim?h&asa?ciakkoy??k&a&mat?sosik?t??iat??raban??o&dat?hik?n&amuk?ihseru?o&du?mok????ust????kilbew,lasrepus,mihe!.&a&m&a&h&ataway?iin??yustam??ij&awu?imak???taki!man???ebot?i&anoh?kasam?rabami??n&ania?egokamuk?oot??o&jias?kihcu?nustam?uhcukokihs?yi!es???u&kohik?zo????n!.&arukas,lapo,n&erukom,riheg,?omomus,stnim,teniesa.resu,xob-liam,yrovi,zapot,?amihs!.&a&d&amah?ho?usam??kustay?m&a?ihsoni&hsin?ko???wakih??e&namihs?ustam??i&g&aka?usay??konikak?mikih??nannu?o&mu&kay?zi!ihsagih?uko???nawust?tasim??u&stog?yamat????nep,?rotsnoihsaf,srev,t&awi!.&a&bahay?d&amay?on??koirom?t&a&honat?katnezukir??imus??w&as&ijuf?uzim??ihs???e&hon&i&hci?n??uk??tawi??i&a&duf?murak?wak??h&custo?si&amak?ukuzihs???j&oboj?uk??k&a&m&anah?uzuk??sagenak??esonihci??m&akatik?uzia&rih?wi????o&kayim?no&rih?t??tanufo??uhso???isarap,saman,tococ,?ulbybab,?g&3zsiu--nx?71qstn--nx?jw,l?olb&anetah,looc,??h&03pv23--nx?13ynr--nx?22tsiu--nx?61qqle--nx?o-hu,sulb,?i&54urkm--nx?azosbew,ced,g&ayim!.&a&dukak?m&a&goihs?kihs??ihsustam!ihsagih??unawi??r&awago?iho??ta&bihs?rum??w&a&gano?kuruf??iat??y&imot?ukaw???e&mot?nimes??i&hsiorihs?ka&monihsi?s&awak?o???mak?r&ataw?o&muram?tan????o&az?jagat?t&asim?omamay???u&fir?k&irnasimanim?uhsakihcihs?????ihcot!.&a&g&a&h?kihsa??ust??kom?m&ay&o?usarak??unak??r&a&boihsusan?watho??iho?ukas??t&akihsin?iay??wa&konimak?zenakat??y&imonustu?oihs???e&iiju?kustomihs?nufawi??i&akihci?g&etom?ihcot?on???o&k&ihsam?kin??nas?sioruk?tab??u&bim?san?????h&c&ia!.&a&dnah?m&a!h&akat?im??yuni??ihs&ibot?ust???r&a&hat?tihs??ik?u&ihsagih?kawi???t&ihc?o&k?yot???wa&koyot?zani??yi&monihci?rak???e&inak?k&aoyot?usa??manokot?noyot??i&a&gusak?kot?sia??eot?h&asairawo?cugo?s&ahoyot?oyim???k&a&mok?zako??ihssi??motay?rogamag??n&an&ikeh?ok??ihssin??o&got?ihsin?jna?rihsnihs?suf?tes??u&bo?raho?s&oyik?takihs??yrihc?zah????ok!.&a&dusay?kadih?mayotom?r&ah&im?usuy??umakan??sot!ihsin??wa&g&atik?odoyin??k&as?o????i&esieg?hco!k??jamu?k&a!sus??usto??ma&gak?k??rahan??o&mukus?n&i?ust!ihsagih???torum?yot!o???u&koknan?zimihsasot????ugamay!.&a&m&ayukot?ihso??toyot??e&bu?subat??i&gah?kesonomihs?nukawi?rakih??nanuhs?otagan?u&ba?foh?otim?stamaduk?uy?????s&anamay!.&a&dihsoyijuf?mayabat?r&ahoneu?ustakihsin??w&a&k&ayah?ijuf??suran??ohs???egusok?i&ak?h&cimakan?s&anamay?od???k&asarin?u&feuf?sto????o&k&akanamay?ihcugawakijuf??nihso?t&asimawakihci?ukoh??uhc??spla-imanim?u&b&nan?onim??fok?hsok?rust????ubon,??ix,ka&rabi!.&a&bukust?gok?kan!ihcatih??m&a&sak?timo?wi??ihsak?ustomihs??ni?r&a&hihcu?way??u&agimusak?ihcust???t&ag&amay?eman??oihcatih??w&ag&arukas?o??os??yi&moihcatih?rom???e&bomot?dirot?not?tadomihs??i&a&k&as?ot??rao??esukihc?gahakat?h&asa?catih??k&a&rabi?saguyr??ihsani?uy??ma?rukustamat??o&dnab?giad?him?kati?rihsijuf?soj?t&asorihs?im??yihcay??u&fius?kihsu?simak????sagan!.&a&m&abo?ihsust??natawak?r&abamihs?u&mo?ustam???wijihc?yahasi??i&akias?hies?k&asagan?i??masah??neznu?o&besas?darih?t&eso?og!imaknihs????ust&igot?onihcuk?uf????zayim!.&a&biihs?guyh?k&oebon?ustorom??mihsuk?r&emihsin?uatik??ta&katik?mim??wag&atik?odak??ya??e&banakat?sakog??i&hsayabok?kaza&kat?yim??m&animawak?ot&inuk?nihs????nanihcin?o&j&ik?onokayim??n&ibe?ust??tias??urahakat????ro&cep,moa!.&a&dawot?turust?wasim??e&hon&ihc&ah?ihs??nas?og?ukor??sario??i&anarih?ganayati?hsioruk?jehon?kasorih?makihsah?nawo?r&amodakan?omoa???o&gnihs?kkat??u&ragust?stum????ttot!.&a&r&ahawak?uotok??sa&kaw?sim???egok?irottot?nanihcin?o&ganoy?nih?tanimiakas??u&bnan?z&ay?ihc??????ukuf!.&a&deki?gurust?ma&bo?h&akat?im??yustak??sakaw??eabas?i&akas?ho?jiehie?ukuf??nezihce!imanim??ono????k&26rtl8--nx?4&3qtr5--nx?ytjd--nx??522tin--nx?797ti4--nx?ci&gid,ht,sevol,?ee,limybab,n&at,upatilol,??l&33ussp--nx?e&ccabew.&resu,sr,?llarap,?lik,oof,rigetuc,?m&11tqqq--nx?41s3c--nx?a0,ef,sioge,?n&30sql1--nx?65zqhe--nx?a&ebyllej,i&lognom,viv,??iam,n7p7qrt0--nx?o&o&las,mflah,?ruk,staw,??o&131rot--nx?7qrbk--nx?aic,c?d&iakkoh!.&a&deki?gakihset?hcebihs?k&adih?u&fib?narihs???m&ayiruk?hot?ihs&orihatik?ukuf??oras?usta??r&ib&a!ka??o?uruf??ozo?u&gakihsagih?oyot???sakim?ta&gikust?mun??w&a&ga&k&an?uf??nus!imak???k&aru?i&h&asa?sagih??kat?mak??omihs?um??zimawi??ine?oyk??yot??e&a&mustam?nan??b&a&kihs?yak??o&noroh?to???ian?k&ihsam?ufoto??nakami?ppoko!ihsin??sotihc?tad!okah??uonikat??i&a&bib?mokamot?n&a&k&kaw?oroh??wi??eomak?ihsatu?okik?usta&moruk?sakan????eib?h&c&ioy?u&bmek?irihs???s&ase?ekka?oknar?uesom???jufirihsir?k&amamihs?i&at?n???m&atik?otoyot??oa&kihs?rihs??r&a&hs?kihsi?mot??ihs&aba?ir??otarib???n&a&hctuk?rorum?se?tokahs??uber??o&kayot?m&ire?ukay??naruf!ima&k?nim???orih?r&ih&ibo?suk??o&bah?h&i&b?hsimak??sa??pnan?yan??umen??t&asoyik?eko?ukoh???u&bassa?kotnihs?m&assaw?uo??pp&akiin?en&ioto?nuk??ip??rato?s&akat?t&eb&e?i&a?hs!a??robon??m&e?o&m?takan???no&h?tamah??o&mik?s?t??u&kir?ppihc?st???onihsnihs?ufuras??uaru??yru!koh??zimihs!ok?????nu,?g!iti,oyh!.&a&bmat?dnas?gusak?k&at?o&oyot?y??uzarakat??m&ayasas?irah??wa&g&ani?okak??k&i&hci?mak??oy???yi&hsa?monihsin???i&asak?hs&aka?i&at?nawak???j&awa!imanim??emih??k&a&goa?s&agama?ukuf??wihsin??i&hsog?m???mati?oia?rogimak??n&annas?esnonihs??o&gasa!kat??ka?n&ikat?o?ustat??rihsay?sihs?tomus?yas??u&bay?gnihs?????hih,konip,l&b&etah,s,?ik,?mol,nagan!.&a&bukah?d&a&w?yim??e&ki?u??ii??k&a&s&ay?uki??zus??ihsoo?ousay??m&ay&akat?ii??i&hsukufosik?jii??ukihc??n&i!hsetat??uzii??r&ah?ugot??saim?t&agamay?oyim??w&a&g&a&kan?n??o??kustam?ziurak??onim!imanim??u&koo?s!omihs????ya&ko?rih???e&akas?nagamok?subo??i&gakat?h&asa?c&a!mo!nanihs???uonamay??sukagot??k&a&kas?mimanim?to??ia&atik?imanim??oa?uzihc", + "om??m&akawak?ijuf?o!t???r&ato?ijoihs?omakat???n&ana?esnoawazon??o&hukas?n&a&gan?kan??i&hc?muza??ustat??romok?si&gan?k??tomustam??u&k&as?ohukihc??stamega????o&b,m,pac,?to&mamuk!.&a&gamay?rahihsin?sukama!imak??tamanim??enufim?i&hcukik?k&ihsam?u??nugo!imanim??romakat??o&ara?rihsustay?sa?t&amay?om&amuk?us??u!koyg???yohc??u&sagan?zo????yk!.&a&bmatoyk?k&ies?oemak?uzaw??mayi&h&cukuf?sagih??muk??nihsamay?rawatiju?t&away?ik???e&ba&nat!oyk??ya??di?ni??i&ju?kazamayo?manim??natnan?o&gnatoyk?kum?mak?rihsamayimanim?y&gakan?ka&koagan?s??oj???u&ruziam?z&ayim?ik??????wtc1--nx?ykot!.&a&d&i&hcam?mus??oyihc??k&atim?ihsustak??m&a&t!uko??yarumihsa&gih?sum???i&hs&agoa?ika?o!t??uzuok??ren???r&a&honih?wasago??iadok?umah??ssuf?t&ik?o??wa&g&anihs?ode??k&ara?ihcat???y&agates?ubihs???e&amok?donih?m&o?urukihsagih??soyik??i&enagok?gani?h&ca&da?tinuk??sabati??j&nubukok?oihcah??manigus??o&huzim?jihcah?n&akan?ih!sasum??urika??rugem?t&a&mayihsagih?nim??iat?ok??uhc?yknub??u&fohc?hcuf?kujnihs?????p&a&ehc,rc,?o&hs&eht,iiawak,yub,?lf,p&evol,ydnac,?rd&kcab,niar,???r&2xro6--nx?atselttil,e&d&nu,wohc,?h,ilf,pp&ep,irts,u,?t&aerg,tib,??g!r,?ks,o!on,?ufekaf,?s&9nvfe--nx?dom,ndym,p&ihc,oo,?remagten,sikhcnerf,u&bloohcs,ruci,srev,?xvp4--nx??t&a&cyssup,obgip,?e&rces,vlev,?hginyad,netnocresu,sidas,u&b,ollihc,??u&4rvp8--nx?fig!.&a&d&eki?ih??kimot?m&ayakat?ihsah??ne?raha&gi&kes?makak??sak??taga&may?tik??wa&g&ibi?ustakan??karihs!ihsagih????e&katim?uawak??i&gohakas?hc&apna?uonaw??k&ago?es?ot??m&anuzim?ijat??nak?urat??nanig?o&dog?jug?makonim?nim?roy?sihcih??u&fig?s&otom?t&amasak?oay??????hc,pup,stoknot,ynup,?w&gp,onsetihw,?x&5ytlk--nx?irtam,?y&adynnus,dr,knarc,l&oh,rig,?moolg,ob,pp&ih,olf,?r&aidanetah,gn&a,uh,??u6d27srjd--nx?vaeh,?z&72thr--nx?e&ej,lur,??井福?京東?分大?取鳥?口山?城&宮?茨??媛愛?山&富?岡?歌和??岡&福?静??島&児鹿?広?徳?福??崎&宮?長??川&奈神?石?香??庫兵?形山?手岩?木栃?本熊?根島?梨山?森青?潟新?玉埼?田秋?知&愛?高??縄沖?良奈?葉千?賀&佐?滋??道海北?都京?重三?野長?阜岐?阪大?馬群???k!.&art?gro?moc?per?ude?vog???l&eh?l??m!.uj,ac!.fme.ta,?j??nd?o&g?h&pih?s!.&e&nilnoysrab,rawpohs,sab,?xilpoh,ysrab,???lnud?oc?t!.lldtn,??pa!.&a&rusah,ted,?b&ew,sc:.weiverp,,?e&erf-korgn,gatskrelc,lbatpada,niln&igol,okoob,?tupmocegde,?h&dw,sadtob,?ilressem,k&eelf-no,orgn,relc,?le&crev,napysae,?maerdepyt,n&aecolatigidno,evia,?opxe:.gnigats,,poon,r&cne,emarf,ubaez,?s&jasudem,serpirots,?t&ayn,i&belet,l&maerts,per:.di,,??luavreve.yaler,xenw,?wolfrettulf,yf&ilten,ten,???ra&a?hs??u&ekam?llag?org!.esruocsid,cts?kouk?nayalo???vsr?xece4ibgm--nx??q&a!3a9y--nx??g?i!.&gro?lim?moc?ten?ude?vog???m?se??r&a!.&a&cisum?sanes??bog?gro?l&autum?im??moc?pooc?rut?t&e&b?n??ni??ude?vog??4d5a4prebgm--nx?b?c?eydoog?los?t&at?s!uen???ugaj??b!.&21g?a&b&a&coros?iuc??itiruc??cnogoas?dicerapa?gniram?i&naiog?ramatnas??n&erom?irdnol??op?p&acam?irolf?ma&j?s???rief?tsivaob??b!aj?ib?mi?sb??c&ba?e&r?t??js?sp?t!e???d&em?mb?n&f?i??rt??e&dnarganipmac?ficer?ht?llivnioj?rdnaotnas??f&dj?ed?gg?n&e?i???g&e&l!.&a&b,m,p,?bp,c&a,s,?e&c,p,s,?fd,gm,ip,jr,la,ma,nr,o&g,r,t,?p&a,s,?r&p,r,?s&e,m,r,?tm,??s??l&s?z??n&c?e?o??ol!b?f?v??pp?ro??hvp?i&du?kiw?nana?oretin?r&c?eurab??sp?te?xat??l&at&an?rof??el?im?sq??m&a?da?e&gatnoc?leb??f?ic?oc!.etiselpmis,??nce?o&a&liel?riebir??c&e?narboir?saso??d&o?ranreboas??e&g?t??i&b?dar?ecam?r??rp?t&a?erpoir???p&er?m!e?t??ooc?pa?se??qra?r&af?ga?o&davlas?j??tn?ut??s&a&ixac?mlap?nipmac??ed?u&anam?j?m???t&am?e&b?d?n?v??nc?o&f?n??ra?sf??u&caug9?de?ja?rg??v&da?ed?og!.&a&b?m?p??bp?c&a?s??e&c?p?s??fd?gm?ip?jr?la?ma?nr?o&g?r?t??p&a?s??r&p?r??s&e?m?r??tm???rs?t??xiv?z&hb?ls?o&c?f?????c!.&as?ca?de?if?o&c?g??ro???e&bew?ccos?e&b?n&igne?oip??rac??gni&arg?rheob??h&sok?t&aew?orb???itnorf?k&col?o&p?rb???l&aed?ffeahcs??mal?nes?pinuj?t&a&eht?rebsnegömrev??law?nec?s&acnal?nom?ubkcolb??upmoc??v&o&csid?rdnal??resbo??wulksretlow?ywal?zifp??f!.&aterg?bew&-no,etis321,?drp?e&c&itsuj-reissiuh?narf-ne-setsitned-sneigrurihc,?lipuog,rianiretev,?hny,i&cc?rgabmahc,?m&o&c?n??t??n&eicamrahp,icedem,?ossa?pohsdaerpsym,s&e&lbatpmoc-strepxe,riaton,tsitned-sneigrurihc,uova??o&-x&bf,obeerf,?x&bf,obeerf,???t&acova,oor-ne,rop:orea,,?vuog?xobided,?avc7ylqbgm--nx?s??g!.&etiselpmis,gro?moc?ten?ude?vog?ysrab,??h!.&eman?mo&c?rf??yldnerb.pohs,zi??ur??i!.&a&61f4a3abgm--nx?rf4a3abgm--nx??ca?di?egdenavra,gro?hcs?oc?ten?vog?نار&يا?یا???a&h?per??ew?lf??k!.&c&a?s??e&n?p?r??gk?iggnoeyg?kub&gn&oeyg?uhc??noej??l&im?uoes??man&gn&oeyg?uhc??noej??n&as&lu?ub??o&e&hcni?jead??wgnag???o&c?g??ro?s&e?h?m??u&gead?j&ej?gnawg????cilf??l!.&gro?moc?ten?ude?vog???m!.vog??n!.&gro?moc?ofni?ten?ude?vog?zib???o&htua?t&c&a?od??laer???p!.&alsi?ca?eman?forp?gro?moc?o&fni?rp??t&en?se??ude?vog?zib???s?t!.&21k?bew?cn!.vog??eman?gro?kst?l&e&b?t??im?op??moc?neg?ofni?pek?rd?sbb?ten?ude?v&a?og?t??zib??f?m??vd??s&8sqif--nx?9zqif--nx?a!.vog?birappnb?gev?lliv?mtsirhc?s??b!.&ew,gro?moc?ten?ude?vog??oj?s?u??c&i&hparg?p?t&sigolyrrek?ylana???od??d&a?d?ik?l?n&iwriaf?omaid??oogemoh?rac??e!.&b&ewim321,og??gro?mo&c?n??pohsdaerpsym,ude??civres!.enilnigol,?d&d2bgm--nx?oc??h&ctaw?guh??i&lppus?rtsudni?treporp!yrrek???jaiv?l&aw?cycrotom?gnis?pats??m&ag!.y&elp,zeehs,??oh?reh??nut?ohs?picer?r&it?ut&cip!.7331,?nev???s&i&rpretne?urc??ruoc??taicossa?vig??g!nidloh??h5c822qif--nx?i!a09--nx?nnet?rap?targ??k&c&or!.&ecapsbew,snddym,tikcats,ytic-amil,??us??hxda08--nx?row??l!.&c&a?s??gro?o&c?fni??ten?ude?vog?zib??a&ed?tner??e&ssurb?toh!yrrek???lahsram?m?oot!.rdda&.nyd,ym,???m!.&etisinim,gro?moc?ten?ude?vog??b?etsys!.tniopthgink,?ialc??n&a&f?gorf?ol??i&a&grab?mod??giro??o&it&acav?cudorp?ulos??puoc???o&dnoc?geuj?ppaz?t&ohp!.remarf,?ua???p!.&ces?gro?moc?olp?ten?ude?vog??i&hsralohcs?lihp?t??u??r!.&ca?gro?ni?oc?ude?vog?xo,y&ldnerb.pohs,srab,??a&c?p?tiug??c?e&dliub!.etisduolc,?erac?gor?levart?mraf?n&niw?trap??wolf??ot&cartnoc?omatat??pj?uot??s!.&em?gro?hcs?moc?oc?ten?ude?vog?zib??alg?e&n&isub!.oc,?tif??rp!xe!nacirema???xnal??iws??t&a&eb?ob??ek&cit?ram??fig?h&cay?gilf??n&atnuocca?e&mt&rapa?sevni??ve!.&nibook,oc,????rap??u!.&a&c!.&21k?bil?cc???g!.&21k?bil?cc???i!.&21k?bil?cc???l!.&21k?bil?cc???m!.&21k!.&hcorap?rthc?tvp???bil?cc???p!.&21k?bil?cc???si?v!.&21k?bil?cc???w!.&21k?bil?cc????c&d!.&21k?bil?cc???n!.&21k?bil?cc???s!.&21k?bil?cc????d&elacsne.xhp,i!.&21k?bil?cc???m!.&21k?bil?cc???n!.&bil?cc???s!.&bil?cc???uolcrim,?e&d!.&bil,cc???las-4-&dnal,ffuts,?m!.&21k?bil?cc??anrevres,?n!.&21k?bil?cc????h&n!.&21k?bil?cc???o!.&21k?bil?cc????i&h!.&bil?cc???m!.&21k?bil?c&c?et??goc?n&eg?otae??robra-nna?sum?tsd?wanethsaw???nd?r!.&bil?cc???v!.&21k?bil?cc???w!.&21k?bil?cc????jn!.&21k?bil?cc???k&a!.&21k?bil?cc???o!.&21k?bil?cc????l&a!.&21k?bil?cc???f!.&21k?bil?cc???i!.&21k?bil?cc????mn!.&21k?bil?cc???n&afflog,i!.&21k?bil?cc???m!.&21k?bil?cc???sn?t!.&21k?bil?cc????o&c!.&21k?bil?cc???gn,m!.&21k?bil?cc???ttniop,?p&ion,rettalp,?r&a!.&21k?bil?cc???o!.&21k?bil?cc???p!.&21k?bil?cc????s&a!.&21k?bil?cc???k!.&21k?bil?cc???m!.&21k?bil?cc???nd&deerf,uolc,??t&c!.&21k?bil?cc???m!.&21k?bil?cc???sohoileh,u!.&21k?bil?cc???v!.&21k?bil?cc????ug!.&21k?bil?cc???v&n!.&21k?bil?cc???rs:.&hg,lg,?,w!.cc???xt!.&21k?bil?cc???y&b-si,k!.&21k?bil?cc???n!.&21k?bil?cc???w!.&21k?bil?cc????za!.&21k?bil?cc????ah!uab??bria?col?e!.ytrap.resu,?ineserf?lp?xe&l?n???vt?w!.&66duolc,gro?moc?s&ndnyd,tepym,?ten?ude?vog??a!.&no.&1-&htron-ue.ppabew-refsnart,lartnec-ue.ppabew-refsnart,ts&ae&-su.ppabew-refsnart,ht&ron-pa.ppabew-refsnart,uos-pa.ppabew-refsnart,??ew-ue.ppabew-refsnart,??2-ts&ae&-su.ppabew-refsnart,htuos-pa.ppabew-refsnart,?ew-su.ppabew-refsnart,??rekamegas.&1-&ht&ron-ue.&koobeton,oiduts,?uos-&em.&koobeton,oiduts,?fa.&koobeton,oiduts,?pa.&gnilebal,koobeton,oiduts,?ue.&koobeton,oiduts,???lartnec-&ac.&gnilebal,koobeton,oiduts,spif-koobeton,?em.&koobeton,oiduts,?li.&koobeton,oiduts,?ue.&gnilebal,koobeton,oiduts,??ts&ae&-&as.&koobeton,oiduts,?pa.&koobeton,oiduts,?su.&gnilebal,koobeton,oiduts,spif-koobeton,?vog-su.&koobeton,oiduts,spif-&koobeton,oiduts,???ht&ron-pa.&gnilebal,koobeton,oiduts,?uos-pa.&gnilebal,koobeton,oiduts,???ew-&ac.&koobeton,spif-koobeton,?su.&koobeton,oiduts,spif-koobeton,?ue.&gnilebal,koobeton,oiduts,?vog-su.&koobeton,oiduts,spif-&koobeton,oiduts,?????2-&htuos-&pa.koobeton,ue.&koobeton,oiduts,??lartnec-ue.&koobeton,oiduts,?ts&ae&-su.&gnilebal,koobeton,oiduts,spif-koobeton,?ht&ron-pa.&gnilebal,koobeton,oiduts,?uos-pa.&gnilebal,koobeton,oiduts,???ew-&su.&gnilebal,koobeton,oiduts,spif-koobeton,?ue.&gnilebal,koobeton,oiduts,????3-ts&aeht&ron-pa.&koobeton,oiduts,?uos-pa.&koobeton,oiduts,??ew-ue.&koobeton,oiduts,??4-tsaehtuos-pa.koobeton,???e&iver?n!.elbaeciton,??odniw??y&alcrab?ot???t&0srzc--nx?a!.&amil4,ca!.hts??etiesbew321,gni&liamerutuf,tsoherutuf,?o&c?fni,?p&h21,ohsdaerpsym,?r&euefknuf.neiw,o??v&g?irp,?xi2,ytic-amil,zib,?c?e!s??hc?l?mami?rcomed??b!.&gro?moc?ten?ude?vog??b?gl??c&atnoc?e&les?rid!.p2pbil,txen????dimhcs?e!.&eman?gro?moc?ofni?ten?ude?vog?zib??b?em?grat?id?k&circ?ram??n!.&1dna1-sppa,5inu,6vnyd,7&7ndc.r,erauqs,?a&l&-morf,moob,?minifed,remacytirucesym,tadsyawla,z,?b&boi,ewdarym,g,lyltsaf:.pam,,?c&i&manyd-snd,nagro-gnitae,tats-oieboda,?paidemym,?d&e&calpb,ziamaka,?feruza,hiamaka,irgevissam.saap.&1-&gs,nol,rf,yn,?2-&nol,yn,??nab-eht-ni,uolc&-snd,ehtgnituor,ftc,meaeboda,nievas.c&di-etsedron,itsalej,?xednay:.e&garots,tisbew,?,??e&c&narusnihtlaehezitavirp,rofelacs.j,?gde&eruza,iamaka,?ht-no-eciffo,l&acsnoom,ibom-eruza,?m&antenym.ns,ecnuob,itnuroieboda,ohtanyd,tcerider,?n&ilno-evreser,ozdop,?r&alfduolc:.ndc,,ehurht,?s:abapus,,ti&s-repparcs,usegde,?zam&aym,kcar,??f&aeletis,crs.&cos,resu,?ehc-a-si,fgg,?g&ni&gats-&d&eziamaka,hiamaka,?e&gdeiamaka,tiusegde,?iamaka,nigiroiamaka,yekegde,?reesnes,sirkcilc,?olbevres,?hsadtob,i&amaka,pa-eruza,?k&catsvano,eeg-a&-si,si,?u,?l&a&bolgeralfduolc.ndc,colottad,?iamwt,meteh,s&d-ni,s-77ndc,??m&ac&asac,ih,?urofniem,?n&a&f&agp,lhn,?i&bed,llerk,??ceralfduolc.ndc,dcduabkcalb,i:giroiamaka,,o-&drowyek,evil,revres,?pv-ni,?o&c-morf,duppa,jodsnd,lefam,rp-ytinummoc,ttadym,?p&i&-&etsef,on,sndd,?emoh,fles,nwo,?j,mac-dnab-ta,o&-oidar-mah,h&bew,sdaerpsym,??pa&duolc,egde,?tfe&moh,vres,?usnd,?r&e&ganamciffart,tsulcyduolc,vres-xnk,?vdslennahc:.u,,?s&a&ila&nyd,snd,?nymsd,?bbevres,dylimaf,e&gde-ndc,rauqs,suohsyub,t&isbeweruza,ys,??ke", + "kokohcs,n&d&-won,aka,d,golb,npv,?oitcnufduolc,?ppacitatseruza:.&1,2:suts&ae,ew,?,3,4,5,6,7,aisatsae,eporuetsew,sulartnec,?,s&a-skcik,ecca&-citats,duolc,??t,wodniw.&eroc.bolb,subecivres,??t&adies,ce&ffeym,jorprot:.segap,,lespohs,?e&nretnifodne,smem,?farcenimevres,i-&ekorb,s&eod,lles,teg,??n&e&ssidym,tnocresuv,?orfduolc,?rec&lacol,tsohlacol,?s&acynaeralfduolc.ndc,ixetnod,oh&-spv:.citsalej.&cir,lta,sjn,?,gnik,???u&h,nyd,r,?ved-&anafarg,naissalta.dorp.ndc,?x&inuemoh,spym,tsale.&1ots-slj,2ots-slj,3ots-slj,?unilemoh,?y&a&p-csbus,wetag-llawerif,?ekegde,ffijduolc:.&ed-1arf,su-1tsew,?,ltsaf.&dorp.&a,labolg,?lss.&a,b,labolg,?pam,slteerf,?n&-morf,ofipi,?srab,?z&a-morf,tirfym,???p?tcip?v??f&ig?osorcim??g!.&bog?dni?gro?lim?moc?ten?ude???h!.&dem?gro?l&er?op??m&oc?rif??o&fni?rp?s&rep?sa???po&hs?oc??t&en?luda?r:a?,?ude?vuog???i!.&a&2n-loritds--nx?7e-etsoaellav--nx?8&c-aneseclrof--nx?i-lrofanesec--nx??at?b?c!cul??dv?i&blo&-oipmet?oipmet??cserb?drabmol?g&gof?urep??l&gup?i&cis?me&-oigger?oigger???uig&-&aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf???aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf????n&a&brev?cul?pmac?tac??idras?obrac&-saiselgi?saiselgi??resi??otsip?r&b&alac!-oigger?oigger??mu??dna&-&attelrab-inart?inart-attelrab??attelrabinart?inartattelrab?ssela??epmi?ugil??tnelav&-obiv?obiv??vap?z&e&nev?ps&-al?al???irog???l&iuqa!l??leib??m&or?rap??n!acsot?e&dom?is?sec&-&ilrof?ìlrof??ilrof?ìlrof???g&amor&-ailime?ailime??edras?olob??i&ssem?tal??ne!var??o&cna?merc?rev?vas???oneg?p?r!a&csep?rr&ac&-assam?assam??ef??von??etam?tsailgo!-lled?lled???s!ip?sam&-ararrac?ararrac??u&caris?gar???t!a&cilisab?recam??resac?soa!-&d&-&ellav?lav??ellav?lav??ellav??d&-&ellav?lav??ellav?lav??ellav??te&lrab&-&airdna-inart?inart-airdna??airdnainart?inartairdna??ssinatlac???udap?v!o&dap?neg?tnam???zn&airb&-a&lled-e-aznom?znom??a&lledeaznom?znom??eaznom??e&c&aip?iv??soc?top??om???b&-&23,46,61,?3c-lorit-ds-onitnert--nx?be-etsoa&-ellav--nx?dellav--nx??c!f-anesec-lrof--nx?m-lrof-anesec--nx??he-etsoa-d-ellav--nx?m!u??o2-loritds-nezob--nx?sn-loritds&-nasl&ab--nx?ub--nx??nitnert--nx??v!6-lorit-dsnitnert--nx?7-loritds&-nitnert--nx?onitnert--nx???z&r-lorit-ds&-nitnert--nx?onitnert--nx??s-loritds-onitnert--nx???c&f?is?l?m?p?r?v??d&p?u!olcnys,??e&c!cel?inev?nerolf??f?g!apemoh321,ida&-&a&-onitnert?onitnert??otla!-onitnert?onitnert???a&-onitnert?onitnert??otla!-on&azlob?itnert??onitnert????hcram?l?m!or??n&idu?o&n&edrop?isorf??torc???p?r?s&erav?ilom??t!nomeip?s&eirt?oa!-&d-e&ellav?éllav??e&ellav?éllav???de&ellav?éllav??e&ellav?éllav?????v?znerif??g&a?b?f?il?o?p?r?up?vf??hc?i&b?c?dol?f?l!lecrev?opan?rof&-anesec?anesec???m?n&a&part?rt&-attelrab-airdna?attelrabairdna???imir?ret??p?r!a&b?ilgac?ssas???s!idnirb??t&ei&hc?r??sa??v??l&a!c??b?c?o&m?rit&-&d&eus&-&nitnert?onitnert??nitnert?onitnert??us&-&nitnert?onitnert??nitnert?onitnert??üs&-&nitnert?onitnert??nitnert?onitnert???s&-onitnert?onitnert???d&eus!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??us&-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??üs!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert???s&-onitnert?onitnert?????m&ac?f?i?ol?r??n&a!lim?sl&ab?ub???b?c?e!en.cj,v?zob??irut?m!p??p?r?t??o&a!v??b!retiv??c!cel??enuc?g!ivor??i&dem&-onadipmac?onadipmac??pmet&-aiblo?aiblo??rdnos?zal??l?m!a&greb?ret??oc?re&f?lap???n!a&dipmac&-oidem?oidem??lim?tsiro?zlob??ecip&-ilocsa?ilocsa??i&bru&-orasep?orasep??lleva?rot?tnert??r&elas?ovil??ulleb??p?r!a&sep&-onibru?onibru??znatac??oun??s!ivert?sabopmac??t!arp?e&nev?ssorg??n&arat?e&girga?rt?veneb????zz&era?urba???p&a?ohsdaerpsym,s?t??qa?r&a!m?s??b!a??c?f?g?k?me?o?p?s?t?v??s&a&b?iselgi&-ainobrac?ainobrac???b?c?elpan?i?m?o&t?x&bi,obdaili,??rahc21,s?t?v??t&a?b?c?l?m?nomdeip?o?p?v??u&de?l?n?p??v&a?og?p?s?t?v??y&drabmol?ellav&-atsoa?atsoa??licis?nacsut??z&al?b?c?p??ìlrof&-anesec?anesec???derc?er?f?m?utni??je3a3abgm--nx?kh?l!.vog?uda??m!.&gro?moc?ten?ude???n&a&morockivdnas?ruatser?tnuocca??e&g?m&eganam!.retuor,?piuqe??r??i!.ue?m?opdlog??opud?uocsid??o&b?cs!.vog:.ecivres,,?d?g?h?j?oferab?p&edemoh?s???p!.&bewanigap321,emon?gro?lbup?moc?t&en?ni??ude?vog???r&a!m&law?s???epxe?op&er?pus!.ysrab,?s???s!.&a&daxiabme?rarik,?e&motoas?picnirp?rots??gro?lim?moc?o&c?dalusnoc?ho&ileh,n,??ten?ude??af?e&b?r?uq??i!rolf?tned??o&h!.&2pw,duolcrim,e&lej,tiseerf,?flah,l&enapysae,rupmet,?s&pvtsaf,seccaduolc,?tsafym,vedumpw,??p!sua???urt??t!.&eman?gro?lim?moc?o&c?fni?rp??ten?ude?vog?zib??ayh?n?o!bba?irram???uognah?xen?y!.gro,?ztej??u&2&5te9--nx?yssp--nx??a!.&a&s?w??civ?d&i?lq??fnoc?gro?moc!.&pohsdaerpsym,stelduolc.lem,??nsa?sat?t&ca?en?n??ude!.&a&s?w??ci&lohtac?v??dlq?sat?t&ca?n??wsn!.sloohcs????vog!.&a&s?w??civ?dlq?sat???wsn?zo??ti??c!.&bog?fni?gro?moc?t&an?en??ude??i??d&e!.tir.segap-tig,?iab??e!.&noitatsksid,odagod.citsalej,s&nd&ps,uolc,?ppatikria,?ysrab,??g!.&bew?gro?m&aug?oc??ofni?ten?ude?vog???h!.&0002?a&citore?idem?kitore??edszot?gro?ilus?letoh?m&alker?lif?t?urof??naltagni?o&c?ediv?fni?levynok?nisac??pohs?rarga?s&a&kal?zatu??emag?wen??t&lob?rops??virp?xe&s?zs??ytic?zsagoj??os?sut??l!.etisbew321,?m!.&ca?gro?moc?oc?ro?ten?vog???n!.&duolcesirpretne,eni&esrem,m,?tenkcahs,?em!.&enilnoysrab,ysrab,???o&ggnaw?y!c???r!.&3kl,a&i&kymlak,rikhsab,vodrom,?yegyda,?bps,ca,duolcrim,e&niram,rpcm,?g&bc,ro,?ianatsuk,k&ihclan,s&m,rogitayp,??li&amdlc.bh,m,?moc,natsegad,onijym,pp,ri&b,d&cm:.spv,,orue,?midalv,?s&ar,itym,?t&en,ni,?u&4an,de,?vo&g,n,?ynzorg,zakvakidalv,?myc?p?ug??s!.&a&d&golov,nagarak,?gulak,i&groeg,kymlak,lerak,nemra,rikhsab,ssakahk,vodrom,zahkba,?lut,rahkub,vut,yegyda,znep,?bps,da&baghsa,rgonilest,?gunel,i&anatsuk,hcos,ovan,ttailgot,?k&alhsygnam,ihclan,s&legnahkra,m,n&a&mrum,yrb,?i&buytka,nbo,??tiort,vorkop,??l&ocarak,ybmaj,?na&gruk,jiabreza,ts&egad,hkazak-&htron,tsae,???ovonavi,r&adonsark,imidalv,?t&enxe,nek&hsat,mihc,??vo&hsalab,n,?ynzorg,z&akvakidalv,emret,??t&amok?i&juf?masih????v!.&gro?moc?ten?ude???ykuyr??v&b?c!.&di?emon?gro?lbup?moc?t&en?ni??ude???ed!.&2r,a&-&si,ton-si,?ted,?doog-a-si,e&erf-korgn,nilnigol,?gnigats-oned,h&cetaidem,sadtob,?k&catslluf-a-si,orgn,?l&e&crev,nap,?ooc-si,?nsrh:.lsp.bus,,oned,ppa-rettalp,r&ddaym,eyalplacol,?s&egap,r&ahc21,e&krow,niatnocnur,???t&i&lper:.&arik,d&eer,i,racip,?e&kip,saelererp,?frow,gnigats,k&cops,rik,?labolg,mik,o&do,ksis,?re&hcra,k&c&ah,ut,?ir,??s&enob,irap,maet,?tiprat,ulus,y&awenaj,elsew,ranac,??,mx,?luavreve.yaler,?vresi,weiverpbuhtig,xdom,y&lf,srab,???ih?l!.&di?fnoc?gro?lim?moc?nsa?ten?ude?vog???m!.&eman?gro?lim?m&oc?uesum??o&fni?r&ea?p???pooc?t&en?ni??ude?vog?zib???o&g?m??rt?s!.&bog?der?gro?moc?ude???t!.&arukas,bew-eht-no,morf,naht-&esrow,retteb,?sndnyd,?d?i?won??uqhv--nx??w&a!.moc?l??b!.&ca?gro?oc?ten?vog???c!.&gro?moc?ten?ude??cp??e&iver?n?s??g?k!.&bme?dni?gro?moc?ten?ude?vog???m!.&ca?gro?moc?oc?pooc?t&en?ni??ude?vog?zib??b??o&csom?h!s??n?w??p!.&344x,snduolc,vog???r!.&ca?gro?lim?oc?pooc?ten?vog??n??t!.&bulc?emag?gro?l&im?ru,?moc!.reliamym,?sndym,ten?ude?v&di?og??zibe???z!.&ca?gro?lim?oc?vog????x&a!t??c!.&dehcraeser,hta,ofni,s&ezziuq,lennuf,nduolc,rotaluclac,t&nemssessa,set,??vog?wonyap,??e&d&ef?nay??ma!nab??rof?s??ilften?jt?m!.&bog?gro?moc?ten?ude??g?ma2ibgy--nx??o&b!x??f?rex??rbgn--nx?s!.vog??x&am&jt?kt??x???y&4punu--nx?7rr03--nx?a&d!i&loh?rfkcalb??ot!.emyfilauqerp,??g!.segap,?lp?p!ila??rot?ssin?wdaorb??b!.&fo?hcetaidem,lim?moc?vog??ab?gur??c!.&ca?dtl?gro?lim?m&oc!.ecrofelacs.j,?t??orp?s&egolke?serp??ten?vog?zib??amrahp?nega??d&dadog?uts??e&kcoh?ltneb?n&dys?om?rotta??snikcm??g!.&gro?moc?oc?ten?ude?vog??olonhcet!.oc,?rene??hpargotohp?id?k!.&gro?moc?ten?ude??s??l!.&clp?d&em?i??gro?hcs?moc?ten?ude?vog??f?imaf!nacirema??l&a?il??ppus??m!.&eman?gro?lim?moc?ten?ude?vog?zib??edaca!.laiciffo,?ra??n&apmoc?os??o&j?s??p!.&gro?lim?moc?pooc?ten?ude?vog???r&e&corg?grus?llag?viled??lewej?otcerid?tnuoc?uxul??s!.&gro?lim?moc?ten?ude?vog??pil??t&efas?i&c?ledif?nummoc!.&bdnevar,gon,murofym,??r&ahc?uces??srevinu??laer?r&ap?eporp??uaeb??u!.&bug?gro?lim?moc?ten?ude??b!tseb???van?xes??z&a!.&eman?gro?lim?moc?o&c?fni?rp??pp?t&en?ni??ude?vog?zib???b!.&az,gro?jsg,moc?oc?sndym,ten?ude?vog???c!.&4e,9yxorptnetnoc.csr,inum.duolc.&rsu,tlf,?m&laer,urtnecatem.motsuc,?oc,??d!.&cos?gro?lop?m&oc?t??ossa?t&en?ra??ude?vog???ib!.&duolcsd,e&ht-rof,mos-rof,rom-rof,?izoj,liartevitca,nafamm,p&i&-&duolc,on,?fles,?ohbew,tfym,?retteb-rof,snd&nyd,uolc,?xro,?g??k!.&duolcj,gro?lim?moc?ten?ude?vog???m!.&ca?gro?lim?oc?ten?ude?v&da?og????n!.&asq-irom--nx?ca?gro?htlaeh?i&r&c?o&am?ām???wi!k???keeg?l&im?oohcs??neg?oc?snduolc,t&en?nemailrap?vog???a!niflla???rawhcs?s!.&ca?gro?oc???t!.&c&a?s??e&m?n??ibom?l&etoh?im??o&c?fni?g??ro?vt???u!.&gro?moc?oc?ten??rwon??yx!.hsadtob,?zub??λε?υε?авксом?брс!.&гро?до?ка?р&бо?п!у?????г&б?ро??дкм?зақ?итед?килотак?леб?мок?н&йално?ом??рку?сур!.&арамас,бпс,гро,зиб,ичос,ксм,м&ок,ырк,?рим,я,??тйас?фр?юе?յահ?לארשי!.&בושי?הימדקא?ל&הצ?שממ????םוק?اي&روس?سيلم?ناتيروم??بر&ع?غملا??ة&كبش?ي&دوعسلا?روس??یدوعسلا??ت&اراما?را&ب?ڀ?ھب???ر&ئازجلا?ازاب?صم?طق??سنوت?عقوم?قارع?ك&تيب?يلوثاك??موك?ن&ا&تس&كاپ?کاپ??دوس?ر&يا?یا??مع?يلعلا??درالا?ميلا?ي&رحبلا?طسلف???ه&ارمه?يدوعسلا??وكمارا?يبظوبا?ۃیدوعسلا?टेन?त&राभ?ोराभ??नठगंस?मॉक?्मतराभ?ত&রাভ?ৰাভ??ালংাব?ਤਰਾਭ?તરાભ?ତରାଭ?ாயித்நஇ?ைக்ஙலஇ?்ரூப்பக்ஙிச?్తరాభ?ತರಾಭ?ംതരാഭ?ාකංල?มอค?ยทไ!.&จิกรุธ?ต็นเ?ร&ก์คงอ?าหท??ลาบฐัร?าษกึศ???ວາລ?ეგ?なんみ?アトス?トンイポ?ドウラク?ムコ?ル&グーグ?ーセ??ン&ゾマア?ョシッァフ??业企?东广?乐娱?你爱我?信中?务政?动移?博微?卦八?厅餐?司公?品食?善慈?团集?国中?國中?址网?坡加新?城商?尚时?山佛?店&商?网?酒大里嘉??府政?康健?息信?戏游?拉里格香?拿大?教主天?机手?构机!织组??标商?歌谷?浦利飞?港香!.&人個?司公?府政?絡網?織組?育教???湾台?灣&台?臺??物购?界世?益公?看点?科盈訊電?站网?籍書?线在?络网?网文中?聘招?販通?逊马亚?通联?里嘉?锡马淡?門澳?门澳?闻新?電家?국한?넷닷?성삼?컴닷??"); /** * If a hostname is not a key in the EXCLUDE map, and if removing its leftmost component results @@ -50,7 +56,7 @@ private PublicSuffixPatterns() {} */ public static final ImmutableMap UNDER = TrieParser.parseTrie( - "ac.vedwa,d&b?i.ym.ssr,uolc.&etiso&isnes,tnegam,?iaznab,rehcnar-no,scitats,??e&b.lrusnart,d.&ecapsrebu,yksurf,?noz.notirt,t&atse.etupmoc,is.&areduolc,hsmroftalp,tst,???g&oog.tnetnocresu,p??h&c.tenerif:.cvs,,k?trae.sppad:.zzb,,?k&c?f?nil.bewd,rowten.secla,u.hcs??ln.lrusnart,m&j?m?oc.&duolcmeaeboda.ved,edonil.recnalabedon,ico-remotsuc:.&ico,pco,sco,?,lrihwyap,mme0,osseccandcved,s&ecapsnaecolatigid,t&cejboedonil,nemelepiuq,?wanozama.&1-etupmoc,ble,etupmoc,??t&neyoj.snc,opsppa.r,???n&c.moc.swanozama.&ble,etupmoc,?ur.edoc,?o&c.pato,i.&duolciaznab.sdraykcab,elacsnoom,oir-no,reniatnoceruza,s&3k-no,olots,?xcq.sys,y5s,??p&j.&a&mahokoy?yogan??ebok?i&adnes?kasawak??oroppas?uhsuykatik??n?pa.&knalfhtron,repoleved,??r&b.mon?e??s&edoc.owo,noitulos.rehid,w.rosivda,?t&a.&ofnistro.&nednuk,xe,?smcerutuf:.&ni,xe,?,?en.&cimonotpyrc,hvo.&gnitsoh,saapbew,???u&e.lrusnart,r.onijym.&gni&dnal,tsoh,?murtceps,spv,??ved.&e&gats>s,lcl,?rahbew,?gts,lcl,yawetag,?z&c.murtnecatem.duolc,yx.tibelet,??"); + "ac.vedwa,bup.&di,nik,?cv.e0,d&b?uolc.&etisotnegam,rehcnar-no,scitats,??e&b.lrusnart,d.&ecapsrebu,yksurf,?no&.nik,z.notirt,?t&atse.etupmoc,is.&a&reduolc,vnac.ym,?hsmroftalp,tst,??vil.pwe,?g&oog.tnetnocresu,p??h&c.tenerif:.cvs,,k??k&c?f?nil.bewd,rowten.secla,u.&hcs?sppaduolcvogelcaro,??ln.lrusnart,m&f.resu,j?m?oc.&duolc&-revelc.secivres,meaeboda.ved,?e&crofselas.mroftalp.gts-redliub-edoc.tset.100,do&c.redliub:->s,ved,?,nil.recnalabedon,??ico-remotsuc:.&ico,pco,sco,?,lrihwyap,mme0,rennurppaswa,s&ecapsnaecolatigid,ppaduolc&elcaro,vogelcaro,?t&cejbo&edonil,rtluv,?nemelepiuq,?wanozama.&1-etupmoc,ble,etupmoc,wolfria.&1-&ht&ron-ue,uos-&em,fa,pa,ue,??lartnec-&ac,em,li,ue,?ts&ae&-&as,pa,su,?ht&ron-pa,uos-pa,??ew-&ac,su,ue,???2-&htuos-&pa,ue,?lartnec-ue,ts&ae&-su,ht&ron-pa,uos-pa,??ew-&su,ue,???3-ts&aeht&ron-pa,uos-pa,?ew-ue,?4-tsaehtuos-pa,???t&neyoj.snc,opsppa.r,???n&c.&etisavnac.ym,moc.swanozama.&ble,etupmoc,wolfria.1-&htron-nc,tsewhtron-nc,???ur.&dliub,e&doc,sabatad,?noitargim,??o&c.&pato,timx,?i.&e&lacsnoom,varb.s,?nroca-no,oir-no,reniatnoceruza,s&3k-no,olots,?xcq.sys,??p&j.&a&mahokoy?yogan??ebok?i&adnes?kasawak??oroppas?uhsuykatik??n?ot.ldaw,pa.&detsoh,ekalfwons:.kniletavirp,,knalfhtron,nu&r,spu,?repoleved,tegeb,??r&b.mon?e??s&edoc.owo,w&.rosivda,a.&rekamegas.stnemirepxe,tsoper.etavirp,???t&a.&ofnistro.&nednuk,xe,?smcerutuf:.&ni,xe,?,?en.&cimonotpyrc,hvo.&gnitsoh,saapbew,?st.c,??u&e.lrusnart,r.onijym.&gni&dnal,tsoh,?murtceps,spv,??ved.&e&gats>s,lcl,?rahbew,?gts,lcl,mrc.&aw,bw,cw,d:w,,ew,fw,w,?nsrh.lsp.cw:.bus,,treclacol.resu,yawetag,?z&c.murtnecatem.duolc,yx.tibelet,??"); /** * The elements in this map would pass the UNDER test, but are known not to be public suffixes and @@ -59,5 +65,5 @@ private PublicSuffixPatterns() {} */ public static final ImmutableMap EXCLUDED = TrieParser.parseTrie( - "kc.www?pj.&a&mahokoy.ytic?yogan.ytic??ebok.ytic?i&adnes.ytic?kasawak.ytic??oroppas.ytic?uhsuykatik.ytic???"); + "kc.www?pj.&a&mahokoy.ytic?yogan.ytic??ebok.ytic?i&adnes.ytic?kasawak.ytic??oroppas.ytic?uhsuykatik.ytic??ved.nsrh.lsp.cw.&bus.derongi,derongi,??"); } diff --git a/android/guava/src/com/google/thirdparty/publicsuffix/TrieParser.java b/android/guava/src/com/google/thirdparty/publicsuffix/TrieParser.java index dd7778040ef2..82bf8b3c1121 100644 --- a/android/guava/src/com/google/thirdparty/publicsuffix/TrieParser.java +++ b/android/guava/src/com/google/thirdparty/publicsuffix/TrieParser.java @@ -14,29 +14,41 @@ package com.google.thirdparty.publicsuffix; +import static com.google.common.collect.Queues.newArrayDeque; + import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Queues; import java.util.Deque; /** Parser for a map of reversed domain names stored as a serialized radix tree. */ @GwtCompatible final class TrieParser { - private static final Joiner PREFIX_JOINER = Joiner.on(""); + + private static final Joiner DIRECT_JOINER = Joiner.on(""); /** * Parses a serialized trie representation of a map of reversed public suffixes into an immutable - * map of public suffixes. + * map of public suffixes. The encoded trie string may be broken into multiple chunks to avoid the + * 64k limit on string literal size. In-memory strings can be much larger (2G). */ - static ImmutableMap parseTrie(CharSequence encoded) { + static ImmutableMap parseTrie(CharSequence... encodedChunks) { + String encoded = DIRECT_JOINER.join(encodedChunks); + return parseFullString(encoded); + } + + @VisibleForTesting + static ImmutableMap parseFullString(String encoded) { ImmutableMap.Builder builder = ImmutableMap.builder(); int encodedLen = encoded.length(); int idx = 0; + while (idx < encodedLen) { - idx += doParseTrieToBuilder(Queues.newArrayDeque(), encoded, idx, builder); + idx += doParseTrieToBuilder(newArrayDeque(), encoded, idx, builder); } - return builder.build(); + + return builder.buildOrThrow(); } /** @@ -59,9 +71,10 @@ private static int doParseTrieToBuilder( int idx = start; char c = '\0'; - // Read all of the characters for this node. + // Read all the characters for this node. for (; idx < encodedLen; idx++) { c = encoded.charAt(idx); + if (c == '&' || c == '?' || c == '!' || c == ':' || c == ',') { break; } @@ -74,17 +87,20 @@ private static int doParseTrieToBuilder( // '?' represents a leaf node, which represents a REGISTRY entry in map. // ':' represents an interior node that represents a private entry in the map // ',' represents a leaf node, which represents a private entry in the map. - String domain = PREFIX_JOINER.join(stack); + String domain = DIRECT_JOINER.join(stack); + if (domain.length() > 0) { builder.put(domain, PublicSuffixType.fromCode(c)); } } + idx++; if (c != '?' && c != ',') { while (idx < encodedLen) { // Read all the children idx += doParseTrieToBuilder(stack, encoded, idx, builder); + if (encoded.charAt(idx) == '?' || encoded.charAt(idx) == ',') { // An extra '?' or ',' after a child node indicates the end of all children of this node. idx++; @@ -92,6 +108,7 @@ private static int doParseTrieToBuilder( } } } + stack.pop(); return idx - start; } diff --git a/android/pom.xml b/android/pom.xml index f040407448f3..f984ec426d66 100644 --- a/android/pom.xml +++ b/android/pom.xml @@ -6,20 +6,45 @@ 4.0.0 com.google.guava guava-parent - HEAD-android-SNAPSHOT + 999.0.0-HEAD-android-SNAPSHOT pom Guava Maven Parent Parent for guava artifacts https://github.com/google/guava + + ${java.specification.version} %regex[.*.class] - 1.1.2 - 3.12.0 - 1.20 - 3.1.0 - 3.2.1 + 1.4.4 + 1.0.0 + 2.36.0 + 3.0.0 + 2025-01-02T00:00:00Z UTF-8 + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/sun.security.jca=ALL-UNNAMED + + + integration + android + android + 999.0.0-HEAD-jre-SNAPSHOT + standard-jvm + jre GitHub Issues @@ -68,11 +93,11 @@ test - src - - **/*.java - **/*.sw* - + ../.. + + LICENSE + + META-INF @@ -85,7 +110,6 @@ - org.apache.maven.plugins maven-enforcer-plugin @@ -106,19 +130,21 @@ - - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - + + maven-antrun-plugin + 1.6 + maven-compiler-plugin - 3.8.1 + 3.13.0 1.8 1.8 + UTF-8 + true -sourcepath doesnotexist + + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + + + + -Xplugin:ErrorProne -Xep:NullArgumentForNonNullParameter:OFF -Xep:Java8ApiChecker:ERROR + + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + + com.google.errorprone + error_prone_core + 2.36.0 + + + + true - maven-jar-plugin - 3.2.0 + maven-dependency-plugin + 3.1.1 - maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - post-integration-test - jar - - + maven-deploy-plugin + 3.1.3 - org.codehaus.mojo - animal-sniffer-maven-plugin - ${animal.sniffer.version} - - true - - org.codehaus.mojo.signature - java16-sun - 1.10 - - - - java.util.Objects - - sun.misc.Unsafe - - - - - check-java-version-compatibility - test - - check - - - + maven-enforcer-plugin + 3.0.0-M3 + + + maven-install-plugin + 3.1.3 + + + maven-jar-plugin + 3.2.0 maven-javadoc-plugin - ${maven-javadoc-plugin.version} + 3.11.2 + + 23 + true true UTF-8 @@ -194,23 +235,41 @@ attach-docs - post-integration-test jar - maven-dependency-plugin - 3.1.1 + maven-resources-plugin + 3.3.1 - maven-antrun-plugin - 1.6 + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + org.codehaus.plexus + plexus-io + + 3.5.1 + + maven-surefire-plugin - 2.7.2 + 3.3.1 + + ${surefire.toolchain.version} + ${test.include} @@ -227,13 +286,111 @@ alphabetical - -Xmx1536M -Duser.language=hi -Duser.country=IN + -Xmx1536M -Duser.language=hi -Duser.country=IN ${test.add.args} ${test.add.opens} - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M3 + maven-toolchains-plugin + 3.2.0 + + + + toolchain + + + + + + + 23 + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.23 + + + org.ow2.asm + asm + 9.6 + + + + + com.google.common.base.IgnoreJRERequirement + com.google.common.cache.IgnoreJRERequirement + com.google.common.collect.IgnoreJRERequirement + com.google.common.collect.testing.IgnoreJRERequirement + com.google.common.collect.testing.testers.IgnoreJRERequirement + com.google.common.hash.IgnoreJRERequirement + com.google.common.io.IgnoreJRERequirement + com.google.common.math.IgnoreJRERequirement + com.google.common.primitives.IgnoreJRERequirement + com.google.common.reflect.IgnoreJRERequirement + com.google.common.testing.IgnoreJRERequirement + com.google.common.util.concurrent.IgnoreJRERequirement + + true + + com.toasttab.android + gummy-bears-api-21 + 0.8.0 + + + + + sun.misc.Unsafe + + + + + check-java-version-compatibility + test + + check + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.4.0 + + + org.mvnsearch + toolchains-maven-plugin + 4.5.0 + + + + download-23-and-surefire-version + + toolchain + + + + + 23 + temurin + + + ${surefire.toolchain.version} + temurin + + + + + @@ -258,129 +415,155 @@ - com.google.code.findbugs - jsr305 - 3.0.2 - - - org.checkerframework - checker-qual - ${checker-framework.version} - - - org.checkerframework - checker-qual - ${checker-framework.version} - sources + org.jspecify + jspecify + ${jspecify.version} com.google.errorprone error_prone_annotations - 2.7.1 + ${errorprone.version} com.google.j2objc j2objc-annotations - 1.3 - - - junit - junit - 4.13.2 - test - - - org.easymock - easymock - 4.3 - test - - - org.mockito - mockito-core - 3.9.0 - test - - - com.google.jimfs - jimfs - 1.2 - test - - - com.google.truth - truth - ${truth.version} - test - - - - com.google.guava - guava - - - - - com.google.caliper - caliper - 1.0-beta-2 - test - - - - com.google.guava - guava - - + ${j2objc.version} + - sonatype-oss-release - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - + sonatype-oss-release + + + + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + + + + suppress-open-jre-modules-for-toolchain-1.8 + + + surefire.toolchain.version + + 1.8 + + + + + + + + suppress-open-jre-modules-for-toolchain-8 + + + surefire.toolchain.version + + 8 + + + + + + + + + print-java-11-home + + + + org.mvnsearch + toolchains-maven-plugin + + + download-11 + initialize + + toolchain + + + + + 11 + temurin + + + + + + + + maven-toolchains-plugin + + + select-java-11 + initialize + + toolchain + + + + + + + 11 + + + + + + com.diamondq.maven + javahome-resolver-maven-plugin + 1.0.2 + + + resolve-java-11 + initialize + + resolve + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + + print-java-11-home + initialize + + exec + + + echo + + ${javaHome} + + ${project.build.directory}/java_11_home + + + + + diff --git a/futures/failureaccess/pom.xml b/futures/failureaccess/pom.xml index 1e10465b6260..077a86accc81 100644 --- a/futures/failureaccess/pom.xml +++ b/futures/failureaccess/pom.xml @@ -5,11 +5,11 @@ com.google.guava guava-parent - 26.0-android + 33.4.0-android failureaccess - 1.0.1 - bundle + 1.0.3 + jar Guava InternalFutureFailureAccess and InternalFutures Contains @@ -22,6 +22,69 @@ + + maven-compiler-plugin + + + default-compile + + compile + + + 8 + + module-info.java + + + + -sourcepath + doesnotexist + + + + + compile-java9 + + compile + + + 9 + + ${project.basedir}/src + + + + + -sourcepath + ${project.basedir}/src + --add-reads=com.google.common=ALL-UNNAMED + + -XDcompilePolicy=simple + + true + + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + true + + + + /module-info.class + META-INF/versions/9/com/google/common/util/concurrent/internal/*.class + + + maven-source-plugin @@ -33,7 +96,7 @@ true org.apache.felix maven-bundle-plugin - 2.5.0 + 5.1.8 bundle-manifest @@ -45,7 +108,9 @@ - com.google.common.util.concurrent.internal + + <_fixupmessages>^Classes found in the wrong directory: .* + com.google.common.util.concurrent.internal,!META-INF.* https://github.com/google/guava/ @@ -65,4 +130,26 @@ + + + sonatype-oss-release + + + + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + + + diff --git a/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutureFailureAccess.java b/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutureFailureAccess.java index 7cc84898bfb8..5b3e2924c2d5 100644 --- a/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutureFailureAccess.java +++ b/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutureFailureAccess.java @@ -14,6 +14,7 @@ package com.google.common.util.concurrent.internal; + /** * A future that, if it fails, may optionally provide access to the cause of the failure. * @@ -48,5 +49,7 @@ protected InternalFutureFailureAccess() {} * instance method. In the unlikely event that you need to call this method, call {@link * InternalFutures#tryInternalFastPathGetFailure(InternalFutureFailureAccess)}. */ - protected abstract Throwable tryInternalFastPathGetFailure(); + protected abstract + Throwable + tryInternalFastPathGetFailure(); } diff --git a/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutures.java b/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutures.java index 42df5ec14588..99e3fb41a079 100644 --- a/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutures.java +++ b/futures/failureaccess/src/com/google/common/util/concurrent/internal/InternalFutures.java @@ -14,6 +14,7 @@ package com.google.common.util.concurrent.internal; + /** * Static utilities for {@link InternalFutureFailureAccess}. Most users will never need to use this * class. @@ -37,7 +38,9 @@ public final class InternalFutures { * return value of this method as its cause *

    */ - public static Throwable tryInternalFastPathGetFailure(InternalFutureFailureAccess future) { + public static + Throwable + tryInternalFastPathGetFailure(InternalFutureFailureAccess future) { return future.tryInternalFastPathGetFailure(); } diff --git a/guava/src/com/google/common/hash/LongAddable.java b/futures/failureaccess/src/module-info.java similarity index 65% rename from guava/src/com/google/common/hash/LongAddable.java rename to futures/failureaccess/src/module-info.java index 5c6a7f0c7db8..d50986876f76 100644 --- a/guava/src/com/google/common/hash/LongAddable.java +++ b/futures/failureaccess/src/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Guava Authors + * Copyright (C) 2024 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -12,19 +12,7 @@ * the License. */ -package com.google.common.hash; - - -/** - * Abstract interface for objects that can concurrently add longs. - * - * @author Louis Wasserman - */ -@ElementTypesAreNonnullByDefault -interface LongAddable { - void increment(); - - void add(long x); - - long sum(); +/** Guava: {@code Future} Internals. */ +module com.google.common.util.concurrent.internal { + exports com.google.common.util.concurrent.internal; } diff --git a/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java b/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java index a8d9dd4de5f9..c3d84c6200f7 100644 --- a/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java +++ b/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java @@ -18,7 +18,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A {@link Future} that accepts completion listeners. Each listener has an associated executor, and @@ -36,10 +37,11 @@ * *

    The main purpose of {@code ListenableFuture} is to help you chain together a graph of * asynchronous operations. You can chain them together manually with calls to methods like {@link - * Futures#transform(ListenableFuture, com.google.common.base.Function, Executor) - * Futures.transform}, but you will often find it easier to use a framework. Frameworks automate the - * process, often adding features like monitoring, debugging, and cancellation. Examples of - * frameworks include: + * Futures#transform(ListenableFuture, com.google.common.base.Function, Executor) Futures.transform} + * (or {@link FluentFuture#transform(com.google.common.base.Function, Executor) + * FluentFuture.transform}), but you will often find it easier to use a framework. Frameworks + * automate the process, often adding features like monitoring, debugging, and cancellation. + * Examples of frameworks include: * *