Upgrade apache-commons-io to rel/commons-io-2.14.0 am: f93b23c2f8 am: d189b34b4e am: b2a374b626 am: fa59b6cdfd

Original change: https://android-review.googlesource.com/c/platform/external/apache-commons-io/+/2780899

Change-Id: If7b7a8273ca3eb4618fb2d290f556933639f296f
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 2e4cbac..1420421 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -45,10 +45,10 @@
 
     steps:
     - name: Checkout repository
-      uses: actions/[email protected]
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
       with:
         persist-credentials: false
-    - uses: actions/[email protected]
+    - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
       with:
         path: ~/.m2/repository
         key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -57,7 +57,7 @@
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@v2
+      uses: github/codeql-action/init@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -68,7 +68,7 @@
     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
     # If this step fails, then you should remove it and run the build manually (see below)
     - name: Autobuild
-      uses: github/codeql-action/autobuild@v2
+      uses: github/codeql-action/autobuild@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
 
     # â„šī¸ Command-line programs to run using the OS shell.
     # 📚 https://git.io/JvXDl
@@ -82,4 +82,4 @@
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v2
+      uses: github/codeql-action/analyze@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index ce7402a..f100bf3 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -29,17 +29,17 @@
         java: [ 8 ]
 
     steps:
-    - uses: actions/[email protected]
+    - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
       with:
         persist-credentials: false
-    - uses: actions/[email protected]
+    - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
       with:
         path: ~/.m2/repository
         key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
         restore-keys: |
           ${{ runner.os }}-maven-
     - name: Set up JDK ${{ matrix.java }}
-      uses: actions/[email protected]
+      uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
       with:
         distribution: 'temurin'
         java-version: ${{ matrix.java }}
@@ -47,6 +47,6 @@
       run: mvn -V test jacoco:report --file pom.xml --no-transfer-progress
 
     - name: Upload coverage to Codecov
-      uses: codecov/codecov-action@v3
+      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
       with:
         files: ./target/site/jacoco/jacoco.xml
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 3088a45..6bd303e 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -43,17 +43,17 @@
       fail-fast: false
         
     steps:
-    - uses: actions/[email protected]
+    - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
       with:
         persist-credentials: false
-    - uses: actions/[email protected]
+    - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
       with:
         path: ~/.m2/repository
         key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
         restore-keys: |
           ${{ runner.os }}-maven-
     - name: Set up JDK ${{ matrix.java }}
-      uses: actions/[email protected]
+      uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
       with:
         distribution: 'temurin'
         java-version: ${{ matrix.java }}
diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml
index 672a3e4..3f533c0 100644
--- a/.github/workflows/scorecards-analysis.yml
+++ b/.github/workflows/scorecards-analysis.yml
@@ -40,12 +40,12 @@
     steps:
 
       - name: "Checkout code"
-        uses: actions/[email protected]   # 3.1.0
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
         with:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af    # 2.1.3
+        uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031    # 2.2.0
         with:
           results_file: results.sarif
           results_format: sarif
@@ -57,13 +57,13 @@
           publish_results: true
 
       - name: "Upload artifact"
-        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce    # 3.1.2
+        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32    # 3.1.3
         with:
           name: SARIF file
           path: results.sarif
           retention-days: 5
 
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b    # 2.1.22
+        uses: github/codeql-action/upload-sarif@04daf014b50eaf774287bf3f0f1869d4b4c4b913    # 2.21.7
         with:
           sarif_file: results.sarif
diff --git a/METADATA b/METADATA
index 4a83dd9..b462024 100644
--- a/METADATA
+++ b/METADATA
@@ -1,6 +1,6 @@
 # This project was upgraded with external_updater.
 # Usage: tools/external_updater/updater.sh update apache-commons-io
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
 
 name: "apache-commons-io"
 description: "The Apache Commons IO library contains utility classes, stream implementations, file filters, file comparators, endian transformation classes, and much more."
@@ -13,11 +13,11 @@
     type: GIT
     value: "https://github.com/apache/commons-io.git"
   }
-  version: "rel/commons-io-2.13.0"
+  version: "rel/commons-io-2.14.0"
   license_type: NOTICE
   last_upgrade_date {
     year: 2023
-    month: 6
-    day: 30
+    month: 10
+    day: 11
   }
 }
