You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/10/12 13:35:35 UTC

[commons-io] 02/02: [IO-632] Add PathUtils for operations on NIO Path.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git

commit 7c111fecd837f22f1f8c1faa38dbef0f4a0e91ee
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Oct 12 09:35:29 2019 -0400

    [IO-632] Add PathUtils for operations on NIO Path.
    
    Refactor for counting longs and big integers. Add cleaning visitor.
---
 ...thFileVisitor.java => CleaningPathVisitor.java} |  40 ++-
 .../java/org/apache/commons/io/file/Counters.java  | 290 +++++++++++++++++++++
 ...thFileVisitor.java => CountingPathVisitor.java} |  56 ++--
 ...thFileVisitor.java => DeletingPathVisitor.java} |  33 ++-
 .../org/apache/commons/io/file/PathCounts.java     |  66 -----
 .../java/org/apache/commons/io/file/PathUtils.java |  81 ++++--
 ...PathFileVisitor.java => SimplePathVisitor.java} |   4 +-
 .../commons/io/file/CleaningPathVisitorTest.java   | 129 +++++++++
 .../apache/commons/io/file/CounterAssertions.java  |  45 ++++
 .../org/apache/commons/io/file/CountersTest.java   |  56 ++++
 .../commons/io/file/CountingFileVisitorTest.java   |  91 -------
 .../commons/io/file/CountingPathVisitorTest.java   |  84 ++++++
 .../commons/io/file/DeletingFileVisitorTest.java   | 143 ----------
 .../commons/io/file/DeletingPathVisitorTest.java   | 136 ++++++++++
 .../org/apache/commons/io/file/PathCountsTest.java |  38 ---
 .../commons/io/file/PathUtilsCountingTest.java     |  41 +--
 .../io/file/PathUtilsDeleteDirectoryTest.java      |  50 ++--
 .../commons/io/file/PathUtilsDeleteFileTest.java   |  46 ++--
 .../commons/io/file/PathUtilsDeleteTest.java       |  49 ++--
 .../commons/io/file/PathUtilsIsEmptyTest.java      |  13 +-
 .../org/apache/commons/io/file/TestArguments.java  |  65 +++++
 21 files changed, 1034 insertions(+), 522 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java b/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
similarity index 69%
copy from src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java
copy to src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
index fac4c4b..485773f 100644
--- a/src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
@@ -24,22 +24,43 @@ import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Arrays;
 
+import org.apache.commons.io.file.Counters.PathCounters;
+
 /**
- * Deletes files and directories as a visit proceeds.
+ * Deletes files but not directories as a visit proceeds.
  *
  * @since 2.7
  */