diff --git a/README.md b/README.md
index 50559e1..25c792e 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@
 [![GitHub Actions Status](https://github.com/apache/commons-io/workflows/Java%20CI/badge.svg)](https://github.com/apache/commons-io/actions)
 [![Coverage Status](https://codecov.io/gh/apache/commons-io/branch/master/graph/badge.svg)](https://app.codecov.io/gh/apache/commons-io)
 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/commons-io/commons-io/badge.svg?gav=true)](https://maven-badges.herokuapp.com/maven-central/commons-io/commons-io/?gav=true)
-[![Javadocs](https://javadoc.io/badge/commons-io/commons-io/2.13.0.svg)](https://javadoc.io/doc/commons-io/commons-io/2.13.0)
+[![Javadocs](https://javadoc.io/badge/commons-io/commons-io/2.14.0.svg)](https://javadoc.io/doc/commons-io/commons-io/2.14.0)
 [![CodeQL](https://github.com/apache/commons-io/workflows/CodeQL/badge.svg)](https://github.com/apache/commons-io/actions/workflows/codeql-analysis.yml?query=workflow%3ACodeQL)
 [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/apache/commons-io/badge)](https://api.securityscorecards.dev/projects/github.com/apache/commons-io)
 
@@ -70,7 +70,7 @@
 <dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
-  <version>2.13.0</version>
+  <version>2.14.0</version>
 </dependency>
 ```
 
@@ -89,7 +89,7 @@
 
 License
 -------
-This code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0).
+This code is under the [Apache License v2](https://www.apache.org/licenses/LICENSE-2.0).
 
 See the `NOTICE.txt` file for required notices and attributions.
 
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 0b351a4..8300ad4 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,3 +1,83 @@
+
+Apache Commons IO 
+Version 2.14.0
+Release Notes
+
+INTRODUCTION:
+
+Commons IO is a package of Java utility classes like java.io.  
+Classes in this package are considered to be so standard and of such high 
+reuse as to justify existence in java.io.
+
+The Apache Commons IO library contains utility classes, stream implementations, file filters,
+file comparators, endian transformation classes, and much more.
+
+Java 8 required.
+
+Changes in this version include:
+
+New features:
+o           Add DeferredFileOutputStream.getPath(). Thanks to Gary Gregory. 
+o           Add FileCleaningTracker.track(Path, Object[, FileDeleteStrategy]). Thanks to Gary Gregory. 
+o           Add IOUtils.skip[Fully](InputStream, long, Supplier<byte[]>). Thanks to Gary Gregory. 
+o           Add FilesUncheck.find(Path, int, BiPredicate%lt;Path, BasicFileAttributes>, FileVisitOption...) Thanks to Gary Gregory. 
+o           Add IOIntSupplier. Thanks to Gary Gregory. 
+o           Add IOLongSupplier. Thanks to Gary Gregory. 
+o           Add Uncheck.getAsInt(IOIntSupplier [, Supplier<String>]). Thanks to Gary Gregory. 
+o           Add Uncheck.getAsLong(IOLongSupplier [, Supplier<String>]). Thanks to Gary Gregory. 
+o           Add Uncheck.run(IORunnable, Supplier<String>) Thanks to Gary Gregory. 
+o           Add Uncheck.get(IOSupplier, Supplier<String>) Thanks to Gary Gregory. 
+o           IOFileFilter now also extends java.nio.file.PathMatcher. Thanks to Gary Gregory. 
+o           Add PathMatcherFileFilter to adapt java.nio.file.PathMatcher. Thanks to Gary Gregory. 
+o           Add ThresholdingOutputStream.getOutputStream() and deprecate getStream(). Thanks to Gary Gregory. 
+o           Add DeferredFileOutputStream.Builder.setOutputFile(Path). Thanks to Gary Gregory. 
+o           Add DeferredFileOutputStream.Builder.setDirectory(Path). Thanks to Gary Gregory. 
+o           Add AbstractStreamBuilder.setBufferSizeChecker(IntToIntFunction). Thanks to Gary Gregory. 
+o           Add AbstractStreamBuilder.setBufferSizeMax(int). Thanks to Gary Gregory. 
+
+Fixed Bugs:
+o IO-799:  ReaderInputStream.read() throws an exception instead of returning -1 when called again after returning -1. Thanks to Jeroen van der Vegt, Gary Gregory. 
+o IO-804:  FileUtils.forceMkdirParent() Javadoc is incorrect. Thanks to Elliotte Rusty Harold, Gary Gregory. 
+o           [StepSecurity] ci: Harden GitHub Actions #461. Thanks to step-security-bot, Gary Gregory. 
+o           MagicNumberFileFilter.accept(Path, BasicFileAttributes) doesn't its byteOffset before reading. Thanks to Gary Gregory. 
+o           Javadoc improvements. Thanks to Gary Gregory. 
+o           Spelling #468. Thanks to Josh Soref, Gary Gregory. 
+o           Use assertThrows #475. Thanks to Jakub Kupczyk. 
+
+Changes:
+o           Bump jimfs from 1.2 to 1.3.0 #465 (tests). Thanks to Dependabot. 
+o           Bump commons-parent from 58 to 62. Thanks to Gary Gregory. 
+o           Bump commons-lang3 from 3.12 to 3.13.0. Thanks to Gary Gregory. 
+
+Compatibility with 2.6:
+Binary compatible: Yes.
+Source compatible: Yes.
+Semantic compatible: Yes.
+
+Commons IO 2.9.0 requires Java 8.
+Commons IO 2.8.0 requires Java 8.
+Commons IO 2.7 requires Java 8.
+Commons IO 2.6 requires Java 7.
+Commons IO 2.5 requires Java 6.
+Commons IO 2.4 requires Java 6.
+Commons IO 2.3 requires Java 6.
+Commons IO 2.2 requires Java 5.
+Commons IO 1.4 requires Java 1.3.
+
+Historical list of changes: https://commons.apache.org/proper/commons-io/changes-report.html
+
+For complete information on Apache Commons IO, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache Commons IO website:
+
+https://commons.apache.org/proper/commons-io/
+
+Download page: https://commons.apache.org/proper/commons-io/download_io.cgi
+
+Have fun!
+-Apache Commons Team
+
+==============================================================================
+
 Apache Commons IO 
 Version 2.13.0
 Release Notes
@@ -1747,7 +1827,7 @@
 - LockableFileWriter - encoding support [36825]
     Add support for character encodings to LockableFileWriter
     Improve the validation
-    Create directories if necesssary
+    Create directories if necessary
 
 - IOUtils and EndianUtils are no longer final  [28978]
     Allows developers to have subclasses if desired   
diff --git a/pom.xml b/pom.xml
index 7b8660b..18c11d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,12 +19,12 @@
   <parent>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-parent</artifactId>
-    <version>58</version>
+    <version>62</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
-  <version>2.13.0</version>
+  <version>2.14.0</version>
   <name>Apache Commons IO</name>
 
   <inceptionYear>2002</inceptionYear>
@@ -265,13 +265,13 @@
     <dependency>
       <groupId>com.google.jimfs</groupId>
       <artifactId>jimfs</artifactId>
-      <version>1.2</version>
+      <version>1.3.0</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
-      <version>3.12.0</version>
+      <version>3.13.0</version>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -294,8 +294,8 @@
     <commons.componentid>io</commons.componentid>
     <commons.module.name>org.apache.commons.io</commons.module.name>
     <commons.rc.version>RC1</commons.rc.version>
-    <commons.bc.version>2.12.0</commons.bc.version>
-    <commons.release.version>2.13.0</commons.release.version>
+    <commons.bc.version>2.13.0</commons.bc.version>
+    <commons.release.version>2.14.0</commons.release.version>
     <commons.release.desc>(requires Java 8)</commons.release.desc>
     <commons.jira.id>IO</commons.jira.id>
     <commons.jira.pid>12310477</commons.jira.pid>
@@ -324,7 +324,7 @@
     <commons.scmPubCheckoutDirectory>site-content</commons.scmPubCheckoutDirectory>
     <commons.javadoc.java.link>${commons.javadoc8.java.link}</commons.javadoc.java.link>
     <commons.moditect.version>1.0.0.Final</commons.moditect.version>
-    <jmh.version>1.36</jmh.version>
+    <jmh.version>1.37</jmh.version>
     <japicmp.skip>false</japicmp.skip>
     <jacoco.skip>${env.JACOCO_SKIP}</jacoco.skip>
     <commons.release.isDistModule>true</commons.release.isDistModule>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index f9ff3ae..28efbb7 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -47,6 +47,92 @@
   </properties>
 
   <body>
+    <release version="2.14.0" date="2023-09-24" description="Java 8 required.">
+      <!-- FIX -->
+      <action dev="ggregory" type="fix" issue="IO-799" due-to="Jeroen van der Vegt, Gary Gregory">
+        ReaderInputStream.read() throws an exception instead of returning -1 when called again after returning -1.
+      </action>
+      <action dev="ggregory" type="fix" issue="IO-804" due-to="Elliotte Rusty Harold, Gary Gregory">
+        FileUtils.forceMkdirParent() Javadoc is incorrect.
+      </action>
+      <action dev="ggregory" type="fix" due-to="step-security-bot, Gary Gregory">
+        [StepSecurity] ci: Harden GitHub Actions #461.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        MagicNumberFileFilter.accept(Path, BasicFileAttributes) doesn't set its byteOffset before reading.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Javadoc improvements.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Josh Soref, Gary Gregory">
+        Spelling #468.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Jakub Kupczyk">
+        Use assertThrows #475.
+      </action>
+      <!-- ADD -->
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add DeferredFileOutputStream.getPath().
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add FileCleaningTracker.track(Path, Object[, FileDeleteStrategy]).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add IOUtils.skip[Fully](InputStream, long, Supplier&lt;byte[]&gt;).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add FilesUncheck.find(Path, int, BiPredicate%lt;Path, BasicFileAttributes&gt;, FileVisitOption...)
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add IOIntSupplier.
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add IOLongSupplier.
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add Uncheck.getAsInt(IOIntSupplier [, Supplier&lt;String&gt;]).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add Uncheck.getAsLong(IOLongSupplier [, Supplier&lt;String&gt;]).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add Uncheck.run(IORunnable, Supplier&lt;String&gt;)
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add Uncheck.get(IOSupplier, Supplier&lt;String&gt;)
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        IOFileFilter now also extends java.nio.file.PathMatcher.
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add PathMatcherFileFilter to adapt java.nio.file.PathMatcher.
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add ThresholdingOutputStream.getOutputStream() and deprecate getStream().
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add DeferredFileOutputStream.Builder.setOutputFile(Path).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add DeferredFileOutputStream.Builder.setDirectory(Path).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add AbstractStreamBuilder.setBufferSizeChecker(IntToIntFunction).
+      </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add AbstractStreamBuilder.setBufferSizeMax(int).
+      </action>
+      <!-- UPDATE -->
+      <action dev="ggregory" type="update" due-to="Dependabot">
+        Bump jimfs from 1.2 to 1.3.0 #465 (tests).
+      </action>
+      <action dev="ggregory" type="update" due-to="Gary Gregory">
+        Bump commons-parent from 58 to 62.
+      </action>
+      <action type="update" dev="ggregory" due-to="Gary Gregory">
+        Bump commons-lang3 from 3.12 to 3.13.0.
+      </action>
+    </release>
     <release version="2.13.0" date="2023-06-03" description="Java 8 required.">
       <!-- FIX -->
       <action issue="IO-791" dev="ggregory" type="fix" due-to="Chad Wilson, Gary Gregory">
@@ -62,10 +148,10 @@
         FileAlreadyExistsException in PathUtils.createParentDirectories(Path, LinkOption, FileAttribute...).
       </action>
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
-        Only read the relevant portion of a file in AbstractOrigin.FileOrigin.getByteArray(long, int)
+        Only read the relevant portion of a file in AbstractOrigin.FileOrigin.getByteArray(long, int).
       </action>
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
-        Only read the relevant portion of a file in AbstractOrigin.PathOrigin.getByteArray(long, int)
+        Only read the relevant portion of a file in AbstractOrigin.PathOrigin.getByteArray(long, int).
       </action>
       <action dev="ggregory" type="fix" due-to="Shai Shapira, Gary Gregory" issue="IO-798">
         DeferredFileOutputStream throws exception when system temp dir is a symlink.
@@ -76,6 +162,12 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         ReaderInputStream.Builder.setCharset(null) should reset to a default object, not throw an NPE.
       </action>
+      <action dev="ggregory" type="fix" issue="IO-800" due-to="Jan Høydahl, Gary Gregory">
+        Fix back-incompatible change for PathUtils.deleteDirectory(): throw NoSuchFileException instead of IllegalArgumentException #459.
+      </action>
+      <action dev="ggregory" type="fix" issue="IO-801" due-to="James Howe, Gary Gregory">
+        Deprecation documentation gives incorrect alternative.
+      </action>
       <!-- ADD -->
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add CharSequenceInputStream.Builder.
diff --git a/src/changes/release-notes.vm b/src/changes/release-notes.vm
index 5739b66..8697381 100644
--- a/src/changes/release-notes.vm
+++ b/src/changes/release-notes.vm
@@ -166,3 +166,5 @@
 
 Have fun!
 -Apache Commons Team
+
+==============================================================================
diff --git a/src/conf/checkstyle.xml b/src/conf/checkstyle.xml
index bbc848f..d2fc392 100644
--- a/src/conf/checkstyle.xml
+++ b/src/conf/checkstyle.xml
@@ -16,8 +16,8 @@
 limitations under the License.
 -->
 <!DOCTYPE module PUBLIC
-    "-//Checkstyle//DTD Checkstyle Configuration 1.2//EN"
-    "https://checkstyle.org/dtds/configuration_1_2.dtd">
+    "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
+    "https://checkstyle.org/dtds/configuration_1_3.dtd">
 <module name="Checker">
   <property name="localeLanguage" value="en" />
   <module name="JavadocPackage">
diff --git a/src/main/java/org/apache/commons/io/FileCleaningTracker.java b/src/main/java/org/apache/commons/io/FileCleaningTracker.java
index 5d7ad0e..98c33b0 100644
--- a/src/main/java/org/apache/commons/io/FileCleaningTracker.java
+++ b/src/main/java/org/apache/commons/io/FileCleaningTracker.java
@@ -19,6 +19,7 @@
 import java.io.File;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -228,7 +229,7 @@ public int getTrackCount() {
     }
 
     /**
-     * Track the specified file, using the provided marker, deleting the file
+     * Tracks the specified file, using the provided marker, deleting the file
      * when the marker instance is garbage collected.
      * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
      *
@@ -260,6 +261,36 @@ public void track(final File file, final Object marker, final FileDeleteStrategy
      * when the marker instance is garbage collected.
      * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
      *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the file is null
+     * @since 2.14.0
+     */
+    public void track(final Path file, final Object marker) {
+        track(file, marker, null);
+    }
+
+    /**
+     * Tracks the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The specified deletion strategy is used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the file is null
+     * @since 2.14.0
+     */
+    public void track(final Path file, final Object marker, final FileDeleteStrategy deleteStrategy) {
+        Objects.requireNonNull(file, "file");
+        addTracker(file.toAbsolutePath().toString(), marker, deleteStrategy);
+    }
+
+    /**
+     * Tracks the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
      * @param path  the full path to the file to be tracked, not null
      * @param marker  the marker object used to track the file, not null
      * @throws NullPointerException if the path is null
diff --git a/src/main/java/org/apache/commons/io/FileSystem.java b/src/main/java/org/apache/commons/io/FileSystem.java
index d71bd37..c7dbdfa 100644
--- a/src/main/java/org/apache/commons/io/FileSystem.java
+++ b/src/main/java/org/apache/commons/io/FileSystem.java
@@ -215,7 +215,7 @@ private static String getSystemProperty(final String property) {
      * (this.codePointAt(<i>k</i>) == searchChar) &amp;&amp; (<i>k</i> &gt;= start)
      * </pre></blockquote>
      * <p>
-     * is true. In either case, if no such character occurs inm {@code cs}
+     * is true. In either case, if no such character occurs in {@code cs}
      * at or after position {@code start}, then
      * {@code -1} is returned.
      * </p>
diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java
index ef1f508..807dc37 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -1088,8 +1088,8 @@ public static void copyURLToFile(final URL source, final File destination, final
     }
 
     /**
-     * Creates all parent directories for a File object, including any necessary but nonexistent parent directories. If a parent directory already exists or is
-     * null, nothing happens.
+     * Creates all parent directories for a File object, including any necessary but non-existent parent directories. If a parent directory already exists or
+     * is null, nothing happens.
      *
      * @param file the File that may need parents, may be null.
      * @return The parent directory, or {@code null} if the given File does have a parent.
@@ -1281,7 +1281,7 @@ public static boolean directoryContains(final File directory, final File child)
 
     /**
      * Internal copy directory method. Creates all destination parent directories,
-     * including any necessary but nonexistent parent directories.
+     * including any necessary but non-existent parent directories.
      *
      * @param srcDir the validated source directory, must not be {@code null}.
      * @param destDir the validated destination directory, must not be {@code null}.
@@ -1364,7 +1364,7 @@ public static void forceDeleteOnExit(final File file) throws IOException {
     }
 
     /**
-     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
      * null, nothing happens.
      * <p>
      * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
@@ -1381,16 +1381,15 @@ public static void forceMkdir(final File directory) throws IOException {
     }
 
     /**
-     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
-     * <p>
-     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the parent directory already exists or is
      * null, nothing happens.
+     * <p>
+     * Calls {@link File#mkdirs()} for the parent of @{code file}.
      * </p>
      *
      * @param file file with parents to create, must not be {@code null}.
      * @throws NullPointerException if the file is {@code null}.
      * @throws IOException          if the directory was not created along with all its parent directories.
-     * @throws IOException          if the given file object is not a directory.
      * @throws SecurityException    See {@link File#mkdirs()}.
      * @see File#mkdirs()
      * @since 2.5
@@ -2271,7 +2270,7 @@ public static Collection<File> listFilesAndDirs(final File directory, final IOFi
     /**
      * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
      * <p>
-     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
      * null, nothing happens.
      * </p>
      *
@@ -2323,7 +2322,7 @@ public static void moveDirectory(final File srcDir, final File destDir) throws I
     /**
      * Moves a directory to another directory.
      * <p>
-     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
      * </p>
      *
      * @param source the file to be moved.
@@ -2408,7 +2407,7 @@ public static void moveFile(final File srcFile, final File destFile, final CopyO
     /**
      * Moves a file to a directory.
      * <p>
-     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
      * </p>
      *
      * @param srcFile the file to be moved.
@@ -2437,7 +2436,7 @@ public static void moveFileToDirectory(final File srcFile, final File destDir, f
     /**
      * Moves a file or directory to a destination directory.
      * <p>
-     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
      * </p>
      * <p>
      * When the destination is on another file system, do a "copy and delete".
diff --git a/src/main/java/org/apache/commons/io/IOExceptionList.java b/src/main/java/org/apache/commons/io/IOExceptionList.java
index ca3f880..8ee519b 100644
--- a/src/main/java/org/apache/commons/io/IOExceptionList.java
+++ b/src/main/java/org/apache/commons/io/IOExceptionList.java
@@ -65,7 +65,7 @@ private static String toMessage(final List<? extends Throwable> causeList) {
     private final List<? extends Throwable> causeList;
 
     /**
-     * Creates a new exception caused by a list of exceptions.
+     * Constructs a new exception caused by a list of exceptions.
      *
      * @param causeList a list of cause exceptions.
      */
@@ -74,7 +74,7 @@ public IOExceptionList(final List<? extends Throwable> causeList) {
     }
 
     /**
-     * Creates a new exception caused by a list of exceptions.
+     * Constructs a new exception caused by a list of exceptions.
      *
      * @param message The detail message, see {@link #getMessage()}.
      * @param causeList a list of cause exceptions.
diff --git a/src/main/java/org/apache/commons/io/IOIndexedException.java b/src/main/java/org/apache/commons/io/IOIndexedException.java
index 71253d4..9a85328 100644
--- a/src/main/java/org/apache/commons/io/IOIndexedException.java
+++ b/src/main/java/org/apache/commons/io/IOIndexedException.java
@@ -45,7 +45,7 @@ protected static String toMessage(final int index, final Throwable cause) {
     private final int index;
 
     /**
-     * Creates a new exception.
+     * Constructs a new exception.
      *
      * @param index index of this exception.
      * @param cause cause exceptions.
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index a8d8ffb..b6b6f13 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -52,8 +52,10 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import java.util.zip.InflaterInputStream;
 
 import org.apache.commons.io.function.IOConsumer;
 import org.apache.commons.io.function.IOSupplier;
@@ -2375,6 +2377,36 @@ public static URL resourceToURL(final String name, final ClassLoader classLoader
      * @since 2.0
      */
     public static long skip(final InputStream input, final long toSkip) throws IOException {
+        return skip(input, toSkip, IOUtils::getScratchByteArrayWriteOnly);
+    }
+
+    /**
+     * Skips bytes from an input byte stream.
+     * <p>
+     * Intended for special cases when customization of the temporary buffer is needed because, for example, a nested input stream has requirements for the
+     * bytes read. For example, when using {@link InflaterInputStream}s from multiple threads.
+     * </p>
+     * <p>
+     * This implementation guarantees that it will read as many bytes as possible before giving up; this may not always be the case for skip() implementations
+     * in subclasses of {@link InputStream}.
+     * </p>
+     * <p>
+     * Note that the implementation uses {@link InputStream#read(byte[], int, int)} rather than delegating to {@link InputStream#skip(long)}. This means that
+     * the method may be considerably less efficient than using the actual skip implementation, this is done to guarantee that the correct number of bytes are
+     * skipped.
+     * </p>
+     *
+     * @param input              byte stream to skip
+     * @param toSkip             number of bytes to skip.
+     * @param skipBufferSupplier Supplies the buffer to use for reading.
+     * @return number of bytes actually skipped.
+     * @throws IOException              if there is a problem reading the file
+     * @throws IllegalArgumentException if toSkip is negative
+     * @see InputStream#skip(long)
+     * @see <a href="https://issues.apache.org/jira/browse/IO-203">IO-203 - Add skipFully() method for InputStreams</a>
+     * @since 2.14.0
+     */
+    public static long skip(final InputStream input, final long toSkip, final Supplier<byte[]> skipBufferSupplier) throws IOException {
         if (toSkip < 0) {
             throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip);
         }
@@ -2385,9 +2417,9 @@ public static long skip(final InputStream input, final long toSkip) throws IOExc
         //
         long remain = toSkip;
         while (remain > 0) {
+            final byte[] skipBuffer = skipBufferSupplier.get();
             // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
-            final byte[] byteArray = getScratchByteArrayWriteOnly();
-            final long n = input.read(byteArray, 0, (int) Math.min(remain, byteArray.length));
+            final long n = input.read(skipBuffer, 0, (int) Math.min(remain, skipBuffer.length));
             if (n < 0) { // EOF
                 break;
             }
@@ -2485,10 +2517,40 @@ public static long skip(final Reader reader, final long toSkip) throws IOExcepti
      * @since 2.0
      */
     public static void skipFully(final InputStream input, final long toSkip) throws IOException {
+        final long skipped = skip(input, toSkip, IOUtils::getScratchByteArrayWriteOnly);
+        if (skipped != toSkip) {
+            throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped);
+        }
+    }
+
+    /**
+     * Skips the requested number of bytes or fail if there are not enough left.
+     * <p>
+     * Intended for special cases when customization of the temporary buffer is needed because, for example, a nested input stream has requirements for the
+     * bytes read. For example, when using {@link InflaterInputStream}s from multiple threads.
+     * </p>
+     * <p>
+     * This allows for the possibility that {@link InputStream#skip(long)} may not skip as many bytes as requested (most likely because of reaching EOF).
+     * </p>
+     * <p>
+     * Note that the implementation uses {@link #skip(InputStream, long)}. This means that the method may be considerably less efficient than using the actual
+     * skip implementation, this is done to guarantee that the correct number of characters are skipped.
+     * </p>
+     *
+     * @param input              stream to skip
+     * @param toSkip             the number of bytes to skip
+     * @param skipBufferSupplier Supplies the buffer to use for reading.
+     * @throws IOException              if there is a problem reading the file
+     * @throws IllegalArgumentException if toSkip is negative
+     * @throws EOFException             if the number of bytes skipped was incorrect
+     * @see InputStream#skip(long)
+     * @since 2.14.0
+     */
+    public static void skipFully(final InputStream input, final long toSkip, final Supplier<byte[]> skipBufferSupplier) throws IOException {
         if (toSkip < 0) {
             throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip);
         }
-        final long skipped = skip(input, toSkip);
+        final long skipped = skip(input, toSkip, skipBufferSupplier);
         if (skipped != toSkip) {
             throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped);
         }
diff --git a/src/main/java/org/apache/commons/io/RandomAccessFileMode.java b/src/main/java/org/apache/commons/io/RandomAccessFileMode.java
index bb2c123..5406158 100644
--- a/src/main/java/org/apache/commons/io/RandomAccessFileMode.java
+++ b/src/main/java/org/apache/commons/io/RandomAccessFileMode.java
@@ -57,7 +57,7 @@ public enum RandomAccessFileMode {
     }
 
     /**
-     * Creates a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
+     * Constructs a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
      * argument.
      *
      * @param file the file object
@@ -69,7 +69,7 @@ public RandomAccessFile create(final File file) throws FileNotFoundException {
     }
 
     /**
-     * Creates a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
+     * Constructs a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
      * argument.
      *
      * @param file the file object
@@ -81,7 +81,7 @@ public RandomAccessFile create(final Path file) throws FileNotFoundException {
     }
 
     /**
-     * Creates a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
+     * Constructs a random access file stream to read from, and optionally to write to, the file specified by the {@link File}
      * argument.
      *
      * @param file the file object
diff --git a/src/main/java/org/apache/commons/io/TaggedIOException.java b/src/main/java/org/apache/commons/io/TaggedIOException.java
index 43d7730..9c95d32 100644
--- a/src/main/java/org/apache/commons/io/TaggedIOException.java
+++ b/src/main/java/org/apache/commons/io/TaggedIOException.java
@@ -103,7 +103,7 @@ public static void throwCauseIfTaggedWith(final Throwable throwable, final Objec
     private final Serializable tag;
 
     /**
-     * Creates a tagged wrapper for the given exception.
+     * Constructs a tagged wrapper for the given exception.
      *
      * @param original the exception to be tagged
      * @param tag tag of this exception
diff --git a/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java b/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
index 797213f..97271af 100644
--- a/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
+++ b/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
@@ -29,7 +29,7 @@
 final class UncheckedIOExceptions {
 
     /**
-     * Creates a new UncheckedIOException for the given detail message.
+     * Constructs a new UncheckedIOException for the given detail message.
      * <p>
      * This method exists because there is no String constructor in {@link UncheckedIOException}.
      * </p>
@@ -43,7 +43,7 @@ public static UncheckedIOException create(final Object message) {
     }
 
     /**
-     * Creates a new UncheckedIOException for the given detail message.
+     * Constructs a new UncheckedIOException for the given detail message.
      * <p>
      * This method exists because there is no String constructor in {@link UncheckedIOException}.
      * </p>
diff --git a/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java b/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
index bc79dba..bd82371 100644
--- a/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
+++ b/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
@@ -46,7 +46,7 @@
 public abstract class AbstractOriginSupplier<T, B extends AbstractOriginSupplier<T, B>> extends AbstractSupplier<T, B> {
 
     /**
-     * Creates a new byte array origin for a byte array.
+     * Constructs a new byte array origin for a byte array.
      *
      * @param origin the byte array.
      * @return a new byte array origin.
@@ -56,7 +56,7 @@ protected static ByteArrayOrigin newByteArrayOrigin(final byte[] origin) {
     }
 
     /**
-     * Creates a new CharSequence origin for a CharSequence.
+     * Constructs a new CharSequence origin for a CharSequence.
      *
      * @param origin the CharSequence.
      * @return a new file origin.
@@ -67,7 +67,7 @@ protected static CharSequenceOrigin newCharSequenceOrigin(final CharSequence ori
     }
 
     /**
-     * Creates a new file origin for a file.
+     * Constructs a new file origin for a file.
      *
      * @param origin the file.
      * @return a new file origin.
@@ -77,7 +77,7 @@ protected static FileOrigin newFileOrigin(final File origin) {
     }
 
     /**
-     * Creates a new file origin for a file path.
+     * Constructs a new file origin for a file path.
      *
      * @param origin the file path.
      * @return a new file origin.
@@ -87,7 +87,7 @@ protected static FileOrigin newFileOrigin(final String origin) {
     }
 
     /**
-     * Creates a new input stream origin for a file.
+     * Constructs a new input stream origin for a file.
      *
      * @param origin the input stream.
      * @return a new input stream origin.
@@ -97,7 +97,7 @@ protected static InputStreamOrigin newInputStreamOrigin(final InputStream origin
     }
 
     /**
-     * Creates a new output stream origin for a file.
+     * Constructs a new output stream origin for a file.
      *
      * @param origin the output stream.
      * @return a new output stream origin.
@@ -107,7 +107,7 @@ protected static OutputStreamOrigin newOutputStreamOrigin(final OutputStream ori
     }
 
     /**
-     * Creates a new path origin for a file.
+     * Constructs a new path origin for a file.
      *
      * @param origin the path.
      * @return a new path origin.
@@ -117,7 +117,7 @@ protected static PathOrigin newPathOrigin(final Path origin) {
     }
 
     /**
-     * Creates a new path name origin for a path name.
+     * Constructs a new path name origin for a path name.
      *
      * @param origin the path name.
      * @return a new path name origin.
@@ -127,7 +127,7 @@ protected static PathOrigin newPathOrigin(final String origin) {
     }
 
     /**
-     * Creates a new reader origin for a reader.
+     * Constructs a new reader origin for a reader.
      *
      * @param origin the reader.
      * @return a new reader origin.
@@ -137,7 +137,7 @@ protected static ReaderOrigin newReaderOrigin(final Reader origin) {
     }
 
     /**
-     * Creates a new reader origin for a URI.
+     * Constructs a new reader origin for a URI.
      *
      * @param origin the URI.
      * @return a new URI origin.
@@ -147,7 +147,7 @@ protected static URIOrigin newURIOrigin(final URI origin) {
     }
 
     /**
-     * Creates a new writer origin for a file.
+     * Constructs a new writer origin for a file.
      *
      * @param origin the writer.
      * @return a new writer .
diff --git a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
index 8beccd9..d94638a 100644
--- a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
+++ b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
@@ -24,6 +24,7 @@
 import java.nio.charset.Charset;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
+import java.util.function.IntUnaryOperator;
 
 import org.apache.commons.io.Charsets;
 import org.apache.commons.io.IOUtils;
@@ -38,6 +39,8 @@
  */
 public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> {
 
+    private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE;
+
     private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY;
 
     /**
@@ -51,6 +54,11 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T
     private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE;
 
     /**
+     * The maximum buffer size.
+     */
+    private int bufferSizeMax = DEFAULT_MAX_VALUE;
+
+    /**
      * The Charset, defaults to {@link Charset#defaultCharset()}.
      */
     private Charset charset = Charset.defaultCharset();
@@ -63,6 +71,26 @@ public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T
     private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS;
 
     /**
+     * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default.
+     */
+    private final IntUnaryOperator defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size;
+
+    /**
+     * The checking behavior for a buffer size request.
+     */
+    private IntUnaryOperator bufferSizeChecker = defaultSizeChecker;
+
+    /**
+     * Applies the buffer size request.
+     *
+     * @param size the size request.
+     * @return the size to use, usually the input, or can throw an unchecked exception, like {@link IllegalArgumentException}.
+     */
+    private int checkBufferSize(final int size) {
+        return bufferSizeChecker.applyAsInt(size);
+    }
+
+    /**
      * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
      *
      * @return the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
@@ -86,7 +114,7 @@ protected int getBufferSizeDefault() {
      * @return An input stream
      * @throws IOException                   if an I/O error occurs.
      * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence.
-     * @throws IllegalStateException if the {@code origin} is {@code null}.
+     * @throws IllegalStateException         if the {@code origin} is {@code null}.
      * @see AbstractOrigin#getCharSequence(Charset)
      * @since 2.13.0
      */
@@ -135,8 +163,8 @@ protected InputStream getInputStream() throws IOException {
      *
      * @return An OutputStream
      * @throws IOException                   if an I/O error occurs.
-     * @throws UnsupportedOperationException if the origin cannot be converted to an OututStream.
-     * @throws IllegalStateException if the {@code origin} is {@code null}.
+     * @throws UnsupportedOperationException if the origin cannot be converted to an OutputStream.
+     * @throws IllegalStateException         if the {@code origin} is {@code null}.
      * @see AbstractOrigin#getOutputStream(OpenOption...)
      * @since 2.13.0
      */
@@ -149,7 +177,7 @@ protected OutputStream getOutputStream() throws IOException {
      *
      * @return A Path
      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
-     * @throws IllegalStateException if the {@code origin} is {@code null}.
+     * @throws IllegalStateException         if the {@code origin} is {@code null}.
      * @see AbstractOrigin#getPath()
      * @since 2.13.0
      */
@@ -163,7 +191,7 @@ protected Path getPath() {
      * @return An writer.
      * @throws IOException                   if an I/O error occurs.
      * @throws UnsupportedOperationException if the origin cannot be converted to a Writer.
-     * @throws IllegalStateException if the {@code origin} is {@code null}.
+     * @throws IllegalStateException         if the {@code origin} is {@code null}.
      * @see AbstractOrigin#getOutputStream(OpenOption...)
      * @since 2.13.0
      */
@@ -181,7 +209,7 @@ protected Writer getWriter() throws IOException {
      * @return this.
      */
     public B setBufferSize(final int bufferSize) {
-        this.bufferSize = bufferSize > 0 ? bufferSize : bufferSizeDefault;
+        this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault);
         return asThis();
     }
 
@@ -200,6 +228,18 @@ public B setBufferSize(final Integer bufferSize) {
     }
 
     /**
+     * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default.
+     *
+     * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior.
+     * @return this
+     * @since 2.14.0
+     */
+    public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) {
+        this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker;
+        return asThis();
+    }
+
+    /**
      * Sets the buffer size for subclasses to initialize.
      * <p>
      * Subclasses may ignore this setting.
@@ -214,6 +254,19 @@ protected B setBufferSizeDefault(final int bufferSizeDefault) {
     }
 
     /**
+     * The maximum buffer size checked by the buffer size checker. Values less or equal to 0, resets to the int max value. By default, if this value is
+     * exceeded, this methods throws an {@link IllegalArgumentException}.
+     *
+     * @param bufferSizeMax maximum buffer size checked by the buffer size checker.
+     * @return this.
+     * @since 2.14.0
+     */
+    public B setBufferSizeMax(final int bufferSizeMax) {
+        this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE;
+        return asThis();
+    }
+
+    /**
      * Sets the Charset.
      * <p>
      * Subclasses may ignore this setting.
@@ -274,4 +327,8 @@ public B setOpenOptions(final OpenOption... openOptions) {
         this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS;
         return asThis();
     }
+
+    private int throwIae(final int size, final int max) {
+        throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max));
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/charset/package-info.java b/src/main/java/org/apache/commons/io/charset/package-info.java
index 56ee6c0..7888dcc 100644
--- a/src/main/java/org/apache/commons/io/charset/package-info.java
+++ b/src/main/java/org/apache/commons/io/charset/package-info.java
@@ -17,6 +17,7 @@
 
 /**
  * Provides classes to work with code from {@link java.nio.charset}.
+ *
  * @since 2.12.0
  */
 package org.apache.commons.io.charset;
diff --git a/src/main/java/org/apache/commons/io/comparator/package-info.java b/src/main/java/org/apache/commons/io/comparator/package-info.java
index 3a0926d..17f6a96 100644
--- a/src/main/java/org/apache/commons/io/comparator/package-info.java
+++ b/src/main/java/org/apache/commons/io/comparator/package-info.java
@@ -17,7 +17,7 @@
 
 /**
  * Provides various {@link java.util.Comparator} implementations
- * for {@link java.io.File}s.
+ * for {@link java.io.File}s and {@link java.nio.file.Path}.
  * <h2>Sorting</h2>
  * <p>
  * All the comparators include <i>convenience</i> utility <code>sort(File...)</code> and
diff --git a/src/main/java/org/apache/commons/io/file/AccumulatorPathVisitor.java b/src/main/java/org/apache/commons/io/file/AccumulatorPathVisitor.java
index 0574ddd..ecfdd2b 100644
--- a/src/main/java/org/apache/commons/io/file/AccumulatorPathVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/AccumulatorPathVisitor.java
@@ -61,7 +61,7 @@
 public class AccumulatorPathVisitor extends CountingPathVisitor {
 
     /**
-     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
      *
      * @return a new instance configured with a BigInteger {@link PathCounters}.
      */
@@ -70,7 +70,7 @@ public static AccumulatorPathVisitor withBigIntegerCounters() {
     }
 
     /**
-     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
      *
      * @param fileFilter Filters files to accumulate and count.
      * @param dirFilter Filters directories to accumulate and count.
@@ -83,7 +83,7 @@ public static AccumulatorPathVisitor withBigIntegerCounters(final PathFilter fil
     }
 
     /**
-     * Creates a new instance configured with a long {@link PathCounters}.
+     * Constructs a new instance configured with a long {@link PathCounters}.
      *
      * @return a new instance configured with a long {@link PathCounters}.
      */
@@ -92,7 +92,7 @@ public static AccumulatorPathVisitor withLongCounters() {
     }
 
     /**
-     * Creates a new instance configured with a long {@link PathCounters}.
+     * Constructs a new instance configured with a long {@link PathCounters}.
      *
      * @param fileFilter Filters files to accumulate and count.
      * @param dirFilter Filters directories to accumulate and count.
diff --git a/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java b/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
index a94cf63..79162f3 100644
--- a/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
@@ -36,7 +36,7 @@
 public class CleaningPathVisitor extends CountingPathVisitor {
 
     /**
-     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
      *
      * @return a new instance configured with a BigInteger {@link PathCounters}.
      */
@@ -45,7 +45,7 @@ public static CountingPathVisitor withBigIntegerCounters() {
     }
 
     /**
-     * Creates a new instance configured with a long {@link PathCounters}.
+     * Constructs a new instance configured with a long {@link PathCounters}.
      *
      * @return a new instance configured with a long {@link PathCounters}.
      */
diff --git a/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java b/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
index b841019..00368ef 100644
--- a/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
@@ -49,7 +49,7 @@ static IOFileFilter defaultFileFilter() {
     }
 
     /**
-     * Creates a new instance configured with a {@link BigInteger} {@link PathCounters}.
+     * Constructs a new instance configured with a {@link BigInteger} {@link PathCounters}.
      *
      * @return a new instance configured with a {@link BigInteger} {@link PathCounters}.
      */
@@ -58,7 +58,7 @@ public static CountingPathVisitor withBigIntegerCounters() {
     }
 
     /**
-     * Creates a new instance configured with a {@code long} {@link PathCounters}.
+     * Constructs a new instance configured with a {@code long} {@link PathCounters}.
      *
      * @return a new instance configured with a {@code long} {@link PathCounters}.
      */
diff --git a/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java b/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
index 0d3b533..aff9bcd 100644
--- a/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
@@ -37,7 +37,7 @@
 public class DeletingPathVisitor extends CountingPathVisitor {
 
     /**
-     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
      *
      * @return a new instance configured with a BigInteger {@link PathCounters}.
      */
@@ -46,7 +46,7 @@ public static DeletingPathVisitor withBigIntegerCounters() {
     }
 
     /**
-     * Creates a new instance configured with a long {@link PathCounters}.
+     * Constructs a new instance configured with a long {@link PathCounters}.
      *
      * @return a new instance configured with a long {@link PathCounters}.
      */
diff --git a/src/main/java/org/apache/commons/io/file/FilesUncheck.java b/src/main/java/org/apache/commons/io/file/FilesUncheck.java
index e99d09c..02a1feb 100644
--- a/src/main/java/org/apache/commons/io/file/FilesUncheck.java
+++ b/src/main/java/org/apache/commons/io/file/FilesUncheck.java
@@ -42,6 +42,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiPredicate;
 import java.util.stream.Stream;
 
 import org.apache.commons.io.function.Uncheck;
@@ -242,6 +243,22 @@ public static boolean deleteIfExists(final Path path) {
     }
 
     /**
+     * Delegates to {@link Files#find(Path, int, BiPredicate, FileVisitOption...)} throwing {@link UncheckedIOException} instead of {@link IOException}.
+     *
+     * @param start    See delegate.
+     * @param maxDepth See delegate.
+     * @param matcher  See delegate.
+     * @param options  See delegate.
+     * @return See delegate.
+     * @throws UncheckedIOException Wraps an {@link IOException}.
+     * @since 2.14.0
+     */
+    public static Stream<Path> find(final Path start, final int maxDepth, final BiPredicate<Path, BasicFileAttributes> matcher,
+            final FileVisitOption... options) {
+        return Uncheck.apply(Files::find, start, maxDepth, matcher, options);
+    }
+
+    /**
      * Delegates to {@link Files#getAttribute(Path, String, LinkOption...)} throwing {@link UncheckedIOException} instead of
      * {@link IOException}.
      *
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 1c6bd0d..56b2a05 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -1646,11 +1646,11 @@ public static Path touch(final Path file) throws IOException {
      * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
      * @return the given visitor.
      *
+     * @throws NoSuchFileException if the directory does not exist.
      * @throws IOException if an I/O error is thrown by a visitor method.
      * @throws NullPointerException if the directory is {@code null}.
      */
     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path directory) throws IOException {
-        requireExists(directory, "directory");
         Files.walkFileTree(directory, visitor);
         return visitor;
     }
diff --git a/src/main/java/org/apache/commons/io/filefilter/IOFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/IOFileFilter.java
index 72abc5d..2bdeab4 100644
--- a/src/main/java/org/apache/commons/io/filefilter/IOFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/IOFileFilter.java
@@ -21,6 +21,7 @@
 import java.io.FilenameFilter;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Path;
+import java.nio.file.PathMatcher;
 import java.nio.file.attribute.BasicFileAttributes;
 
 import org.apache.commons.io.file.PathFilter;
@@ -30,7 +31,7 @@
  *
  * @since 1.0
  */
-public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter {
+public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter, PathMatcher {
 
     /**
      * An empty String array.
@@ -38,7 +39,7 @@ public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter {
     String[] EMPTY_STRING_ARRAY = {};
 
     /**
-     * Checks to see if the File should be accepted by this filter.
+     * Tests if a File should be accepted by this filter.
      * <p>
      * Defined in {@link java.io.FileFilter}.
      * </p>
@@ -50,7 +51,7 @@ public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter {
     boolean accept(File file);
 
     /**
-     * Checks to see if the File should be accepted by this filter.
+     * Tests if a File should be accepted by this filter.
      * <p>
      * Defined in {@link java.io.FilenameFilter}.
      * </p>
@@ -63,7 +64,7 @@ public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter {
     boolean accept(File dir, String name);
 
     /**
-     * Checks to see if the Path should be accepted by this filter.
+     * Checks to see if a Path should be accepted by this filter.
      *
      * @param path the Path to check.
      * @return true if this path matches the test.
@@ -71,11 +72,11 @@ public interface IOFileFilter extends FileFilter, FilenameFilter, PathFilter {
      */
     @Override
     default FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
-        return AbstractFileFilter.toDefaultFileVisitResult(accept(path.toFile()));
+        return AbstractFileFilter.toDefaultFileVisitResult(path != null && accept(path.toFile()));
     }
 
     /**
-     * Creates a new "and" filter with this filter.
+     * Constructs a new "and" filter with this filter.
      *
      * @param fileFilter the filter to "and".
      * @return a new filter.
@@ -86,7 +87,19 @@ default IOFileFilter and(final IOFileFilter fileFilter) {
     }
 
     /**
-     * Creates a new "not" filter with this filter.
+     * Tests if a Path should be accepted by this filter.
+     *
+     * @param path the Path to check.
+     * @return true if this path matches the test.
+     * @since 2.14.0
+     */
+    @Override
+    default boolean matches(final Path path) {
+        return accept(path, null) != FileVisitResult.TERMINATE;
+    }
+
+    /**
+     * Constructs a new "not" filter with this filter.
      *
      * @return a new filter.
      * @since 2.9.0
@@ -96,7 +109,7 @@ default IOFileFilter negate() {
     }
 
     /**
-     * Creates a new "or" filter with this filter.
+     * Constructs a new "or" filter with this filter.
      *
      * @param fileFilter the filter to "or".
      * @return a new filter.
diff --git a/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
index e774340..1793ef2 100644
--- a/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
@@ -293,6 +293,7 @@ public FileVisitResult accept(final Path file, final BasicFileAttributes attribu
             try {
                 try (FileChannel fileChannel = FileChannel.open(file)) {
                     final ByteBuffer byteBuffer = ByteBuffer.allocate(this.magicNumbers.length);
+                    fileChannel.position(byteOffset);
                     final int read = fileChannel.read(byteBuffer);
                     if (read != magicNumbers.length) {
                         return FileVisitResult.TERMINATE;
diff --git a/src/main/java/org/apache/commons/io/filefilter/PathMatcherFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/PathMatcherFileFilter.java
new file mode 100644
index 0000000..76b73eb
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/filefilter/PathMatcherFileFilter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.filefilter;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.util.Objects;
+
+/**
+ * Delegates matching to a {@link PathMatcher}.
+ *
+ * @since 2.14.0
+ */
+public class PathMatcherFileFilter extends AbstractFileFilter {
+
+    private final PathMatcher pathMatcher;
+
+    /**
+     * Constructs a new instance to perform matching with a PathMatcher.
+     *
+     * @param pathMatcher The PathMatcher delegate.
+     */
+    public PathMatcherFileFilter(final PathMatcher pathMatcher) {
+        this.pathMatcher = Objects.requireNonNull(pathMatcher, "pathMatcher");
+    }
+
+    @Override
+    public boolean accept(final File file) {
+        return file != null && matches(file.toPath());
+    }
+    @Override
+    public boolean matches(final Path path) {
+        return pathMatcher.matches(path);
+    }
+}
diff --git a/src/main/java/org/apache/commons/io/function/IOBaseStream.java b/src/main/java/org/apache/commons/io/function/IOBaseStream.java
index 6ac9bee..2048709 100644
--- a/src/main/java/org/apache/commons/io/function/IOBaseStream.java
+++ b/src/main/java/org/apache/commons/io/function/IOBaseStream.java
@@ -34,7 +34,7 @@
 public interface IOBaseStream<T, S extends IOBaseStream<T, S, B>, B extends BaseStream<T, B>> extends Closeable {
 
     /**
-     * Creates a {@link BaseStream} for this instance that throws {@link UncheckedIOException} instead of
+     * Constructs a {@link BaseStream} for this instance that throws {@link UncheckedIOException} instead of
      * {@link IOException}.
      *
      * @return an {@link UncheckedIOException} {@link BaseStream}.
diff --git a/src/main/java/org/apache/commons/io/function/IOIntSupplier.java b/src/main/java/org/apache/commons/io/function/IOIntSupplier.java
new file mode 100644
index 0000000..420498b
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOIntSupplier.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.function;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+
+/**
+ * Like {@link IntSupplier} but throws {@link IOException}.
+ *
+ * @since 2.14.0
+ */
+@FunctionalInterface
+public interface IOIntSupplier {
+
+    /**
+     * Creates a {@link Supplier} for this instance that throws {@link UncheckedIOException} instead of {@link IOException}.
+     *
+     * @return an UncheckedIOException Supplier.
+     */
+    default IntSupplier asIntSupplier() {
+        return () -> Uncheck.getAsInt(this);
+    }
+
+    /**
+     * Gets a result.
+     *
+     * @return a result
+     * @throws IOException if an I/O error occurs.
+     */
+    int getAsInt() throws IOException;
+}
diff --git a/src/main/java/org/apache/commons/io/function/IOLongSupplier.java b/src/main/java/org/apache/commons/io/function/IOLongSupplier.java
new file mode 100644
index 0000000..0741654
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOLongSupplier.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.function;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+/**
+ * Like {@link LongSupplier} but throws {@link IOException}.
+ *
+ * @since 2.14.0
+ */
+@FunctionalInterface
+public interface IOLongSupplier {
+
+    /**
+     * Creates a {@link Supplier} for this instance that throws {@link UncheckedIOException} instead of {@link IOException}.
+     *
+     * @return an UncheckedIOException Supplier.
+     */
+    default LongSupplier asSupplier() {
+        return () -> Uncheck.getAsLong(this);
+    }
+
+    /**
+     * Gets a result.
+     *
+     * @return a result
+     * @throws IOException if an I/O error occurs.
+     */
+    long getAsLong() throws IOException;
+}
diff --git a/src/main/java/org/apache/commons/io/function/IOSpliterator.java b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
index e9031cd..1e8dafa 100644
--- a/src/main/java/org/apache/commons/io/function/IOSpliterator.java
+++ b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
@@ -43,7 +43,7 @@ static <E> IOSpliterator<E> adapt(final Spliterator<E> iterator) {
     }
 
     /**
-     * Creates a {@link Spliterator} for this instance that throws {@link UncheckedIOException} instead of
+     * Constructs a {@link Spliterator} for this instance that throws {@link UncheckedIOException} instead of
      * {@link IOException}.
      *
      * @return an {@link UncheckedIOException} {@link Spliterator}.
diff --git a/src/main/java/org/apache/commons/io/function/Uncheck.java b/src/main/java/org/apache/commons/io/function/Uncheck.java
index e18937f..a593b56 100644
--- a/src/main/java/org/apache/commons/io/function/Uncheck.java
+++ b/src/main/java/org/apache/commons/io/function/Uncheck.java
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.util.function.Supplier;
 
 /**
  * Unchecks calls by throwing {@link UncheckedIOException} instead of {@link IOException}.
@@ -200,6 +201,89 @@ public static <T> T get(final IOSupplier<T> supplier) {
     }
 
     /**
+     * Gets the result from an IO supplier.
+     *
+     * @param <T> the return type of the operations.
+     * @param supplier Supplies the return value.
+     * @param message The UncheckedIOException message if an I/O error occurs.
+     * @return result from the supplier.
+     * @throws UncheckedIOException if an I/O error occurs.
+     */
+    public static <T> T get(final IOSupplier<T> supplier, final Supplier<String> message) {
+        try {
+            return supplier.get();
+        } catch (final IOException e) {
+            throw wrap(e, message);
+        }
+    }
+
+    /**
+     * Gets the result from an IO int supplier.
+     *
+     * @param supplier Supplies the return value.
+     * @return result from the supplier.
+     * @throws UncheckedIOException if an I/O error occurs.
+     * @since 2.14.0
+     */
+    public static int getAsInt(final IOIntSupplier supplier) {
+        try {
+            return supplier.getAsInt();
+        } catch (final IOException e) {
+            throw wrap(e);
+        }
+    }
+
+    /**
+     * Gets the result from an IO int supplier.
+     *
+     * @param supplier Supplies the return value.
+     * @param message The UncheckedIOException message if an I/O error occurs.
+     * @return result from the supplier.
+     * @throws UncheckedIOException if an I/O error occurs.
+     * @since 2.14.0
+     */
+    public static int getAsInt(final IOIntSupplier supplier, final Supplier<String> message) {
+        try {
+            return supplier.getAsInt();
+        } catch (final IOException e) {
+            throw wrap(e, message);
+        }
+    }
+
+    /**
+     * Gets the result from an IO long supplier.
+     *
+     * @param supplier Supplies the return value.
+     * @return result from the supplier.
+     * @throws UncheckedIOException if an I/O error occurs.
+     * @since 2.14.0
+     */
+    public static long getAsLong(final IOLongSupplier supplier) {
+        try {
+            return supplier.getAsLong();
+        } catch (final IOException e) {
+            throw wrap(e);
+        }
+    }
+
+    /**
+     * Gets the result from an IO long supplier.
+     *
+     * @param supplier Supplies the return value.
+     * @param message The UncheckedIOException message if an I/O error occurs.
+     * @return result from the supplier.
+     * @throws UncheckedIOException if an I/O error occurs.
+     * @since 2.14.0
+     */
+    public static long getAsLong(final IOLongSupplier supplier, final Supplier<String> message) {
+        try {
+            return supplier.getAsLong();
+        } catch (final IOException e) {
+            throw wrap(e, message);
+        }
+    }
+
+    /**
      * Runs an IO runnable.
      *
      * @param runnable The runnable to run.
@@ -214,6 +298,22 @@ public static void run(final IORunnable runnable) {
     }
 
     /**
+     * Runs an IO runnable.
+     *
+     * @param runnable The runnable to run.
+     * @param message The UncheckedIOException message if an I/O error occurs.
+     * @throws UncheckedIOException if an I/O error occurs.
+     * @since 2.14.0
+     */
+    public static void run(final IORunnable runnable, final Supplier<String> message) {
+        try {
+            runnable.run();
+        } catch (final IOException e) {
+            throw wrap(e, message);
+        }
+    }
+
+    /**
      * Tests an IO predicate.
      *
      * @param <T> the type of the input to the predicate.
@@ -230,10 +330,7 @@ public static <T> boolean test(final IOPredicate<T> predicate, final T t) {
     }
 
     /**
-     * Creates a new UncheckedIOException for the given detail message.
-     * <p>
-     * This method exists because there is no String constructor in {@link UncheckedIOException}.
-     * </p>
+     * Constructs a new UncheckedIOException for the given exception.
      *
      * @param e The exception to wrap.
      * @return a new {@link UncheckedIOException}.
@@ -243,6 +340,17 @@ private static UncheckedIOException wrap(final IOException e) {
     }
 
     /**
+     * Constructs a new UncheckedIOException for the given exception and detail message.
+     *
+     * @param e The exception to wrap.
+     * @param message The UncheckedIOException message if an I/O error occurs.
+     * @return a new {@link UncheckedIOException}.
+     */
+    private static UncheckedIOException wrap(final IOException e, final Supplier<String> message) {
+        return new UncheckedIOException(message.get(), e);
+    }
+
+    /**
      * No instances needed.
      */
     private Uncheck() {
diff --git a/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java b/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
index 187e59b..7a6089d 100644
--- a/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
@@ -91,7 +91,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates an automatically closing proxy for the given input stream.
+     * Constructs an automatically closing proxy for the given input stream.
      *
      * @param in underlying input stream
      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
diff --git a/src/main/java/org/apache/commons/io/input/BrokenInputStream.java b/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
index 4d84625..5eabb97 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
@@ -43,14 +43,14 @@ public class BrokenInputStream extends InputStream {
     private final Supplier<IOException> exceptionSupplier;
 
     /**
-     * Creates a new stream that always throws an {@link IOException}.
+     * Constructs a new stream that always throws an {@link IOException}.
      */
     public BrokenInputStream() {
         this(() -> new IOException("Broken input stream"));
     }
 
     /**
-     * Creates a new stream that always throws the given exception.
+     * Constructs a new stream that always throws the given exception.
      *
      * @param exception the exception to be thrown.
      */
@@ -59,7 +59,7 @@ public BrokenInputStream(final IOException exception) {
     }
 
     /**
-     * Creates a new stream that always throws an {@link IOException}.
+     * Constructs a new stream that always throws an {@link IOException}.
      *
      * @param exceptionSupplier a supplier for the exception to be thrown.
      * @since 2.12.0
diff --git a/src/main/java/org/apache/commons/io/input/BrokenReader.java b/src/main/java/org/apache/commons/io/input/BrokenReader.java
index 9748933..03530e8 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenReader.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenReader.java
@@ -43,14 +43,14 @@ public class BrokenReader extends Reader {
     private final Supplier<IOException> exceptionSupplier;
 
     /**
-     * Creates a new reader that always throws an {@link IOException}.
+     * Constructs a new reader that always throws an {@link IOException}.
      */
     public BrokenReader() {
         this(() -> new IOException("Broken reader"));
     }
 
     /**
-     * Creates a new reader that always throws the given exception.
+     * Constructs a new reader that always throws the given exception.
      *
      * @param exception the exception to be thrown.
      */
@@ -59,7 +59,7 @@ public BrokenReader(final IOException exception) {
     }
 
     /**
-     * Creates a new reader that always throws an {@link IOException}
+     * Constructs a new reader that always throws an {@link IOException}
      *
      * @param exceptionSupplier a supplier for the exception to be thrown.
      * @since 2.12.0
diff --git a/src/main/java/org/apache/commons/io/input/CircularInputStream.java b/src/main/java/org/apache/commons/io/input/CircularInputStream.java
index d7e04a4..26d1c3f 100644
--- a/src/main/java/org/apache/commons/io/input/CircularInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/CircularInputStream.java
@@ -56,7 +56,7 @@ public class CircularInputStream extends InputStream {
     private final long targetByteCount;
 
     /**
-     * Creates an instance from the specified array of bytes.
+     * Constructs an instance from the specified array of bytes.
      *
      * @param repeatContent Input buffer to be repeated this buffer is not copied.
      * @param targetByteCount How many bytes the read. A negative number means an infinite target count.
diff --git a/src/main/java/org/apache/commons/io/input/CloseShieldInputStream.java b/src/main/java/org/apache/commons/io/input/CloseShieldInputStream.java
index 0fd10d5..03521d9 100644
--- a/src/main/java/org/apache/commons/io/input/CloseShieldInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/CloseShieldInputStream.java
@@ -31,7 +31,7 @@
 public class CloseShieldInputStream extends ProxyInputStream {
 
     /**
-     * Creates a proxy that shields the given input stream from being closed.
+     * Constructs a proxy that shields the given input stream from being closed.
      *
      * @param inputStream the input stream to wrap
      * @return the created proxy
@@ -42,7 +42,7 @@ public static CloseShieldInputStream wrap(final InputStream inputStream) {
     }
 
     /**
-     * Creates a proxy that shields the given input stream from being closed.
+     * Constructs a proxy that shields the given input stream from being closed.
      *
      * @param inputStream underlying input stream
      * @deprecated Using this constructor prevents IDEs from warning if the
diff --git a/src/main/java/org/apache/commons/io/input/CloseShieldReader.java b/src/main/java/org/apache/commons/io/input/CloseShieldReader.java
index 375ae20..585a297 100644
--- a/src/main/java/org/apache/commons/io/input/CloseShieldReader.java
+++ b/src/main/java/org/apache/commons/io/input/CloseShieldReader.java
@@ -31,7 +31,7 @@
 public class CloseShieldReader extends ProxyReader {
 
     /**
-     * Creates a proxy that shields the given reader from being closed.
+     * Constructs a proxy that shields the given reader from being closed.
      *
      * @param reader the reader to wrap
      * @return the created proxy
@@ -42,7 +42,7 @@ public static CloseShieldReader wrap(final Reader reader) {
     }
 
     /**
-     * Creates a proxy that shields the given reader from being closed.
+     * Constructs a proxy that shields the given reader from being closed.
      *
      * @param reader underlying reader
      * @deprecated Using this constructor prevents IDEs from warning if the
diff --git a/src/main/java/org/apache/commons/io/input/InfiniteCircularInputStream.java b/src/main/java/org/apache/commons/io/input/InfiniteCircularInputStream.java
index 0514c7e..630177e 100644
--- a/src/main/java/org/apache/commons/io/input/InfiniteCircularInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/InfiniteCircularInputStream.java
@@ -32,7 +32,7 @@
 public class InfiniteCircularInputStream extends CircularInputStream {
 
     /**
-     * Creates an instance from the specified array of bytes.
+     * Constructs an instance from the specified array of bytes.
      *
      * @param repeatContent Input buffer to be repeated this buffer is not copied.
      */
diff --git a/src/main/java/org/apache/commons/io/input/MarkShieldInputStream.java b/src/main/java/org/apache/commons/io/input/MarkShieldInputStream.java
index 083525b..97653fc 100644
--- a/src/main/java/org/apache/commons/io/input/MarkShieldInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/MarkShieldInputStream.java
@@ -36,7 +36,7 @@
 public class MarkShieldInputStream extends ProxyInputStream {
 
     /**
-     * Creates a proxy that shields the given input stream from being
+     * Constructs a proxy that shields the given input stream from being
      * marked or rest.
      *
      * @param in underlying input stream
diff --git a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
index 96198aa..7aed418 100644
--- a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
@@ -120,7 +120,7 @@ public static class MessageDigestMaintainingObserver extends Observer {
         private final MessageDigest messageDigest;
 
         /**
-         * Creates an MessageDigestMaintainingObserver for the given MessageDigest.
+         * Constructs an MessageDigestMaintainingObserver for the given MessageDigest.
          *
          * @param messageDigest the message digest to use
          */
@@ -171,7 +171,7 @@ static MessageDigest getDefaultMessageDigest() throws NoSuchAlgorithmException {
     private final MessageDigest messageDigest;
 
     /**
-     * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm.
+     * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the "MD5" algorithm.
      * <p>
      * The MD5 algorithm is weak and should not be used.
      * </p>
@@ -186,7 +186,7 @@ public MessageDigestCalculatingInputStream(final InputStream inputStream) throws
     }
 
     /**
-     * Creates a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}.
+     * Constructs a new instance, which calculates a signature on the given stream, using the given {@link MessageDigest}.
      *
      * @param inputStream   the stream to calculate the message digest for
      * @param messageDigest the message digest to use
@@ -199,7 +199,7 @@ public MessageDigestCalculatingInputStream(final InputStream inputStream, final
     }
 
     /**
-     * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm.
+     * Constructs a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the given algorithm.
      *
      * @param inputStream the stream to calculate the message digest for
      * @param algorithm   the name of the algorithm requested. See the MessageDigest section in the
diff --git a/src/main/java/org/apache/commons/io/input/NullReader.java b/src/main/java/org/apache/commons/io/input/NullReader.java
index 194d16f..ba221e0 100644
--- a/src/main/java/org/apache/commons/io/input/NullReader.java
+++ b/src/main/java/org/apache/commons/io/input/NullReader.java
@@ -82,7 +82,7 @@ public class NullReader extends Reader {
     private final boolean markSupported;
 
     /**
-     * Creates a {@link Reader} that emulates a size 0 reader
+     * Constructs a {@link Reader} that emulates a size 0 reader
      * which supports marking and does not throw EOFException.
      *
      * @since 2.7
@@ -92,7 +92,7 @@ public NullReader() {
     }
 
     /**
-     * Creates a {@link Reader} that emulates a specified size
+     * Constructs a {@link Reader} that emulates a specified size
      * which supports marking and does not throw EOFException.
      *
      * @param size The size of the reader to emulate.
@@ -102,7 +102,7 @@ public NullReader(final long size) {
     }
 
     /**
-     * Creates a {@link Reader} that emulates a specified
+     * Constructs a {@link Reader} that emulates a specified
      * size with option settings.
      *
      * @param size The size of the reader to emulate.
diff --git a/src/main/java/org/apache/commons/io/input/ObservableInputStream.java b/src/main/java/org/apache/commons/io/input/ObservableInputStream.java
index 259f64c..ee080f0 100644
--- a/src/main/java/org/apache/commons/io/input/ObservableInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ObservableInputStream.java
@@ -110,7 +110,7 @@ public void finished() throws IOException {
     private final List<Observer> observers;
 
     /**
-     * Creates a new ObservableInputStream for the given InputStream.
+     * Constructs a new ObservableInputStream for the given InputStream.
      *
      * @param inputStream the input stream to observe.
      */
@@ -119,7 +119,7 @@ public ObservableInputStream(final InputStream inputStream) {
     }
 
     /**
-     * Creates a new ObservableInputStream for the given InputStream.
+     * Constructs a new ObservableInputStream for the given InputStream.
      *
      * @param inputStream the input stream to observe.
      * @param observers List of observer callbacks.
@@ -130,7 +130,7 @@ private ObservableInputStream(final InputStream inputStream, final List<Observer
     }
 
     /**
-     * Creates a new ObservableInputStream for the given InputStream.
+     * Constructs a new ObservableInputStream for the given InputStream.
      *
      * @param inputStream the input stream to observe.
      * @param observers List of observer callbacks.
diff --git a/src/main/java/org/apache/commons/io/input/QueueInputStream.java b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
index 5956fcf..1ed2e84 100644
--- a/src/main/java/org/apache/commons/io/input/QueueInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
@@ -183,7 +183,7 @@ Duration getTimeout() {
     }
 
     /**
-     * Creates a new QueueOutputStream instance connected to this. Writes to the output stream will be visible to this input stream.
+     * Constructs a new QueueOutputStream instance connected to this. Writes to the output stream will be visible to this input stream.
      *
      * @return QueueOutputStream connected to this stream.
      */
@@ -204,7 +204,7 @@ public int read() {
             return value == null ? EOF : 0xFF & value;
         } catch (final InterruptedException e) {
             Thread.currentThread().interrupt();
-            // throw runtime unchecked exception to maintain signature backward-compatibilty of
+            // throw runtime unchecked exception to maintain signature backward-compatibility of
             // this read method, which does not declare IOException
             throw new IllegalStateException(e);
         }
diff --git a/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java b/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
index 0221869..647ee4d 100644
--- a/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
@@ -211,17 +211,6 @@ public int read(final byte[] bytes, final int offset, final int length) throws I
         return randomAccessFile.read(bytes, offset, length);
     }
 
-    /**
-     * Delegates to the underlying file.
-     *
-     * @param position See {@link RandomAccessFile#seek(long)}.
-     * @throws IOException See {@link RandomAccessFile#seek(long)}.
-     * @see RandomAccessFile#seek(long)
-     */
-    private void seek(final long position) throws IOException {
-        randomAccessFile.seek(position);
-    }
-
     @Override
     public long skip(final long skipCount) throws IOException {
         if (skipCount <= 0) {
@@ -235,7 +224,7 @@ public long skip(final long skipCount) throws IOException {
         final long targetPos = filePointer + skipCount;
         final long newPos = targetPos > fileLength ? fileLength - 1 : targetPos;
         if (newPos > 0) {
-            seek(newPos);
+            randomAccessFile.seek(newPos);
         }
         return randomAccessFile.getFilePointer() - filePointer;
     }
diff --git a/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java b/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
index 02b79a0..dc4fc08 100644
--- a/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
@@ -113,7 +113,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates a new daemon thread.
+     * Constructs a new daemon thread.
      *
      * @param r the thread's runnable.
      * @return a new daemon thread.
@@ -125,7 +125,7 @@ private static Thread newDaemonThread(final Runnable r) {
     }
 
     /**
-     * Creates a new daemon executor service.
+     * Constructs a new daemon executor service.
      *
      * @return a new daemon executor service.
      */
@@ -178,7 +178,7 @@ private static ExecutorService newExecutorService() {
     private final Condition asyncReadComplete = stateChangeLock.newCondition();
 
     /**
-     * Creates an instance with the specified buffer size and read-ahead threshold
+     * Constructs an instance with the specified buffer size and read-ahead threshold
      *
      * @param inputStream       The underlying input stream.
      * @param bufferSizeInBytes The buffer size.
@@ -190,7 +190,7 @@ public ReadAheadInputStream(final InputStream inputStream, final int bufferSizeI
     }
 
     /**
-     * Creates an instance with the specified buffer size and read-ahead threshold
+     * Constructs an instance with the specified buffer size and read-ahead threshold
      *
      * @param inputStream       The underlying input stream.
      * @param bufferSizeInBytes The buffer size.
@@ -203,7 +203,7 @@ public ReadAheadInputStream(final InputStream inputStream, final int bufferSizeI
     }
 
     /**
-     * Creates an instance with the specified buffer size and read-ahead threshold
+     * Constructs an instance with the specified buffer size and read-ahead threshold
      *
      * @param inputStream             The underlying input stream.
      * @param bufferSizeInBytes       The buffer size.
diff --git a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
index 0933079..cfe79fc 100644
--- a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
@@ -148,14 +148,6 @@ public Builder setCharsetEncoder(final CharsetEncoder newEncoder) {
 
     }
 
-    private static CharsetEncoder newEncoder(final Charset charset) {
-        // @formatter:off
-        return Charsets.toCharset(charset).newEncoder()
-                .onMalformedInput(CodingErrorAction.REPLACE)
-                .onUnmappableCharacter(CodingErrorAction.REPLACE);
-        // @formatter:on
-    }
-
     /**
      * Constructs a new {@link Builder}.
      *
@@ -179,6 +171,14 @@ static float minBufferSize(final CharsetEncoder charsetEncoder) {
         return charsetEncoder.maxBytesPerChar() * 2;
     }
 
+    private static CharsetEncoder newEncoder(final Charset charset) {
+        // @formatter:off
+        return Charsets.toCharset(charset).newEncoder()
+                .onMalformedInput(CodingErrorAction.REPLACE)
+                .onUnmappableCharacter(CodingErrorAction.REPLACE);
+        // @formatter:on
+    }
+
     private final Reader reader;
 
     private final CharsetEncoder charsetEncoder;
@@ -339,6 +339,9 @@ public void close() throws IOException {
      * @throws IOException If an I/O error occurs
      */
     private void fillBuffer() throws IOException {
+        if (endOfInput) {
+            return;
+        }
         if (!endOfInput && (lastCoderResult == null || lastCoderResult.isUnderflow())) {
             encoderIn.compact();
             final int position = encoderIn.position();
diff --git a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
index ef5fdf1..04c9cf9 100644
--- a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
+++ b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
@@ -132,7 +132,7 @@ private FilePart(final long no, final int length, final byte[] leftOverOfLastFil
         }
 
         /**
-         * Creates the buffer containing any leftover bytes.
+         * Constructs the buffer containing any leftover bytes.
          */
         private void createLeftOver() {
             final int lineLengthBytes = currentLastBytePos + 1;
@@ -275,12 +275,12 @@ public static Builder builder() {
     private boolean trailingNewlineOfFileSkipped;
 
     /**
-     * Creates a ReversedLinesFileReader with default block size of 4KB and the
+     * Constructs a ReversedLinesFileReader with default block size of 4KB and the
      * platform's default encoding.
      *
      * @param file the file to be read
      * @throws IOException if an I/O error occurs.
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final File file) throws IOException {
@@ -288,14 +288,14 @@ public ReversedLinesFileReader(final File file) throws IOException {
     }
 
     /**
-     * Creates a ReversedLinesFileReader with default block size of 4KB and the
+     * Constructs a ReversedLinesFileReader with default block size of 4KB and the
      * specified encoding.
      *
      * @param file    the file to be read
      * @param charset the charset to use, null uses the default Charset.
      * @throws IOException if an I/O error occurs.
      * @since 2.5
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final File file, final Charset charset) throws IOException {
@@ -303,7 +303,7 @@ public ReversedLinesFileReader(final File file, final Charset charset) throws IO
     }
 
     /**
-     * Creates a ReversedLinesFileReader with the given block size and encoding.
+     * Constructs a ReversedLinesFileReader with the given block size and encoding.
      *
      * @param file      the file to be read
      * @param blockSize size of the internal buffer (for ideal performance this
@@ -312,7 +312,7 @@ public ReversedLinesFileReader(final File file, final Charset charset) throws IO
      * @param charset  the encoding of the file, null uses the default Charset.
      * @throws IOException if an I/O error occurs.
      * @since 2.3
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final File file, final int blockSize, final Charset charset) throws IOException {
@@ -320,7 +320,7 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
     }
 
     /**
-     * Creates a ReversedLinesFileReader with the given block size and encoding.
+     * Constructs a ReversedLinesFileReader with the given block size and encoding.
      *
      * @param file      the file to be read
      * @param blockSize size of the internal buffer (for ideal performance this
@@ -333,7 +333,7 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
      *                                                      in version 2.2 if the
      *                                                      encoding is not
      *                                                      supported.
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final File file, final int blockSize, final String charsetName) throws IOException {
@@ -341,14 +341,14 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Strin
     }
 
     /**
-     * Creates a ReversedLinesFileReader with default block size of 4KB and the
+     * Constructs a ReversedLinesFileReader with default block size of 4KB and the
      * specified encoding.
      *
      * @param file    the file to be read
      * @param charset the charset to use, null uses the default Charset.
      * @throws IOException if an I/O error occurs.
      * @since 2.7
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final Path file, final Charset charset) throws IOException {
@@ -356,7 +356,7 @@ public ReversedLinesFileReader(final Path file, final Charset charset) throws IO
     }
 
     /**
-     * Creates a ReversedLinesFileReader with the given block size and encoding.
+     * Constructs a ReversedLinesFileReader with the given block size and encoding.
      *
      * @param file      the file to be read
      * @param blockSize size of the internal buffer (for ideal performance this
@@ -365,7 +365,7 @@ public ReversedLinesFileReader(final Path file, final Charset charset) throws IO
      * @param charset  the encoding of the file, null uses the default Charset.
      * @throws IOException if an I/O error occurs.
      * @since 2.7
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final Path file, final int blockSize, final Charset charset) throws IOException {
@@ -425,7 +425,7 @@ public ReversedLinesFileReader(final Path file, final int blockSize, final Chars
     }
 
     /**
-     * Creates a ReversedLinesFileReader with the given block size and encoding.
+     * Constructs a ReversedLinesFileReader with the given block size and encoding.
      *
      * @param file        the file to be read
      * @param blockSize   size of the internal buffer (for ideal performance this
@@ -439,7 +439,7 @@ public ReversedLinesFileReader(final Path file, final int blockSize, final Chars
      *                                                      encoding is not
      *                                                      supported.
      * @since 2.7
-     * @deprecated Use {@link ReaderInputStream#builder()} instead
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
      */
     @Deprecated
     public ReversedLinesFileReader(final Path file, final int blockSize, final String charsetName) throws IOException {
diff --git a/src/main/java/org/apache/commons/io/input/SequenceReader.java b/src/main/java/org/apache/commons/io/input/SequenceReader.java
index 514569d..fc718bd 100644
--- a/src/main/java/org/apache/commons/io/input/SequenceReader.java
+++ b/src/main/java/org/apache/commons/io/input/SequenceReader.java
@@ -21,11 +21,12 @@
 import java.io.IOException;
 import java.io.Reader;
 import java.io.SequenceInputStream;
-import java.io.UncheckedIOException;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Objects;
 
+import org.apache.commons.io.function.Uncheck;
+
 /**
  * Provides the contents of multiple {@link Reader}s in sequence.
  * <p>
@@ -46,11 +47,7 @@ public class SequenceReader extends Reader {
      */
     public SequenceReader(final Iterable<? extends Reader> readers) {
         this.readers = Objects.requireNonNull(readers, "readers").iterator();
-        try {
-            this.reader = nextReader();
-        } catch (final IOException e) {
-            throw new UncheckedIOException(e);
-        }
+        this.reader = Uncheck.get(this::nextReader);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
index 6134046..b42d44c 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
@@ -74,7 +74,7 @@ public class TaggedInputStream extends ProxyInputStream {
     private final Serializable tag = UUID.randomUUID();
 
     /**
-     * Creates a tagging decorator for the given input stream.
+     * Constructs a tagging decorator for the given input stream.
      *
      * @param proxy input stream to be decorated
      */
diff --git a/src/main/java/org/apache/commons/io/input/TaggedReader.java b/src/main/java/org/apache/commons/io/input/TaggedReader.java
index 8d74b60..dc0d57f 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedReader.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedReader.java
@@ -73,7 +73,7 @@ public class TaggedReader extends ProxyReader {
     private final Serializable tag = UUID.randomUUID();
 
     /**
-     * Creates a tagging decorator for the given reader.
+     * Constructs a tagging decorator for the given reader.
      *
      * @param proxy reader to be decorated
      */
diff --git a/src/main/java/org/apache/commons/io/input/TeeInputStream.java b/src/main/java/org/apache/commons/io/input/TeeInputStream.java
index ae1abff..806dec7 100644
--- a/src/main/java/org/apache/commons/io/input/TeeInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/TeeInputStream.java
@@ -51,7 +51,7 @@ public class TeeInputStream extends ProxyInputStream {
     private final boolean closeBranch;
 
     /**
-     * Creates a TeeInputStream that proxies the given {@link InputStream}
+     * Constructs a TeeInputStream that proxies the given {@link InputStream}
      * and copies all read bytes to the given {@link OutputStream}. The given
      * output stream will not be closed when this stream gets closed.
      *
@@ -63,7 +63,7 @@ public TeeInputStream(final InputStream input, final OutputStream branch) {
     }
 
     /**
-     * Creates a TeeInputStream that proxies the given {@link InputStream}
+     * Constructs a TeeInputStream that proxies the given {@link InputStream}
      * and copies all read bytes to the given {@link OutputStream}. The given
      * output stream will be closed when this stream gets closed if the
      * closeBranch parameter is {@code true}.
diff --git a/src/main/java/org/apache/commons/io/input/TeeReader.java b/src/main/java/org/apache/commons/io/input/TeeReader.java
index 354cac9..d857f7b 100644
--- a/src/main/java/org/apache/commons/io/input/TeeReader.java
+++ b/src/main/java/org/apache/commons/io/input/TeeReader.java
@@ -47,7 +47,7 @@ public class TeeReader extends ProxyReader {
     private final boolean closeBranch;
 
     /**
-     * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
+     * Constructs a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
      * {@link Writer}. The given writer will not be closed when this reader gets closed.
      *
      * @param input  reader to be proxied
@@ -58,7 +58,7 @@ public TeeReader(final Reader input, final Writer branch) {
     }
 
     /**
-     * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
+     * Constructs a TeeReader that proxies the given {@link Reader} and copies all read characters to the given
      * {@link Writer}. The given writer will be closed when this reader gets closed if the closeBranch parameter is
      * {@code true}.
      *
diff --git a/src/main/java/org/apache/commons/io/input/TimestampedObserver.java b/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
index df18a6c..c331c86 100644
--- a/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
+++ b/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
@@ -30,12 +30,12 @@
  * </p>
  *
  * <pre>
- * final TimestampedObserver timetampedObserver = new TimestampedObserver();
+ * final TimestampedObserver timestampedObserver = new TimestampedObserver();
  * try (ObservableInputStream inputStream = new ObservableInputStream(...),
- *     timetampedObserver)) {
+ *     timestampedObserver)) {
  *     ...
  * }
- * System.out.printf("IO duration: %s%n", timetampedObserver.getOpenToCloseDuration());
+ * System.out.printf("IO duration: %s%n", timestampedObserver.getOpenToCloseDuration());
  * </pre>
  *
  * @since 2.9.0
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java b/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
index 1a49c9b..c0cd7c5 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
@@ -99,7 +99,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates a buffering character-input stream that uses an input buffer of the specified size.
+     * Constructs a buffering character-input stream that uses an input buffer of the specified size.
      *
      * @param reader     A Reader
      * @param bufferSize Input-buffer size
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java b/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
index 58609c1..d1b00c2 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
@@ -92,7 +92,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates a {@link UncheckedFilterInputStream}.
+     * Constructs a {@link UncheckedFilterInputStream}.
      *
      * @param inputStream the underlying input stream, or {@code null} if this instance is to be created without an
      *        underlying stream.
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java b/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
index 768a16b..4b9fe8e 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
@@ -95,7 +95,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates a new filtered reader.
+     * Constructs a new filtered reader.
      *
      * @param reader a Reader object providing the underlying stream.
      * @throws NullPointerException if {@code reader} is {@code null}.
diff --git a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
index 175776f..7b0ebb2 100644
--- a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
@@ -41,7 +41,7 @@ public class UnixLineEndingInputStream extends InputStream {
     private final boolean lineFeedAtEndOfFile;
 
     /**
-     * Creates an input stream that filters another stream
+     * Constructs an input stream that filters another stream
      *
      * @param inputStream                        The input stream to wrap
      * @param ensureLineFeedAtEndOfFile true to ensure that the file ends with LF
diff --git a/src/main/java/org/apache/commons/io/input/UnsupportedOperationExceptions.java b/src/main/java/org/apache/commons/io/input/UnsupportedOperationExceptions.java
index 9799e99..f850752 100644
--- a/src/main/java/org/apache/commons/io/input/UnsupportedOperationExceptions.java
+++ b/src/main/java/org/apache/commons/io/input/UnsupportedOperationExceptions.java
@@ -29,7 +29,7 @@ class UnsupportedOperationExceptions {
     private static final String MARK_RESET = "mark/reset";
 
     /**
-     * Creates a new instance of UnsupportedOperationException for a {@code mark} method.
+     * Constructs a new instance of UnsupportedOperationException for a {@code mark} method.
      *
      * @return a new instance of UnsupportedOperationException
      */
@@ -39,7 +39,7 @@ static UnsupportedOperationException mark() {
     }
 
     /**
-     * Creates a new instance of UnsupportedOperationException for the given unsupported a {@code method} name.
+     * Constructs a new instance of UnsupportedOperationException for the given unsupported a {@code method} name.
      *
      * @param method A method name
      * @return a new instance of UnsupportedOperationException
@@ -49,7 +49,7 @@ static UnsupportedOperationException method(final String method) {
     }
 
     /**
-     * Creates a new instance of UnsupportedOperationException for a {@code reset} method.
+     * Constructs a new instance of UnsupportedOperationException for a {@code reset} method.
      *
      * @return a new instance of UnsupportedOperationException
      */
diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
index a3f489b..a4fc696 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
@@ -161,7 +161,7 @@ public static Builder builder() {
     private int markedOffset;
 
     /**
-     * Creates a new byte array input stream.
+     * Constructs a new byte array input stream.
      *
      * @param data the buffer
      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
@@ -175,7 +175,7 @@ public UnsynchronizedByteArrayInputStream(final byte[] data) {
     }
 
     /**
-     * Creates a new byte array input stream.
+     * Constructs a new byte array input stream.
      *
      * @param data   the buffer
      * @param offset the offset into the buffer
@@ -196,7 +196,7 @@ public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset) {
     }
 
     /**
-     * Creates a new byte array input stream.
+     * Constructs a new byte array input stream.
      *
      * @param data   the buffer
      * @param offset the offset into the buffer
diff --git a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
index ee908e1..e5ba818 100644
--- a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
@@ -43,7 +43,7 @@ public class WindowsLineEndingInputStream  extends InputStream {
     private final boolean lineFeedAtEndOfFile;
 
     /**
-     * Creates an input stream that filters another stream
+     * Constructs an input stream that filters another stream
      *
      * @param in                        The input stream to wrap
      * @param ensureLineFeedAtEndOfFile true to ensure that the file ends with CRLF
diff --git a/src/main/java/org/apache/commons/io/input/XmlStreamReader.java b/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
index 1e0c2f4..a38ef80 100644
--- a/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
+++ b/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
@@ -201,14 +201,23 @@ public Builder setLenient(final boolean lenient) {
 
     /**
      * Pattern capturing the encoding of the "xml" processing instruction.
+     * <p>
+     * See also the <a href="https://www.w3.org/TR/2008/REC-xml-20081126/#NT-EncName">XML specification</a>.
+     * </p>
      */
-    public static final Pattern ENCODING_PATTERN = Pattern.compile("<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))", Pattern.MULTILINE);
+    public static final Pattern ENCODING_PATTERN = Pattern.compile(
+    // @formatter:off
+            "^<\\?xml\\s+"
+            + "version\\s*=\\s*(?:(?:\"1\\.[0-9]+\")|(?:'1.[0-9]+'))\\s+"
+            + "encoding\\s*=\\s*((?:\"[A-Za-z]([A-Za-z0-9\\._]|-)*\")|(?:'[A-Za-z]([A-Za-z0-9\\\\._]|-)*'))",
+            Pattern.MULTILINE);
+    // @formatter:on
 
     private static final String RAW_EX_1 = "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch";
 
     private static final String RAW_EX_2 = "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM";
 
-    private static final String HTTP_EX_1 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL";
+    private static final String HTTP_EX_1 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be null";
 
     private static final String HTTP_EX_2 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch";
 
@@ -225,7 +234,7 @@ public static Builder builder() {
     }
 
     /**
-     * Gets the charset parameter value, NULL if not present, NULL if httpContentType is NULL.
+     * Gets the charset parameter value, {@code null} if not present, {@code null} if httpContentType is {@code null}.
      *
      * @param httpContentType the HTTP content type
      * @return The content type encoding (upcased)
@@ -245,7 +254,7 @@ static String getContentTypeEncoding(final String httpContentType) {
     }
 
     /**
-     * Gets the MIME type or NULL if httpContentType is NULL.
+     * Gets the MIME type or {@code null} if httpContentType is {@code null}.
      *
      * @param httpContentType the HTTP content type
      * @return The mime content type
@@ -265,7 +274,7 @@ static String getContentTypeMime(final String httpContentType) {
     }
 
     /**
-     * Gets the encoding declared in the <?xml encoding=...?>, NULL if none.
+     * Gets the encoding declared in the <?xml encoding=...?>, {@code null} if none.
      *
      * @param inputStream InputStream to create the reader from.
      * @param guessedEnc  guessed encoding
@@ -855,7 +864,7 @@ private String doRawStream(final BOMInputStream bom, final BOMInputStream pis, f
     /**
      * Gets the default encoding to use if none is set in HTTP content-type, XML prolog and the rules based on content-type are not adequate.
      * <p>
-     * If it is NULL the content-type based rules are used.
+     * If it is {@code null} the content-type based rules are used.
      * </p>
      *
      * @return the default encoding to use.
diff --git a/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java b/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
index 7570329..1402a74 100644
--- a/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
+++ b/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
@@ -46,7 +46,7 @@ public class XmlStreamReaderException extends IOException {
     private final String contentTypeEncoding;
 
     /**
-     * Creates an exception instance if the charset encoding could not be
+     * Constructs an exception instance if the charset encoding could not be
      * determined.
      * <p>
      * Instances of this exception are thrown by the XmlStreamReader.
@@ -63,7 +63,7 @@ public XmlStreamReaderException(final String msg, final String bomEnc,
     }
 
     /**
-     * Creates an exception instance if the charset encoding could not be
+     * Constructs an exception instance if the charset encoding could not be
      * determined.
      * <p>
      * Instances of this exception are thrown by the XmlStreamReader.
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
index 3ac9ae1..2432cd5 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
@@ -41,7 +41,7 @@ public class CircularBufferInputStream extends FilterInputStream {
     private boolean eof;
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size
+     * Constructs a new instance, which filters the given input stream, and uses a reasonable default buffer size
      * ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
      *
      * @param inputStream The input stream, which is being buffered.
@@ -51,7 +51,7 @@ public CircularBufferInputStream(final InputStream inputStream) {
     }
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses the given buffer size.
+     * Constructs a new instance, which filters the given input stream, and uses the given buffer size.
      *
      * @param inputStream The input stream, which is being buffered.
      * @param bufferSize The size of the {@link CircularByteBuffer}, which is used internally.
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
index b65992a..f8e6dd3 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
@@ -34,14 +34,14 @@ public class CircularByteBuffer {
     private int currentNumberOfBytes;
 
     /**
-     * Creates a new instance with a reasonable default buffer size ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
+     * Constructs a new instance with a reasonable default buffer size ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
      */
     public CircularByteBuffer() {
         this(IOUtils.DEFAULT_BUFFER_SIZE);
     }
 
     /**
-     * Creates a new instance with the given buffer size.
+     * Constructs a new instance with the given buffer size.
      *
      * @param size the size of buffer to create
      */
diff --git a/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java b/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
index a2c55f8..086ac9d 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
@@ -29,7 +29,7 @@
 public class PeekableInputStream extends CircularBufferInputStream {
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
+     * Constructs a new instance, which filters the given input stream, and uses a reasonable default buffer size ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
      *
      * @param inputStream The input stream, which is being buffered.
      */
@@ -38,7 +38,7 @@ public PeekableInputStream(final InputStream inputStream) {
     }
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses the given buffer size.
+     * Constructs a new instance, which filters the given input stream, and uses the given buffer size.
      *
      * @param inputStream The input stream, which is being buffered.
      * @param bufferSize The size of the {@link CircularByteBuffer}, which is used internally.
diff --git a/src/main/java/org/apache/commons/io/monitor/FileEntry.java b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
index 63964e9..837f2df 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileEntry.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
@@ -183,7 +183,7 @@ public boolean isExists() {
     }
 
     /**
-     * Creates a new child instance.
+     * Constructs a new child instance.
      * <p>
      * Custom implementations should override this method to return
      * a new instance of the appropriate type.
diff --git a/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java b/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
index 59d655a..4accc56 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
@@ -45,14 +45,14 @@ public class BrokenOutputStream extends OutputStream {
     private final Supplier<IOException> exceptionSupplier;
 
     /**
-     * Creates a new stream that always throws an {@link IOException}.
+     * Constructs a new stream that always throws an {@link IOException}.
      */
     public BrokenOutputStream() {
         this(() -> new IOException("Broken output stream"));
     }
 
     /**
-     * Creates a new stream that always throws the given exception.
+     * Constructs a new stream that always throws the given exception.
      *
      * @param exception the exception to be thrown.
      */
@@ -61,7 +61,7 @@ public BrokenOutputStream(final IOException exception) {
     }
 
     /**
-     * Creates a new stream that always throws an {@link IOException}.
+     * Constructs a new stream that always throws an {@link IOException}.
      *
      * @param exceptionSupplier a supplier for the exception to be thrown.
      * @since 2.12.0
diff --git a/src/main/java/org/apache/commons/io/output/BrokenWriter.java b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
index 9755a53..c56d9a3 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenWriter.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
@@ -43,14 +43,14 @@ public class BrokenWriter extends Writer {
     private final Supplier<IOException> exceptionSupplier;
 
     /**
-     * Creates a new writer that always throws an {@link IOException}.
+     * Constructs a new writer that always throws an {@link IOException}.
      */
     public BrokenWriter() {
         this(() -> new IOException("Broken writer"));
     }
 
     /**
-     * Creates a new writer that always throws the given exception.
+     * Constructs a new writer that always throws the given exception.
      *
      * @param exception the exception to be thrown.
      */
@@ -59,7 +59,7 @@ public BrokenWriter(final IOException exception) {
     }
 
     /**
-     * Creates a new writer that always throws an {@link IOException}.
+     * Constructs a new writer that always throws an {@link IOException}.
      *
      * @param exceptionSupplier a supplier for the exception to be thrown.
      * @since 2.12.0
diff --git a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
index 23474f9..b2b310a 100644
--- a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
@@ -86,7 +86,7 @@ public static InputStream toBufferedInputStream(final InputStream input, final i
     }
 
     /**
-     * Creates a new byte array output stream. The buffer capacity is
+     * Constructs a new byte array output stream. The buffer capacity is
      * initially {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
      */
     public ByteArrayOutputStream() {
@@ -94,7 +94,7 @@ public ByteArrayOutputStream() {
     }
 
     /**
-     * Creates a new byte array output stream, with a buffer capacity of
+     * Constructs a new byte array output stream, with a buffer capacity of
      * the specified size, in bytes.
      *
      * @param size  the initial size
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
index 396fd36..b8e2abe 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
@@ -68,7 +68,7 @@ public static class Builder extends AbstractStreamBuilder<ChunkedOutputStream, B
          *
          * @return a new instance.
          * @throws IOException                   if an I/O error occurs.
-         * @throws UnsupportedOperationException if the origin cannot be converted to an OututStream.
+         * @throws UnsupportedOperationException if the origin cannot be converted to an OutputStream.
          * @see #getOutputStream()
          */
         @Override
@@ -94,7 +94,7 @@ public static Builder builder() {
     private final int chunkSize;
 
     /**
-     * Creates a new stream that uses a chunk size of {@link IOUtils#DEFAULT_BUFFER_SIZE}.
+     * Constructs a new stream that uses a chunk size of {@link IOUtils#DEFAULT_BUFFER_SIZE}.
      *
      * @param stream the stream to wrap
      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
@@ -105,7 +105,7 @@ public ChunkedOutputStream(final OutputStream stream) {
     }
 
     /**
-     * Creates a new stream that uses the specified chunk size.
+     * Constructs a new stream that uses the specified chunk size.
      *
      * @param stream    the stream to wrap
      * @param chunkSize the chunk size to use; must be a positive number.
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
index 37930ac..417f19e 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
@@ -42,7 +42,7 @@ public class ChunkedWriter extends FilterWriter {
     private final int chunkSize;
 
     /**
-     * Creates a new writer that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
+     * Constructs a new writer that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
      * @param writer the writer to wrap
      */
     public ChunkedWriter(final Writer writer) {
@@ -50,7 +50,7 @@ public ChunkedWriter(final Writer writer) {
     }
 
     /**
-     * Creates a new writer that uses the specified chunk size.
+     * Constructs a new writer that uses the specified chunk size.
      *
      * @param writer the writer to wrap
      * @param chunkSize the chunk size to use; must be a positive number.
@@ -65,7 +65,8 @@ public ChunkedWriter(final Writer writer, final int chunkSize) {
     }
 
     /**
-     * writes the data buffer in chunks to the underlying writer
+     * Writes the data buffer in chunks to the underlying writer.
+     *
      * @param data The data
      * @param srcOffset the offset
      * @param length the number of bytes to write
diff --git a/src/main/java/org/apache/commons/io/output/CloseShieldOutputStream.java b/src/main/java/org/apache/commons/io/output/CloseShieldOutputStream.java
index a5c5485..28ff289 100644
--- a/src/main/java/org/apache/commons/io/output/CloseShieldOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/CloseShieldOutputStream.java
@@ -31,7 +31,7 @@
 public class CloseShieldOutputStream extends ProxyOutputStream {
 
     /**
-     * Creates a proxy that shields the given output stream from being closed.
+     * Constructs a proxy that shields the given output stream from being closed.
      *
      * @param outputStream the output stream to wrap
      * @return the created proxy
@@ -42,7 +42,7 @@ public static CloseShieldOutputStream wrap(final OutputStream outputStream) {
     }
 
     /**
-     * Creates a proxy that shields the given output stream from being closed.
+     * Constructs a proxy that shields the given output stream from being closed.
      *
      * @param outputStream underlying output stream
      * @deprecated Using this constructor prevents IDEs from warning if the
diff --git a/src/main/java/org/apache/commons/io/output/CloseShieldWriter.java b/src/main/java/org/apache/commons/io/output/CloseShieldWriter.java
index 8d741ac..f481b75 100644
--- a/src/main/java/org/apache/commons/io/output/CloseShieldWriter.java
+++ b/src/main/java/org/apache/commons/io/output/CloseShieldWriter.java
@@ -31,7 +31,7 @@
 public class CloseShieldWriter extends ProxyWriter {
 
     /**
-     * Creates a proxy that shields the given writer from being closed.
+     * Constructs a proxy that shields the given writer from being closed.
      *
      * @param writer the writer to wrap
      * @return the created proxy
@@ -42,7 +42,7 @@ public static CloseShieldWriter wrap(final Writer writer) {
     }
 
     /**
-     * Creates a proxy that shields the given writer from being closed.
+     * Constructs a proxy that shields the given writer from being closed.
      *
      * @param writer underlying writer
      * @deprecated Using this constructor prevents IDEs from warning if the
diff --git a/src/main/java/org/apache/commons/io/output/CountingOutputStream.java b/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
index c73acb6..5be3721 100644
--- a/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
@@ -58,6 +58,7 @@ protected synchronized void beforeWrite(final int n) {
      * NOTE: This method is an alternative for {@code getCount()}.
      * It was added because that method returns an integer which will
      * result in incorrect count for files over 2GB.
+     * </p>
      *
      * @return the number of bytes accumulated
      * @since 1.3
@@ -67,11 +68,12 @@ public synchronized long getByteCount() {
     }
 
     /**
-     * The number of bytes that have passed through this stream.
+     * Gets the number of bytes that have passed through this stream.
      * <p>
      * NOTE: From v1.3 this method throws an ArithmeticException if the
      * count is greater than can be expressed by an {@code int}.
      * See {@link #getByteCount()} for a method using a {@code long}.
+     * </p>
      *
      * @return the number of bytes accumulated
      * @throws ArithmeticException if the byte count is too large
@@ -90,6 +92,7 @@ public int getCount() {
      * NOTE: This method is an alternative for {@code resetCount()}.
      * It was added because that method returns an integer which will
      * result in incorrect count for files over 2GB.
+     * </p>
      *
      * @return the count previous to resetting
      * @since 1.3
@@ -106,6 +109,7 @@ public synchronized long resetByteCount() {
      * NOTE: From v1.3 this method throws an ArithmeticException if the
      * count is greater than can be expressed by an {@code int}.
      * See {@link #resetByteCount()} for a method using a {@code long}.
+     * </p>
      *
      * @return the count previous to resetting
      * @throws ArithmeticException if the byte count is too large
diff --git a/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java b/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java
index 78bc4b9..d4dc19e 100644
--- a/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java
@@ -48,7 +48,6 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream {
      * </p>
      * <pre>{@code
      * DeferredFileOutputStream s = DeferredFileOutputStream.builder()
-     *   .setPath(path)
      *   .setBufferSize(4096)
      *   .setDirectory(dir)
      *   .setOutputFile(outputFile)
@@ -57,15 +56,19 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream {
      *   .setThreshold(threshold)
      *   .get();}
      * </pre>
+     * <p>
+     * The only super's aspect used us buffer size.
+     * </p>
+     *
      * @since 2.12.0
      */
     public static class Builder extends AbstractStreamBuilder<DeferredFileOutputStream, Builder> {
 
         private int threshold;
-        private File outputFile;
+        private Path outputFile;
         private String prefix;
         private String suffix;
-        private File directory;
+        private Path directory;
 
         public Builder() {
             setBufferSizeDefault(AbstractByteArrayOutputStream.DEFAULT_SIZE);
@@ -92,7 +95,19 @@ public DeferredFileOutputStream get() {
          * @return this
          */
         public Builder setDirectory(final File directory) {
-            this.directory = directory;
+            this.directory = toPath(directory, null);
+            return this;
+        }
+
+        /**
+         * Sets the temporary file directory.
+         *
+         * @param directory Temporary file directory.
+         * @return this
+         * @since 2.14.0
+         */
+        public Builder setDirectory(final Path directory) {
+            this.directory = toPath(directory, null);
             return this;
         }
 
@@ -103,7 +118,19 @@ public Builder setDirectory(final File directory) {
          * @return this
          */
         public Builder setOutputFile(final File outputFile) {
-            this.outputFile = outputFile;
+            this.outputFile = toPath(outputFile, null);
+            return this;
+        }
+
+        /**
+         * Sets the file to which data is saved beyond the threshold.
+         *
+         * @param outputFile The file to which data is saved beyond the threshold.
+         * @return this
+         * @since 2.14.0
+         */
+        public Builder setOutputFile(final Path outputFile) {
+            this.outputFile = toPath(outputFile, null);
             return this;
         }
 
@@ -159,6 +186,14 @@ private static int checkBufferSize(final int initialBufferSize) {
         return initialBufferSize;
     }
 
+    private static Path toPath(final File file, final Supplier<Path> defaultPathSupplier) {
+        return file != null ? file.toPath() : defaultPathSupplier == null ? null : defaultPathSupplier.get();
+    }
+
+    private static Path toPath(final Path file, final Supplier<Path> defaultPathSupplier) {
+        return file != null ? file : defaultPathSupplier == null ? null : defaultPathSupplier.get();
+    }
+
     /**
      * The output stream to which data will be written prior to the threshold being reached.
      */
@@ -230,6 +265,28 @@ private DeferredFileOutputStream(final int threshold, final File outputFile, fin
     }
 
     /**
+     * Constructs an instance of this class which will trigger an event at the specified threshold, and save data either to a file beyond that point.
+     *
+     * @param threshold         The number of bytes at which to trigger an event.
+     * @param outputFile        The file to which data is saved beyond the threshold.
+     * @param prefix            Prefix to use for the temporary file.
+     * @param suffix            Suffix to use for the temporary file.
+     * @param directory         Temporary file directory.
+     * @param initialBufferSize The initial size of the in memory buffer.
+     * @throws IllegalArgumentException if initialBufferSize &lt; 0.
+     */
+    private DeferredFileOutputStream(final int threshold, final Path outputFile, final String prefix, final String suffix, final Path directory,
+            final int initialBufferSize) {
+        super(threshold);
+        this.outputPath = toPath(outputFile, null);
+        this.prefix = prefix;
+        this.suffix = suffix;
+        this.directory = toPath(directory, PathUtils::getTempDirectory);
+        this.memoryOutputStream = new ByteArrayOutputStream(checkBufferSize(initialBufferSize));
+        this.currentOutputStream = memoryOutputStream;
+    }
+
+    /**
      * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a file beyond that point.
      *
      * @param threshold         The number of bytes at which to trigger an event.
@@ -297,20 +354,35 @@ public void close() throws IOException {
     }
 
     /**
-     * Gets either the output file specified in the constructor or the temporary file created or null.
+     * Gets either the output File specified in the constructor or the temporary File created or null.
      * <p>
-     * If the constructor specifying the file is used then it returns that same output file, even when threshold has not been reached.
+     * If the constructor specifying the File is used then it returns that same output File, even when threshold has not been reached.
      * <p>
-     * If constructor specifying a temporary file prefix/suffix is used then the temporary file created once the threshold is reached is returned If the
+     * If constructor specifying a temporary File prefix/suffix is used then the temporary File created once the threshold is reached is returned if the
      * threshold was not reached then {@code null} is returned.
      *
-     * @return The file for this output stream, or {@code null} if no such file exists.
+     * @return The File for this output stream, or {@code null} if no such File exists.
      */
     public File getFile() {
         return outputPath != null ? outputPath.toFile() : null;
     }
 
     /**
+     * Gets either the output Path specified in the constructor or the temporary Path created or null.
+     * <p>
+     * If the constructor specifying the file is used then it returns that same output Path, even when threshold has not been reached.
+     * <p>
+     * If constructor specifying a temporary Path prefix/suffix is used then the temporary Path created once the threshold is reached is returned if the
+     * threshold was not reached then {@code null} is returned.
+     *
+     * @return The Path for this output stream, or {@code null} if no such Path exists.
+     * @since 2.14.0
+     */
+    public Path getPath() {
+        return outputPath;
+    }
+
+    /**
      * Gets the current output stream. This may be memory based or disk based, depending on the current state with respect to the threshold.
      *
      * @return The underlying output stream.
@@ -379,10 +451,6 @@ public InputStream toInputStream() throws IOException {
         return Files.newInputStream(outputPath);
     }
 
-    private Path toPath(final File file, final Supplier<Path> defaultPathSupplier) {
-        return file != null ? file.toPath() : defaultPathSupplier == null ? null : defaultPathSupplier.get();
-    }
-
     /**
      * Writes the data from this output stream to the specified output stream, after it has been closed.
      *
diff --git a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
index f6c0aa8..d8ce429 100644
--- a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
+++ b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
@@ -57,7 +57,7 @@ public class FilterCollectionWriter extends Writer {
     protected final Collection<Writer> writers;
 
     /**
-     * Creates a new filtered collection writer.
+     * Constructs a new filtered collection writer.
      *
      * @param writers Writers to provide the underlying targets.
      */
@@ -66,7 +66,7 @@ protected FilterCollectionWriter(final Collection<Writer> writers) {
     }
 
     /**
-     * Creates a new filtered collection writer.
+     * Constructs a new filtered collection writer.
      *
      * @param writers Writers to provide the underlying targets.
      */
diff --git a/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java b/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java
index 0a4c037..008feb5 100644
--- a/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java
@@ -36,7 +36,7 @@
 public class ProxyCollectionWriter extends FilterCollectionWriter {
 
     /**
-     * Creates a new proxy collection writer.
+     * Constructs a new proxy collection writer.
      *
      * @param writers Writers object to provide the underlying targets.
      */
@@ -45,7 +45,7 @@ public ProxyCollectionWriter(final Collection<Writer> writers) {
     }
 
     /**
-     * Creates a new proxy collection writer.
+     * Constructs a new proxy collection writer.
      *
      * @param writers Writers to provide the underlying targets.
      */
diff --git a/src/main/java/org/apache/commons/io/output/QueueOutputStream.java b/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
index 4281aec..dc2319b 100644
--- a/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
@@ -74,7 +74,7 @@ public QueueOutputStream(final BlockingQueue<Integer> blockingQueue) {
     }
 
     /**
-     * Creates a new QueueInputStream instance connected to this. Writes to this output stream will be visible to the
+     * Constructs a new QueueInputStream instance connected to this. Writes to this output stream will be visible to the
      * input stream.
      *
      * @return QueueInputStream connected to this stream
diff --git a/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java b/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
index eeb23a0..72d0ec9 100644
--- a/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
@@ -70,7 +70,7 @@ public class TaggedOutputStream extends ProxyOutputStream {
     private final Serializable tag = UUID.randomUUID();
 
     /**
-     * Creates a tagging decorator for the given output stream.
+     * Constructs a tagging decorator for the given output stream.
      *
      * @param proxy output stream to be decorated
      */
diff --git a/src/main/java/org/apache/commons/io/output/TaggedWriter.java b/src/main/java/org/apache/commons/io/output/TaggedWriter.java
index eb6f79d..828d9ae 100644
--- a/src/main/java/org/apache/commons/io/output/TaggedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/TaggedWriter.java
@@ -70,7 +70,7 @@ public class TaggedWriter extends ProxyWriter {
     private final Serializable tag = UUID.randomUUID();
 
     /**
-     * Creates a tagging decorator for the given writer.
+     * Constructs a tagging decorator for the given writer.
      *
      * @param proxy writer to be decorated
      */
diff --git a/src/main/java/org/apache/commons/io/output/TeeWriter.java b/src/main/java/org/apache/commons/io/output/TeeWriter.java
index e373bc1..2db4e6a 100644
--- a/src/main/java/org/apache/commons/io/output/TeeWriter.java
+++ b/src/main/java/org/apache/commons/io/output/TeeWriter.java
@@ -32,7 +32,7 @@
 public class TeeWriter extends ProxyCollectionWriter {
 
     /**
-     * Creates a new filtered collection writer.
+     * Constructs a new filtered collection writer.
      *
      * @param writers Writers to provide the underlying targets.
      */
@@ -41,7 +41,7 @@ public TeeWriter(final Collection<Writer> writers) {
     }
 
     /**
-     * Creates a new filtered collection writer.
+     * Constructs a new filtered collection writer.
      *
      * @param writers Writers to provide the underlying targets.
      */
diff --git a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
index 9153f0b..1d8a3a0 100644
--- a/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ThresholdingOutputStream.java
@@ -118,6 +118,7 @@ public void close() throws IOException {
         } catch (final IOException ignored) {
             // ignore
         }
+        // TODO for 4.0: Replace with getOutputStream()
         getStream().close();
     }
 
@@ -129,11 +130,12 @@ public void close() throws IOException {
     @SuppressWarnings("resource") // the underlying stream is managed by a subclass.
     @Override
     public void flush() throws IOException {
+        // TODO for 4.0: Replace with getOutputStream()
         getStream().flush();
     }
 
     /**
-     * Returns the number of bytes that have been written to this output stream.
+     * Gets the number of bytes that have been written to this output stream.
      *
      * @return The number of bytes written.
      */
@@ -142,19 +144,32 @@ public long getByteCount() {
     }
 
     /**
-     * Returns the underlying output stream, to which the corresponding {@link OutputStream} methods in this class will
+     * Gets the underlying output stream, to which the corresponding {@link OutputStream} methods in this class will
      * ultimately delegate.
      *
      * @return The underlying output stream.
-     *
      * @throws IOException if an error occurs.
+     * @deprecated Use {@link #getOutputStream()}.
      */
+    @Deprecated
     protected OutputStream getStream() throws IOException {
+        return getOutputStream();
+    }
+
+    /**
+     * Gets the underlying output stream, to which the corresponding {@link OutputStream} methods in this class will
+     * ultimately delegate.
+     *
+     * @return The underlying output stream.
+     * @throws IOException if an error occurs.
+     * @since 2.14.0
+     */
+    protected OutputStream getOutputStream() throws IOException {
         return outputStreamGetter.apply(this);
     }
 
     /**
-     * Returns the threshold, in bytes, at which an event will be triggered.
+     * Gets the threshold, in bytes, at which an event will be triggered.
      *
      * @return The threshold point, in bytes.
      */
@@ -163,7 +178,7 @@ public int getThreshold() {
     }
 
     /**
-     * Determines whether or not the configured threshold has been exceeded for this output stream.
+     * Tests whether or not the configured threshold has been exceeded for this output stream.
      *
      * @return {@code true} if the threshold has been reached; {@code false} otherwise.
      */
@@ -212,6 +227,7 @@ protected void thresholdReached() throws IOException {
     @Override
     public void write(final byte[] b) throws IOException {
         checkThreshold(b.length);
+        // TODO for 4.0: Replace with getOutputStream()
         getStream().write(b);
         written += b.length;
     }
@@ -229,6 +245,7 @@ public void write(final byte[] b) throws IOException {
     @Override
     public void write(final byte[] b, final int off, final int len) throws IOException {
         checkThreshold(len);
+        // TODO for 4.0: Replace with getOutputStream()
         getStream().write(b, off, len);
         written += len;
     }
@@ -244,6 +261,7 @@ public void write(final byte[] b, final int off, final int len) throws IOExcepti
     @Override
     public void write(final int b) throws IOException {
         checkThreshold(1);
+        // TODO for 4.0: Replace with getOutputStream()
         getStream().write(b);
         written++;
     }
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedAppendable.java b/src/main/java/org/apache/commons/io/output/UncheckedAppendable.java
index 8abda70..e606db4 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedAppendable.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedAppendable.java
@@ -31,7 +31,7 @@
 public interface UncheckedAppendable extends Appendable {
 
     /**
-     * Creates a new instance on the given Appendable.
+     * Constructs a new instance on the given Appendable.
      *
      * @param appendable The Appendable to uncheck.
      * @return a new instance.
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
index c1e9b7a..91f115d 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
@@ -91,7 +91,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates an output stream filter built on top of the specified underlying output stream.
+     * Constructs an output stream filter built on top of the specified underlying output stream.
      *
      * @param outputStream the underlying output stream, or {@code null} if this instance is to be created without an
      *        underlying stream.
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java b/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
index 14a9822..f152f48 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
@@ -91,7 +91,7 @@ public static Builder builder() {
     }
 
     /**
-     * Creates a new filtered writer.
+     * Constructs a new filtered writer.
      *
      * @param writer a Writer object providing the underlying stream.
      * @throws NullPointerException if {@code writer} is {@code null}.
diff --git a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
index 15f2fb7..232e718 100644
--- a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
@@ -131,7 +131,7 @@ public static InputStream toBufferedInputStream(final InputStream input, final i
     }
 
     /**
-     * Creates a new byte array output stream. The buffer capacity is initially
+     * Constructs a new byte array output stream. The buffer capacity is initially
      *
      * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
@@ -142,11 +142,11 @@ public UnsynchronizedByteArrayOutputStream() {
     }
 
     /**
-     * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
+     * Constructs a new byte array output stream, with a buffer capacity of the specified size, in bytes.
      *
      * @param size the initial size
      * @throws IllegalArgumentException if size is negative
-     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
+     * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}. Will be private in 3.0.0.
      */
     @Deprecated
     public UnsynchronizedByteArrayOutputStream(final int size) {
diff --git a/src/site/xdoc/download_io.xml b/src/site/xdoc/download_io.xml
index 7432635..1d7ef8e 100644
--- a/src/site/xdoc/download_io.xml
+++ b/src/site/xdoc/download_io.xml
@@ -113,32 +113,32 @@
       </p>
     </subsection>
     </section>
-    <section name="Apache Commons IO 2.13.0 (requires Java 8)">
+    <section name="Apache Commons IO 2.14.0 (requires Java 8)">
       <subsection name="Binaries">
         <table>
           <tr>
-              <td><a href="[preferred]/commons/io/binaries/commons-io-2.13.0-bin.tar.gz">commons-io-2.13.0-bin.tar.gz</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.13.0-bin.tar.gz.sha512">sha512</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.13.0-bin.tar.gz.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/io/binaries/commons-io-2.14.0-bin.tar.gz">commons-io-2.14.0-bin.tar.gz</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.14.0-bin.tar.gz.sha512">sha512</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.14.0-bin.tar.gz.asc">pgp</a></td>
           </tr>
           <tr>
-              <td><a href="[preferred]/commons/io/binaries/commons-io-2.13.0-bin.zip">commons-io-2.13.0-bin.zip</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.13.0-bin.zip.sha512">sha512</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.13.0-bin.zip.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/io/binaries/commons-io-2.14.0-bin.zip">commons-io-2.14.0-bin.zip</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.14.0-bin.zip.sha512">sha512</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/binaries/commons-io-2.14.0-bin.zip.asc">pgp</a></td>
           </tr>
         </table>
       </subsection>
       <subsection name="Source">
         <table>
           <tr>
-              <td><a href="[preferred]/commons/io/source/commons-io-2.13.0-src.tar.gz">commons-io-2.13.0-src.tar.gz</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.13.0-src.tar.gz.sha512">sha512</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.13.0-src.tar.gz.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/io/source/commons-io-2.14.0-src.tar.gz">commons-io-2.14.0-src.tar.gz</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.14.0-src.tar.gz.sha512">sha512</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.14.0-src.tar.gz.asc">pgp</a></td>
           </tr>
           <tr>
-              <td><a href="[preferred]/commons/io/source/commons-io-2.13.0-src.zip">commons-io-2.13.0-src.zip</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.13.0-src.zip.sha512">sha512</a></td>
-              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.13.0-src.zip.asc">pgp</a></td>
+              <td><a href="[preferred]/commons/io/source/commons-io-2.14.0-src.zip">commons-io-2.14.0-src.zip</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.14.0-src.zip.sha512">sha512</a></td>
+              <td><a href="https://downloads.apache.org/commons/io/source/commons-io-2.14.0-src.zip.asc">pgp</a></td>
           </tr>
         </table>
       </subsection>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
index 1d311b0..1723036 100644
--- a/src/site/xdoc/index.xml
+++ b/src/site/xdoc/index.xml
@@ -16,247 +16,143 @@
 limitations under the License.
 -->
 <document>
- <properties>
-  <title>Commons IO Overview</title>
-  <author email="[email protected]">Commons Documentation Team</author>
- </properties>
-<body>
-<!-- ================================================== -->
-<section name="Apache Commons IO">
-<p>
-Apache Commons IO is a library of utilities to assist with developing IO functionality.
-</p>
-<p>
-There are six main areas included:
-</p>
-<ul>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/package-summary.html">io</a>
-		- This package defines utility classes for working with streams, readers, writers and files.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/comparator/package-summary.html">comparator</a>
-		- This package provides various Comparator implementations for Files.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/file/package-summary.html">file</a>
-		- This package provides extensions in the realm of java.nio.file.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/filefilter/package-summary.html">filefilter</a>
-		- This package defines an interface (IOFileFilter) that combines both FileFilter and FilenameFilter.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/function/package-summary.html">function</a>
-		- This package defines IO-only related functional interfaces for lambda expressions and method references.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/input/package-summary.html">input</a>
-		- This package provides implementations of input classes, such as InputStream and Reader.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/input/buffer/package-summary.html">input.buffer</a>
-		- This package provides implementations of buffered input classes, such as CircularBufferInputStream and PeekableInputStream.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/monitor/package-summary.html">monitor</a>
-		- This package provides a component for monitoring file system events (directory and file create, update and delete events).
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/output/package-summary.html">output</a>
-		- This package provides implementations of output classes, such as OutputStream and Writer.
-	</li>
-	<li>
-		<a
-			href="apidocs/index.html?org/apache/commons/io/serialization/package-summary.html">serialization</a>
-		- This package provides a framework for controlling the deserialization of classes.
-	</li>
-</ul>
-</section>
-<!-- ================================================== -->
-<section name="Releases">
+  <properties>
+    <title>Commons IO Overview</title>
+    <author email="[email protected]">Commons Documentation Team</author>
+  </properties>
+  <body>
+    <!-- ================================================== -->
+    <section name="Apache Commons IO">
+      <p>
+        Apache Commons IO is a library of utilities to assist with developing IO functionality.
+      </p>
+      <p>
+        There are six main areas included:
+      </p>
+      <ul>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/package-summary.html">io</a>
+          - This package defines utility classes for working with streams, readers, writers and files.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/build/package-summary.html">build</a>
+          - This package provides classes to implement IO builders.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/charset/package-summary.html">charset</a>
+          - This package provides classes to work with code from <code>java.nio.charset</code>.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/comparator/package-summary.html">comparator</a>
+          - This package provides various Comparator implementations for Files and Paths.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/file/package-summary.html">file</a>
+          - This package provides extensions in the realm of java.nio.file.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/file/attribute/package-summary.html">file.attribute</a>
+          - This package provides help using <code>java.nio.file.attribute</code> types.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/file/spi/package-summary.html">file SPI</a>
+          - This package provides extensions in the realm of <code>java.nio.file.spi</code>.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/filefilter/package-summary.html">filefilter</a>
+          - This package defines an interface (IOFileFilter) that combines both FileFilter and FilenameFilter.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/function/package-summary.html">function</a>
+          - This package defines IO-only related functional interfaces for lambda expressions and method references.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/input/package-summary.html">input</a>
+          - This package provides implementations of input classes, such as InputStream and Reader.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/input/buffer/package-summary.html">input.buffer</a>
+          - This package provides implementations of buffered input classes, such as CircularBufferInputStream and PeekableInputStream.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/monitor/package-summary.html">monitor</a>
+          - This package provides a component for monitoring file system events (directory and file create, update and delete events).
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/output/package-summary.html">output</a>
+          - This package provides implementations of output classes, such as OutputStream and Writer.
+        </li>
+        <li>
+          <a href="apidocs/index.html?org/apache/commons/io/serialization/package-summary.html">serialization</a>
+          - This package provides a framework for controlling the deserialization of classes.
+        </li>
+      </ul>
+    </section>
+    <!-- ================================================== -->
+    <section name="Releases">
 
-    <subsection name="Commons IO 2.13.0 (requires Java 8)">
+      <subsection name="Latest Release (Java 8 and up)">
         <p>
-            Commons IO 2.13.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
+          Commons IO 2.13.0 requires a minimum of Java 8 -
+          <a href="https://commons.apache.org/io/download_io.cgi">Download now</a>
+          .
         </p>
         <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="apidocs/index.html">Javadoc API documents</a>
+          View the
+          <a href="changes-report.html">Release Notes</a>
+          and
+          <a href="apidocs/index.html">Javadoc API documents</a>
+          .
         </p>
-    </subsection>
+      </subsection>
 
-    <subsection name="Commons IO 2.12.0 (requires Java 8)">
+      <subsection name="Previous Releases">
         <p>
-            Commons IO 2.12.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
+          See the
+          <a href="https://archive.apache.org/dist/commons/io/">download archive</a>
+          and
+          <a href="https://javadoc.io/doc/commons-io/commons-io/">Javadoc archive</a>
+          .
         </p>
         <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.12.0/index.html">Javadoc API documents</a>
+          The Java platform requirements are:
         </p>
-    </subsection>
+        <ul>
+          <li>Version 2.14.0 requires Java 8.</li>
+          <li>Version 2.13.0 requires Java 8.</li>
+          <li>Version 2.12.0 requires Java 8.</li>
+          <li>Version 2.11.0 requires Java 8.</li>
+          <li>Version 2.10.0 requires Java 8.</li>
+          <li>Version 2.9.0 requires Java 8.</li>
+          <li>Version 2.8.0 requires Java 8.</li>
+          <li>Version 2.7 requires Java 8.</li>
+          <li>Version 2.6 requires Java 7.</li>
+          <li>Version 2.5 requires Java 6.</li>
+          <li>Version 2.4 requires Java 6.</li>
+          <li>Version 2.3 requires Java 6.</li>
+          <li>Version 2.2 requires Java 5.</li>
+        </ul>
+      </subsection>
 
-    <subsection name="Commons IO 2.11.0 (requires Java 8)">
-        <p>
-            Commons IO 2.11.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.11.0/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.10.0 (requires Java 8)">
-        <p>
-            Commons IO 2.10.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.10.0/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.9.0 (requires Java 8)">
-        <p>
-            Commons IO 2.9.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.9.0/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.8.0 (requires Java 8)">
-        <p>
-            Commons IO 2.8.0 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.8.0/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.7 (requires Java 8)">
-        <p>
-            Commons IO 2.7 requires a minimum of Java 8 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="changes-report.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.7/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.6 (requires Java 7)">
-        <p>
-            Commons IO 2.6 requires a minimum of Java 7 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="upgradeto2_6.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.6/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.5 (requires Java 6)">
-        <p>
-            Commons IO 2.5 requires a minimum of Java 6 -
-            <a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-        </p>
-        <p>
-            View the
-            <a href="upgradeto2_5.html">Release Notes</a>
-            and
-            <a href="https://javadoc.io/doc/commons-io/commons-io/2.5/index.html">Javadoc API documents</a>
-        </p>
-    </subsection>
-
-    <subsection name="Commons IO 2.4 (requires Java 6)">
-<p>
-    Commons IO 2.4 requires a minimum of JDK 1.6 -
-<a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-</p>
-<p>
-View the <a href="upgradeto2_4.html">Release Notes</a> and
-<a href="https://javadoc.io/doc/commons-io/commons-io/2.4/index.html">Javadoc API documents</a>
-</p>
-</subsection>
-
-    <subsection name="Commons IO 2.3 (requires Java 6)">
-<p>
-    Commons IO 2.3 requires a minimum of JDK 1.6 -
-<a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-</p>
-<p>
-View the <a href="upgradeto2_3.html">Release Notes</a> and
-<a href="https://javadoc.io/doc/commons-io/commons-io/2.3/index.html">Javadoc API documents</a>
-</p>
-</subsection>
-
-<subsection name="Commons IO 2.2 (requires Java 5)">
-<p>
-Commons IO 2.2 requires a minimum of JDK 1.5 -
-<a href="https://commons.apache.org/io/download_io.cgi">Download now!</a>
-</p> 
-<p>
-View the <a href="upgradeto2_2.html">Release Notes</a> and
-<a href="https://javadoc.io/doc/commons-io/commons-io/2.2/index.html">Javadoc API documents</a>
-</p> 
-</subsection>
-
-<subsection name="Older Releases">
-<p>
-For previous releases, see the <a href="https://archive.apache.org/dist/commons/io/">Apache Archive</a>
-and <a href="https://javadoc.io/doc/commons-io/commons-io/">Javadoc Archive</a>
-</p> 
-</subsection>
-
-</section>
-<!-- ================================================== -->
-<section name="Support">
-<p>
-The <a href="mail-lists.html">commons mailing lists</a> act as the main support forum.
-The user list is suitable for most library usage queries.
-The dev list is intended for the development discussion.
-Please remember that the lists are shared between all commons components,
-so prefix your email by [io].
-</p>
-<p>
-Issues may be reported via <a href="issue-tracking.html">ASF JIRA</a>.
-Please read the instructions carefully to submit a useful bug report or enhancement request.
-</p>
-</section>
-<!-- ================================================== -->
-</body>
+    </section>
+    <!-- ================================================== -->
+    <section name="Support">
+      <p>
+        The
+        <a href="mail-lists.html">commons mailing lists</a>
+        act as the main support forum.
+        The user list is suitable for most library usage queries.
+        The dev list is intended for the development discussion.
+        Please remember that the lists are shared between all commons components,
+        so prefix your email by [io].
+      </p>
+      <p>
+        Issues may be reported via
+        <a href="issue-tracking.html">ASF JIRA</a>
+        .
+        Please read the instructions carefully to submit a useful bug report or enhancement request.
+      </p>
+    </section>
+    <!-- ================================================== -->
+  </body>
 </document>
diff --git a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryBaseTest.java b/src/test/java/org/apache/commons/io/AbstractFileUtilsDeleteDirectoryTest.java
similarity index 98%
rename from src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryBaseTest.java
rename to src/test/java/org/apache/commons/io/AbstractFileUtilsDeleteDirectoryTest.java
index 48621ad..da77488 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryBaseTest.java
+++ b/src/test/java/org/apache/commons/io/AbstractFileUtilsDeleteDirectoryTest.java
@@ -30,7 +30,7 @@
 /**
  * Test cases for FileUtils.deleteDirectory() method.
  */
-public abstract class FileUtilsDeleteDirectoryBaseTest {
+public abstract class AbstractFileUtilsDeleteDirectoryTest {
     @TempDir
     public File top;
 
diff --git a/src/test/java/org/apache/commons/io/DirectoryWalkerTest.java b/src/test/java/org/apache/commons/io/DirectoryWalkerTest.java
index b90a7ed..5ac3aa9 100644
--- a/src/test/java/org/apache/commons/io/DirectoryWalkerTest.java
+++ b/src/test/java/org/apache/commons/io/DirectoryWalkerTest.java
@@ -47,7 +47,7 @@ static class TestCancelWalker extends DirectoryWalker<File> {
         private final String cancelFileName;
         private final boolean suppressCancel;
 
-        TestCancelWalker(final String cancelFileName,final boolean suppressCancel) {
+        TestCancelWalker(final String cancelFileName, final boolean suppressCancel) {
             this.cancelFileName = cancelFileName;
             this.suppressCancel = suppressCancel;
         }
diff --git a/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java b/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
index bb47ae2..b507451 100644
--- a/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
+++ b/src/test/java/org/apache/commons/io/FileCleaningTrackerTest.java
@@ -29,6 +29,8 @@
 import java.io.RandomAccessFile;
 import java.lang.ref.ReferenceQueue;
 import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -44,6 +46,7 @@
 public class FileCleaningTrackerTest extends AbstractTempDirTest {
 
     private File testFile;
+    private Path testPath;
 
     private FileCleaningTracker theInstance;
 
@@ -63,9 +66,18 @@ private void pauseForDeleteToComplete(File file) {
         }
     }
 
+    private void pauseForDeleteToComplete(Path file) {
+        int count = 0;
+        while (Files.exists(file) && count++ < 40) {
+            TestUtils.sleepQuietly(500L);
+            file = Paths.get(file.toAbsolutePath().toString());
+        }
+    }
+
     @BeforeEach
     public void setUp() {
         testFile = new File(tempDirFile, "file-test.txt");
+        testPath = testFile.toPath();
         theInstance = newInstance();
     }
 
@@ -100,27 +112,7 @@ public void tearDown() {
     }
 
     @Test
-    public void testFileCleanerDirectory() throws Exception {
-        TestUtils.createFile(testFile, 100);
-        assertTrue(testFile.exists());
-        assertTrue(tempDirFile.exists());
-
-        Object obj = new Object();
-        assertEquals(0, theInstance.getTrackCount());
-        theInstance.track(tempDirFile, obj);
-        assertEquals(1, theInstance.getTrackCount());
-
-        obj = null;
-
-        waitUntilTrackCount();
-
-        assertEquals(0, theInstance.getTrackCount());
-        assertTrue(testFile.exists());  // not deleted, as dir not empty
-        assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
-    }
-
-    @Test
-    public void testFileCleanerDirectory_ForceStrategy() throws Exception {
+    public void testFileCleanerDirectory_ForceStrategy_FileSource() throws Exception {
         if (!testFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile
                     + " as the parent directory does not exist");
@@ -148,6 +140,34 @@ public void testFileCleanerDirectory_ForceStrategy() throws Exception {
     }
 
     @Test
+    public void testFileCleanerDirectory_ForceStrategy_PathSource() throws Exception {
+        if (!Files.exists(testPath.getParent())) {
+            throw new IOException("Cannot create file " + testPath
+                    + " as the parent directory does not exist");
+        }
+        try (BufferedOutputStream output =
+                new BufferedOutputStream(Files.newOutputStream(testPath))) {
+            TestUtils.generateTestData(output, 100);
+        }
+        assertTrue(Files.exists(testPath));
+        assertTrue(Files.exists(tempDirPath));
+
+        Object obj = new Object();
+        assertEquals(0, theInstance.getTrackCount());
+        theInstance.track(tempDirPath, obj, FileDeleteStrategy.FORCE);
+        assertEquals(1, theInstance.getTrackCount());
+
+        obj = null;
+
+        waitUntilTrackCount();
+        pauseForDeleteToComplete(testPath.getParent());
+
+        assertEquals(0, theInstance.getTrackCount());
+        assertFalse(Files.exists(testPath), showFailures());
+        assertFalse(Files.exists(testPath.getParent()), showFailures());
+    }
+
+    @Test
     public void testFileCleanerDirectory_NullStrategy() throws Exception {
         TestUtils.createFile(testFile, 100);
         assertTrue(testFile.exists());
@@ -168,6 +188,46 @@ public void testFileCleanerDirectory_NullStrategy() throws Exception {
     }
 
     @Test
+    public void testFileCleanerDirectoryFileSource() throws Exception {
+        TestUtils.createFile(testFile, 100);
+        assertTrue(testFile.exists());
+        assertTrue(tempDirFile.exists());
+
+        Object obj = new Object();
+        assertEquals(0, theInstance.getTrackCount());
+        theInstance.track(tempDirFile, obj);
+        assertEquals(1, theInstance.getTrackCount());
+
+        obj = null;
+
+        waitUntilTrackCount();
+
+        assertEquals(0, theInstance.getTrackCount());
+        assertTrue(testFile.exists());  // not deleted, as dir not empty
+        assertTrue(testFile.getParentFile().exists());  // not deleted, as dir not empty
+    }
+
+    @Test
+    public void testFileCleanerDirectoryPathSource() throws Exception {
+        TestUtils.createFile(testPath, 100);
+        assertTrue(Files.exists(testPath));
+        assertTrue(Files.exists(tempDirPath));
+
+        Object obj = new Object();
+        assertEquals(0, theInstance.getTrackCount());
+        theInstance.track(tempDirPath, obj);
+        assertEquals(1, theInstance.getTrackCount());
+
+        obj = null;
+
+        waitUntilTrackCount();
+
+        assertEquals(0, theInstance.getTrackCount());
+        assertTrue(Files.exists(testPath));  // not deleted, as dir not empty
+        assertTrue(Files.exists(testPath.getParent()));  // not deleted, as dir not empty
+    }
+
+    @Test
     public void testFileCleanerExitWhenFinished_NoTrackAfter() {
         assertFalse(theInstance.exitWhenFinished);
         theInstance.exitWhenFinished();
@@ -187,11 +247,13 @@ public void testFileCleanerExitWhenFinished1() throws Exception {
         final String path = testFile.getPath();
 
         assertFalse(testFile.exists(), "1-testFile exists: " + testFile);
-        RandomAccessFile r = createRandomAccessFile();
+
+        // Do NOT used a try-with-resources statement here or the test will fail.
+        RandomAccessFile raf = createRandomAccessFile();
         assertTrue(testFile.exists(), "2-testFile exists");
 
         assertEquals(0, theInstance.getTrackCount(), "3-Track Count");
-        theInstance.track(path, r);
+        theInstance.track(path, raf);
         assertEquals(1, theInstance.getTrackCount(), "4-Track Count");
         assertFalse(theInstance.exitWhenFinished, "5-exitWhenFinished");
         assertTrue(theInstance.reaper.isAlive(), "6-reaper.isAlive");
@@ -201,9 +263,9 @@ public void testFileCleanerExitWhenFinished1() throws Exception {
         assertTrue(theInstance.exitWhenFinished, "8-exitWhenFinished");
         assertTrue(theInstance.reaper.isAlive(), "9-reaper.isAlive");
 
-        r.close();
+        raf.close();
         testFile = null;
-        r = null;
+        raf = null;
 
         waitUntilTrackCount();
         pauseForDeleteToComplete(new File(path));
diff --git a/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java b/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
index 138ebdc..2e28f36 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsCleanDirectoryTest.java
@@ -17,8 +17,8 @@
 package org.apache.commons.io;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.io.File;
@@ -123,9 +123,7 @@ public void testThrowsOnNullList() throws Exception {
 
         try {
             // cleanDirectory calls forceDelete
-            FileUtils.cleanDirectory(tempDirFile);
-            fail("expected IOException");
-        } catch (final IOException e) {
+            final IOException e = assertThrows(IOException.class, () -> FileUtils.cleanDirectory(tempDirFile));
             assertEquals("Unknown I/O error listing contents of directory: " + tempDirFile.getAbsolutePath(), e.getMessage());
         } finally {
             chmod(tempDirFile, 755, false);
diff --git a/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTest.java b/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTest.java
index 8e7194c..38db764 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsCleanSymlinksTest.java
@@ -192,16 +192,16 @@ public void testIdentifiesBrokenSymlinkFile() throws Exception {
             return;
         }
 
-        final File noexistFile = new File(top, "noexist");
+        final File nonExistentFile = new File(top, "non-existent");
         final File symlinkFile = new File(top, "fakeinner");
         final File badSymlinkInPathFile = new File(symlinkFile, "fakeinner");
-        final File noexistParentFile = new File("noexist", "file");
+        final File nonExistentParentFile = new File("non-existent", "file");
 
-        assertTrue(setupSymlink(noexistFile, symlinkFile));
+        assertTrue(setupSymlink(nonExistentFile, symlinkFile));
 
         assertTrue(FileUtils.isSymlink(symlinkFile));
-        assertFalse(FileUtils.isSymlink(noexistFile));
-        assertFalse(FileUtils.isSymlink(noexistParentFile));
+        assertFalse(FileUtils.isSymlink(nonExistentFile));
+        assertFalse(FileUtils.isSymlink(nonExistentParentFile));
         assertFalse(FileUtils.isSymlink(badSymlinkInPathFile));
     }
 
diff --git a/src/test/java/org/apache/commons/io/FileUtilsCopyToFileTest.java b/src/test/java/org/apache/commons/io/FileUtilsCopyToFileTest.java
index 9994ef0..ac38785 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsCopyToFileTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsCopyToFileTest.java
@@ -60,7 +60,7 @@ public boolean isClosed() {
     @BeforeEach
     public void setUp() throws Exception {
         testFile = new File(temporaryFolder, "file1-test.txt");
-        if(!testFile.getParentFile().exists()) {
+        if (!testFile.getParentFile().exists()) {
             throw new IOException("Cannot create file " + testFile +
                 " as the parent directory does not exist");
         }
diff --git a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryLinuxTest.java b/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryLinuxTest.java
index c4e5ccb..60600ff 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryLinuxTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryLinuxTest.java
@@ -17,8 +17,8 @@
 package org.apache.commons.io;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.io.File;
@@ -35,7 +35,7 @@
  * Tests {@link FileUtils}.
  */
 @DisabledOnOs({OS.WINDOWS, OS.MAC})
-public class FileUtilsDeleteDirectoryLinuxTest extends FileUtilsDeleteDirectoryBaseTest {
+public class FileUtilsDeleteDirectoryLinuxTest extends AbstractFileUtilsDeleteDirectoryTest {
 
     /** Only runs on Linux. */
     private boolean chmod(final File file, final int mode, final boolean recurse) throws InterruptedException {
@@ -87,11 +87,8 @@ public void testThrowsOnCannotDeleteFile() throws Exception {
 
         try {
             // deleteDirectory calls forceDelete
-            FileUtils.deleteDirectory(nested);
-            fail("expected IOException");
-        } catch (final IOException e) {
-            final IOExceptionList list = (IOExceptionList) e;
-            assertTrue(list.getCause(0).getMessage().endsWith("Cannot delete file: " + file.getAbsolutePath()));
+            final IOExceptionList ioExceptionList = (IOExceptionList) assertThrows(IOException.class, () -> FileUtils.deleteDirectory(nested));
+            assertTrue(ioExceptionList.getCause(0).getMessage().endsWith("Cannot delete file: " + file.getAbsolutePath()));
         } finally {
             chmod(nested, 755, false);
             FileUtils.deleteDirectory(nested);
@@ -110,9 +107,7 @@ public void testThrowsOnNullList() throws Exception {
 
         try {
             // cleanDirectory calls forceDelete
-            FileUtils.deleteDirectory(nested);
-            fail("expected IOException");
-        } catch (final IOException e) {
+            final IOException e = assertThrows(IOException.class, () -> FileUtils.deleteDirectory(nested));
             assertEquals("Unknown I/O error listing contents of directory: " + nested.getAbsolutePath(), e.getMessage());
         } finally {
             chmod(nested, 755, false);
diff --git a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryWindowsTest.java b/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryWindowsTest.java
index 521ec6e..5111bf9 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryWindowsTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsDeleteDirectoryWindowsTest.java
@@ -30,7 +30,7 @@
  * Requires Windows admin karma or you get "You do not have sufficient privilege to perform this operation."
  */
 @EnabledOnOs(OS.WINDOWS)
-public class FileUtilsDeleteDirectoryWindowsTest extends FileUtilsDeleteDirectoryBaseTest {
+public class FileUtilsDeleteDirectoryWindowsTest extends AbstractFileUtilsDeleteDirectoryTest {
 
     @Override
     protected boolean setupSymlink(final File res, final File link) throws Exception {
diff --git a/src/test/java/org/apache/commons/io/FileUtilsTest.java b/src/test/java/org/apache/commons/io/FileUtilsTest.java
index 4f2bfeb..4d976c4 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsTest.java
@@ -2101,7 +2101,7 @@ public boolean renameTo(final File dest) {
     public void testMoveDirectory_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectory(null, new File("foo")));
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectory(new File("foo"), null));
-        assertThrows(FileNotFoundException.class, () -> FileUtils.moveDirectory(new File("nonexistant"), new File("foo")));
+        assertThrows(FileNotFoundException.class, () -> FileUtils.moveDirectory(new File("non-existent"), new File("foo")));
 
         final File testFile = new File(tempDirFile, "testMoveDirectoryFile");
         if (!testFile.getParentFile().exists()) {
@@ -2201,8 +2201,8 @@ public void testMoveDirectoryToDirectory_Errors() throws Exception {
         }
         assertThrows(IOException.class, () -> FileUtils.moveDirectoryToDirectory(testFile1, testFile2, true));
 
-        final File nonexistant = new File(tempDirFile, "testMoveFileNonExistant");
-        assertThrows(IOException.class, () -> FileUtils.moveDirectoryToDirectory(testFile1, nonexistant, false));
+        final File nonExistent = new File(tempDirFile, "testMoveFileNonExistent");
+        assertThrows(IOException.class, () -> FileUtils.moveDirectoryToDirectory(testFile1, nonExistent, false));
     }
 
     @Test
@@ -2316,7 +2316,7 @@ public boolean renameTo(final File f) {
     public void testMoveFile_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveFile(null, new File("foo")));
         assertThrows(NullPointerException.class, () -> FileUtils.moveFile(new File("foo"), null));
-        assertThrows(FileNotFoundException.class, () -> FileUtils.moveFile(new File("nonexistant"), new File("foo")));
+        assertThrows(FileNotFoundException.class, () -> FileUtils.moveFile(new File("non-existent"), new File("foo")));
         assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFile(tempDirFile, new File("foo")));
         final File testSourceFile = new File(tempDirFile, "testMoveFileSource");
         final File testDestFile = new File(tempDirFile, "testMoveFileSource");
@@ -2384,8 +2384,8 @@ public void testMoveFileToDirectory_Errors() throws Exception {
         }
         assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFileToDirectory(testFile1, testFile2, true));
 
-        final File nonexistant = new File(tempDirFile, "testMoveFileNonExistant");
-        assertThrows(IOException.class, () -> FileUtils.moveFileToDirectory(testFile1, nonexistant, false));
+        final File nonExistent = new File(tempDirFile, "testMoveFileNonExistent");
+        assertThrows(IOException.class, () -> FileUtils.moveFileToDirectory(testFile1, nonExistent, false));
     }
 
     @Test
@@ -2426,9 +2426,9 @@ public void testMoveToDirectory() throws Exception {
     public void testMoveToDirectory_Errors() throws Exception {
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(null, new File("foo"), true));
         assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(new File("foo"), null, true));
-        final File nonexistant = new File(tempDirFile, "nonexistant");
+        final File nonExistent = new File(tempDirFile, "non-existent");
         final File destDir = new File(tempDirFile, "MoveToDirectoryDestDir");
-        assertThrows(IOException.class, () -> FileUtils.moveToDirectory(nonexistant, destDir, true), "Expected IOException when source does not exist");
+        assertThrows(IOException.class, () -> FileUtils.moveToDirectory(nonExistent, destDir, true), "Expected IOException when source does not exist");
 
     }
 
@@ -2592,7 +2592,7 @@ public void testSizeOfDirectoryAsBigInteger() throws Exception {
         file.delete();
         file.mkdir();
 
-        final File nonEmptyFile = new File(file, "nonEmptyFile" + System.nanoTime());
+        final File nonEmptyFile = new File(file, "non-emptyFile" + System.nanoTime());
         assertTrue(nonEmptyFile.getParentFile().exists(), () -> "Cannot create file " + nonEmptyFile + " as the parent directory does not exist");
         final OutputStream output = new BufferedOutputStream(Files.newOutputStream(nonEmptyFile.toPath()));
         try {
diff --git a/src/test/java/org/apache/commons/io/FilenameUtilsTest.java b/src/test/java/org/apache/commons/io/FilenameUtilsTest.java
index 5055eaf..31b750e 100644
--- a/src/test/java/org/apache/commons/io/FilenameUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FilenameUtilsTest.java
@@ -218,7 +218,7 @@ public void testEqualsOnSystem() {
     @Test
     public void testGetBaseName() {
         assertNull(FilenameUtils.getBaseName(null));
-        assertEquals("noseperator", FilenameUtils.getBaseName("noseperator.inthispath"));
+        assertEquals("noseparator", FilenameUtils.getBaseName("noseparator.inthispath"));
         assertEquals("c", FilenameUtils.getBaseName("a/b/c.txt"));
         assertEquals("c", FilenameUtils.getBaseName("a/b/c"));
         assertEquals("", FilenameUtils.getBaseName("a/b/c/"));
@@ -249,12 +249,8 @@ public void testGetExtension() {
 
         if (FilenameUtils.isSystemWindows()) {
             // Special case handling for NTFS ADS names
-            try {
-                FilenameUtils.getExtension("foo.exe:bar.txt");
-                throw new AssertionError("Expected Exception");
-            } catch (final IllegalArgumentException e) {
-                assertEquals("NTFS ADS separator (':') in file name is forbidden.", e.getMessage());
-            }
+            final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> FilenameUtils.getExtension("foo.exe:bar.txt"));
+            assertEquals("NTFS ADS separator (':') in file name is forbidden.", e.getMessage());
         } else {
             // Upwards compatibility:
             assertEquals("txt", FilenameUtils.getExtension("foo.exe:bar.txt"));
@@ -264,7 +260,7 @@ public void testGetExtension() {
     @Test
     public void testGetFullPath() {
         assertNull(FilenameUtils.getFullPath(null));
-        assertEquals("", FilenameUtils.getFullPath("noseperator.inthispath"));
+        assertEquals("", FilenameUtils.getFullPath("noseparator.inthispath"));
         assertEquals("a/b/", FilenameUtils.getFullPath("a/b/c.txt"));
         assertEquals("a/b/", FilenameUtils.getFullPath("a/b/c"));
         assertEquals("a/b/c/", FilenameUtils.getFullPath("a/b/c/"));
@@ -306,7 +302,7 @@ public void testGetFullPath() {
     @Test
     public void testGetFullPathNoEndSeparator() {
         assertNull(FilenameUtils.getFullPathNoEndSeparator(null));
-        assertEquals("", FilenameUtils.getFullPathNoEndSeparator("noseperator.inthispath"));
+        assertEquals("", FilenameUtils.getFullPathNoEndSeparator("noseparator.inthispath"));
         assertEquals("a/b", FilenameUtils.getFullPathNoEndSeparator("a/b/c.txt"));
         assertEquals("a/b", FilenameUtils.getFullPathNoEndSeparator("a/b/c"));
         assertEquals("a/b/c", FilenameUtils.getFullPathNoEndSeparator("a/b/c/"));
@@ -367,7 +363,7 @@ public void testGetFullPathNoEndSeparator_IO_248() {
     @Test
     public void testGetName() {
         assertNull(FilenameUtils.getName(null));
-        assertEquals("noseperator.inthispath", FilenameUtils.getName("noseperator.inthispath"));
+        assertEquals("noseparator.inthispath", FilenameUtils.getName("noseparator.inthispath"));
         assertEquals("c.txt", FilenameUtils.getName("a/b/c.txt"));
         assertEquals("c", FilenameUtils.getName("a/b/c"));
         assertEquals("", FilenameUtils.getName("a/b/c/"));
@@ -377,9 +373,9 @@ public void testGetName() {
     @Test
     public void testGetPath() {
         assertNull(FilenameUtils.getPath(null));
-        assertEquals("", FilenameUtils.getPath("noseperator.inthispath"));
-        assertEquals("", FilenameUtils.getPath("/noseperator.inthispath"));
-        assertEquals("", FilenameUtils.getPath("\\noseperator.inthispath"));
+        assertEquals("", FilenameUtils.getPath("noseparator.inthispath"));
+        assertEquals("", FilenameUtils.getPath("/noseparator.inthispath"));
+        assertEquals("", FilenameUtils.getPath("\\noseparator.inthispath"));
         assertEquals("a/b/", FilenameUtils.getPath("a/b/c.txt"));
         assertEquals("a/b/", FilenameUtils.getPath("a/b/c"));
         assertEquals("a/b/c/", FilenameUtils.getPath("a/b/c/"));
@@ -420,9 +416,9 @@ public void testGetPath_with_null_character() {
     @Test
     public void testGetPathNoEndSeparator() {
         assertNull(FilenameUtils.getPath(null));
-        assertEquals("", FilenameUtils.getPath("noseperator.inthispath"));
-        assertEquals("", FilenameUtils.getPathNoEndSeparator("/noseperator.inthispath"));
-        assertEquals("", FilenameUtils.getPathNoEndSeparator("\\noseperator.inthispath"));
+        assertEquals("", FilenameUtils.getPath("noseparator.inthispath"));
+        assertEquals("", FilenameUtils.getPathNoEndSeparator("/noseparator.inthispath"));
+        assertEquals("", FilenameUtils.getPathNoEndSeparator("\\noseparator.inthispath"));
         assertEquals("a/b", FilenameUtils.getPathNoEndSeparator("a/b/c.txt"));
         assertEquals("a/b", FilenameUtils.getPathNoEndSeparator("a/b/c"));
         assertEquals("a/b/c", FilenameUtils.getPathNoEndSeparator("a/b/c/"));
@@ -585,12 +581,8 @@ public void testIndexOfExtension() {
 
         if (FilenameUtils.isSystemWindows()) {
             // Special case handling for NTFS ADS names
-            try {
-                FilenameUtils.indexOfExtension("foo.exe:bar.txt");
-                throw new AssertionError("Expected Exception");
-            } catch (final IllegalArgumentException e) {
-                assertEquals("NTFS ADS separator (':') in file name is forbidden.", e.getMessage());
-            }
+            final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> FilenameUtils.indexOfExtension("foo.exe:bar.txt"));
+            assertEquals("NTFS ADS separator (':') in file name is forbidden.", e.getMessage());
         } else {
             // Upwards compatibility on other systems
             assertEquals(11, FilenameUtils.indexOfExtension("foo.exe:bar.txt"));
@@ -601,7 +593,7 @@ public void testIndexOfExtension() {
     @Test
     public void testIndexOfLastSeparator() {
         assertEquals(-1, FilenameUtils.indexOfLastSeparator(null));
-        assertEquals(-1, FilenameUtils.indexOfLastSeparator("noseperator.inthispath"));
+        assertEquals(-1, FilenameUtils.indexOfLastSeparator("noseparator.inthispath"));
         assertEquals(3, FilenameUtils.indexOfLastSeparator("a/b/c"));
         assertEquals(3, FilenameUtils.indexOfLastSeparator("a\\b\\c"));
     }
diff --git a/src/test/java/org/apache/commons/io/IOUtilsCopyTest.java b/src/test/java/org/apache/commons/io/IOUtilsCopyTest.java
index 344f34b..1dbe9c2 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsCopyTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsCopyTest.java
@@ -95,7 +95,7 @@ public void testCopy_inputStreamToOutputStream() throws Exception {
         assertEquals(0, in.available(), "Not all bytes were read");
         assertEquals(inData.length, baout.size(), "Sizes differ");
         assertArrayEquals(inData, baout.toByteArray(), "Content differs");
-        assertEquals(inData.length,count);
+        assertEquals(inData.length, count);
     }
 
     /**
@@ -161,7 +161,7 @@ private void testCopy_inputStreamToOutputStreamWithBufferSize(final int bufferSi
         assertEquals(0, in.available(), "Not all bytes were read");
         assertEquals(inData.length, baout.size(), "Sizes differ");
         assertArrayEquals(inData, baout.toByteArray(), "Content differs");
-        assertEquals(inData.length,count);
+        assertEquals(inData.length, count);
     }
 
     @SuppressWarnings({ "resource", "deprecation" }) // 'in' is deliberately not closed
diff --git a/src/test/java/org/apache/commons/io/IOUtilsMultithreadedSkipTest.java b/src/test/java/org/apache/commons/io/IOUtilsMultithreadedSkipTest.java
new file mode 100644
index 0000000..9db0d67
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/IOUtilsMultithreadedSkipTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.function.Supplier;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * See Jira ticket IO-802.
+ */
+public class IOUtilsMultithreadedSkipTest {
+
+    private static final String FIXTURE = "TIKA-4065.bin";
+    long seed = 1;
+    private final ThreadLocal<byte[]> threadLocal = ThreadLocal.withInitial(() -> new byte[4096]);
+
+    private int[] generateExpected(final InputStream is, final int[] skips) throws IOException {
+        final int[] testBytes = new int[skips.length];
+        for (int i = 0; i < skips.length; i++) {
+            try {
+                IOUtils.skipFully(is, skips[i]);
+                testBytes[i] = is.read();
+            } catch (final EOFException e) {
+                testBytes[i] = -1;
+            }
+        }
+        return testBytes;
+    }
+
+    private int[] generateSkips(final byte[] bytes, final int numSkips, final Random random) {
+        final int[] skips = new int[numSkips];
+        for (int i = 0; i < skips.length; i++) {
+            skips[i] = random.nextInt(bytes.length / numSkips) + bytes.length / 10;
+        }
+        return skips;
+    }
+
+    private InputStream inflate(final byte[] deflated) throws IOException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        IOUtils.copy(new InflaterInputStream(new ByteArrayInputStream(deflated), new Inflater(true)), bos);
+        return new ByteArrayInputStream(bos.toByteArray());
+    }
+
+    @BeforeEach
+    public void setUp() {
+        // Not the best random we can use but good enough here.
+        seed = new Random().nextLong();
+    }
+
+    private void testSkipFullyOnInflaterInputStream(final Supplier<byte[]> baSupplier) throws Exception {
+        final long thisSeed = seed;
+        // thisSeed = -727624427837034313l;
+        final Random random = new Random(thisSeed);
+        final byte[] bytes;
+        try (final InputStream inputStream = getClass().getResourceAsStream(FIXTURE)) {
+            bytes = IOUtils.toByteArray(inputStream);
+        }
+        final int numSkips = (random.nextInt(bytes.length) / 100) + 1;
+
+        final int[] skips = generateSkips(bytes, numSkips, random);
+        final int[] expected;
+        try (final InputStream inflate = inflate(bytes)) {
+            expected = generateExpected(inflate, skips);
+        }
+
+        final int numThreads = 2;
+        final int iterations = 100;
+        final ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
+        final ExecutorCompletionService<Integer> executorCompletionService = new ExecutorCompletionService<>(executorService);
+
+        for (int i = 0; i < numThreads; i++) {
+            executorCompletionService.submit(() -> {
+                for (int iteration = 0; iteration < iterations; iteration++) {
+                    try (InputStream is = new InflaterInputStream(new ByteArrayInputStream(bytes), new Inflater(true))) {
+                        for (int skipIndex = 0; skipIndex < skips.length; skipIndex++) {
+                            try {
+                                IOUtils.skipFully(is, skips[skipIndex], baSupplier);
+                                final int c = is.read();
+                                assertEquals(expected[skipIndex], c, "failed on seed=" + seed + " iteration=" + iteration);
+                            } catch (final EOFException e) {
+                                assertEquals(expected[skipIndex], is.read(), "failed on " + "seed=" + seed + " iteration=" + iteration);
+                            }
+                        }
+                    }
+                }
+                return 1;
+            });
+        }
+
+        int finished = 0;
+        while (finished < numThreads) {
+            // blocking
+            final Future<Integer> future = executorCompletionService.take();
+            try {
+                future.get();
+            } catch (final Exception e) {
+                // printStackTrace() for simpler debugging
+                e.printStackTrace();
+                fail("failed on seed=" + seed);
+            }
+            finished++;
+        }
+    }
+
+    @Test
+    public void testSkipFullyOnInflaterInputStream_New_bytes() throws Exception {
+        testSkipFullyOnInflaterInputStream(() -> new byte[4096]);
+    }
+
+    @Test
+    public void testSkipFullyOnInflaterInputStream_ThreadLocal() throws Exception {
+        testSkipFullyOnInflaterInputStream(threadLocal::get);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTest.java b/src/test/java/org/apache/commons/io/IOUtilsTest.java
index 909893d..e361e88 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTest.java
@@ -60,6 +60,7 @@
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.List;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import org.apache.commons.io.function.IOConsumer;
@@ -1310,13 +1311,53 @@ public void testSkip_ReadableByteChannel() throws Exception {
     public void testSkipFully_InputStream() throws Exception {
         final int size = 1027;
 
-        final InputStream input = new ByteArrayInputStream(new byte[size]);
-        assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
+        try (final InputStream input = new ByteArrayInputStream(new byte[size])) {
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
 
-        IOUtils.skipFully(input, 0);
-        IOUtils.skipFully(input, size - 1);
-        assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2), "Should have failed with IOException");
-        IOUtils.closeQuietly(input);
+            IOUtils.skipFully(input, 0);
+            IOUtils.skipFully(input, size - 1);
+            assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2), "Should have failed with IOException");
+        }
+    }
+
+    @Test
+    public void testSkipFully_InputStream_Buffer_New_bytes() throws Exception {
+        final int size = 1027;
+        final Supplier<byte[]> bas = () -> new byte[size];
+        try (final InputStream input = new ByteArrayInputStream(new byte[size])) {
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1, bas), "Should have failed with IllegalArgumentException");
+
+            IOUtils.skipFully(input, 0, bas);
+            IOUtils.skipFully(input, size - 1, bas);
+            assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2, bas), "Should have failed with IOException");
+        }
+    }
+
+    @Test
+    public void testSkipFully_InputStream_Buffer_Reuse_bytes() throws Exception {
+        final int size = 1027;
+        final byte[] ba = new byte[size];
+        final Supplier<byte[]> bas = () -> ba;
+        try (final InputStream input = new ByteArrayInputStream(new byte[size])) {
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1, bas), "Should have failed with IllegalArgumentException");
+
+            IOUtils.skipFully(input, 0, bas);
+            IOUtils.skipFully(input, size - 1, bas);
+            assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2, bas), "Should have failed with IOException");
+        }
+    }
+
+    @Test
+    public void testSkipFully_InputStream_Buffer_Reuse_ThreadLocal() throws Exception {
+        final int size = 1027;
+        final ThreadLocal<byte[]> tl = ThreadLocal.withInitial(() -> new byte[size]);
+        try (final InputStream input = new ByteArrayInputStream(new byte[size])) {
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1, tl::get), "Should have failed with IllegalArgumentException");
+
+            IOUtils.skipFully(input, 0, tl::get);
+            IOUtils.skipFully(input, size - 1, tl::get);
+            assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2, tl::get), "Should have failed with IOException");
+        }
     }
 
     @Test
@@ -1336,13 +1377,12 @@ public void testSkipFully_ReadableByteChannel() throws Exception {
     @Test
     public void testSkipFully_Reader() throws Exception {
         final int size = 1027;
-        final Reader input = new CharArrayReader(new char[size]);
-
-        IOUtils.skipFully(input, 0);
-        IOUtils.skipFully(input, size - 3);
-        assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
-        assertThrows(IOException.class, () -> IOUtils.skipFully(input, 5), "Should have failed with IOException");
-        IOUtils.closeQuietly(input);
+        try (final Reader input = new CharArrayReader(new char[size])) {
+            IOUtils.skipFully(input, 0);
+            IOUtils.skipFully(input, size - 3);
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
+            assertThrows(IOException.class, () -> IOUtils.skipFully(input, 5), "Should have failed with IOException");
+        }
     }
 
     @Test
@@ -1472,11 +1512,11 @@ public void testToByteArray_InputStream_SizeZero() throws Exception {
     @Test
     public void testToByteArray_Reader() throws IOException {
         final String charsetName = UTF_8;
-        final byte[] expecteds = charsetName.getBytes(charsetName);
-        byte[] actuals = IOUtils.toByteArray(new InputStreamReader(new ByteArrayInputStream(expecteds)));
-        assertArrayEquals(expecteds, actuals);
-        actuals = IOUtils.toByteArray(new InputStreamReader(new ByteArrayInputStream(expecteds)), charsetName);
-        assertArrayEquals(expecteds, actuals);
+        final byte[] expected = charsetName.getBytes(charsetName);
+        byte[] actual = IOUtils.toByteArray(new InputStreamReader(new ByteArrayInputStream(expected)));
+        assertArrayEquals(expected, actual);
+        actual = IOUtils.toByteArray(new InputStreamReader(new ByteArrayInputStream(expected)), charsetName);
+        assertArrayEquals(expected, actual);
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/UncheckedIOTest.java b/src/test/java/org/apache/commons/io/UncheckedIOTest.java
deleted file mode 100644
index 38aae53..0000000
--- a/src/test/java/org/apache/commons/io/UncheckedIOTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.commons.io;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.io.ByteArrayInputStream;
-
-import org.apache.commons.io.function.IOBiFunction;
-import org.apache.commons.io.function.IOConsumer;
-import org.apache.commons.io.function.IOFunction;
-import org.apache.commons.io.function.IORunnable;
-import org.apache.commons.io.function.IOSupplier;
-import org.apache.commons.io.function.IOTriFunction;
-import org.apache.commons.io.function.Uncheck;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests {@link Uncheck}.
- */
-public class UncheckedIOTest {
-
-    private static final byte[] BYTES = {'a', 'b'};
-
-    private ByteArrayInputStream newInputStream() {
-        return new ByteArrayInputStream(BYTES);
-    }
-
-    /**
-     * Tests {@link Uncheck#accept(IOConsumer, Object)}.
-     */
-    @Test
-    public void testAccept() {
-        final ByteArrayInputStream stream = newInputStream();
-        Uncheck.accept(n -> stream.skip(n), 1);
-        assertEquals('b', Uncheck.get(stream::read).intValue());
-    }
-
-    /**
-     * Tests {@link Uncheck#apply(IOFunction, Object)}.
-     */
-    @Test
-    public void testApply1() {
-        final ByteArrayInputStream stream = newInputStream();
-        assertEquals(1, Uncheck.apply(n -> stream.skip(n), 1).intValue());
-        assertEquals('b', Uncheck.get(stream::read).intValue());
-    }
-
-    /**
-     * Tests {@link Uncheck#apply(IOBiFunction, Object, Object)}.
-     */
-    @Test
-    public void testApply2() {
-        final ByteArrayInputStream stream = newInputStream();
-        final byte[] buf = new byte[BYTES.length];
-        assertEquals(1, Uncheck.apply((o, l) -> stream.read(buf, o, l), 0, 1).intValue());
-        assertEquals('a', buf[0]);
-    }
-
-    /**
-     * Tests {@link Uncheck#apply(IOTriFunction, Object, Object, Object)}.
-     */
-    @Test
-    public void testApply3() {
-        final ByteArrayInputStream stream = newInputStream();
-        final byte[] buf = new byte[BYTES.length];
-        assertEquals(1, Uncheck.apply((b, o, l) -> stream.read(b, o, l), buf, 0, 1).intValue());
-        assertEquals('a', buf[0]);
-    }
-
-    /**
-     * Tests {@link Uncheck#get(IOSupplier)}.
-     */
-    @Test
-    public void testGet() {
-        assertEquals('a', Uncheck.get(() -> newInputStream().read()).intValue());
-    }
-
-    /**
-     * Tests {@link Uncheck#run(IORunnable)}.
-     */
-    @Test
-    public void testRun() {
-        final ByteArrayInputStream stream = newInputStream();
-        Uncheck.run(() -> stream.skip(1));
-        assertEquals('b', Uncheck.get(stream::read).intValue());
-    }
-}
diff --git a/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java b/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java
new file mode 100644
index 0000000..425ba4a
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/build/AbstractStreamBuilderTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.build;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link AbstractStreamBuilder}.
+ */
+public class AbstractStreamBuilderTest {
+
+    public static class Builder extends AbstractStreamBuilder<char[], Builder> {
+
+        @Override
+        public char[] get() {
+            final char[] arr = new char[getBufferSize()];
+            Arrays.fill(arr, 'a');
+            return arr;
+        }
+
+    }
+
+    private void assertResult(final char[] arr, final int size) {
+        assertNotNull(arr);
+        assertEquals(size, arr.length);
+        for (final char c : arr) {
+            assertEquals('a', c);
+        }
+    }
+
+    protected Builder builder() {
+        return new Builder();
+    }
+
+    @Test
+    public void testBufferSizeChecker() {
+        // sanity
+        final Builder builder = builder();
+        assertResult(builder.get(), builder.getBufferSize());
+        // basic failure
+        assertThrows(IllegalArgumentException.class, () -> builder().setBufferSizeMax(2).setBufferSize(3));
+        // reset
+        assertResult(builder.setBufferSizeMax(2).setBufferSizeMax(0).setBufferSize(3).get(), 3);
+        // resize
+        assertResult(builder().setBufferSizeMax(2).setBufferSizeChecker(i -> 100).setBufferSize(3).get(), 100);
+    }
+}
diff --git a/src/test/java/org/apache/commons/io/comparator/SizeFileComparatorTest.java b/src/test/java/org/apache/commons/io/comparator/SizeFileComparatorTest.java
index 50f34c5..34e2236 100644
--- a/src/test/java/org/apache/commons/io/comparator/SizeFileComparatorTest.java
+++ b/src/test/java/org/apache/commons/io/comparator/SizeFileComparatorTest.java
@@ -95,9 +95,9 @@ public void testCompareDirectorySizes() {
      * Test a file which doesn't exist.
      */
     @Test
-    public void testNonexistantFile() {
-        final File nonexistantFile = new File(FileUtils.current(), "nonexistant.txt");
-        assertFalse(nonexistantFile.exists());
-        assertTrue(comparator.compare(nonexistantFile, moreFile) < 0, "less");
+    public void testNonExistentFile() {
+        final File nonExistentFile = new File(FileUtils.current(), "non-existent.txt");
+        assertFalse(nonExistentFile.exists());
+        assertTrue(comparator.compare(nonExistentFile, moreFile) < 0, "less");
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathWrapper.java b/src/test/java/org/apache/commons/io/file/AbstractPathWrapper.java
similarity index 95%
rename from src/test/java/org/apache/commons/io/file/PathWrapper.java
rename to src/test/java/org/apache/commons/io/file/AbstractPathWrapper.java
index 2bf1daa..674ee3d 100644
--- a/src/test/java/org/apache/commons/io/file/PathWrapper.java
+++ b/src/test/java/org/apache/commons/io/file/AbstractPathWrapper.java
@@ -37,7 +37,7 @@
  *
  * @since 2.12.0
  */
-public abstract class PathWrapper implements Path {
+public abstract class AbstractPathWrapper implements Path {
 
     /**
      * The path delegate.
@@ -49,7 +49,7 @@ public abstract class PathWrapper implements Path {
      *
      * @param path The path to wrap.
      */
-    protected PathWrapper(final Path path) {
+    protected AbstractPathWrapper(final Path path) {
         this.path = Objects.requireNonNull(path, "path");
     }
 
@@ -73,10 +73,10 @@ public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
-        if (!(obj instanceof PathWrapper)) {
+        if (!(obj instanceof AbstractPathWrapper)) {
             return false;
         }
-        final PathWrapper other = (PathWrapper) obj;
+        final AbstractPathWrapper other = (AbstractPathWrapper) obj;
         return Objects.equals(path, other.path);
     }
 
diff --git a/src/test/java/org/apache/commons/io/file/CopyDirectoryVisitorTest.java b/src/test/java/org/apache/commons/io/file/CopyDirectoryVisitorTest.java
index fb0fe15..e61c5cf 100644
--- a/src/test/java/org/apache/commons/io/file/CopyDirectoryVisitorTest.java
+++ b/src/test/java/org/apache/commons/io/file/CopyDirectoryVisitorTest.java
@@ -56,7 +56,7 @@ public void testCopyDirectoryEmptyFolder(final PathCounters pathCounters) throws
             final CopyDirectoryVisitor visitFileTree = PathUtils.visitFileTree(supplier.get(), sourceDir.get());
             assertCounts(1, 0, 0, visitFileTree);
             assertArrayEquals(EXPECTED_COPY_OPTIONS, visitFileTree.getCopyOptions());
-            assertEquals(sourceDir.get(), ((PathWrapper) visitFileTree.getSourceDirectory()).get());
+            assertEquals(sourceDir.get(), ((AbstractPathWrapper) visitFileTree.getSourceDirectory()).get());
             assertEquals(sourceDir, visitFileTree.getSourceDirectory());
             assertEquals(targetDir, visitFileTree.getTargetDirectory());
             assertEquals(targetDir, visitFileTree.getTargetDirectory());
diff --git a/src/test/java/org/apache/commons/io/file/DeletablePath.java b/src/test/java/org/apache/commons/io/file/DeletablePath.java
index 28aa20b..9f9fbf0 100644
--- a/src/test/java/org/apache/commons/io/file/DeletablePath.java
+++ b/src/test/java/org/apache/commons/io/file/DeletablePath.java
@@ -28,7 +28,7 @@
  *
  * @since 2.12.0
  */
-public class DeletablePath extends PathWrapper implements Closeable {
+public class DeletablePath extends AbstractPathWrapper implements Closeable {
 
     /**
      * Constructs a new instance wrapping the given delegate.
diff --git a/src/test/java/org/apache/commons/io/file/FilesUncheckTest.java b/src/test/java/org/apache/commons/io/file/FilesUncheckTest.java
index 1882353..4fa7368 100644
--- a/src/test/java/org/apache/commons/io/file/FilesUncheckTest.java
+++ b/src/test/java/org/apache/commons/io/file/FilesUncheckTest.java
@@ -165,6 +165,11 @@ public void testDeleteIfExists() {
     }
 
     @Test
+    public void testFind() {
+        assertNotNull(FilesUncheck.find(FILE_PATH_EMPTY, 0, (t, u) -> false));
+    }
+
+    @Test
     public void testGetAttribute() {
         assertEquals(0L, FilesUncheck.getAttribute(FILE_PATH_EMPTY, "basic:size", LinkOption.NOFOLLOW_LINKS));
     }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
index 121b2dc..a22630d 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
@@ -18,9 +18,13 @@
 package org.apache.commons.io.file;
 
 import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import org.junit.jupiter.api.Test;
@@ -30,6 +34,18 @@
  */
 public class PathUtilsDeleteDirectoryTest extends AbstractTempDirTest {
 
+    @Test
+    public void testDeleteAbsentDirectory() throws IOException {
+        final Path absent = tempDirPath.resolve("ThisDirectoryDoesNotExist");
+        assertFalse(Files.exists(absent));
+        final Class<NoSuchFileException> expectedType = NoSuchFileException.class;
+        assertThrows(expectedType, () -> PathUtils.deleteDirectory(absent));
+        assertThrows(expectedType, () -> PathUtils.deleteDirectory(absent, StandardDeleteOption.OVERRIDE_READ_ONLY));
+        assertThrows(expectedType, () -> PathUtils.deleteDirectory(absent, PathUtils.EMPTY_DELETE_OPTION_ARRAY));
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDirPath);
+    }
+
     /**
      * Tests a directory with one file of size 0.
      */
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
index 860ae36..622937e 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
@@ -99,7 +99,7 @@ private Long getLastModifiedMillis(final Path file) throws IOException {
         return Files.getLastModifiedTime(file).toMillis();
     }
 
-    private Path getNonExistantPath() {
+    private Path getNonExistentPath() {
         return Paths.get("/does not exist/for/certain");
     }
 
@@ -252,13 +252,13 @@ public void testGetLastModifiedFileTime_File_Present() throws IOException {
 
     @Test
     public void testGetLastModifiedFileTime_Path_Absent() throws IOException {
-        assertNull(PathUtils.getLastModifiedFileTime(getNonExistantPath()));
+        assertNull(PathUtils.getLastModifiedFileTime(getNonExistentPath()));
     }
 
     @Test
     public void testGetLastModifiedFileTime_Path_FileTime_Absent() throws IOException {
         final FileTime fromMillis = FileTime.fromMillis(0);
-        assertEquals(fromMillis, PathUtils.getLastModifiedFileTime(getNonExistantPath(), fromMillis));
+        assertEquals(fromMillis, PathUtils.getLastModifiedFileTime(getNonExistentPath(), fromMillis));
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/filefilter/ConditionalFileFilterAbstractTest.java b/src/test/java/org/apache/commons/io/filefilter/AbstractConditionalFileFilterTest.java
similarity index 98%
rename from src/test/java/org/apache/commons/io/filefilter/ConditionalFileFilterAbstractTest.java
rename to src/test/java/org/apache/commons/io/filefilter/AbstractConditionalFileFilterTest.java
index daffdb4..5a99d62 100644
--- a/src/test/java/org/apache/commons/io/filefilter/ConditionalFileFilterAbstractTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/AbstractConditionalFileFilterTest.java
@@ -27,7 +27,7 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-public abstract class ConditionalFileFilterAbstractTest extends IOFileFilterAbstractTest {
+public abstract class AbstractConditionalFileFilterTest extends AbstractIOFileFilterTest {
 
     private static final String TEST_FILE_NAME_PREFIX = "TestFile";
     private static final String TEST_FILE_TYPE = ".tst";
diff --git a/src/test/java/org/apache/commons/io/filefilter/AbstractFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/AbstractFilterTest.java
index 4ed27be..ca6f887 100644
--- a/src/test/java/org/apache/commons/io/filefilter/AbstractFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/AbstractFilterTest.java
@@ -24,6 +24,7 @@
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
 
 import org.apache.commons.io.IOCase;
 import org.junit.jupiter.api.io.TempDir;
@@ -43,16 +44,24 @@ public class AbstractFilterTest {
     @TempDir
     public File temporaryFolder;
 
-    void assertFiltering(final IOFileFilter filter, final File file, final boolean expected) {
+    void assertFiltering(final IOFileFilter filter, final File file, final boolean expected) throws IOException {
         // Note. This only tests the (File, String) version if the parent of
         // the File passed in is not null
         assertEquals(expected, filter.accept(file), "Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
 
         if (file != null && file.getParentFile() != null) {
             assertEquals(expected, filter.accept(file.getParentFile(), file.getName()),
-                "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for " + file);
+                    "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for " + file);
+            final Path path = file.toPath();
+            assertEquals(expected, filter.accept(path, null) != FileVisitResult.TERMINATE, filter::toString);
+            if (Files.isRegularFile(path)) {
+                assertEquals(expected, filter.accept(path, Files.readAttributes(path, BasicFileAttributes.class)) != FileVisitResult.TERMINATE,
+                        filter::toString);
+            }
+            assertEquals(expected, filter.matches(path), filter::toString);
         } else if (file == null) {
-            assertEquals(expected, filter.accept(file), "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
+            assertEquals(expected, filter.accept(null), "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
+            assertEquals(expected, filter.matches(null), "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
         }
         assertNotNull(filter.toString());
     }
@@ -62,19 +71,23 @@ void assertFiltering(final IOFileFilter filter, final Path path, final boolean e
         // the File passed in is not null
         final FileVisitResult expectedFileVisitResult = AbstractFileFilter.toDefaultFileVisitResult(expected);
         assertEquals(expectedFileVisitResult, filter.accept(path, null),
-            "Filter(Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
+                "Filter(Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
 
         if (path != null && path.getParent() != null) {
             assertEquals(expectedFileVisitResult, filter.accept(path, null),
-                "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
+                    "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
+            assertEquals(expectedFileVisitResult != FileVisitResult.TERMINATE, filter.matches(path),
+                    "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
         } else if (path == null) {
             assertEquals(expectedFileVisitResult, filter.accept(path, null),
-                "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for null");
+                    "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for null");
+            assertEquals(expectedFileVisitResult != FileVisitResult.TERMINATE, filter.matches(path),
+                    "Filter(Path, Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for null");
         }
         assertNotNull(filter.toString());
     }
 
-    void assertFooBarFileFiltering(IOFileFilter filter) {
+    void assertFooBarFileFiltering(IOFileFilter filter) throws IOException {
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("bar"), true);
@@ -84,7 +97,7 @@ void assertFooBarFileFiltering(IOFileFilter filter) {
         assertFiltering(filter, new File("bar").toPath(), true);
         assertFiltering(filter, new File("fred").toPath(), false);
 
-        filter = new NameFileFilter(new String[] {"foo", "bar"}, IOCase.SENSITIVE);
+        filter = new NameFileFilter(new String[] { "foo", "bar" }, IOCase.SENSITIVE);
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("bar"), true);
         assertFiltering(filter, new File("FOO"), false);
@@ -94,7 +107,7 @@ void assertFooBarFileFiltering(IOFileFilter filter) {
         assertFiltering(filter, new File("FOO").toPath(), false);
         assertFiltering(filter, new File("BAR").toPath(), false);
 
-        filter = new NameFileFilter(new String[] {"foo", "bar"}, IOCase.INSENSITIVE);
+        filter = new NameFileFilter(new String[] { "foo", "bar" }, IOCase.INSENSITIVE);
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("bar"), true);
         assertFiltering(filter, new File("FOO"), true);
@@ -104,7 +117,7 @@ void assertFooBarFileFiltering(IOFileFilter filter) {
         assertFiltering(filter, new File("FOO").toPath(), true);
         assertFiltering(filter, new File("BAR").toPath(), true);
 
-        filter = new NameFileFilter(new String[] {"foo", "bar"}, IOCase.SYSTEM);
+        filter = new NameFileFilter(new String[] { "foo", "bar" }, IOCase.SYSTEM);
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("bar"), true);
         assertFiltering(filter, new File("FOO"), WINDOWS);
@@ -114,7 +127,7 @@ void assertFooBarFileFiltering(IOFileFilter filter) {
         assertFiltering(filter, new File("FOO").toPath(), WINDOWS);
         assertFiltering(filter, new File("BAR").toPath(), WINDOWS);
 
-        filter = new NameFileFilter(new String[] {"foo", "bar"}, null);
+        filter = new NameFileFilter(new String[] { "foo", "bar" }, null);
         assertFiltering(filter, new File("foo"), true);
         assertFiltering(filter, new File("bar"), true);
         assertFiltering(filter, new File("FOO"), false);
diff --git a/src/test/java/org/apache/commons/io/filefilter/IOFileFilterAbstractTest.java b/src/test/java/org/apache/commons/io/filefilter/AbstractIOFileFilterTest.java
similarity index 87%
rename from src/test/java/org/apache/commons/io/filefilter/IOFileFilterAbstractTest.java
rename to src/test/java/org/apache/commons/io/filefilter/AbstractIOFileFilterTest.java
index 5009ec5..f5ba279 100644
--- a/src/test/java/org/apache/commons/io/filefilter/IOFileFilterAbstractTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/AbstractIOFileFilterTest.java
@@ -22,7 +22,7 @@
 import java.util.Objects;
 import java.util.stream.Stream;
 
-public abstract class IOFileFilterAbstractTest {
+public abstract class AbstractIOFileFilterTest {
 
     class TesterFalseFileFilter extends FalseFileFilter {
 
@@ -93,6 +93,8 @@ public static void assertFalseFiltersInvoked(final int testNumber, final TesterF
     public static void assertFileFiltering(final int testNumber, final IOFileFilter filter, final File file, final boolean expected) {
         assertEquals(expected, filter.accept(file),
                 "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
+        assertEquals(expected, filter.matches(file.toPath()),
+                "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
     }
 
     public static void assertFilenameFiltering(final int testNumber, final IOFileFilter filter, final File file, final boolean expected) {
@@ -107,14 +109,18 @@ public static void assertFiltering(final int testNumber, final IOFileFilter filt
         assertEquals(expected, filter.accept(file),
             "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
         assertEquals(expected, filter.accept(file.toPath(), null),
-            "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
+                "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
 
         if (file != null && file.getParentFile() != null) {
             assertEquals(expected, filter.accept(file.getParentFile(), file.getName()),
                     "test " + testNumber + " Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for " + file);
+            assertEquals(expected, filter.matches(file.toPath()),
+                    "test " + testNumber + " Filter(File) " + filter.getClass().getName() + " not " + expected + " for " + file);
         } else if (file == null) {
             assertEquals(expected, filter.accept(file),
                     "test " + testNumber + " Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
+            assertEquals(expected, filter.matches(null),
+                    "test " + testNumber + " Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
         }
     }
 
diff --git a/src/test/java/org/apache/commons/io/filefilter/AndFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/AndFileFilterTest.java
index 28886bd..3dffd9a 100644
--- a/src/test/java/org/apache/commons/io/filefilter/AndFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/AndFileFilterTest.java
@@ -28,7 +28,7 @@
 /**
  * Tests {@link AndFileFilter}.
  */
-public class AndFileFilterTest extends ConditionalFileFilterAbstractTest {
+public class AndFileFilterTest extends AbstractConditionalFileFilterTest {
 
   private static final String DEFAULT_WORKING_PATH = "./AndFileFilterTestCase/";
   private static final String WORKING_PATH_NAME_PROPERTY_KEY = AndFileFilterTest.class.getName() + ".workingDirectory";
diff --git a/src/test/java/org/apache/commons/io/filefilter/FileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/FileFilterTest.java
index 3b8f4bc..858c4a1 100644
--- a/src/test/java/org/apache/commons/io/filefilter/FileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/FileFilterTest.java
@@ -29,6 +29,7 @@
 import java.io.File;
 import java.io.FileFilter;
 import java.io.FilenameFilter;
+import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.FileVisitResult;
@@ -153,7 +154,7 @@ public void testAgeFilter() throws Exception {
     }
 
     @Test
-    public void testAnd() {
+    public void testAnd() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         assertFiltering(trueFilter.and(trueFilter), new File("foo.test"), true);
@@ -163,7 +164,7 @@ public void testAnd() {
     }
 
     @Test
-    public void testAnd2() {
+    public void testAnd2() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         assertFiltering(new AndFileFilter(trueFilter, trueFilter), new File("foo.test"), true);
@@ -181,7 +182,7 @@ public void testAnd2() {
     }
 
     @Test
-    public void testAndArray() {
+    public void testAndArray() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         assertFiltering(new AndFileFilter(trueFilter, trueFilter, trueFilter), new File("foo.test"), true);
@@ -252,7 +253,7 @@ public void testCanWrite() throws Exception {
     }
 
     @Test
-    public void testDelegateFileFilter() {
+    public void testDelegateFileFilter() throws IOException {
         final OrFileFilter orFilter = new OrFileFilter();
         final File testFile = new File("test.txt");
 
@@ -276,7 +277,7 @@ public void testDelegation() { // TODO improve these tests
 
     @SuppressWarnings("deprecation")
     @Test
-    public void testDeprecatedWildcard() {
+    public void testDeprecatedWildcard() throws IOException {
         IOFileFilter filter = new WildcardFilter("*.txt");
         final List<String> patternList = Arrays.asList("*.txt", "*.xml", "*.gif");
         final IOFileFilter listFilter = new WildcardFilter(patternList);
@@ -328,9 +329,14 @@ public void testDeprecatedWildcard() {
         assertFiltering(listFilter, new File("Test.gif").toPath(), true);
         assertFiltering(listFilter, new File("Test.bmp").toPath(), false);
 
+        // File
         assertTrue(listFilter.accept(txtFile));
         assertFalse(listFilter.accept(bmpFile));
         assertFalse(listFilter.accept(dirFile));
+        // Path
+        assertTrue(listFilter.matches(txtPath));
+        assertFalse(listFilter.matches(bmpPath));
+        assertFalse(listFilter.matches(dirPath));
         //
         assertEquals(FileVisitResult.CONTINUE, listFilter.accept(txtPath, null));
         assertEquals(FileVisitResult.TERMINATE, listFilter.accept(bmpPath, null));
@@ -350,7 +356,7 @@ public void testDeprecatedWildcard() {
     }
 
     @Test
-    public void testDirectory() {
+    public void testDirectory() throws IOException {
         // XXX: This test presumes the current working dir is the base dir of the source checkout.
         final IOFileFilter filter = new DirectoryFileFilter();
 
@@ -427,7 +433,7 @@ public void testEnsureTestCoverage() {
     }
 
     @Test
-    public void testFalse() {
+    public void testFalse() throws IOException {
         final IOFileFilter filter = FileFilterUtils.falseFileFilter();
         assertFiltering(filter, new File("foo.test"), false);
         assertFiltering(filter, new File("foo.test").toPath(), false);
@@ -442,13 +448,13 @@ public void testFalse() {
     }
 
     @Test
-    public void testFileEqualsFilter() {
+    public void testFileEqualsFilter() throws IOException {
         assertFooBarFileFiltering(
             new FileEqualsFileFilter(new File("foo")).or(new FileEqualsFileFilter(new File("bar"))));
     }
 
     @Test
-    public void testFileFilterUtils_and() {
+    public void testFileFilterUtils_and() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         assertFiltering(FileFilterUtils.and(trueFilter, trueFilter, trueFilter), new File("foo.test"), true);
@@ -458,7 +464,7 @@ public void testFileFilterUtils_and() {
     }
 
     @Test
-    public void testFileFilterUtils_or() {
+    public void testFileFilterUtils_or() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         final File testFile = new File("foo.test");
@@ -469,7 +475,7 @@ public void testFileFilterUtils_or() {
     }
 
     @Test
-    public void testFiles() {
+    public void testFiles() throws IOException {
         // XXX: This test presumes the current working dir is the base dir of the source checkout.
         final IOFileFilter filter = FileFileFilter.INSTANCE;
 
@@ -695,7 +701,7 @@ public void testFilterSetNullParameters() {
     }
 
     @Test
-    public void testHidden() {
+    public void testHidden() throws IOException {
         final File hiddenDirFile = new File(SVN_DIR_NAME);
         final Path hiddenDirPath = hiddenDirFile.toPath();
         if (hiddenDirFile.exists()) {
@@ -792,8 +798,6 @@ public void testMagicNumberFileFilterBytesOffset() throws Exception {
         assertFiltering(filter, dir, false);
     }
 
-    // -----------------------------------------------------------------------
-
     @Test
     public void testMagicNumberFileFilterString() throws Exception {
         final byte[] classFileMagicNumber = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
@@ -1032,7 +1036,7 @@ public void testMakeSVNAware() throws Exception {
     }
 
     @Test
-    public void testNameFilter() {
+    public void testNameFilter() throws IOException {
         assertFooBarFileFiltering(new NameFileFilter("foo", "bar"));
     }
 
@@ -1056,7 +1060,7 @@ public void testNameFilterNullListArgument() {
     }
 
     @Test
-    public void testNegate() {
+    public void testNegate() throws IOException {
         final IOFileFilter filter = FileFilterUtils.notFileFilter(FileFilterUtils.trueFileFilter());
         assertFiltering(filter, new File("foo.test"), false);
         assertFiltering(filter, new File("foo"), false);
@@ -1072,7 +1076,7 @@ public void testNullFilters() {
     }
 
     @Test
-    public void testOr() {
+    public void testOr() throws IOException {
         final IOFileFilter trueFilter = TrueFileFilter.INSTANCE;
         final IOFileFilter falseFilter = FalseFileFilter.INSTANCE;
         final File testFile = new File("foo.test");
@@ -1109,21 +1113,23 @@ public void testOr() {
 
         assertTrue(orFilter.accept(testFile.getParentFile(), testFile.getName()));
         assertEquals(FileVisitResult.CONTINUE, orFilter.accept(testPath, null));
+        assertTrue(orFilter.matches(testPath));
         orFilter.removeFileFilter(trueFilter);
         assertFalse(orFilter.accept(testFile.getParentFile(), testFile.getName()));
         assertEquals(FileVisitResult.TERMINATE, orFilter.accept(testPath, null));
+        assertFalse(orFilter.matches(testPath));
 
         assertThrows(NullPointerException.class, () -> new OrFileFilter(falseFilter, null));
     }
 
     @Test
-    public void testPathEqualsFilter() {
+    public void testPathEqualsFilter() throws IOException {
         assertFooBarFileFiltering(
             new PathEqualsFileFilter(Paths.get("foo")).or(new PathEqualsFileFilter(Paths.get("bar"))));
     }
 
     @Test
-    public void testPrefix() {
+    public void testPrefix() throws IOException {
         IOFileFilter filter = new PrefixFileFilter("foo", "bar");
         final File testFile = new File("test");
         final Path testPath = testFile.toPath();
@@ -1165,6 +1171,9 @@ public void testPrefix() {
         //
         assertEquals(FileVisitResult.CONTINUE, filter.accept(testPath, null));
         assertEquals(FileVisitResult.TERMINATE, filter.accept(fredPath, null));
+        //
+        assertTrue(filter.matches(testPath));
+        assertFalse(filter.matches(fredPath));
 
         final List<String> prefixes = Arrays.asList("foo", "fre");
         final IOFileFilter listFilter = new PrefixFileFilter(prefixes);
@@ -1174,6 +1183,9 @@ public void testPrefix() {
         //
         assertEquals(FileVisitResult.TERMINATE, listFilter.accept(testPath, null));
         assertEquals(FileVisitResult.CONTINUE, listFilter.accept(fredPath, null));
+        //
+        assertFalse(listFilter.matches(testPath));
+        assertTrue(listFilter.matches(fredPath));
 
         assertThrows(NullPointerException.class, () -> new PrefixFileFilter((String) null));
         assertThrows(NullPointerException.class, () -> new PrefixFileFilter((String[]) null));
@@ -1181,7 +1193,7 @@ public void testPrefix() {
     }
 
     @Test
-    public void testPrefixCaseInsensitive() {
+    public void testPrefixCaseInsensitive() throws IOException {
 
         IOFileFilter filter = new PrefixFileFilter(new String[] {"foo", "bar"}, IOCase.INSENSITIVE);
         assertFiltering(filter, new File("foo.test1"), true);
@@ -1310,7 +1322,7 @@ public void testSizeFilterOnPaths() throws Exception {
     }
 
     @Test
-    public void testSuffix() {
+    public void testSuffix() throws IOException {
         IOFileFilter filter = new SuffixFileFilter("tes", "est");
         final File testFile = new File("test");
         final Path testPath = testFile.toPath();
@@ -1348,6 +1360,9 @@ public void testSuffix() {
         //
         assertEquals(FileVisitResult.CONTINUE, filter.accept(testPath, null));
         assertEquals(FileVisitResult.TERMINATE, filter.accept(fredPath, null));
+        //
+        assertTrue(filter.matches(testPath));
+        assertFalse(filter.matches(fredPath));
 
         final List<String> prefixes = Arrays.asList("ood", "red");
         final IOFileFilter listFilter = new SuffixFileFilter(prefixes);
@@ -1364,7 +1379,7 @@ public void testSuffix() {
     }
 
     @Test
-    public void testSuffixCaseInsensitive() {
+    public void testSuffixCaseInsensitive() throws IOException {
 
         IOFileFilter filter = new SuffixFileFilter(new String[] {"tes", "est"}, IOCase.INSENSITIVE);
         assertFiltering(filter, new File("foo.tes"), true);
@@ -1398,7 +1413,7 @@ public void testSuffixCaseInsensitive() {
     }
 
     @Test
-    public void testTrue() {
+    public void testTrue() throws IOException {
         final IOFileFilter filter = FileFilterUtils.trueFileFilter();
         assertFiltering(filter, new File("foo.test"), true);
         assertFiltering(filter, new File("foo"), true);
diff --git a/src/test/java/org/apache/commons/io/filefilter/OrFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/OrFileFilterTest.java
index 932bca0..76df6f4 100644
--- a/src/test/java/org/apache/commons/io/filefilter/OrFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/OrFileFilterTest.java
@@ -24,7 +24,7 @@
 /**
  * Tests {@link IOFileFilter}.
  */
-public class OrFileFilterTest extends ConditionalFileFilterAbstractTest {
+public class OrFileFilterTest extends AbstractConditionalFileFilterTest {
 
   private static final String DEFAULT_WORKING_PATH = "./OrFileFilterTestCase/";
   private static final String WORKING_PATH_NAME_PROPERTY_KEY = OrFileFilterTest.class.getName() + ".workingDirectory";
diff --git a/src/test/java/org/apache/commons/io/filefilter/PathMatcherFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/PathMatcherFileFilterTest.java
new file mode 100644
index 0000000..b2ab982
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/filefilter/PathMatcherFileFilterTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.filefilter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+
+import org.apache.commons.io.FileSystem;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link PathMatcherFileFilter}.
+ */
+public class PathMatcherFileFilterTest extends AbstractFilterTest {
+
+    @Test
+    public void testGlob() throws IOException {
+        @SuppressWarnings("resource")
+        final IOFileFilter filter = new PathMatcherFileFilter(FileSystems.getDefault().getPathMatcher("glob:*.txt"));
+        final File file1 = new File("log.txt");
+        final File file2 = new File("log.TXT");
+        //
+        assertTrue(filter.accept(file1));
+        assertEquals(!FileSystem.getCurrent().isCaseSensitive(), filter.accept(file2));
+        assertTrue(filter.accept(file1.getParentFile(), file1.getName()));
+        assertEquals(!FileSystem.getCurrent().isCaseSensitive(), filter.accept(file2.getParentFile(), file2.getName()));
+        assertFiltering(filter, file1, true);
+        assertFiltering(filter, file1.toPath(), true);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
index e80ec10..c057129 100644
--- a/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
@@ -49,9 +49,13 @@ public void assertFiltering(final IOFileFilter filter, final File file, final bo
         if (file != null && file.getParentFile() != null) {
             assertEquals(expected, filter.accept(file.getParentFile(), file.getName()),
                     "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for " + file);
+            assertEquals(expected, filter.matches(file.toPath()),
+                    "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for " + file);
         } else if (file == null) {
             assertEquals(expected, filter.accept(file),
                     "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
+            assertEquals(expected, filter.matches(null),
+                    "Filter(File, String) " + filter.getClass().getName() + " not " + expected + " for null");
         }
         // Just don't blow up
         assertNotNull(filter.toString());
@@ -62,7 +66,9 @@ public void assertFiltering(final IOFileFilter filter, final Path path, final bo
         // the Path passed in is not null
         final FileVisitResult expectedFileVisitResult = AbstractFileFilter.toDefaultFileVisitResult(expected);
         assertEquals(expectedFileVisitResult, filter.accept(path, null),
-            "Filter(Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
+                "Filter(Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
+        assertEquals(expectedFileVisitResult != FileVisitResult.TERMINATE, filter.matches(path),
+                "Filter(Path) " + filter.getClass().getName() + " not " + expectedFileVisitResult + " for " + path);
 
         if (path != null && path.getParent() != null) {
             assertEquals(expectedFileVisitResult, filter.accept(path, null),
diff --git a/src/test/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilterTest.java
index 380ad0c..49478a8 100644
--- a/src/test/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilterTest.java
@@ -56,7 +56,8 @@ public class SymbolicLinkFileFilterTest {
     private static Path targetDirPath; // hard directory Path
     private static Path testLinkDirPath; // symbolic link to hardDirectory
     private static File testLinkDirFile;
-    private static File missingFile; // non-existent file
+    private static File missingFileFile; // non-existent file
+    private static Path missingFilePath; // non-existent file
     private static SymbolicLinkFileFilter filter;
 
     // Mock filter for testing on Windows.
@@ -127,7 +128,8 @@ static void testSetup() throws IOException {
         targetDirFile = targetDirPath.toFile();
         testLinkDirPath = symbolicLinkCreator.apply(parentDirectoryPath.resolve(DIRECTORY_LINK_NAME), targetDirPath);
         testLinkDirFile = testLinkDirPath.toFile();
-        missingFile = new File(parentDirectoryPath.toFile(), MISSING);
+        missingFileFile = new File(parentDirectoryPath.toFile(), MISSING);
+        missingFilePath = missingFileFile.toPath();
     }
 
     @Test
@@ -147,7 +149,7 @@ public void testFileFilter_Link() {
 
     @Test
     public void testFileFilter_missingFile() {
-        assertFalse(filter.accept(missingFile));
+        assertFalse(filter.accept(missingFileFile));
     }
 
     @Test
@@ -183,30 +185,37 @@ public void testFileNameFilter_PathLink() {
     @Test
     public void testPathFilter_HardDirectory() {
         assertEquals(FileVisitResult.TERMINATE, filter.accept(targetDirPath, null));
+        assertFalse(filter.matches(targetDirPath));
     }
 
     @Test
     public void testPathFilter_HardFile() {
         assertEquals(FileVisitResult.TERMINATE, filter.accept(testTargetPath, null));
+        assertFalse(filter.matches(testTargetPath));
     }
 
     @Test
     public void testPathFilter_Link() {
         assertEquals(FileVisitResult.CONTINUE, filter.accept(testLinkPath, null));
+        assertTrue(filter.matches(testLinkPath));
+
     }
 
     @Test
     public void testPathFilter_missingFile() {
-        assertEquals(FileVisitResult.TERMINATE, filter.accept(missingFile.toPath(), null));
+        assertEquals(FileVisitResult.TERMINATE, filter.accept(missingFilePath, null));
+        assertFalse(filter.matches(missingFilePath));
     }
 
     @Test
     public void testPathFilter_PathLink() {
         assertEquals(FileVisitResult.CONTINUE, filter.accept(testLinkDirPath, null));
+        assertTrue(filter.matches(testLinkDirPath));
     }
 
     @Test
     public void testSymbolicLinkFileFilter() {
         assertEquals(FileVisitResult.TERMINATE, SymbolicLinkFileFilter.INSTANCE.accept(PathUtils.current(), null));
+        assertFalse(filter.matches(PathUtils.current()));
     }
 }
diff --git a/src/test/java/org/apache/commons/io/filefilter/WildcardFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/WildcardFileFilterTest.java
index b9ab69c..44ee1fa 100644
--- a/src/test/java/org/apache/commons/io/filefilter/WildcardFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/WildcardFileFilterTest.java
@@ -23,6 +23,7 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
+import java.io.IOException;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -34,7 +35,7 @@
 public class WildcardFileFilterTest extends AbstractFilterTest {
 
     @Test
-    public void testWildcard() {
+    public void testWildcard() throws IOException {
         IOFileFilter filter = new WildcardFileFilter("*.txt");
         assertFiltering(filter, new File("log.txt"), true);
         assertFiltering(filter, new File("log.TXT"), false);
@@ -138,13 +139,18 @@ public void testWildcard() {
         final Path bmpPath = bmpFile.toPath();
         final File dirFile = new File("src/java");
         final Path dirPath = dirFile.toPath();
+        // File
         assertTrue(listFilter.accept(txtFile));
         assertFalse(listFilter.accept(bmpFile));
         assertFalse(listFilter.accept(dirFile));
+        // Path
+        assertTrue(listFilter.matches(txtPath));
+        assertFalse(listFilter.matches(bmpPath));
+        assertFalse(listFilter.matches(dirPath));
         //
-        assertEquals(FileVisitResult.CONTINUE, listFilter.accept(txtFile.toPath(), null));
-        assertEquals(FileVisitResult.TERMINATE, listFilter.accept(bmpFile.toPath(), null));
-        assertEquals(FileVisitResult.TERMINATE, listFilter.accept(dirFile.toPath(), null));
+        assertEquals(FileVisitResult.CONTINUE, listFilter.accept(txtPath, null));
+        assertEquals(FileVisitResult.TERMINATE, listFilter.accept(bmpPath, null));
+        assertEquals(FileVisitResult.TERMINATE, listFilter.accept(dirPath, null));
 
         assertTrue(listFilter.accept(txtFile.getParentFile(), txtFile.getName()));
         assertFalse(listFilter.accept(bmpFile.getParentFile(), bmpFile.getName()));
diff --git a/src/test/java/org/apache/commons/io/function/IOBaseStreamTest.java b/src/test/java/org/apache/commons/io/function/IOBaseStreamTest.java
index 45f68c9..1c05418 100644
--- a/src/test/java/org/apache/commons/io/function/IOBaseStreamTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOBaseStreamTest.java
@@ -107,14 +107,14 @@ public void beforeEach() {
         baseStream = createStreamOfPaths();
         ioBaseStream = createIOBaseStream();
         ioBaseStreamPath = createIOBaseStreamPath();
-        ioBaseStreamAdapter = createIOBaseStreamApapter();
+        ioBaseStreamAdapter = createIOBaseStreamAdapter();
     }
 
     private IOBaseStreamFixture<Path, ?, Stream<Path>> createIOBaseStream() {
         return new IOBaseStreamFixture<>(createStreamOfPaths());
     }
 
-    private IOStream<Path> createIOBaseStreamApapter() {
+    private IOStream<Path> createIOBaseStreamAdapter() {
         return IOStreamAdapter.adapt(createStreamOfPaths());
     }
 
diff --git a/src/test/java/org/apache/commons/io/function/IOIntSupplierTest.java b/src/test/java/org/apache/commons/io/function/IOIntSupplierTest.java
new file mode 100644
index 0000000..b23f8f6
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/function/IOIntSupplierTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link IOIntSupplier}.
+ */
+public class IOIntSupplierTest {
+
+    private AtomicInteger atomicInt;
+
+    private int getThrowsIO(final IOIntSupplier supplier) throws IOException {
+        return supplier.getAsInt();
+    }
+
+    private int getThrowsNone(final IOIntSupplier supplier) {
+        return supplier.asIntSupplier().getAsInt();
+    }
+
+    @BeforeEach
+    public void initEach() {
+        atomicInt = new AtomicInteger();
+    }
+
+    @Test
+    public void testAsSupplier() {
+        assertThrows(UncheckedIOException.class, () -> TestConstants.THROWING_IO_INT_SUPPLIER.asIntSupplier().getAsInt());
+        assertEquals(1, getThrowsNone(() -> TestUtils.compareAndSetThrowsIO(atomicInt, 1)));
+        assertEquals(1, atomicInt.get());
+        assertNotEquals(TestConstants.THROWING_IO_INT_SUPPLIER.asIntSupplier(), TestConstants.THROWING_IO_INT_SUPPLIER.asIntSupplier());
+    }
+
+    @Test
+    public void testGet() throws IOException {
+        assertThrows(IOException.class, () -> TestConstants.THROWING_IO_INT_SUPPLIER.getAsInt());
+        assertThrows(IOException.class, () -> {
+            throw new IOException();
+        });
+        assertEquals(1, getThrowsIO(() -> TestUtils.compareAndSetThrowsIO(atomicInt, 1)));
+        assertEquals(1, atomicInt.get());
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/io/function/IOLongSupplierTest.java b/src/test/java/org/apache/commons/io/function/IOLongSupplierTest.java
new file mode 100644
index 0000000..07748de
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/function/IOLongSupplierTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.io.function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link IOLongSupplier}.
+ */
+public class IOLongSupplierTest {
+
+    private AtomicLong atomicLong;
+
+    private long getThrowsIO(final IOLongSupplier supplier) throws IOException {
+        return supplier.getAsLong();
+    }
+
+    private long getThrowsNone(final IOLongSupplier supplier) {
+        return supplier.asSupplier().getAsLong();
+    }
+
+    @BeforeEach
+    public void initEach() {
+        atomicLong = new AtomicLong();
+    }
+
+    @Test
+    public void testAsSupplier() throws IOException {
+        assertThrows(UncheckedIOException.class, () -> TestConstants.THROWING_IO_LONG_SUPPLIER.asSupplier().getAsLong());
+        assertEquals(1L, getThrowsNone(() -> TestUtils.compareAndSetThrowsIO(atomicLong, 1L)));
+        assertEquals(1L, atomicLong.get());
+        assertNotEquals(TestConstants.THROWING_IO_LONG_SUPPLIER.asSupplier(), TestConstants.THROWING_IO_LONG_SUPPLIER.asSupplier());
+    }
+
+    @Test
+    public void testGet() throws IOException {
+        assertThrows(IOException.class, () -> TestConstants.THROWING_IO_LONG_SUPPLIER.getAsLong());
+        assertThrows(IOException.class, () -> {
+            throw new IOException();
+        });
+        assertEquals(1L, getThrowsIO(() -> TestUtils.compareAndSetThrowsIO(atomicLong, 1L)));
+        assertEquals(1L, atomicLong.get());
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/io/function/IOStreamTest.java b/src/test/java/org/apache/commons/io/function/IOStreamTest.java
index 4fb8abe..198a361 100644
--- a/src/test/java/org/apache/commons/io/function/IOStreamTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOStreamTest.java
@@ -214,7 +214,7 @@ public void testFlatMapToLong() throws IOException {
     public void testForaAllIOConsumer() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer()));
-        // compile vs inlnine
+        // compile vs inline
         assertThrows(IOException.class, () -> IOStream.of("A").forAll(e -> {
             throw new IOException("Failure");
         }));
@@ -229,7 +229,7 @@ public void testForaAllIOConsumer() throws IOException {
     public void testForaAllIOConsumerBiFunction() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer(), (i, e) -> e));
-        // compile vs inlnine
+        // compile vs inline
         assertThrows(IOException.class, () -> IOStream.of("A").forAll(e -> {
             throw new IOException("Failure");
         }, (i, e) -> e));
@@ -244,7 +244,7 @@ public void testForaAllIOConsumerBiFunction() throws IOException {
     public void testForaAllIOConsumerBiFunctionNull() throws IOException {
         // compile vs type
         assertDoesNotThrow(() -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer(), null));
-        // compile vs inlnine
+        // compile vs inline
         assertDoesNotThrow(() -> IOStream.of("A").forAll(e -> {
             throw new IOException("Failure");
         }, null));
@@ -259,7 +259,7 @@ public void testForaAllIOConsumerBiFunctionNull() throws IOException {
     public void testForEachIOConsumerOfQsuperT() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
-        // compile vs inlnine
+        // compile vs inline
         assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
             throw new IOException("Failure");
         }));
@@ -274,7 +274,7 @@ public void testForEachIOConsumerOfQsuperT() throws IOException {
     public void testForEachOrdered() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
-        // compile vs inlnine
+        // compile vs inline
         assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
             throw new IOException("Failure");
         }));
diff --git a/src/test/java/org/apache/commons/io/function/IOSupplierTest.java b/src/test/java/org/apache/commons/io/function/IOSupplierTest.java
index 4b18f48..7a8e94f 100644
--- a/src/test/java/org/apache/commons/io/function/IOSupplierTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOSupplierTest.java
@@ -35,7 +35,7 @@ public class IOSupplierTest {
 
     private AtomicReference<String> ref1;
 
-    private String getThrows(final IOSupplier<String> supplier) throws IOException {
+    private String getThrowsIO(final IOSupplier<String> supplier) throws IOException {
         return supplier.get();
     }
 
@@ -62,7 +62,7 @@ public void testGet() throws IOException {
         assertThrows(IOException.class, () -> {
             throw new IOException();
         });
-        assertEquals("new1", getThrows(() -> TestUtils.compareAndSetThrowsIO(ref1, "new1")));
+        assertEquals("new1", getThrowsIO(() -> TestUtils.compareAndSetThrowsIO(ref1, "new1")));
         assertEquals("new1", ref1.get());
     }
 
diff --git a/src/test/java/org/apache/commons/io/function/TestConstants.java b/src/test/java/org/apache/commons/io/function/TestConstants.java
index 5f0581c..bafe7ef 100644
--- a/src/test/java/org/apache/commons/io/function/TestConstants.java
+++ b/src/test/java/org/apache/commons/io/function/TestConstants.java
@@ -44,6 +44,10 @@ class TestConstants {
 
     static IOFunction<Object, Object> THROWING_IO_FUNCTION = t -> throwIOException();
 
+    static IOIntSupplier THROWING_IO_INT_SUPPLIER = () -> throwIOException();
+
+    static IOLongSupplier THROWING_IO_LONG_SUPPLIER = () -> throwIOException();
+
     static IOPredicate<Object> THROWING_IO_PREDICATE = t -> throwIOException();
 
     static IOQuadFunction<Object, Object, Object, Object, Object> THROWING_IO_QUAD_FUNCTION = (t, u, v, w) -> throwIOException();
diff --git a/src/test/java/org/apache/commons/io/function/TestUtils.java b/src/test/java/org/apache/commons/io/function/TestUtils.java
index 9b6f3aa..9d05f02 100644
--- a/src/test/java/org/apache/commons/io/function/TestUtils.java
+++ b/src/test/java/org/apache/commons/io/function/TestUtils.java
@@ -18,10 +18,34 @@
 package org.apache.commons.io.function;
 
 import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
 class TestUtils {
 
+    static int compareAndSetThrowsIO(final AtomicInteger ref, final int update) throws IOException {
+        return compareAndSetThrowsIO(ref, 0, update);
+    }
+
+    static int compareAndSetThrowsIO(final AtomicInteger ref, final int expected, final int update) throws IOException {
+        if (!ref.compareAndSet(expected, update)) {
+            throw new IOException("Unexpected");
+        }
+        return ref.get(); // same as update
+    }
+
+    static long compareAndSetThrowsIO(final AtomicLong ref, final long update) throws IOException {
+        return compareAndSetThrowsIO(ref, 0, update);
+    }
+
+    static long compareAndSetThrowsIO(final AtomicLong ref, final long expected, final long update) throws IOException {
+        if (!ref.compareAndSet(expected, update)) {
+            throw new IOException("Unexpected");
+        }
+        return ref.get(); // same as update
+    }
+
     static <T> T compareAndSetThrowsIO(final AtomicReference<T> ref, final T update) throws IOException {
         return compareAndSetThrowsIO(ref, null, update);
     }
diff --git a/src/test/java/org/apache/commons/io/function/UncheckTest.java b/src/test/java/org/apache/commons/io/function/UncheckTest.java
index ee73abc..3eff77e 100644
--- a/src/test/java/org/apache/commons/io/function/UncheckTest.java
+++ b/src/test/java/org/apache/commons/io/function/UncheckTest.java
@@ -20,11 +20,17 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
+import org.apache.commons.io.input.BrokenInputStream;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -33,18 +39,46 @@
  */
 public class UncheckTest {
 
+    private static final byte[] BYTES = { 'a', 'b' };
+    private static final String CAUSE_MESSAGE = "CauseMessage";
+    private static final String CUSTOM_MESSAGE = "Custom message";
+
+    private AtomicInteger atomicInt;
+    private AtomicLong atomicLong;
     private AtomicReference<String> ref1;
     private AtomicReference<String> ref2;
     private AtomicReference<String> ref3;
-
     private AtomicReference<String> ref4;
 
+    private void assertUncheckedIOException(final IOException expected, final UncheckedIOException e) {
+        assertEquals(CUSTOM_MESSAGE, e.getMessage());
+        final IOException cause = e.getCause();
+        assertEquals(expected.getClass(), cause.getClass());
+        assertEquals(CAUSE_MESSAGE, cause.getMessage());
+    }
+
     @BeforeEach
     public void initEach() {
         ref1 = new AtomicReference<>();
         ref2 = new AtomicReference<>();
         ref3 = new AtomicReference<>();
         ref4 = new AtomicReference<>();
+        atomicInt = new AtomicInteger();
+        atomicLong = new AtomicLong();
+    }
+
+    private ByteArrayInputStream newInputStream() {
+        return new ByteArrayInputStream(BYTES);
+    }
+
+    /**
+     * Tests {@link Uncheck#accept(IOConsumer, Object)}.
+     */
+    @Test
+    public void testAccept() {
+        final ByteArrayInputStream stream = newInputStream();
+        Uncheck.accept(n -> stream.skip(n), 1);
+        assertEquals('b', Uncheck.get(stream::read).intValue());
     }
 
     @Test
@@ -87,6 +121,38 @@ public void testAcceptIOTriConsumerOfTUVTUV() {
         assertEquals("new3", ref3.get());
     }
 
+    /**
+     * Tests {@link Uncheck#apply(IOFunction, Object)}.
+     */
+    @Test
+    public void testApply1() {
+        final ByteArrayInputStream stream = newInputStream();
+        assertEquals(1, Uncheck.apply(n -> stream.skip(n), 1).intValue());
+        assertEquals('b', Uncheck.get(stream::read).intValue());
+    }
+
+    /**
+     * Tests {@link Uncheck#apply(IOBiFunction, Object, Object)}.
+     */
+    @Test
+    public void testApply2() {
+        final ByteArrayInputStream stream = newInputStream();
+        final byte[] buf = new byte[BYTES.length];
+        assertEquals(1, Uncheck.apply((o, l) -> stream.read(buf, o, l), 0, 1).intValue());
+        assertEquals('a', buf[0]);
+    }
+
+    /**
+     * Tests {@link Uncheck#apply(IOTriFunction, Object, Object, Object)}.
+     */
+    @Test
+    public void testApply3() {
+        final ByteArrayInputStream stream = newInputStream();
+        final byte[] buf = new byte[BYTES.length];
+        assertEquals(1, Uncheck.apply((b, o, l) -> stream.read(b, o, l), buf, 0, 1).intValue());
+        assertEquals('a', buf[0]);
+    }
+
     @Test
     public void testApplyIOBiFunctionOfTURTU() {
         assertThrows(UncheckedIOException.class, () -> Uncheck.apply((t, u) -> {
@@ -148,8 +214,12 @@ public void testApplyIOTriFunctionOfTUVRTUV() {
         assertEquals("new3", ref3.get());
     }
 
+    /**
+     * Tests {@link Uncheck#get(IOSupplier)}.
+     */
     @Test
     public void testGet() {
+        assertEquals('a', Uncheck.get(() -> newInputStream().read()).intValue());
         assertThrows(UncheckedIOException.class, () -> Uncheck.get(() -> {
             throw new IOException();
         }));
@@ -159,7 +229,89 @@ public void testGet() {
     }
 
     @Test
+    public void testGetAsInt() {
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsInt(() -> {
+            throw new IOException();
+        }));
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsInt(TestConstants.THROWING_IO_INT_SUPPLIER));
+        assertEquals(1, Uncheck.getAsInt(() -> TestUtils.compareAndSetThrowsIO(atomicInt, 1)));
+        assertEquals(1, atomicInt.get());
+    }
+
+    @Test
+    public void testGetAsIntMessage() {
+        // No exception
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsInt(() -> {
+            throw new IOException();
+        }, () -> CUSTOM_MESSAGE));
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsInt(TestConstants.THROWING_IO_INT_SUPPLIER, () -> CUSTOM_MESSAGE));
+        assertEquals(1, Uncheck.getAsInt(() -> TestUtils.compareAndSetThrowsIO(atomicInt, 1), () -> CUSTOM_MESSAGE));
+        assertEquals(1, atomicInt.get());
+        // exception
+        final IOException expected = new IOException(CAUSE_MESSAGE);
+        try {
+            Uncheck.getAsInt(() -> new BrokenInputStream(expected).read(), () -> CUSTOM_MESSAGE);
+            fail();
+        } catch (final UncheckedIOException e) {
+            assertUncheckedIOException(expected, e);
+        }
+    }
+
+    @Test
+    public void testGetAsLong() {
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsLong(() -> {
+            throw new IOException();
+        }));
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsLong(TestConstants.THROWING_IO_LONG_SUPPLIER));
+        assertEquals(1L, Uncheck.getAsLong(() -> TestUtils.compareAndSetThrowsIO(atomicLong, 1L)));
+        assertEquals(1L, atomicLong.get());
+    }
+
+    @Test
+    public void testGetAsLongMessage() {
+        // No exception
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsLong(() -> {
+            throw new IOException();
+        }, () -> CUSTOM_MESSAGE));
+        assertThrows(UncheckedIOException.class, () -> Uncheck.getAsLong(TestConstants.THROWING_IO_LONG_SUPPLIER, () -> CUSTOM_MESSAGE));
+        assertEquals(1L, Uncheck.getAsLong(() -> TestUtils.compareAndSetThrowsIO(atomicLong, 1L), () -> CUSTOM_MESSAGE));
+        assertEquals(1L, atomicLong.get());
+        // exception
+        final IOException expected = new IOException(CAUSE_MESSAGE);
+        try {
+            Uncheck.getAsLong(() -> new BrokenInputStream(expected).read(), () -> CUSTOM_MESSAGE);
+            fail();
+        } catch (final UncheckedIOException e) {
+            assertUncheckedIOException(expected, e);
+        }
+    }
+
+    /**
+     * Tests {@link Uncheck#get(IOSupplier, Supplier)}.
+     */
+    @Test
+    public void testGetMessage() {
+        // No exception
+        assertEquals('a', Uncheck.get(() -> newInputStream().read()).intValue(), () -> CUSTOM_MESSAGE);
+        // Exception
+        final IOException expected = new IOException(CAUSE_MESSAGE);
+        try {
+            Uncheck.get(() -> new BrokenInputStream(expected).read(), () -> CUSTOM_MESSAGE);
+            fail();
+        } catch (final UncheckedIOException e) {
+            assertUncheckedIOException(expected, e);
+        }
+    }
+
+    /**
+     * Tests {@link Uncheck#run(IORunnable)}.
+     */
+    @Test
     public void testRun() {
+        final ByteArrayInputStream stream = newInputStream();
+        Uncheck.run(() -> stream.skip(1));
+        assertEquals('b', Uncheck.get(stream::read).intValue());
+        //
         assertThrows(UncheckedIOException.class, () -> Uncheck.run(() -> {
             throw new IOException();
         }));
@@ -168,6 +320,27 @@ public void testRun() {
         assertEquals("new1", ref1.get());
     }
 
+    /**
+     * Tests {@link Uncheck#run(IORunnable, Supplier))}.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void testRunMessage() throws IOException {
+        // No exception
+        final ByteArrayInputStream stream = newInputStream();
+        Uncheck.run(() -> stream.skip(1), () -> CUSTOM_MESSAGE);
+        assertEquals('b', Uncheck.get(stream::read).intValue());
+        final IOException expected = new IOException(CAUSE_MESSAGE);
+        // Exception
+        try {
+            Uncheck.run(() -> new BrokenInputStream(expected).read(), () -> CUSTOM_MESSAGE);
+            fail();
+        } catch (final UncheckedIOException e) {
+            assertUncheckedIOException(expected, e);
+        }
+    }
+
     @Test
     public void testTest() {
         assertThrows(UncheckedIOException.class, () -> Uncheck.test(t -> {
diff --git a/src/test/java/org/apache/commons/io/input/CharSequenceInputStreamTest.java b/src/test/java/org/apache/commons/io/input/CharSequenceInputStreamTest.java
index 97c3f1d..7bc997e 100644
--- a/src/test/java/org/apache/commons/io/input/CharSequenceInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/CharSequenceInputStreamTest.java
@@ -425,19 +425,6 @@ public void testResetBeforeEndSetCharSequence() throws IOException {
         }
     }
 
-    private void testSingleByteRead(final String testString, final String charsetName) throws IOException {
-        final byte[] bytes = testString.getBytes(charsetName);
-        try (InputStream in = new CharSequenceInputStream(testString, charsetName, 512)) {
-            for (final byte b : bytes) {
-                final int read = in.read();
-                assertTrue(read >= 0, "read " + read + " >=0 ");
-                assertTrue(read <= 255, "read " + read + " <= 255");
-                assertEquals(b, (byte) read, "Should agree with input");
-            }
-            assertEquals(-1, in.read());
-        }
-    }
-
     @Test
     public void testResetCharset() {
         assertNotNull(CharSequenceInputStream.builder().setReader(new StringReader("\uD800")).setCharset((Charset) null).getCharset());
@@ -453,6 +440,19 @@ public void testResetCharsetName() {
         assertNotNull(CharSequenceInputStream.builder().setReader(new StringReader("\uD800")).setCharset((String) null).getCharset());
     }
 
+    private void testSingleByteRead(final String testString, final String charsetName) throws IOException {
+        final byte[] bytes = testString.getBytes(charsetName);
+        try (InputStream in = new CharSequenceInputStream(testString, charsetName, 512)) {
+            for (final byte b : bytes) {
+                final int read = in.read();
+                assertTrue(read >= 0, "read " + read + " >=0 ");
+                assertTrue(read <= 255, "read " + read + " <= 255");
+                assertEquals(b, (byte) read, "Should agree with input");
+            }
+            assertEquals(-1, in.read());
+        }
+    }
+
     @Test
     public void testSingleByteRead_RequiredCharsets() throws IOException {
         for (final String csName : getRequiredCharsetNames()) {
diff --git a/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java b/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java
index 77f3c07..219c530 100644
--- a/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/NullInputStreamTest.java
@@ -20,7 +20,6 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.EOFException;
 import java.io.IOException;
@@ -75,12 +74,8 @@ public void testMarkAndReset() throws Exception {
             assertTrue(input.markSupported(), "Mark Should be Supported");
 
             // No Mark
-            try {
-                input.reset();
-                fail("Read limit exceeded, expected IOException ");
-            } catch (final IOException e) {
-                assertEquals("No position has been marked", e.getMessage(), "No Mark IOException message");
-            }
+            final IOException noMarkException = assertThrows(IOException.class, input::reset);
+            assertEquals("No position has been marked", noMarkException.getMessage(), "No Mark IOException message");
 
             for (; position < 3; position++) {
                 assertEquals(position, input.read(), "Read Before Mark [" + position + "]");
@@ -103,13 +98,9 @@ public void testMarkAndReset() throws Exception {
             }
 
             // Reset after read limit passed
-            try {
-                input.reset();
-                fail("Read limit exceeded, expected IOException ");
-            } catch (final IOException e) {
-                assertEquals("Marked position [" + position + "] is no longer valid - passed the read limit [" + readlimit + "]", e.getMessage(),
+            final IOException resetException = assertThrows(IOException.class, input::reset, "Read limit exceeded, expected IOException");
+            assertEquals("Marked position [" + position + "] is no longer valid - passed the read limit [" + readlimit + "]", resetException.getMessage(),
                     "Read limit IOException message");
-            }
         }
     }
 
@@ -118,19 +109,11 @@ public void testMarkNotSupported() throws Exception {
         final InputStream input = new TestNullInputStream(100, false, true);
         assertFalse(input.markSupported(), "Mark Should NOT be Supported");
 
-        try {
-            input.mark(5);
-            fail("mark() should throw UnsupportedOperationException");
-        } catch (final UnsupportedOperationException e) {
-            assertEquals(MARK_RESET_NOT_SUPPORTED, e.getMessage(), "mark() error message");
-        }
+        final UnsupportedOperationException markException = assertThrows(UnsupportedOperationException.class, () -> input.mark(5));
+        assertEquals(MARK_RESET_NOT_SUPPORTED, markException.getMessage(), "mark() error message");
 
-        try {
-            input.reset();
-            fail("reset() should throw UnsupportedOperationException");
-        } catch (final UnsupportedOperationException e) {
-            assertEquals(MARK_RESET_NOT_SUPPORTED, e.getMessage(), "reset() error message");
-        }
+        final UnsupportedOperationException resetException = assertThrows(UnsupportedOperationException.class, input::reset);
+        assertEquals(MARK_RESET_NOT_SUPPORTED, resetException.getMessage(), "reset() error message");
         input.close();
     }
 
@@ -149,12 +132,8 @@ public void testRead() throws Exception {
         assertEquals(0, input.available(), "Available after End of File");
 
         // Test reading after the end of file
-        try {
-            final int result = input.read();
-            fail("Should have thrown an IOException, byte=[" + result + "]");
-        } catch (final IOException e) {
-            assertEquals("Read after end of file", e.getMessage());
-        }
+        final IOException e = assertThrows(IOException.class, input::read);
+        assertEquals("Read after end of file", e.getMessage());
 
         // Close - should reset
         input.close();
@@ -185,12 +164,8 @@ public void testReadByteArray() throws Exception {
         assertEquals(-1, count3, "Read 3 (EOF)");
 
         // Test reading after the end of file
-        try {
-            final int count4 = input.read(bytes);
-            fail("Should have thrown an IOException, byte=[" + count4 + "]");
-        } catch (final IOException e) {
-            assertEquals("Read after end of file", e.getMessage());
-        }
+        final IOException e = assertThrows(IOException.class, () -> input.read(bytes));
+        assertEquals("Read after end of file", e.getMessage());
 
         // reset by closing
         input.close();
@@ -214,12 +189,9 @@ public void testSkip() throws Exception {
         assertEquals(7, input.read(), "Read 3");
         assertEquals(2, input.skip(5), "Skip 2"); // only 2 left to skip
         assertEquals(-1, input.skip(5), "Skip 3 (EOF)"); // End of file
-        try {
-            input.skip(5); //
-            fail("Expected IOException for skipping after end of file");
-        } catch (final IOException e) {
-            assertEquals("Skip after end of file", e.getMessage(), "Skip after EOF IOException message");
-        }
+
+        final IOException e = assertThrows(IOException.class, () -> input.skip(5), "Expected IOException for skipping after end of file");
+        assertEquals("Skip after end of file", e.getMessage(), "Skip after EOF IOException message");
         input.close();
     }
 }
diff --git a/src/test/java/org/apache/commons/io/input/NullReaderTest.java b/src/test/java/org/apache/commons/io/input/NullReaderTest.java
index d2d055f..236a0f9 100644
--- a/src/test/java/org/apache/commons/io/input/NullReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/NullReaderTest.java
@@ -75,12 +75,8 @@ public void testMarkAndReset() throws Exception {
             assertTrue(reader.markSupported(), "Mark Should be Supported");
 
             // No Mark
-            try {
-                reader.reset();
-                fail("Read limit exceeded, expected IOException ");
-            } catch (final IOException e) {
-                assertEquals("No position has been marked", e.getMessage(), "No Mark IOException message");
-            }
+            final IOException resetException = assertThrows(IOException.class, reader::reset);
+            assertEquals("No position has been marked", resetException.getMessage(), "No Mark IOException message");
 
             for (; position < 3; position++) {
                 assertEquals(position, reader.read(), "Read Before Mark [" + position + "]");
@@ -103,13 +99,9 @@ public void testMarkAndReset() throws Exception {
             }
 
             // Reset after read limit passed
-            try {
-                reader.reset();
-                fail("Read limit exceeded, expected IOException ");
-            } catch (final IOException e) {
-                assertEquals("Marked position [" + position + "] is no longer valid - passed the read limit [" + readlimit + "]", e.getMessage(),
+            final IOException e = assertThrows(IOException.class, reader::reset);
+            assertEquals("Marked position [" + position + "] is no longer valid - passed the read limit [" + readlimit + "]", e.getMessage(),
                     "Read limit IOException message");
-            }
         }
     }
 
@@ -211,12 +203,9 @@ public void testSkip() throws Exception {
             assertEquals(7, reader.read(), "Read 3");
             assertEquals(2, reader.skip(5), "Skip 2"); // only 2 left to skip
             assertEquals(-1, reader.skip(5), "Skip 3 (EOF)"); // End of file
-            try {
-                reader.skip(5); //
-                fail("Expected IOException for skipping after end of file");
-            } catch (final IOException e) {
-                assertEquals("Skip after end of file", e.getMessage(), "Skip after EOF IOException message");
-            }
+
+            final IOException e = assertThrows(IOException.class, () -> reader.skip(5));
+            assertEquals("Skip after end of file", e.getMessage(), "Skip after EOF IOException message");
         }
     }
 }
diff --git a/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java b/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
index db104da..7aaaa3d 100644
--- a/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
@@ -36,12 +36,16 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Stream;
 
+import javax.xml.parsers.DocumentBuilderFactory;
+
 import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 public class ReaderInputStreamTest {
 
@@ -200,6 +204,22 @@ public void testConstructNullCharsetNameEncoder() throws IOException {
     }
 
     @Test
+    public void testIo803SAXException() throws IOException {
+        final StringReader reader = new StringReader("");
+        try (final ReaderInputStream inputStream = ReaderInputStream.builder().setCharset(StandardCharsets.UTF_8).setReader(reader).get()) {
+            final InputSource inputSource = new InputSource(inputStream);
+            assertThrows(SAXException.class, () -> DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSource));
+        }
+    }
+
+    @Test
+    public void testIo803StringReaderSanityCheck() {
+        final StringReader reader = new StringReader("");
+        final InputSource inputSource = new InputSource(reader);
+        assertThrows(SAXException.class, () -> DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSource));
+    }
+
+    @Test
     public void testLargeUTF8WithBufferedRead() throws IOException {
         testWithBufferedRead(LARGE_TEST_STRING, UTF_8);
     }
@@ -209,6 +229,17 @@ public void testLargeUTF8WithSingleByteRead() throws IOException {
         testWithSingleByteRead(LARGE_TEST_STRING, UTF_8);
     }
 
+    @Test
+    public void testReadEofTwice() throws IOException {
+        try (ReaderInputStream reader = ReaderInputStream.builder().setCharset(StandardCharsets.UTF_8).setReader(new StringReader("123")).get()) {
+            assertEquals('1', reader.read());
+            assertEquals('2', reader.read());
+            assertEquals('3', reader.read());
+            assertEquals(-1, reader.read());
+            assertEquals(-1, reader.read());
+        }
+    }
+
     @SuppressWarnings("deprecation")
     @Test
     public void testReadZero() throws Exception {
diff --git a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
index f66907b..71795e0 100644
--- a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
+++ b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
@@ -121,14 +121,19 @@ public void testDataIntegrityWithBufferedReader(final String fileName, final Str
             filePath = Files.copy(filePath, fileSystem.getPath("/" + fileName));
         }
 
-        // We want to test null Charset in the ReversedLinesFileReaderconstructor.
+        // We want to test null Charset in the ReversedLinesFileReader constructor.
         final Charset charset = charsetName != null ? Charset.forName(charsetName) : null;
         try (ReversedLinesFileReader reversedLinesFileReader = blockSize == null ? new ReversedLinesFileReader(filePath, charset)
                 : new ReversedLinesFileReader(filePath, blockSize, charset)) {
             testDataIntegrityWithBufferedReader(filePath, fileSystem, charset, reversedLinesFileReader);
         }
-        try (ReversedLinesFileReader reversedLinesFileReader = ReversedLinesFileReader.builder().setPath(filePath).setBufferSize(blockSize).setCharset(charset)
+        // @formatter:off
+        try (ReversedLinesFileReader reversedLinesFileReader = ReversedLinesFileReader.builder()
+                .setPath(filePath)
+                .setBufferSize(blockSize)
+                .setCharset(charset)
                 .get()) {
+            // @formatter:on
             testDataIntegrityWithBufferedReader(filePath, fileSystem, charset, reversedLinesFileReader);
         }
     }
diff --git a/src/test/java/org/apache/commons/io/input/TaggedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/TaggedInputStreamTest.java
index 01fbbb9..a61c0fc 100644
--- a/src/test/java/org/apache/commons/io/input/TaggedInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/TaggedInputStreamTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -41,46 +41,22 @@ public void testBrokenStream() {
             new TaggedInputStream(new BrokenInputStream(exception));
 
         // Test the available() method
-        try {
-            stream.available();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(stream.isCauseOf(e));
-            try {
-                stream.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException exceptionAvailable = assertThrows(IOException.class, stream::available);
+        assertTrue(stream.isCauseOf(exceptionAvailable));
+        final IOException exceptionAvailableCause = assertThrows(IOException.class, () -> stream.throwIfCauseOf(exceptionAvailable));
+        assertEquals(exception, exceptionAvailableCause);
 
         // Test the read() method
-        try {
-            stream.read();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(stream.isCauseOf(e));
-            try {
-                stream.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException exceptionRead = assertThrows(IOException.class, stream::read);
+        assertTrue(stream.isCauseOf(exceptionRead));
+        final IOException exceptionReadCause = assertThrows(IOException.class, () -> stream.throwIfCauseOf(exceptionRead));
+        assertEquals(exception, exceptionReadCause);
 
         // Test the close() method
-        try {
-            stream.close();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(stream.isCauseOf(e));
-            try {
-                stream.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException exceptionClose = assertThrows(IOException.class, stream::close);
+        assertTrue(stream.isCauseOf(exceptionClose));
+        final IOException exceptionCloseCause = assertThrows(IOException.class, () -> stream.throwIfCauseOf(exceptionClose));
+        assertEquals(exception, exceptionCloseCause);
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java b/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java
index 63d23bf..e5bd83c 100644
--- a/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java
@@ -18,8 +18,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.IOException;
 import java.io.Reader;
@@ -40,46 +40,22 @@ public void testBrokenReader() {
         final TaggedReader reader = new TaggedReader(new BrokenReader(exception));
 
         // Test the ready() method
-        try {
-            reader.ready();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(reader.isCauseOf(e));
-            try {
-                reader.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException readyException = assertThrows(IOException.class, reader::ready);
+        assertTrue(reader.isCauseOf(readyException));
+        final IOException rethrownReadyException = assertThrows(IOException.class, () -> reader.throwIfCauseOf(readyException));
+        assertEquals(exception, rethrownReadyException);
 
         // Test the read() method
-        try {
-            reader.read();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(reader.isCauseOf(e));
-            try {
-                reader.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException readException = assertThrows(IOException.class, reader::read);
+        assertTrue(reader.isCauseOf(readException));
+        final IOException rethrownReadException = assertThrows(IOException.class, () -> reader.throwIfCauseOf(readException));
+        assertEquals(exception, rethrownReadException);
 
         // Test the close() method
-        try {
-            reader.close();
-            fail("Expected exception not thrown.");
-        } catch (final IOException e) {
-            assertTrue(reader.isCauseOf(e));
-            try {
-                reader.throwIfCauseOf(e);
-                fail("Expected exception not thrown.");
-            } catch (final IOException e2) {
-                assertEquals(exception, e2);
-            }
-        }
+        final IOException closeException = assertThrows(IOException.class, reader::close);
+        assertTrue(reader.isCauseOf(closeException));
+        final IOException rethrownCloseException = assertThrows(IOException.class, () -> reader.throwIfCauseOf(closeException));
+        assertEquals(exception, rethrownCloseException);
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/input/TailerTest.java b/src/test/java/org/apache/commons/io/input/TailerTest.java
index a6315eb..f0e1f34 100644
--- a/src/test/java/org/apache/commons/io/input/TailerTest.java
+++ b/src/test/java/org/apache/commons/io/input/TailerTest.java
@@ -266,7 +266,7 @@ public void testCreate() throws Exception {
     }
 
     @Test
-    public void testCreaterWithDelayAndFromStartWithReopen() throws Exception {
+    public void testCreatorWithDelayAndFromStartWithReopen() throws Exception {
         final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen.txt");
         createFile(file, 0);
         final TestTailerListener listener = new TestTailerListener(1);
@@ -326,7 +326,7 @@ public void testCreateWithDelayAndFromStartWithReopenAndBufferSizeAndCharset() t
     }
 
     /*
-     * Tests [IO-357][Tailer] InterruptedException while the thead is sleeping is silently ignored.
+     * Tests [IO-357][Tailer] InterruptedException while the thread is sleeping is silently ignored.
      */
     @Test
     public void testInterrupt() throws Exception {
diff --git a/src/test/java/org/apache/commons/io/input/TeeInputStreamTest.java b/src/test/java/org/apache/commons/io/input/TeeInputStreamTest.java
index 4fdf982..49527a9 100644
--- a/src/test/java/org/apache/commons/io/input/TeeInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/TeeInputStreamTest.java
@@ -17,7 +17,7 @@
 package org.apache.commons.io.input;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -28,6 +28,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 
 import org.apache.commons.io.test.ThrowOnCloseInputStream;
 import org.apache.commons.io.test.ThrowOnCloseOutputStream;
@@ -39,7 +40,7 @@
  */
 public class TeeInputStreamTest  {
 
-    private final String ASCII = "US-ASCII";
+    private final String ASCII = StandardCharsets.US_ASCII.name();
 
     private InputStream tee;
 
@@ -66,12 +67,8 @@ public void testCloseBranchIOException() throws Exception {
         verify(goodIs).close();
 
         final TeeInputStream closingTis = new TeeInputStream(goodIs, badOs, true);
-        try {
-            closingTis.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodIs, times(2)).close();
-        }
+        assertThrows(IOException.class, closingTis::close);
+        verify(goodIs, times(2)).close();
     }
 
     /**
@@ -84,20 +81,12 @@ public void testCloseMainIOException() throws IOException {
         final ByteArrayOutputStream goodOs = mock(ByteArrayOutputStream.class);
 
         final TeeInputStream nonClosingTis = new TeeInputStream(badIs, goodOs, false);
-        try {
-            nonClosingTis.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodOs, never()).close();
-        }
+        assertThrows(IOException.class, nonClosingTis::close);
+        verify(goodOs, never()).close();
 
         final TeeInputStream closingTis = new TeeInputStream(badIs, goodOs, true);
-        try {
-            closingTis.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodOs).close();
-        }
+        assertThrows(IOException.class, closingTis::close);
+        verify(goodOs).close();
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/input/TeeReaderTest.java b/src/test/java/org/apache/commons/io/input/TeeReaderTest.java
index 4b694fc..c494525 100644
--- a/src/test/java/org/apache/commons/io/input/TeeReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/TeeReaderTest.java
@@ -17,7 +17,7 @@
 package org.apache.commons.io.input;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -66,12 +66,8 @@ public void testCloseBranchIOException() throws Exception {
         verify(goodR).close();
 
         final TeeReader closingTr = new TeeReader(goodR, badW, true);
-        try {
-            closingTr.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodR, times(2)).close();
-        }
+        final IOException e = assertThrows(IOException.class, closingTr::close);
+        verify(goodR, times(2)).close();
     }
 
     /**
@@ -84,21 +80,12 @@ public void testCloseMainIOException() throws IOException {
         final StringWriter goodW = mock(StringWriter.class);
 
         final TeeReader nonClosingTr = new TeeReader(badR, goodW, false);
-        try {
-            nonClosingTr.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodW, never()).close();
-        }
+        assertThrows(IOException.class, nonClosingTr::close);
+        verify(goodW, never()).close();
 
         final TeeReader closingTr = new TeeReader(badR, goodW, true);
-        try {
-            closingTr.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            //Assert.assertTrue(goodW.closed);
-            verify(goodW).close();
-        }
+        assertThrows(IOException.class, closingTr::close);
+        verify(goodW).close();
     }
 
     @Test
@@ -179,4 +166,4 @@ public void testSkip() throws Exception {
         assertEquals("ac", output.toString());
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
index 02951f8..f7fcba8 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
@@ -26,6 +26,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 
@@ -63,7 +64,7 @@ public class UnsynchronizedBufferedInputStreamTest {
     @BeforeEach
     protected void setUp() throws IOException {
         fileName = Files.createTempFile(getClass().getSimpleName(), ".tst");
-        Files.write(fileName, DATA.getBytes("UTF-8"));
+        Files.write(fileName, DATA.getBytes(StandardCharsets.UTF_8));
 
         isFile = Files.newInputStream(fileName);
         is = new BufferedInputStream(isFile);
@@ -87,7 +88,7 @@ protected void tearDown() throws IOException {
      */
     @Test
     public void test_available() throws IOException {
-        assertTrue(is.available() == DATA.length(), "Returned incorrect number of available bytes");
+        assertEquals(DATA.length(), is.available(), "Returned incorrect number of available bytes");
 
         // Test that a closed stream throws an IOE for available()
         final BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(new byte[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }));
@@ -271,7 +272,7 @@ public void test_markSupported() {
     public void test_read() throws IOException {
         final InputStreamReader isr = new InputStreamReader(is);
         final int c = isr.read();
-        assertTrue(c == DATA.charAt(0), "read returned incorrect char");
+        assertEquals(DATA.charAt(0), c, "read returned incorrect char");
 
         final byte[] bytes = new byte[256];
         for (int i = 0; i < 256; i++) {
@@ -332,7 +333,7 @@ public int read(final byte[] buf, final int off, final int len) throws IOExcepti
         })) {
             bufin.read();
             final int result = bufin.read(new byte[2], 0, 2);
-            assertTrue(result == 1, () -> "Incorrect result: " + result);
+            assertEquals(1, result, () -> "Incorrect result: " + result);
         }
     }
 
@@ -409,11 +410,11 @@ public void test_reset_Exception() throws IOException {
     @Test
     public void test_reset_scenario1() throws IOException {
         final byte[] input = "12345678900".getBytes();
-        final BufferedInputStream buffis = new BufferedInputStream(new ByteArrayInputStream(input));
-        buffis.read();
-        buffis.mark(5);
-        buffis.skip(5);
-        buffis.reset();
+        final BufferedInputStream bufin = new BufferedInputStream(new ByteArrayInputStream(input));
+        bufin.read();
+        bufin.mark(5);
+        bufin.skip(5);
+        bufin.reset();
     }
 
     /**
@@ -424,10 +425,10 @@ public void test_reset_scenario1() throws IOException {
     @Test
     public void test_reset_scenario2() throws IOException {
         final byte[] input = "12345678900".getBytes();
-        final BufferedInputStream buffis = new BufferedInputStream(new ByteArrayInputStream(input));
-        buffis.mark(5);
-        buffis.skip(6);
-        buffis.reset();
+        final BufferedInputStream bufin = new BufferedInputStream(new ByteArrayInputStream(input));
+        bufin.mark(5);
+        bufin.skip(6);
+        bufin.reset();
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
index ab4e3d9..e09745a 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
@@ -1,5 +1,6 @@
 package org.apache.commons.io.input;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -22,6 +23,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 
@@ -56,7 +58,7 @@ public class UnsynchronizedFilterInputStreamTest {
     @BeforeEach
     protected void setUp() throws IOException {
         fileName = Files.createTempFile(getClass().getSimpleName(), ".tst");
-        Files.write(fileName, DATA.getBytes("UTF-8"));
+        Files.write(fileName, DATA.getBytes(StandardCharsets.UTF_8));
         is = UnsynchronizedFilterInputStream.builder().setInputStream(Files.newInputStream(fileName)).get();
     }
 
@@ -78,7 +80,7 @@ protected void tearDown() throws IOException {
      */
     @Test
     public void test_available() throws IOException {
-        assertTrue(is.available() == DATA.length(), "Returned incorrect number of available bytes");
+        assertEquals(DATA.length(), is.available(), "Returned incorrect number of available bytes");
     }
 
     /**
@@ -116,7 +118,7 @@ public void test_markSupported() {
     @Test
     public void test_read() throws IOException {
         final int c = is.read();
-        assertTrue(c == DATA.charAt(0), "read returned incorrect char");
+        assertEquals(DATA.charAt(0), c, "read returned incorrect char");
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java b/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
index 026ea4f..555cf7c 100644
--- a/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
@@ -435,10 +435,11 @@ public void testHttpValid(final String cT, final String bomEnc, final String str
     @Test
     @DefaultLocale(language = "tr")
     public void testLowerCaseEncodingWithTurkishLocale_IO_557() throws Exception {
-        final String[] encodings = {"iso8859-1", "us-ascii", "utf-8"};
+        final String[] encodings = { "iso8859-1", "us-ascii", "utf-8" }; // lower-case
         for (final String encoding : encodings) {
             final String xml = getXML("no-bom", XML3, encoding, encoding);
-            try (ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(encoding)); XmlStreamReader xmlReader = new XmlStreamReader(is)) {
+            try (ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(encoding));
+                    XmlStreamReader xmlReader = new XmlStreamReader(is)) {
                 assertTrue(encoding.equalsIgnoreCase(xmlReader.getEncoding()), "Check encoding : " + encoding);
                 assertEquals(xml, IOUtils.toString(xmlReader), "Check content");
             }
@@ -529,12 +530,9 @@ public void testRawNoBomCp1047() throws Exception {
 
     protected void testRawNoBomInvalid(final String encoding) throws Exception {
         try (final InputStream is = getXmlInputStream("no-bom", XML3, encoding, encoding)) {
-            try {
-                new XmlStreamReader(is, false).close();
-                fail("It should have failed");
-            } catch (final IOException ex) {
-                assertTrue(ex.getMessage().contains("Invalid encoding,"));
-            }
+            final XmlStreamReader xmlStreamReader = new XmlStreamReader(is, false);
+            final IOException ex = assertThrows(IOException.class, xmlStreamReader::close);
+            assertTrue(ex.getMessage().contains("Invalid encoding,"));
         }
     }
 
diff --git a/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java b/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
index 5d80cab..e2cd2e6 100644
--- a/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
+++ b/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
@@ -38,7 +38,7 @@ private static class MockXmlStreamReader extends XmlStreamReader {
     }
     private static final String RAWMGS1 = "encoding mismatch";
     private static final String RAWMGS2 = "unknown BOM";
-    private static final String HTTPMGS1 = "BOM must be NULL";
+    private static final String HTTPMGS1 = "BOM must be null";
     private static final String HTTPMGS2 = "encoding mismatch";
 
     private static final String HTTPMGS3 = "Illegal MIME";
@@ -121,7 +121,7 @@ private void checkRawEncoding(final String expected,
         builder.append("xmlGuessEnc=[").append(xmlGuessEnc).append("], ");
         builder.append("xmlEnc=[").append(xmlEnc).append("], ");
         builder.append("defaultEncoding=[").append(defaultEncoding).append("],");
-        final String encoding = calculateRawEncoding(bomEnc,xmlGuessEnc,xmlEnc, defaultEncoding);
+        final String encoding = calculateRawEncoding(bomEnc, xmlGuessEnc, xmlEnc, defaultEncoding);
         assertEquals(expected, encoding, builder.toString());
     }
 
diff --git a/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java b/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
index 928ac11..a8299dc 100644
--- a/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
+++ b/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
@@ -104,7 +104,7 @@ public class XmlStreamReader extends Reader {
             "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM");
 
     private static final MessageFormat HTTP_EX_1 = new MessageFormat(
-            "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL");
+            "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be null");
 
     private static final MessageFormat HTTP_EX_2 = new MessageFormat(
             "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch");
@@ -112,7 +112,7 @@ public class XmlStreamReader extends Reader {
     private static final MessageFormat HTTP_EX_3 = new MessageFormat(
             "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Illegal MIME");
 
-    // returns the BOM in the stream, NULL if not present,
+    // returns the BOM in the stream, null if not present,
     // if there was BOM the in the stream it is consumed
     static String getBOMEncoding(final BufferedInputStream is)
             throws IOException {
@@ -141,8 +141,8 @@ static String getBOMEncoding(final BufferedInputStream is)
         return encoding;
     }
 
-    // returns charset parameter value, NULL if not present, NULL if
-    // httpContentType is NULL
+    // returns charset parameter value, null if not present, null if
+    // httpContentType is null
     static String getContentTypeEncoding(final String httpContentType) {
         String encoding = null;
         if (httpContentType != null) {
@@ -157,7 +157,7 @@ static String getContentTypeEncoding(final String httpContentType) {
         return encoding;
     }
 
-    // returns MIME type or NULL if httpContentType is NULL
+    // returns MIME type or null if httpContentType is null
     static String getContentTypeMime(final String httpContentType) {
         String mime = null;
         if (httpContentType != null) {
@@ -172,7 +172,7 @@ static String getContentTypeMime(final String httpContentType) {
      * Returns the default encoding to use if none is set in HTTP content-type,
      * XML prolog and the rules based on content-type are not adequate.
      * <p>
-     * If it is NULL the content-type based rules are used.
+     * If it is null the content-type based rules are used.
      *
      * @return the default encoding to use.
      */
@@ -209,7 +209,7 @@ private static String getXMLGuessEncoding(final BufferedInputStream is)
         return encoding;
     }
 
-    // returns the encoding declared in the <?xml encoding=...?>, NULL if none
+    // returns the encoding declared in the <?xml encoding=...?>, null if none
     private static String getXmlProlog(final BufferedInputStream is, final String guessedEnc)
             throws IOException {
         String encoding = null;
@@ -278,9 +278,9 @@ static boolean isTextXml(final String mime) {
      * Sets the default encoding to use if none is set in HTTP content-type, XML
      * prolog and the rules based on content-type are not adequate.
      * <p>
-     * If it is set to NULL the content-type based rules are used.
+     * If it is set to null the content-type based rules are used.
      * <p>
-     * By default it is NULL.
+     * By default it is null.
      *
      * @param encoding charset encoding to default to.
      */
diff --git a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java
index 7cfcadb..2d11e58 100644
--- a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTest.java
@@ -229,7 +229,7 @@ public void testStream(final String baosName, final BAOSFactory<?> baosFactory)
     @ParameterizedTest(name = "[{index}] {0}")
     @MethodSource("toBufferedInputStreamFunctionFactories")
     public void testToBufferedInputStream(final String baosName, final IOFunction<InputStream, InputStream> toBufferedInputStreamFunction) throws IOException {
-        final byte data[] = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
+        final byte[] data = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE};
 
         try (ByteArrayInputStream bain = new ByteArrayInputStream(data)) {
             assertEquals(data.length, bain.available());
@@ -271,13 +271,13 @@ public void testToInputStream(final String baosName, final BAOSFactory<?> baosFa
 
             // Get data before more writes
             try (InputStream in = baout.toInputStream()) {
-                byte refData[] = ref.toByteArray();
+                byte[] refData = ref.toByteArray();
 
                 // Write some more data
                 writeData(baout, ref, new int[] {2, 4, 8, 16});
 
                 // Check original data
-                byte baoutData[] = IOUtils.toByteArray(in);
+                byte[] baoutData = IOUtils.toByteArray(in);
                 assertEquals(8224, baoutData.length);
                 checkByteArrays(refData, baoutData);
 
@@ -318,7 +318,7 @@ public void testToInputStreamWithReset(final String baosName, final BAOSFactory<
 
             // Get data before reset
             try (InputStream in = baout.toInputStream()) {
-                byte refData[] = ref.toByteArray();
+                byte[] refData = ref.toByteArray();
 
                 // Reset and write some new data
                 baout.reset();
@@ -326,7 +326,7 @@ public void testToInputStreamWithReset(final String baosName, final BAOSFactory<
                 writeData(baout, ref, new int[] {2, 4, 8, 16});
 
                 // Check original data
-                byte baoutData[] = IOUtils.toByteArray(in);
+                byte[] baoutData = IOUtils.toByteArray(in);
                 assertEquals(8224, baoutData.length);
                 checkByteArrays(refData, baoutData);
 
diff --git a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
index 35038d2..e23a40c 100644
--- a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java
@@ -29,6 +29,7 @@
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.stream.IntStream;
 
 import org.apache.commons.io.FileUtils;
@@ -80,6 +81,7 @@ public void testAboveThreshold(final int initialBufferSize) throws IOException {
         dfos.close();
         assertFalse(dfos.isInMemory());
         assertNull(dfos.getData());
+        assertEquals(testFile.length(), dfos.getByteCount());
 
         verifyResultFile(testFile);
 
@@ -107,6 +109,7 @@ public void testAboveThresholdGetInputStream(final int initialBufferSize, final
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertFalse(dfos.isInMemory());
+        assertEquals(testFile.length(), dfos.getByteCount());
 
         try (InputStream is = dfos.toInputStream()) {
             assertArrayEquals(testBytes, IOUtils.toByteArray(is));
@@ -131,6 +134,7 @@ public void testAtThreshold(final int initialBufferSize) throws IOException {
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertTrue(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         final byte[] resultBytes = dfos.getData();
         assertEquals(testBytes.length, resultBytes.length);
@@ -153,6 +157,7 @@ public void testBelowThreshold(final int initialBufferSize) throws IOException {
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertTrue(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         final byte[] resultBytes = dfos.getData();
         assertEquals(testBytes.length, resultBytes.length);
@@ -175,6 +180,7 @@ public void testBelowThresholdGetInputStream(final int initialBufferSize) throws
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertTrue(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         try (InputStream is = dfos.toInputStream()) {
             assertArrayEquals(testBytes, IOUtils.toByteArray(is));
@@ -190,7 +196,7 @@ public void testTempFileAboveThreshold(final int initialBufferSize) throws IOExc
 
         final String prefix = "commons-io-test";
         final String suffix = ".out";
-        final File tempDir = FileUtils.current();
+        final Path tempDir = Paths.get("target");
         // @formatter:off
         final DeferredFileOutputStream dfos = DeferredFileOutputStream.builder()
                 .setThreshold(testBytes.length - 5)
@@ -198,18 +204,21 @@ public void testTempFileAboveThreshold(final int initialBufferSize) throws IOExc
                 .setPrefix(prefix)
                 .setSuffix(suffix)
                 .setDirectory(tempDir)
+                .setDirectory(tempDir.toFile())
                 .get();
         // @formatter:on
-        assertNull(dfos.getFile(), "Check file is null-A");
+        assertNull(dfos.getFile(), "Check File is null-A");
+        assertNull(dfos.getPath(), "Check Path is null-A");
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertFalse(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
         assertNull(dfos.getData());
         assertNotNull(dfos.getFile(), "Check file not null");
         assertTrue(dfos.getFile().exists(), "Check file exists");
         assertTrue(dfos.getFile().getName().startsWith(prefix), "Check prefix");
         assertTrue(dfos.getFile().getName().endsWith(suffix), "Check suffix");
-        assertEquals(tempDir.getPath(), dfos.getFile().getParent(), "Check dir");
+        assertEquals(tempDir, dfos.getPath().getParent(), "Check dir");
 
         verifyResultFile(dfos.getFile());
 
@@ -227,7 +236,7 @@ public void testTempFileAboveThresholdPrefixOnly(final int initialBufferSize) th
 
         final String prefix = "commons-io-test";
         final String suffix = null;
-        final File tempDir = null;
+        final Path tempDir = null;
         // @formatter:off
         final DeferredFileOutputStream dfos = DeferredFileOutputStream.builder()
                 .setThreshold(testBytes.length - 5)
@@ -237,11 +246,13 @@ public void testTempFileAboveThresholdPrefixOnly(final int initialBufferSize) th
                 .setDirectory(tempDir)
                 .get();
         // @formatter:on
-        assertNull(dfos.getFile(), "Check file is null-A");
+        assertNull(dfos.getFile(), "Check File is null-A");
+        assertNull(dfos.getPath(), "Check Path is null-A");
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertFalse(dfos.isInMemory());
         assertNull(dfos.getData());
+        assertEquals(testBytes.length, dfos.getByteCount());
         assertNotNull(dfos.getFile(), "Check file not null");
         assertTrue(dfos.getFile().exists(), "Check file exists");
         assertTrue(dfos.getFile().getName().startsWith(prefix), "Check prefix");
@@ -265,10 +276,12 @@ public void testTempFileBelowThreshold(final int initialBufferSize) throws IOExc
         final String suffix = ".out";
         final File tempDir = FileUtils.current();
         final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, prefix, suffix, tempDir);
-        assertNull(dfos.getFile(), "Check file is null-A");
+        assertNull(dfos.getFile(), "Check File is null-A");
+        assertNull(dfos.getPath(), "Check Path is null-A");
         dfos.write(testBytes, 0, testBytes.length);
         dfos.close();
         assertTrue(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
         assertNull(dfos.getFile(), "Check file is null-B");
     }
 
@@ -312,6 +325,7 @@ public void testThresholdReached(final int initialBufferSize) throws IOException
         dfos.close();
         assertFalse(dfos.isInMemory());
         assertNull(dfos.getData());
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         verifyResultFile(testFile);
 
@@ -335,6 +349,7 @@ public void testWriteToLarge(final int initialBufferSize) throws IOException {
 
         assertTrue(testFile.exists());
         assertFalse(dfos.isInMemory());
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         assertThrows(IOException.class, () -> dfos.writeTo(baos));
 
@@ -364,6 +379,7 @@ public void testWriteToLargeCtor(final int initialBufferSize) throws IOException
         assertFalse(dfos.isInMemory());
 
         assertThrows(IOException.class, () -> dfos.writeTo(baos));
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         dfos.close();
         dfos.writeTo(baos);
@@ -392,6 +408,7 @@ public void testWriteToSmall(final int initialBufferSize) throws IOException {
         assertTrue(dfos.isInMemory());
 
         assertThrows(IOException.class, () -> dfos.writeTo(baos));
+        assertEquals(testBytes.length, dfos.getByteCount());
 
         dfos.close();
         dfos.writeTo(baos);
diff --git a/src/test/java/org/apache/commons/io/output/TeeOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/TeeOutputStreamTest.java
index 1cfa7b1..00b0a92 100644
--- a/src/test/java/org/apache/commons/io/output/TeeOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/TeeOutputStreamTest.java
@@ -17,7 +17,7 @@
 package org.apache.commons.io.output;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -49,12 +49,9 @@ public void testIOExceptionOnClose() throws IOException {
         final OutputStream badOs = new ThrowOnCloseOutputStream();
         final ByteArrayOutputStream goodOs = mock(ByteArrayOutputStream.class);
         final TeeOutputStream tos = new TeeOutputStream(badOs, goodOs);
-        try {
-            tos.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodOs).close();
-        }
+
+        assertThrows(IOException.class, tos::close);
+        verify(goodOs).close();
     }
 
     /**
@@ -66,12 +63,9 @@ public void testIOExceptionOnCloseBranch() throws IOException {
         final OutputStream badOs = new ThrowOnCloseOutputStream();
         final ByteArrayOutputStream goodOs = mock(ByteArrayOutputStream.class);
         final TeeOutputStream tos = new TeeOutputStream(goodOs, badOs);
-        try {
-            tos.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOException e) {
-            verify(goodOs).close();
-        }
+
+        assertThrows(IOException.class, tos::close);
+        verify(goodOs).close();
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/output/TeeWriterTest.java b/src/test/java/org/apache/commons/io/output/TeeWriterTest.java
index 0b5af70..8f0f8ee 100644
--- a/src/test/java/org/apache/commons/io/output/TeeWriterTest.java
+++ b/src/test/java/org/apache/commons/io/output/TeeWriterTest.java
@@ -17,8 +17,8 @@
 package org.apache.commons.io.output;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
@@ -39,99 +39,81 @@
 public class TeeWriterTest {
 
     @Test
-    public void testArrayIOExceptionOnAppendChar1() throws IOException {
+    public void testArrayIOExceptionOnAppendChar1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null);
         final char data = 'A';
-        try {
-            tw.append(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data));
+        verify(goodW).append(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnAppendChar2() throws IOException {
+    public void testArrayIOExceptionOnAppendChar2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null);
         final char data = 'A';
-        try {
-            tw.append(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data));
+        verify(goodW).append(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnAppendCharSequence1() throws IOException {
+    public void testArrayIOExceptionOnAppendCharSequence1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final String data = "A";
-        try {
-            tw.append(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data));
+        verify(goodW).append(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnAppendCharSequence2() throws IOException {
+    public void testArrayIOExceptionOnAppendCharSequence2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final String data = "A";
-        try {
-            tw.append(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data));
+        verify(goodW).append(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnAppendCharSequenceIntInt1() throws IOException {
+    public void testArrayIOExceptionOnAppendCharSequenceIntInt1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final String data = "A";
-        try {
-            tw.append(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data, 0, 0));
+        verify(goodW).append(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnAppendCharSequenceIntInt2() throws IOException {
+    public void testArrayIOExceptionOnAppendCharSequenceIntInt2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final String data = "A";
-        try {
-            tw.append(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).append(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.append(data, 0, 0));
+        verify(goodW).append(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
@@ -139,14 +121,11 @@ public void testArrayIOExceptionOnClose1() throws IOException {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
-        try {
-            tw.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).close();
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, tw::close);
+        verify(goodW).close();
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
@@ -154,44 +133,35 @@ public void testArrayIOExceptionOnClose2() throws IOException {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
-        try {
-            tw.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).close();
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, tw::close);
+        verify(goodW).close();
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnFlush1() throws IOException {
+    public void testArrayIOExceptionOnFlush1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
-        try {
-            tw.flush();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).flush();
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, tw::flush);
+        verify(goodW).flush();
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnFlush2() throws IOException {
+    public void testArrayIOExceptionOnFlush2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
-        try {
-            tw.flush();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).flush();
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, tw::flush);
+        verify(goodW).flush();
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
@@ -200,14 +170,11 @@ public void testArrayIOExceptionOnWriteCharArray1() throws IOException {
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final char[] data = { 'a' };
-        try {
-            tw.write(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data));
+        verify(goodW).write(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
@@ -216,144 +183,114 @@ public void testArrayIOExceptionOnWriteCharArray2() throws IOException {
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final char[] data = { 'a' };
-        try {
-            tw.write(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data));
+        verify(goodW).write(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteCharArrayIntInt1() throws IOException {
+    public void testArrayIOExceptionOnWriteCharArrayIntInt1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final char[] data = { 'a' };
-        try {
-            tw.write(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data, 0, 0));
+        verify(goodW).write(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteCharArrayIntInt2() throws IOException {
+    public void testArrayIOExceptionOnWriteCharArrayIntInt2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final char[] data = { 'a' };
-        try {
-            tw.write(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data, 0, 0));
+        verify(goodW).write(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteInt1() throws IOException {
+    public void testArrayIOExceptionOnWriteInt1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final int data = 32;
-        try {
-            tw.write(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data));
+        verify(goodW).write(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteInt2() throws IOException {
+    public void testArrayIOExceptionOnWriteInt2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
-        try {
-            tw.write(32);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(32);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
 
-        }
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(32));
+        verify(goodW).write(32);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteString1() throws IOException {
+    public void testArrayIOExceptionOnWriteString1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final String data = "A";
-        try {
-            tw.write(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data));
+        verify(goodW).write(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteString2() throws IOException {
+    public void testArrayIOExceptionOnWriteString2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final String data = "A";
-        try {
-            tw.write(data);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
 
-        }
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data));
+        verify(goodW).write(data);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteStringIntInt1() throws IOException {
+    public void testArrayIOExceptionOnWriteStringIntInt1() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(badW, goodW, null);
         final String data = "A";
-        try {
-            tw.write(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
-        }
+
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data, 0, 0));
+        verify(goodW).write(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
-    public void testArrayIOExceptionOnWriteStringIntInt2() throws IOException {
+    public void testArrayIOExceptionOnWriteStringIntInt2() {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(goodW, badW, null);
         final String data = "A";
-        try {
-            tw.write(data, 0, 0);
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).write(data, 0, 0);
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
 
-        }
+        final IOExceptionList e = assertThrows(IOExceptionList.class, () -> tw.write(data, 0, 0));
+        verify(goodW).write(data, 0, 0);
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
@@ -361,15 +298,11 @@ public void testCollectionCloseBranchIOException() throws IOException {
         final Writer badW = BrokenWriter.INSTANCE;
         final StringWriter goodW = mock(StringWriter.class);
         final TeeWriter tw = new TeeWriter(Arrays.asList(goodW, badW, null));
-        try {
-            tw.close();
-            fail("Expected " + IOException.class.getName());
-        } catch (final IOExceptionList e) {
-            verify(goodW).close();
-            assertEquals(1, e.getCauseList().size());
-            assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
 
-        }
+        final IOExceptionList e = assertThrows(IOExceptionList.class, tw::close);
+        verify(goodW).close();
+        assertEquals(1, e.getCauseList().size());
+        assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex());
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
index b342d28..dd1299e 100644
--- a/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/ThresholdingOutputStreamTest.java
@@ -33,7 +33,7 @@
 public class ThresholdingOutputStreamTest {
 
     @Test
-    public void testSetByteCount() throws Exception {
+    public void testSetByteCount_Stream() throws Exception {
         final AtomicBoolean reached = new AtomicBoolean(false);
         try (ThresholdingOutputStream tos = new ThresholdingOutputStream(3) {
             {
@@ -58,6 +58,31 @@ protected void thresholdReached() throws IOException {
     }
 
     @Test
+    public void testSetByteCount_OutputStream() throws Exception {
+        final AtomicBoolean reached = new AtomicBoolean(false);
+        try (ThresholdingOutputStream tos = new ThresholdingOutputStream(3) {
+            {
+                setByteCount(2);
+            }
+
+            @Override
+            protected OutputStream getOutputStream() throws IOException {
+                return new ByteArrayOutputStream(4);
+            }
+
+            @Override
+            protected void thresholdReached() throws IOException {
+                reached.set(true);
+            }
+        }) {
+            tos.write('a');
+            assertFalse(reached.get());
+            tos.write('a');
+            assertTrue(reached.get());
+        }
+    }
+
+    @Test
     public void testThresholdIOConsumer() throws Exception {
         final AtomicBoolean reached = new AtomicBoolean();
         // Null threshold consumer
diff --git a/src/test/java/org/apache/commons/io/output/UncheckedAppendableTest.java b/src/test/java/org/apache/commons/io/output/UncheckedAppendableTest.java
index 75a6437..e22f85d 100644
--- a/src/test/java/org/apache/commons/io/output/UncheckedAppendableTest.java
+++ b/src/test/java/org/apache/commons/io/output/UncheckedAppendableTest.java
@@ -17,7 +17,7 @@
 package org.apache.commons.io.output;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.io.IOException;
 import java.io.StringWriter;
@@ -64,32 +64,20 @@ public void testAppendCharSequenceIndexed() {
 
     @Test
     public void testAppendCharSequenceIndexedThrows() {
-        try {
-            appendableBroken.append("a", 0, 1);
-            fail("Expected exception not thrown.");
-        } catch (final UncheckedIOException e) {
-            assertEquals(exception, e.getCause());
-        }
+        final UncheckedIOException e = assertThrows(UncheckedIOException.class, () -> appendableBroken.append("a", 0, 1));
+        assertEquals(exception, e.getCause());
     }
 
     @Test
     public void testAppendCharSequenceThrows() {
-        try {
-            appendableBroken.append("a");
-            fail("Expected exception not thrown.");
-        } catch (final UncheckedIOException e) {
-            assertEquals(exception, e.getCause());
-        }
+        final UncheckedIOException e = assertThrows(UncheckedIOException.class, () -> appendableBroken.append("a"));
+        assertEquals(exception, e.getCause());
     }
 
     @Test
     public void testAppendCharThrows() {
-        try {
-            appendableBroken.append('a');
-            fail("Expected exception not thrown.");
-        } catch (final UncheckedIOException e) {
-            assertEquals(exception, e.getCause());
-        }
+        final UncheckedIOException e2 = assertThrows(UncheckedIOException.class, () -> appendableBroken.append('a'));
+        assertEquals(exception, e2.getCause());
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/serialization/MockSerializedClass.java b/src/test/java/org/apache/commons/io/serialization/MockSerializedClass.java
index 726dc70..8e0cb82 100644
--- a/src/test/java/org/apache/commons/io/serialization/MockSerializedClass.java
+++ b/src/test/java/org/apache/commons/io/serialization/MockSerializedClass.java
@@ -31,7 +31,7 @@ public class MockSerializedClass implements Serializable {
 
     @Override
     public boolean equals(final Object obj) {
-        if(!(obj instanceof MockSerializedClass)) {
+        if (!(obj instanceof MockSerializedClass)) {
             return false;
         }
         return str.equals(((MockSerializedClass)obj).str);
diff --git a/src/test/java/org/apache/commons/io/test/TestUtils.java b/src/test/java/org/apache/commons/io/test/TestUtils.java
index 8ace275..0620603 100644
--- a/src/test/java/org/apache/commons/io/test/TestUtils.java
+++ b/src/test/java/org/apache/commons/io/test/TestUtils.java
@@ -132,7 +132,7 @@ private static void assertEqualContent(final File f0, final File f1)
                 while (-1 != n0) {
                     n0 = is0.read(buf0);
                     n1 = is1.read(buf1);
-                    assertTrue(n0 == n1,
+                    assertEquals(n0, n1,
                             "The files " + f0 + " and " + f1 +
                             " have differing number of bytes available (" + n0 + " vs " + n1 + ")");
 
@@ -164,14 +164,20 @@ public static void checkWrite(final Writer output) {
         }
     }
 
-    public static void createFile(final File file, final long size)
-            throws IOException {
+    public static void createFile(final File file, final long size) throws IOException {
         if (!file.getParentFile().exists()) {
-            throw new IOException("Cannot create file " + file
-                    + " as the parent directory does not exist");
+            throw new IOException("Cannot create file " + file + " as the parent directory does not exist");
         }
-        try (BufferedOutputStream output =
-                new BufferedOutputStream(Files.newOutputStream(file.toPath()))) {
+        try (BufferedOutputStream output = new BufferedOutputStream(Files.newOutputStream(file.toPath()))) {
+            generateTestData(output, size);
+        }
+    }
+
+    public static void createFile(final Path file, final long size) throws IOException {
+        if (!Files.exists(file.getParent())) {
+            throw new IOException("Cannot create file " + file + " as the parent directory does not exist");
+        }
+        try (BufferedOutputStream output = new BufferedOutputStream(Files.newOutputStream(file))) {
             generateTestData(output, size);
         }
     }
diff --git a/src/test/resources/org/apache/commons/io/TIKA-4065.bin b/src/test/resources/org/apache/commons/io/TIKA-4065.bin
new file mode 100644
index 0000000..1803df9
--- /dev/null
+++ b/src/test/resources/org/apache/commons/io/TIKA-4065.bin
Binary files differ