-public class DeletingPathFileVisitor extends CountingPathFileVisitor {
+public class CleaningPathVisitor extends CountingPathVisitor {
+
+    /**
+     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     *
+     * @return a new instance configured with a BigInteger {@link PathCounters}.
+     */
+    public static CountingPathVisitor withBigIntegerCounters() {
+        return new CleaningPathVisitor(Counters.bigIntegerPathCounters());
+    }
+
+    /**
+     * Creates a new instance configured with a long {@link PathCounters}.
+     *
+     * @return a new instance configured with a long {@link PathCounters}.
+     */
+    public static CountingPathVisitor withLongCounters() {
+        return new CleaningPathVisitor(Counters.longPathCounters());
+    }
 
-    private static final String[] EMPTY_STRING_ARRAY = new String[0];
     private final String[] skip;
 
     /**
      * Constructs a new visitor that deletes files except for the files and directories explicitly given.
      *
+     * @param pathCounter How to count visits.
      * @param skip The files to skip deleting.
      */
-    public DeletingPathFileVisitor(final String... skip) {
+    public CleaningPathVisitor(final PathCounters pathCounter, final String... skip) {
+        super(pathCounter);
         final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY;
         Arrays.sort(temp);
         this.skip = temp;
@@ -56,15 +77,6 @@ public class DeletingPathFileVisitor extends CountingPathFileVisitor {
     }
 
     @Override
-    public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
-        super.postVisitDirectory(dir, exc);
-        if (PathUtils.isEmptyDirectory(dir)) {
-            Files.deleteIfExists(dir);
-        }
-        return FileVisitResult.CONTINUE;
-    }
-
-    @Override
     public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
         super.preVisitDirectory(dir, attrs);
         return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
@@ -72,10 +84,10 @@ public class DeletingPathFileVisitor extends CountingPathFileVisitor {
 
     @Override
     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
-        super.visitFile(file, attrs);
         if (accept(file) && Files.exists(file)) {
             Files.deleteIfExists(file);
         }
+        updateFileCounters(file, attrs);
         return FileVisitResult.CONTINUE;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/io/file/Counters.java b/src/main/java/org/apache/commons/io/file/Counters.java
new file mode 100644
index 0000000..f0a32b3
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/file/Counters.java
@@ -0,0 +1,290 @@
+/*
+ * 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.file;
+
+import java.math.BigInteger;
+
+/**
+ * Provides counters for files, directories, and sizes, as a visit proceeds.
+ *
+ * @since 2.7
+ */
+public class Counters {
+
+    /**
+     * Counts files, directories, and sizes, as a visit proceeds.
+     */
+    private static class AbstractPathCounters implements PathCounters {
+
+        private final Counter byteCounter;
+        private final Counter directoryCounter;
+        private final Counter fileCounter;
+
+        /**
+         * Constructs a new instance.
+         * 
+         * @param byteCounter the byte counter.
+         * @param directoryCounter the directory counter.
+         * @param fileCounter the file counter.
+         */
+        protected AbstractPathCounters(final Counter byteCounter, final Counter directoryCounter,
+                final Counter fileCounter) {
+            super();
+            this.byteCounter = byteCounter;
+            this.directoryCounter = directoryCounter;
+            this.fileCounter = fileCounter;
+        }
+
+        @Override
+        public Counter getByteCounter() {
+            return byteCounter;
+        }
+
+        @Override
+        public Counter getDirectoryCounter() {
+            return directoryCounter;
+        }
+
+        /**
+         * Gets the count of visited files.
+         *
+         * @return the byte count of visited files.
+         */
+        @Override
+        public Counter getFileCounter() {
+            return this.fileCounter;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%,d files, %,d directories, %,d bytes", Long.valueOf(fileCounter.get()),
+                    Long.valueOf(directoryCounter.get()), Long.valueOf(byteCounter.get()));
+        }
+
+    }
+
+    /**
+     * Counts using a BigInteger number.
+     */
+    private static class BigIntegerCounter implements Counter {
+
+        private BigInteger value = BigInteger.ZERO;
+
+        @Override
+        public void add(final long val) {
+            value = value.add(BigInteger.valueOf(val));
+
+        }
+
+        @Override
+        public long get() {
+            return value.longValueExact();
+        }
+
+        @Override
+        public BigInteger getBigInteger() {
+            return value;
+        }
+
+        @Override
+        public Long getLong() {
+            return Long.valueOf(value.longValueExact());
+        }
+
+        @Override
+        public void increment() {
+            value = value.add(BigInteger.ONE);
+        }
+
+        @Override
+        public String toString() {
+            return value.toString();
+        }
+    }
+
+    /**
+     * Counts files, directories, and sizes, as a visit proceeds, using BigInteger numbers.
+     */
+    private static class BigIntegerPathCounters extends AbstractPathCounters {
+
+        /**
+         * Constructs a new initialized instance.
+         */
+        protected BigIntegerPathCounters() {
+            super(Counters.bigIntegerCounter(), Counters.bigIntegerCounter(), Counters.bigIntegerCounter());
+        }
+
+    }
+
+    /**
+     * Counts using a number.
+     */
+    public interface Counter {
+
+        /**
+         * Adds the given number to this counter.
+         *
+         * @param val the value to add.
+         */
+        void add(long val);
+
+        /**
+         * Gets the counter as a long.
+         *
+         * @return the counter as a long.
+         */
+        long get();
+
+        /**
+         * Gets the counter as a BigInteger.
+         *
+         * @return the counter as a BigInteger.
+         */
+        BigInteger getBigInteger();
+
+        /**
+         * Gets the counter as a Long.
+         *
+         * @return the counter as a Long.
+         */
+        Long getLong();
+
+        /**
+         * Adds one to this counter.
+         */
+        void increment();
+
+    }
+
+    /**
+     * Counts using a long number.
+     */
+    private static class LongCounter implements Counter {
+
+        private long value;
+
+        @Override
+        public void add(final long add) {
+            value += add;
+
+        }
+
+        @Override
+        public long get() {
+            return value;
+        }
+
+        @Override
+        public BigInteger getBigInteger() {
+            return BigInteger.valueOf(value);
+        }
+
+        @Override
+        public Long getLong() {
+            return Long.valueOf(value);
+        }
+
+        @Override
+        public void increment() {
+            value++;
+        }
+
+        @Override
+        public String toString() {
+            return Long.toString(value);
+        }
+    }
+
+    /**
+     * Counts files, directories, and sizes, as a visit proceeds, using long numbers.
+     */
+    private static class LongPathCounters extends AbstractPathCounters {
+
+        /**
+         * Constructs a new initialized instance.
+         */
+        protected LongPathCounters() {
+            super(Counters.longCounter(), Counters.longCounter(), Counters.longCounter());
+        }
+
+    }
+
+    /**
+     * Counts files, directories, and sizes, as a visit proceeds.
+     */
+    public interface PathCounters {
+
+        /**
+         * Gets the byte counter.
+         *
+         * @return the byte counter.
+         */
+        Counter getByteCounter();
+
+        /**
+         * Gets the directory counter.
+         *
+         * @return the directory counter.
+         */
+        Counter getDirectoryCounter();
+
+        /**
+         * Gets the file counter.
+         *
+         * @return the file counter.
+         */
+        Counter getFileCounter();
+
+    }
+
+    /**
+     * Returns a new BigInteger Counter.
+     *
+     * @return a new BigInteger Counter.
+     */
+    public static Counter bigIntegerCounter() {
+        return new BigIntegerCounter();
+    }
+
+    /**
+     * Returns a new BigInteger PathCounters.
+     *
+     * @return a new BigInteger PathCounters.
+     */
+    public static PathCounters bigIntegerPathCounters() {
+        return new BigIntegerPathCounters();
+    }
+
+    /**
+     * Returns a new long Counter.
+     *
+     * @return a new long Counter.
+     */
+    public static Counter longCounter() {
+        return new LongCounter();
+    }
+
+    /**
+     * Returns a new BigInteger PathCounters.
+     *
+     * @return a new BigInteger PathCounters.
+     */
+    public static PathCounters longPathCounters() {
+        return new LongPathCounters();
+    }
+}
diff --git a/src/main/java/org/apache/commons/io/file/CountingPathFileVisitor.java b/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
similarity index 50%
rename from src/main/java/org/apache/commons/io/file/CountingPathFileVisitor.java
rename to src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
index 6c5085e..2ca589b 100644
--- a/src/main/java/org/apache/commons/io/file/CountingPathFileVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
@@ -22,41 +22,47 @@ import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Objects;
+
+import org.apache.commons.io.file.Counters.PathCounters;
 
 /**
  * Counts files, directories, and sizes, as a visit proceeds.
  *
  * @since 2.7
  */
-public class CountingPathFileVisitor extends SimplePathFileVisitor {
+public class CountingPathVisitor extends SimplePathVisitor {
 
-    private final PathCounts pathCounts = new PathCounts();
+    static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     /**
-     * Gets the byte count of visited files.
+     * Creates a new instance configured with a BigInteger {@link PathCounters}.
      *
-     * @return the byte count of visited files.
+     * @return a new instance configured with a BigInteger {@link PathCounters}.
      */
-    public long getByteCount() {
-        return this.pathCounts.byteCount.get();
+    public static CountingPathVisitor withBigIntegerCounters() {
+        return new CountingPathVisitor(Counters.bigIntegerPathCounters());
     }
 
     /**
-     * Gets the count of visited directories.
+     * Creates a new instance configured with a long {@link PathCounters}.
      *
-     * @return the count of visited directories.
+     * @return a new instance configured with a long {@link PathCounters}.
      */
-    public long getDirectoryCount() {
-        return this.pathCounts.directoryCount.get();
+    public static CountingPathVisitor withLongCounters() {
+        return new CountingPathVisitor(Counters.longPathCounters());
     }
 
+    private final PathCounters pathCounters;
+
     /**
-     * Gets the count of visited files.
+     * Constructs a new instance.
      *
-     * @return the byte count of visited files.
+     * @param pathCounter How to count path visits.
      */
-    public long getFileCount() {
-        return this.pathCounts.fileCount.get();
+    public CountingPathVisitor(final PathCounters pathCounter) {
+        super();
+        this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter");
     }
 
     /**
@@ -64,26 +70,36 @@ public class CountingPathFileVisitor extends SimplePathFileVisitor {
      *
      * @return the visitation counts.
      */
-    public PathCounts getPathCounts() {
-        return pathCounts;
+    public PathCounters getPathCounters() {
+        return pathCounters;
     }
 
     @Override
     public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
-        pathCounts.directoryCount.incrementAndGet();
+        pathCounters.getDirectoryCounter().increment();
         return FileVisitResult.CONTINUE;
     }
 
     @Override
     public String toString() {
-        return pathCounts.toString();
+        return pathCounters.toString();
+    }
+
+    /**
+     * Updates the counters for visiting the given file.
+     * 
+     * @param file the visited file.
+     * @param attrs the visited file attributes.
+     */
+    protected void updateFileCounters(final Path file, final BasicFileAttributes attrs) {
+        pathCounters.getFileCounter().increment();
+        pathCounters.getByteCounter().add(attrs.size());
     }
 
     @Override
     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
         if (Files.exists(file)) {
-            pathCounts.fileCount.incrementAndGet();
-            pathCounts.byteCount.addAndGet(attrs.size());
+            updateFileCounters(file, attrs);
         }
         return FileVisitResult.CONTINUE;
     }
diff --git a/src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java b/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
similarity index 72%
rename from src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java
rename to src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
index fac4c4b..609ac89 100644
--- a/src/main/java/org/apache/commons/io/file/DeletingPathFileVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
@@ -24,22 +24,45 @@ import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Arrays;
 
+import org.apache.commons.io.file.Counters.PathCounters;
+
 /**
  * Deletes files and directories as a visit proceeds.
  *
  * @since 2.7
  */
-public class DeletingPathFileVisitor extends CountingPathFileVisitor {
+public class DeletingPathVisitor extends CountingPathVisitor {
+
+
+    /**
+     * Creates a new instance configured with a BigInteger {@link PathCounters}.
+     *
+     * @return a new instance configured with a BigInteger {@link PathCounters}.
+     */
+    public static DeletingPathVisitor withBigIntegerCounters() {
+        return new DeletingPathVisitor(Counters.bigIntegerPathCounters());
+    }
+
+    /**
+     * Creates a new instance configured with a long {@link PathCounters}.
+     *
+     * @return a new instance configured with a long {@link PathCounters}.
+     */
+    public static DeletingPathVisitor withLongCounters() {
+        return new DeletingPathVisitor(Counters.longPathCounters());
+    }
 
-    private static final String[] EMPTY_STRING_ARRAY = new String[0];
     private final String[] skip;
 
     /**
      * Constructs a new visitor that deletes files except for the files and directories explicitly given.
      *
+     * @param pathCounter How to count visits.
+     *
      * @param skip The files to skip deleting.
      */
-    public DeletingPathFileVisitor(final String... skip) {
+    public DeletingPathVisitor(final PathCounters pathCounter, final String... skip) {
+        super(pathCounter);
         final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY;
         Arrays.sort(temp);
         this.skip = temp;
@@ -57,10 +80,10 @@ public class DeletingPathFileVisitor extends CountingPathFileVisitor {
 
     @Override
     public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
-        super.postVisitDirectory(dir, exc);
         if (PathUtils.isEmptyDirectory(dir)) {
             Files.deleteIfExists(dir);
         }
+        super.postVisitDirectory(dir, exc);
         return FileVisitResult.CONTINUE;
     }
 
@@ -72,10 +95,10 @@ public class DeletingPathFileVisitor extends CountingPathFileVisitor {
 
     @Override
     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
-        super.visitFile(file, attrs);
         if (accept(file) && Files.exists(file)) {
             Files.deleteIfExists(file);
         }
+        updateFileCounters(file, attrs);
         return FileVisitResult.CONTINUE;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/io/file/PathCounts.java b/src/main/java/org/apache/commons/io/file/PathCounts.java
deleted file mode 100644
index 1b2c7b3..0000000
--- a/src/main/java/org/apache/commons/io/file/PathCounts.java
+++ /dev/null
@@ -1,66 +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.file;
-
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * Counts files, directories, and sizes, as a visit proceeds.
- *
- * @since 2.7
- */
-public class PathCounts {
-
-    final AtomicLong byteCount = new AtomicLong();
-    final AtomicLong directoryCount = new AtomicLong();
-    final AtomicLong fileCount = new AtomicLong();
-
-    /**
-     * Gets the byte count of visited files.
-     *
-     * @return the byte count of visited files.
-     */
-    public long getByteCount() {
-        return this.byteCount.get();
-    }
-
-    /**
-     * Gets the count of visited directories.
-     *
-     * @return the count of visited directories.
-     */
-    public long getDirectoryCount() {
-        return this.directoryCount.get();
-    }
-
-    /**
-     * Gets the count of visited files.
-     *
-     * @return the byte count of visited files.
-     */
-    public long getFileCount() {
-        return this.fileCount.get();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("%,d files, %,d directories, %,d bytes", Long.valueOf(fileCount.longValue()),
-                Long.valueOf(directoryCount.longValue()), Long.valueOf(byteCount.longValue()));
-    }
-
-}
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 949a7a3..1dc5705 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -18,11 +18,15 @@
 package org.apache.commons.io.file;
 
 import java.io.IOException;
+import java.net.URI;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
 import java.nio.file.NotDirectoryException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.file.Counters.PathCounters;
 
 /**
  * NIO Path utilities.
@@ -38,8 +42,8 @@ public final class PathUtils {
      * @return The visitor used to count the given directory.
      * @throws IOException if an I/O error is thrown by a visitor method.
      */
-    public static PathCounts countDirectory(final Path directory) throws IOException {
-        return visitFileTree(directory, new CountingPathFileVisitor()).getPathCounts();
+    public static PathCounters countDirectory(final Path directory) throws IOException {
+        return visitFileTree(new CountingPathVisitor(Counters.longPathCounters()), directory).getPathCounters();
     }
 
     /**
@@ -58,11 +62,22 @@ public final class PathUtils {
      * @throws NullPointerException if the directory is {@code null}
      * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs.
      */
-    public static PathCounts delete(final Path path) throws IOException {
+    public static PathCounters delete(final Path path) throws IOException {
         return Files.isDirectory(path) ? deleteDirectory(path) : deleteFile(path);
     }
 
     /**
+     * Deletes a directory including sub-directories.
+     *
+     * @param directory directory to delete.
+     * @return The visitor used to delete the given directory.
+     * @throws IOException if an I/O error is thrown by a visitor method.
+     */
+    public static PathCounters deleteDirectory(final Path directory) throws IOException {
+        return visitFileTree(new DeletingPathVisitor(Counters.longPathCounters()), directory).getPathCounters();
+    }
+
+    /**
      * Deletes the given file.
      *
      * @param file The file to delete.
@@ -70,32 +85,20 @@ public final class PathUtils {
      * @throws IOException if an I/O error occurs.
      * @throws NotDirectoryException if the file is a directory.
      */
-    public static PathCounts deleteFile(final Path file) throws IOException {
+    public static PathCounters deleteFile(final Path file) throws IOException {
         if (Files.isDirectory(file)) {
             throw new NotDirectoryException(file.toString());
         }
-        final PathCounts pathCounts = new PathCounts();
+        final PathCounters pathCounts = Counters.longPathCounters();
         final long size = Files.exists(file) ? Files.size(file) : 0;
         if (Files.deleteIfExists(file)) {
-            pathCounts.fileCount.set(1);
-            pathCounts.directoryCount.set(0);
-            pathCounts.byteCount.set(size);
+            pathCounts.getFileCounter().increment();
+            pathCounts.getByteCounter().add(size);
         }
         return pathCounts;
     }
 
     /**
-     * Deletes a directory including sub-directories.
-     *
-     * @param directory directory to delete.
-     * @return The visitor used to delete the given directory.
-     * @throws IOException if an I/O error is thrown by a visitor method.
-     */
-    public static PathCounts deleteDirectory(final Path directory) throws IOException {
-        return visitFileTree(directory, new DeletingPathFileVisitor()).getPathCounts();
-    }
-
-    /**
      * Returns whether the given file or directory is empty.
      *
      * @param path the the given file or directory to query.
@@ -138,20 +141,56 @@ public final class PathUtils {
      *
      * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
      *
-     * @param directory See {@link Files#walkFileTree(Path,FileVisitor)}.
      * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
+     * @param directory See {@link Files#walkFileTree(Path,FileVisitor)}.
+     *
      * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
      * @return the given visitor.
      *
      * @throws IOException if an I/O error is thrown by a visitor method
      */
-    public static <T extends FileVisitor<? super Path>> T visitFileTree(final Path directory, final T visitor)
+    public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path directory)
             throws IOException {
         Files.walkFileTree(directory, visitor);
         return visitor;
     }
 
     /**
+     * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
+     *
+     * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
+     *
+     * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
+     * @param first See {@link Paths#get(String,String[])}.
+     * @param more See {@link Paths#get(String,String[])}.
+     * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
+     * @return the given visitor.
+     *
+     * @throws IOException if an I/O error is thrown by a visitor method
+     */
+    public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final String first, final String... more)
+            throws IOException {
+        return visitFileTree(visitor, Paths.get(first, more));
+    }
+
+    /**
+     * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
+     *
+     * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
+     *
+     * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
+     * @param uri See {@link Paths#get(URI)}.
+     * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
+     * @return the given visitor.
+     *
+     * @throws IOException if an I/O error is thrown by a visitor method
+     */
+    public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final URI uri)
+            throws IOException {
+        return visitFileTree(visitor, Paths.get(uri));
+    }
+
+    /**
      * Does allow to instantiate.
      */
     private PathUtils() {
diff --git a/src/main/java/org/apache/commons/io/file/SimplePathFileVisitor.java b/src/main/java/org/apache/commons/io/file/SimplePathVisitor.java
similarity index 90%
rename from src/main/java/org/apache/commons/io/file/SimplePathFileVisitor.java
rename to src/main/java/org/apache/commons/io/file/SimplePathVisitor.java
index 8cc95c0..8b2cdc4 100644
--- a/src/main/java/org/apache/commons/io/file/SimplePathFileVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/SimplePathVisitor.java
@@ -25,12 +25,12 @@ import java.nio.file.SimpleFileVisitor;
  *
  * @since 2.7
  */
-public abstract class SimplePathFileVisitor extends SimpleFileVisitor<Path> {
+public abstract class SimplePathVisitor extends SimpleFileVisitor<Path> {
 
     /**
      * Constructs a new instance.
      */
-    protected SimplePathFileVisitor() {
+    protected SimplePathVisitor() {
         super();
     }
 
diff --git a/src/test/java/org/apache/commons/io/file/CleaningPathVisitorTest.java b/src/test/java/org/apache/commons/io/file/CleaningPathVisitorTest.java
new file mode 100644
index 0000000..a6dfdcb
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/CleaningPathVisitorTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.file;
+
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.Counters.PathCounters;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link DeletingPathVisitor}.
+ */
+public class CleaningPathVisitorTest extends TestArguments {
+
+    private Path tempDir;
+
+    @AfterEach
+    public void afterEach() throws IOException {
+        // temp dir should still exist since we are cleaning and not deleting.
+        assertTrue(Files.exists(tempDir));
+        // backstop
+        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
+            Files.deleteIfExists(tempDir);
+        }
+    }
+
+    @BeforeEach
+    public void beforeEach() throws IOException {
+        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
+    }
+
+    /**
+     * Tests an empty folder.
+     */
+    @ParameterizedTest
+    @MethodSource("cleaningPathVisitors")
+    public void testCleanEmptyDirectory(final CleaningPathVisitor visitor) throws IOException {
+        applyCleanEmptyDirectory(visitor);
+    }
+
+    private void applyCleanEmptyDirectory(final CleaningPathVisitor visitor) throws IOException {
+        Files.walkFileTree(tempDir, visitor);
+        assertCounts(1, 0, 0, visitor);
+    }
+
+    /**
+     * Tests an empty folder.
+     */
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testCleanEmptyDirectoryNullCtorArg(final PathCounters pathCounters) throws IOException {
+        applyCleanEmptyDirectory(new CleaningPathVisitor(pathCounters, (String[]) null));
+    }
+
+    /**
+     * Tests a directory with one file of size 0.
+     */
+    @ParameterizedTest
+    @MethodSource("cleaningPathVisitors")
+    public void testCleanFolders1FileSize0(final CleaningPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
+                tempDir.toFile());
+        assertCounts(1, 1, 0, PathUtils.visitFileTree(visitor, tempDir));
+    }
+
+    /**
+     * Tests a directory with one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("cleaningPathVisitors")
+    public void testCleanFolders1FileSize1(final CleaningPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
+                tempDir.toFile());
+        assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
+    }
+
+    /**
+     * Tests a directory with one file of size 1 but skip that file.
+     */
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testCleanFolders1FileSize1Skip(final PathCounters pathCounters) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
+                tempDir.toFile());
+        final String skipFileName = "file-size-1.bin";
+        final CountingPathVisitor visitor = new CleaningPathVisitor(pathCounters, skipFileName);
+        assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
+        final Path skippedFile = tempDir.resolve(skipFileName);
+        Assertions.assertTrue(Files.exists(skippedFile));
+        Files.delete(skippedFile);
+    }
+
+    /**
+     * Tests a directory with two subdirectorys, each containing one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("cleaningPathVisitors")
+    public void testCleanFolders2FileSize2(final CleaningPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
+                tempDir.toFile());
+        assertCounts(3, 2, 2, PathUtils.visitFileTree(visitor, tempDir));
+    }
+}
diff --git a/src/test/java/org/apache/commons/io/file/CounterAssertions.java b/src/test/java/org/apache/commons/io/file/CounterAssertions.java
new file mode 100644
index 0000000..d2e3477
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/CounterAssertions.java
@@ -0,0 +1,45 @@
+/*
+ * 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.file;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.math.BigInteger;
+
+import org.apache.commons.io.file.Counters.Counter;
+import org.apache.commons.io.file.Counters.PathCounters;
+
+class CounterAssertions {
+
+    static void assertCounter(final long expected, final Counter actual) {
+        assertEquals(expected, actual.get());
+        assertEquals(Long.valueOf(expected), actual.getLong());
+        assertEquals(BigInteger.valueOf(expected), actual.getBigInteger());
+    }
+
+    static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount, final CountingPathVisitor actualVisitor) {
+        assertCounts(expectedDirCount, expectedFileCount, expectedByteCount, actualVisitor.getPathCounters());
+    }
+
+    static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount, final PathCounters actualPathCounters) {
+        assertCounter(expectedDirCount, actualPathCounters.getDirectoryCounter());
+        assertCounter(expectedFileCount, actualPathCounters.getFileCounter());
+        assertCounter(expectedByteCount, actualPathCounters.getByteCounter());
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/file/CountersTest.java b/src/test/java/org/apache/commons/io/file/CountersTest.java
new file mode 100644
index 0000000..92c1aa5
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/CountersTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.file;
+
+import static org.apache.commons.io.file.CounterAssertions.assertCounter;
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
+import org.apache.commons.io.file.Counters.Counter;
+import org.apache.commons.io.file.Counters.PathCounters;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class CountersTest extends TestArguments {
+
+    @ParameterizedTest
+    @MethodSource("numberCounters")
+    public void testInitialValue(final Counter counter) {
+        assertCounter(0, counter);
+    }
+
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testInitialValues(final PathCounters pathCounter) {
+        // Does not blow up
+        assertCounts(0, 0, 0, pathCounter);
+    }
+
+    @ParameterizedTest
+    @MethodSource("numberCounters")
+    public void testToString(final Counter counter) {
+        // Does not blow up
+        counter.toString();
+    }
+
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testToString(final PathCounters pathCounter) {
+        // Does not blow up
+        pathCounter.toString();
+    }
+}
diff --git a/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java b/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java
deleted file mode 100644
index 920bb6a..0000000
--- a/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java
+++ /dev/null
@@ -1,91 +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.file;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests {@link CountingPathFileVisitor}.
- */
-public class CountingFileVisitorTest {
-
-    /**
-     * Tests an empty folder.
-     */
-    @Test
-    public void testCountEmptyFolder() throws IOException {
-        final Path tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
-        try {
-            final CountingPathFileVisitor visitor = new CountingPathFileVisitor();
-            Files.walkFileTree(tempDirectory, visitor);
-            Assertions.assertEquals(1, visitor.getDirectoryCount());
-            Assertions.assertEquals(0, visitor.getFileCount());
-            Assertions.assertEquals(0, visitor.getByteCount());
-        } finally {
-            Files.deleteIfExists(tempDirectory);
-        }
-    }
-
-    /**
-     * Tests a directory with one file of size 0.
-     */
-    @Test
-    public void testCountFolders1FileSize0() throws IOException {
-        final CountingPathFileVisitor visitor = new CountingPathFileVisitor();
-        Files.walkFileTree(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(0, visitor.getByteCount());
-    }
-
-    /**
-     * Tests a directory with one file of size 1.
-     */
-    @Test
-    public void testCountFolders1FileSize1() throws IOException {
-        final CountingPathFileVisitor visitor = new CountingPathFileVisitor();
-        Files.walkFileTree(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(1, visitor.getByteCount());
-    }
-
-    /**
-     * Tests a directory with two subdirectorys, each containing one file of size 1.
-     */
-    @Test
-    public void testCountFolders2FileSize2() throws IOException {
-        final CountingPathFileVisitor visitor = new CountingPathFileVisitor();
-        Files.walkFileTree(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), visitor);
-        Assertions.assertEquals(3, visitor.getDirectoryCount());
-        Assertions.assertEquals(2, visitor.getFileCount());
-        Assertions.assertEquals(2, visitor.getByteCount());
-    }
-
-    @Test
-    void testToString() {
-        // Make sure it does not blow up
-        new CountingPathFileVisitor().toString();
-    }
-}
diff --git a/src/test/java/org/apache/commons/io/file/CountingPathVisitorTest.java b/src/test/java/org/apache/commons/io/file/CountingPathVisitorTest.java
new file mode 100644
index 0000000..afdd998
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/CountingPathVisitorTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.file;
+
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link CountingPathVisitor}.
+ */
+public class CountingPathVisitorTest extends TestArguments {
+
+    /**
+     * Tests an empty folder.
+     */
+    @ParameterizedTest
+    @MethodSource("countingPathVisitors")
+    public void testCountEmptyFolder(final CountingPathVisitor visitor) throws IOException {
+        final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
+        try {
+            assertCounts(1, 0, 0, PathUtils.visitFileTree(visitor, tempDir));
+        } finally {
+            Files.deleteIfExists(tempDir);
+        }
+    }
+
+    /**
+     * Tests a directory with one file of size 0.
+     */
+    @ParameterizedTest
+    @MethodSource("countingPathVisitors")
+    public void testCountFolders1FileSize0(final CountingPathVisitor visitor) throws IOException {
+        assertCounts(1, 1, 0, PathUtils.visitFileTree(visitor,
+                "src/test/resources/org/apache/commons/io/dirs-1-file-size-0"));
+    }
+
+    /**
+     * Tests a directory with one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("countingPathVisitors")
+    public void testCountFolders1FileSize1(final CountingPathVisitor visitor) throws IOException {
+        assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor,
+                "src/test/resources/org/apache/commons/io/dirs-1-file-size-1"));
+    }
+
+    /**
+     * Tests a directory with two subdirectorys, each containing one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("countingPathVisitors")
+    public void testCountFolders2FileSize2(final CountingPathVisitor visitor) throws IOException {
+        assertCounts(3, 2, 2, PathUtils.visitFileTree(visitor,
+                "src/test/resources/org/apache/commons/io/dirs-2-file-size-2"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("countingPathVisitors")
+    void testToString(final CountingPathVisitor visitor) {
+        // Make sure it does not blow up
+        visitor.toString();
+    }
+}
diff --git a/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java b/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java
deleted file mode 100644
index 2c5988e..0000000
--- a/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java
+++ /dev/null
@@ -1,143 +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.file;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import org.apache.commons.io.FileUtils;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests {@link DeletingPathFileVisitor}.
- */
-public class DeletingFileVisitorTest {
-
-    private Path tempDirectory;
-
-    @AfterEach
-    public void afterEach() throws IOException {
-        // backstop
-        if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) {
-            Files.deleteIfExists(tempDirectory);
-        }
-    }
-
-    @BeforeEach
-    public void beforeEach() throws IOException {
-        tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
-    }
-
-    /**
-     * Tests an empty folder.
-     */
-    @Test
-    public void testDeleteEmptyDirectory() throws IOException {
-        testDeleteEmptyDirectory(new DeletingPathFileVisitor());
-        // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-
-    private void testDeleteEmptyDirectory(final DeletingPathFileVisitor visitor) throws IOException {
-        Files.walkFileTree(tempDirectory, visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(0, visitor.getFileCount());
-        Assertions.assertEquals(0, visitor.getByteCount());
-    }
-
-    /**
-     * Tests an empty folder.
-     */
-    @Test
-    public void testDeleteEmptyDirectoryNullCtorArg() throws IOException {
-        testDeleteEmptyDirectory(new DeletingPathFileVisitor((String[]) null));
-        // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-
-    /**
-     * Tests a directory with one file of size 0.
-     */
-    @Test
-    public void testDeleteFolders1FileSize0() throws IOException {
-        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
-                tempDirectory.toFile());
-        final CountingPathFileVisitor visitor = new DeletingPathFileVisitor();
-        Files.walkFileTree(tempDirectory, visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(0, visitor.getByteCount());
-        // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-
-    /**
-     * Tests a directory with one file of size 1.
-     */
-    @Test
-    public void testDeleteFolders1FileSize1() throws IOException {
-        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
-                tempDirectory.toFile());
-        final CountingPathFileVisitor visitor = new DeletingPathFileVisitor();
-        Files.walkFileTree(tempDirectory, visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(1, visitor.getByteCount());
-        // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-
-    /**
-     * Tests a directory with one file of size 1 but skip that file.
-     */
-    @Test
-    public void testDeleteFolders1FileSize1Skip() throws IOException {
-        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
-                tempDirectory.toFile());
-        final String skipFileName = "file-size-1.bin";
-        final CountingPathFileVisitor visitor = new DeletingPathFileVisitor(skipFileName);
-        Files.walkFileTree(tempDirectory, visitor);
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(1, visitor.getByteCount());
-        final Path skippedFile = tempDirectory.resolve(skipFileName);
-        Assertions.assertTrue(Files.exists(skippedFile));
-        Files.delete(skippedFile);
-    }
-
-    /**
-     * Tests a directory with two subdirectorys, each containing one file of size 1.
-     */
-    @Test
-    public void testDeleteFolders2FileSize2() throws IOException {
-        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
-                tempDirectory.toFile());
-        final CountingPathFileVisitor visitor = new DeletingPathFileVisitor();
-        Files.walkFileTree(tempDirectory, visitor);
-        Assertions.assertEquals(3, visitor.getDirectoryCount());
-        Assertions.assertEquals(2, visitor.getFileCount());
-        Assertions.assertEquals(2, visitor.getByteCount());
-        // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-}
diff --git a/src/test/java/org/apache/commons/io/file/DeletingPathVisitorTest.java b/src/test/java/org/apache/commons/io/file/DeletingPathVisitorTest.java
new file mode 100644
index 0000000..b31c614
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/DeletingPathVisitorTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.file;
+
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.Counters.PathCounters;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link DeletingPathVisitor}.
+ */
+public class DeletingPathVisitorTest extends TestArguments {
+
+    private Path tempDir;
+
+    @AfterEach
+    public void afterEach() throws IOException {
+        // backstop
+        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
+            Files.deleteIfExists(tempDir);
+        }
+    }
+
+    @BeforeEach
+    public void beforeEach() throws IOException {
+        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
+    }
+
+    /**
+     * Tests an empty folder.
+     */
+    @ParameterizedTest
+    @MethodSource("deletingPathVisitors")
+    public void testDeleteEmptyDirectory(final DeletingPathVisitor visitor) throws IOException {
+        applyDeleteEmptyDirectory(visitor);
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDir);
+    }
+
+    private void applyDeleteEmptyDirectory(final DeletingPathVisitor visitor) throws IOException {
+        Files.walkFileTree(tempDir, visitor);
+        assertCounts(1, 0, 0, visitor);
+    }
+
+    /**
+     * Tests an empty folder.
+     */
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testDeleteEmptyDirectoryNullCtorArg(final PathCounters pathCounters) throws IOException {
+        applyDeleteEmptyDirectory(new DeletingPathVisitor(pathCounters, (String[]) null));
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDir);
+    }
+
+    /**
+     * Tests a directory with one file of size 0.
+     */
+    @ParameterizedTest
+    @MethodSource("deletingPathVisitors")
+    public void testDeleteFolders1FileSize0(final DeletingPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
+                tempDir.toFile());
+        assertCounts(1, 1, 0, PathUtils.visitFileTree(visitor, tempDir));
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDir);
+    }
+
+    /**
+     * Tests a directory with one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("deletingPathVisitors")
+    public void testDeleteFolders1FileSize1(final DeletingPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
+                tempDir.toFile());
+        assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDir);
+    }
+
+    /**
+     * Tests a directory with one file of size 1 but skip that file.
+     */
+    @ParameterizedTest
+    @MethodSource("pathCounters")
+    public void testDeleteFolders1FileSize1Skip(final PathCounters pathCounters) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
+                tempDir.toFile());
+        final String skipFileName = "file-size-1.bin";
+        final CountingPathVisitor visitor = new DeletingPathVisitor(pathCounters, skipFileName);
+        assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
+        final Path skippedFile = tempDir.resolve(skipFileName);
+        Assertions.assertTrue(Files.exists(skippedFile));
+        Files.delete(skippedFile);
+    }
+
+    /**
+     * Tests a directory with two subdirectorys, each containing one file of size 1.
+     */
+    @ParameterizedTest
+    @MethodSource("deletingPathVisitors")
+    public void testDeleteFolders2FileSize2(final DeletingPathVisitor visitor) throws IOException {
+        FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
+                tempDir.toFile());
+        assertCounts(3, 2, 2, PathUtils.visitFileTree(visitor, tempDir));
+        // This will throw if not empty.
+        Files.deleteIfExists(tempDir);
+    }
+}
diff --git a/src/test/java/org/apache/commons/io/file/PathCountsTest.java b/src/test/java/org/apache/commons/io/file/PathCountsTest.java
deleted file mode 100644
index 40292c6..0000000
--- a/src/test/java/org/apache/commons/io/file/PathCountsTest.java
+++ /dev/null
@@ -1,38 +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.file;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class PathCountsTest {
-
-    @Test
-    public void testCtor() {
-        final PathCounts pathCounts = new PathCounts();
-        Assertions.assertEquals(pathCounts.getByteCount(), 0);
-        Assertions.assertEquals(pathCounts.getDirectoryCount(), 0);
-        Assertions.assertEquals(pathCounts.getFileCount(), 0);
-    }
-
-    @Test
-    public void testToString() {
-        // Does not blow up
-        new PathCounts().toString();
-    }
-}
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsCountingTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsCountingTest.java
index 57fe890..e0d1939 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsCountingTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsCountingTest.java
@@ -17,13 +17,14 @@
 
 package org.apache.commons.io.file;
 
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import org.apache.commons.io.file.PathUtils;
-import org.junit.jupiter.api.Assertions;
+import org.apache.commons.io.file.Counters.PathCounters;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -31,27 +32,17 @@ import org.junit.jupiter.api.Test;
  */
 public class PathUtilsCountingTest {
 
-    private static final Path DIR_SIZE_1 = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1");
-
-    private static final Path FILE_SIZE_0 = Paths
-            .get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/file-size-0.bin");
-
-    private static final Path FILE_SIZE_1 = Paths
-            .get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
-
     /**
      * Tests an empty folder.
      */
     @Test
     public void testCountEmptyFolder() throws IOException {
-        final Path tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
         try {
-            final PathCounts pathCounts = PathUtils.countDirectory(tempDirectory);
-            Assertions.assertEquals(1, pathCounts.getDirectoryCount());
-            Assertions.assertEquals(0, pathCounts.getFileCount());
-            Assertions.assertEquals(0, pathCounts.getByteCount());
+            final PathCounters pathCounts = PathUtils.countDirectory(tempDir);
+            assertCounts(1, 0, 0, pathCounts);
         } finally {
-            Files.deleteIfExists(tempDirectory);
+            Files.deleteIfExists(tempDir);
         }
     }
 
@@ -60,11 +51,9 @@ public class PathUtilsCountingTest {
      */
     @Test
     public void testCountFolders1FileSize0() throws IOException {
-        final PathCounts pathCounts = PathUtils
+        final PathCounters pathCounts = PathUtils
                 .countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"));
-        Assertions.assertEquals(1, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+        assertCounts(1, 1, 0, pathCounts);
     }
 
     /**
@@ -72,11 +61,9 @@ public class PathUtilsCountingTest {
      */
     @Test
     public void testCountFolders1FileSize1() throws IOException {
-        final PathCounts visitor = PathUtils
+        final PathCounters visitor = PathUtils
                 .countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"));
-        Assertions.assertEquals(1, visitor.getDirectoryCount());
-        Assertions.assertEquals(1, visitor.getFileCount());
-        Assertions.assertEquals(1, visitor.getByteCount());
+        assertCounts(1, 1, 1, visitor);
     }
 
     /**
@@ -84,10 +71,8 @@ public class PathUtilsCountingTest {
      */
     @Test
     public void testCountFolders2FileSize2() throws IOException {
-        final PathCounts pathCounts = PathUtils
+        final PathCounters pathCounts = PathUtils
                 .countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"));
-        Assertions.assertEquals(3, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(2, pathCounts.getFileCount());
-        Assertions.assertEquals(2, pathCounts.getByteCount());
+        assertCounts(3, 2, 2, pathCounts);
     }
 }
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 af51754..905f2fc 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.io.file;
 
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -24,28 +26,27 @@ import java.nio.file.Paths;
 
 import org.apache.commons.io.FileUtils;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
- * Tests {@link DeletingPathFileVisitor}.
+ * Tests {@link DeletingPathVisitor}.
  */
 public class PathUtilsDeleteDirectoryTest {
 
-    private Path tempDirectory;
+    private Path tempDir;
 
     @AfterEach
     public void afterEach() throws IOException {
         // backstop
-        if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) {
-            Files.deleteIfExists(tempDirectory);
+        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
+            Files.deleteIfExists(tempDir);
         }
     }
 
     @BeforeEach
     public void beforeEach() throws IOException {
-        tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
     }
 
     /**
@@ -54,13 +55,10 @@ public class PathUtilsDeleteDirectoryTest {
     @Test
     public void testDeleteDirectory1FileSize0() throws IOException {
         FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.deleteDirectory(tempDirectory);
-        Assertions.assertEquals(1, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDir));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -69,13 +67,10 @@ public class PathUtilsDeleteDirectoryTest {
     @Test
     public void testDeleteDirectory1FileSize1() throws IOException {
         FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.deleteDirectory(tempDirectory);
-        Assertions.assertEquals(1, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(1, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(1, 1, 1, PathUtils.deleteDirectory(tempDir));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -84,13 +79,10 @@ public class PathUtilsDeleteDirectoryTest {
     @Test
     public void testDeleteDirectory2FileSize2() throws IOException {
         FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.deleteDirectory(tempDirectory);
-        Assertions.assertEquals(3, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(2, pathCounts.getFileCount());
-        Assertions.assertEquals(2, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(3, 2, 2, PathUtils.deleteDirectory(tempDir));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -98,14 +90,8 @@ public class PathUtilsDeleteDirectoryTest {
      */
     @Test
     public void testDeleteEmptyDirectory() throws IOException {
-        testDeleteEmptyDirectory(PathUtils.deleteDirectory(tempDirectory));
+        assertCounts(1, 0, 0, PathUtils.deleteDirectory(tempDir));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
-    }
-
-    private void testDeleteEmptyDirectory(final PathCounts pathCounts) throws IOException {
-        Assertions.assertEquals(1, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(0, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+        Files.deleteIfExists(tempDir);
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteFileTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteFileTest.java
index dfac100..8bc15de 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteFileTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteFileTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.io.file;
 
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.NotDirectoryException;
@@ -24,29 +26,30 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.Counters.PathCounters;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
- * Tests {@link DeletingPathFileVisitor}.
+ * Tests {@link DeletingPathVisitor}.
  */
 public class PathUtilsDeleteFileTest {
 
-    private Path tempDirectory;
+    private Path tempDir;
 
     @AfterEach
     public void afterEach() throws IOException {
         // backstop
-        if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) {
-            Files.deleteIfExists(tempDirectory);
+        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
+            Files.deleteIfExists(tempDir);
         }
     }
 
     @BeforeEach
     public void beforeEach() throws IOException {
-        tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
     }
 
     /**
@@ -57,13 +60,10 @@ public class PathUtilsDeleteFileTest {
         final String fileName = "file-size-0.bin";
         FileUtils.copyFileToDirectory(
                 Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName).toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.deleteFile(tempDirectory.resolve(fileName));
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(0, 1, 0, PathUtils.deleteFile(tempDir.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -74,13 +74,10 @@ public class PathUtilsDeleteFileTest {
         final String fileName = "file-size-1.bin";
         FileUtils.copyFileToDirectory(
                 Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName).toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.deleteFile(tempDirectory.resolve(fileName));
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(1, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(0, 1, 1, PathUtils.deleteFile(tempDir.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -88,15 +85,13 @@ public class PathUtilsDeleteFileTest {
      */
     @Test
     public void testDeleteFileDoesNotExist() throws IOException {
-        testDeleteFileEmpty(PathUtils.deleteFile(tempDirectory.resolve("file-does-not-exist.bin")));
+        testDeleteFileEmpty(PathUtils.deleteFile(tempDir.resolve("file-does-not-exist.bin")));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
-    private void testDeleteFileEmpty(final PathCounts pathCounts) throws IOException {
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount(), "getDirectoryCount()");
-        Assertions.assertEquals(0, pathCounts.getFileCount(), "getFileCount()");
-        Assertions.assertEquals(0, pathCounts.getByteCount(), "getByteCount()");
+    private void testDeleteFileEmpty(final PathCounters pathCounts) {
+        assertCounts(0, 0, 0, pathCounts);
     }
 
     /**
@@ -104,9 +99,8 @@ public class PathUtilsDeleteFileTest {
      */
     @Test
     public void testDeleteFileEmptyDirectory() throws IOException {
-        Assertions.assertThrows(NotDirectoryException.class,
-                () -> testDeleteFileEmpty(PathUtils.deleteFile(tempDirectory)));
+        Assertions.assertThrows(NotDirectoryException.class, () -> testDeleteFileEmpty(PathUtils.deleteFile(tempDir)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
index 9c70e85..ef56b72 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsDeleteTest.java
@@ -17,35 +17,37 @@
 
 package org.apache.commons.io.file;
 
+import static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.Counters.PathCounters;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
- * Tests {@link DeletingPathFileVisitor}.
+ * Tests {@link DeletingPathVisitor}.
  */
 public class PathUtilsDeleteTest {
 
-    private Path tempDirectory;
+    private Path tempDir;
 
     @AfterEach
     public void afterEach() throws IOException {
         // backstop
-        if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) {
-            Files.deleteIfExists(tempDirectory);
+        if (Files.exists(tempDir) && PathUtils.isEmptyDirectory(tempDir)) {
+            Files.deleteIfExists(tempDir);
         }
     }
 
     @BeforeEach
     public void beforeEach() throws IOException {
-        tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        tempDir = Files.createTempDirectory(getClass().getCanonicalName());
     }
 
     /**
@@ -56,13 +58,10 @@ public class PathUtilsDeleteTest {
         final String fileName = "file-size-0.bin";
         FileUtils.copyFileToDirectory(
                 Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0/" + fileName).toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.delete(tempDirectory.resolve(fileName));
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(0, 1, 0, PathUtils.delete(tempDir.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -73,13 +72,10 @@ public class PathUtilsDeleteTest {
         final String fileName = "file-size-1.bin";
         FileUtils.copyFileToDirectory(
                 Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/" + fileName).toFile(),
-                tempDirectory.toFile());
-        final PathCounts pathCounts = PathUtils.delete(tempDirectory.resolve(fileName));
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(1, pathCounts.getFileCount());
-        Assertions.assertEquals(1, pathCounts.getByteCount());
+                tempDir.toFile());
+        assertCounts(0, 1, 1, PathUtils.delete(tempDir.resolve(fileName)));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
     /**
@@ -87,15 +83,13 @@ public class PathUtilsDeleteTest {
      */
     @Test
     public void testDeleteEmptyDirectory() throws IOException {
-        testDeleteEmptyDirectory(PathUtils.delete(tempDirectory));
+        testDeleteEmptyDirectory(PathUtils.delete(tempDir));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 
-    private void testDeleteEmptyDirectory(final PathCounts pathCounts) throws IOException {
-        Assertions.assertEquals(1, pathCounts.getDirectoryCount(), "getDirectoryCount()");
-        Assertions.assertEquals(0, pathCounts.getFileCount(), "getFileCount()");
-        Assertions.assertEquals(0, pathCounts.getByteCount(), "getByteCount()");
+    private void testDeleteEmptyDirectory(final PathCounters pathCounts) {
+        assertCounts(1, 0, 0, pathCounts);
     }
 
     /**
@@ -103,11 +97,8 @@ public class PathUtilsDeleteTest {
      */
     @Test
     public void testDeleteFileDoesNotExist() throws IOException {
-        final PathCounts pathCounts = PathUtils.deleteFile(tempDirectory.resolve("file-does-not-exist.bin"));
-        Assertions.assertEquals(0, pathCounts.getDirectoryCount());
-        Assertions.assertEquals(0, pathCounts.getFileCount());
-        Assertions.assertEquals(0, pathCounts.getByteCount());
+        assertCounts(0, 0, 0, PathUtils.deleteFile(tempDir.resolve("file-does-not-exist.bin")));
         // This will throw if not empty.
-        Files.deleteIfExists(tempDirectory);
+        Files.deleteIfExists(tempDir);
     }
 }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsIsEmptyTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsIsEmptyTest.java
index 20fe56c..647a7ef 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsIsEmptyTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsIsEmptyTest.java
@@ -22,7 +22,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import org.apache.commons.io.file.PathUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -43,22 +42,22 @@ public class PathUtilsIsEmptyTest {
     public void testIsEmpty() throws IOException {
         Assertions.assertTrue(PathUtils.isEmpty(FILE_SIZE_0));
         Assertions.assertFalse(PathUtils.isEmpty(FILE_SIZE_1));
-        final Path tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
         try {
-            Assertions.assertTrue(PathUtils.isEmpty(tempDirectory));
+            Assertions.assertTrue(PathUtils.isEmpty(tempDir));
         } finally {
-            Files.delete(tempDirectory);
+            Files.delete(tempDir);
         }
         Assertions.assertFalse(PathUtils.isEmpty(DIR_SIZE_1));
     }
 
     @Test
     public void testisEmptyDirectory() throws IOException {
-        final Path tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
+        final Path tempDir = Files.createTempDirectory(getClass().getCanonicalName());
         try {
-            Assertions.assertTrue(PathUtils.isEmptyDirectory(tempDirectory));
+            Assertions.assertTrue(PathUtils.isEmptyDirectory(tempDir));
         } finally {
-            Files.delete(tempDirectory);
+            Files.delete(tempDir);
         }
         Assertions.assertFalse(PathUtils.isEmptyDirectory(DIR_SIZE_1));
     }
diff --git a/src/test/java/org/apache/commons/io/file/TestArguments.java b/src/test/java/org/apache/commons/io/file/TestArguments.java
new file mode 100644
index 0000000..428debb
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/TestArguments.java
@@ -0,0 +1,65 @@
+/*
+ * 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.file;
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.provider.Arguments;
+
+class TestArguments {
+
+    static Stream<Arguments> numberCounters() {
+        // @formatter:off
+        return Stream.of(
+          Arguments.of(Counters.longCounter()),
+          Arguments.of(Counters.bigIntegerCounter()));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> countingPathVisitors() {
+        // @formatter:off
+        return Stream.of(
+          Arguments.of(CountingPathVisitor.withBigIntegerCounters()),
+          Arguments.of(CountingPathVisitor.withLongCounters()));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> cleaningPathVisitors() {
+        // @formatter:off
+        return Stream.of(
+          Arguments.of(CleaningPathVisitor.withBigIntegerCounters()),
+          Arguments.of(CleaningPathVisitor.withLongCounters()));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> deletingPathVisitors() {
+        // @formatter:off
+        return Stream.of(
+          Arguments.of(DeletingPathVisitor.withBigIntegerCounters()),
+          Arguments.of(DeletingPathVisitor.withLongCounters()));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> pathCounters() {
+        // @formatter:off
+        return Stream.of(
+          Arguments.of(Counters.longPathCounters()),
+          Arguments.of(Counters.bigIntegerPathCounters()));
+        // @formatter:on
+    }
+}