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
The following commit(s) were added to refs/heads/master by this push: new 59129bcf2 Reimplement FileSystemUtils using NIO 59129bcf2 is described below commit 59129bcf283ffa5a46d0872e173a4e037575c581 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Thu Mar 28 23:08:39 2024 -0400 Reimplement FileSystemUtils using NIO --- src/changes/changes.xml | 3 + .../org/apache/commons/io/FileSystemUtils.java | 412 ++--------------- .../org/apache/commons/io/FileSystemUtilsTest.java | 496 ++------------------- 3 files changed, 73 insertions(+), 838 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b72a82bce..de99df423 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -46,6 +46,9 @@ The <action> type attribute can be add,update,fix,remove. <title>Apache Commons IO Release Notes</title> </properties> <body> + <release version="2.16.1" date="YYYY-MM-DD" description="Java 8 is required."> + <action dev="ggregory" type="fix" due-to="Gary Gregory">Reimplement FileSystemUtils using NIO.</action> + </release> <release version="2.16.0" date="2024-03-25" description="Java 8 is required."> <!-- Fix --> <action dev="ggregory" type="fix" due-to="Elliotte Rusty Harold">Fix and re-enable testSkip_RequiredCharsets #518.</action> diff --git a/src/main/java/org/apache/commons/io/FileSystemUtils.java b/src/main/java/org/apache/commons/io/FileSystemUtils.java index 431f964a9..091b333c7 100644 --- a/src/main/java/org/apache/commons/io/FileSystemUtils.java +++ b/src/main/java/org/apache/commons/io/FileSystemUtils.java @@ -16,148 +16,68 @@ */ package org.apache.commons.io; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.nio.file.InvalidPathException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; import java.util.Objects; -import java.util.StringTokenizer; -import java.util.stream.Collectors; /** * General File System utilities. * <p> - * This class provides static utility methods for general file system functions not provided via the JDK {@link java.io.File File} class. + * This class provides static utility methods for general file system functions not provided before Java 6's {@link java.io.File File} class. + * </p> * <p> * The current functions provided are: + * </p> * <ul> - * <li>Get the free space on a drive + * <li>Get the free space on a drive</li> * </ul> * * @since 1.1 - * @deprecated As of 2.6 deprecated without replacement. Use equivalent methods in {@link java.nio.file.FileStore} instead, e.g. + * @deprecated As of 2.6 deprecated without replacement. Use equivalent methods in {@link java.nio.file.FileStore} instead, * {@code Files.getFileStore(Paths.get("/home")).getUsableSpace()} or iterate over {@code FileSystems.getDefault().getFileStores()} */ @Deprecated public class FileSystemUtils { /** - * Singleton instance, used mainly for testing. - */ - private static final FileSystemUtils INSTANCE = new FileSystemUtils(); - - /** - * Operating system state flag for error. - */ - private static final int INIT_PROBLEM = -1; - - /** - * Operating system state flag for neither UNIX nor Windows. - */ - private static final int OTHER = 0; - - /** - * Operating system state flag for Windows. - */ - private static final int WINDOWS = 1; - - /** - * Operating system state flag for Unix. - */ - private static final int UNIX = 2; - - /** - * Operating system state flag for POSIX flavor Unix. - */ - private static final int POSIX_UNIX = 3; - - /** - * The operating system flag. - */ - private static final int OS; - - /** - * The path to {@code df}. - */ - private static final String DF; - - static { - int os = OTHER; - String dfPath = "df"; - try { - String osName = System.getProperty("os.name"); - if (osName == null) { - throw new IOException("os.name not found"); - } - osName = osName.toLowerCase(Locale.ENGLISH); - // match - if (osName.contains("windows")) { - os = WINDOWS; - } else if (osName.contains("linux") || osName.contains("mpe/ix") || osName.contains("freebsd") || osName.contains("openbsd") - || osName.contains("irix") || osName.contains("digital unix") || osName.contains("unix") || osName.contains("mac os x")) { - os = UNIX; - } else if (osName.contains("sun os") || osName.contains("sunos") || osName.contains("solaris")) { - os = POSIX_UNIX; - dfPath = "/usr/xpg4/bin/df"; - } else if (osName.contains("hp-ux") || osName.contains("aix")) { - os = POSIX_UNIX; - } - - } catch (final Exception ex) { - os = INIT_PROBLEM; - } - OS = os; - DF = dfPath; - } - - /** - * Returns the free space on a drive or volume by invoking the command line. This method does not normalize the result, and typically returns bytes on - * Windows, 512 byte units on OS X and kilobytes on Unix. As this is not very useful, this method is deprecated in favor of {@link #freeSpaceKb(String)} - * which returns a result in kilobytes. + * Gets the number of kibibytes (1024 bytes) available to this Java virtual machine on the given file store. * <p> * Note that some OS's are NOT currently supported, including OS/390, OpenVMS. + * </p> * * <pre> * FileSystemUtils.freeSpace("C:"); // Windows * FileSystemUtils.freeSpace("/volume"); // *nix * </pre> * - * The free space is calculated via the command line. It uses 'dir /-c' on Windows and 'df' on *nix. - * * @param path the path to get free space for, not null, not empty on UNIX * @return the amount of free drive space on the drive or volume + * @throws IOException if an I/O error occurs. * @throws IllegalArgumentException if the path is invalid * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space * @since 1.1, enhanced OS support in 1.2 and 1.3 * @deprecated Use freeSpaceKb(String) Deprecated from 1.3, may be removed in 2.0 */ @Deprecated public static long freeSpace(final String path) throws IOException { - return INSTANCE.freeSpaceOS(path, OS, false, Duration.ofMillis(-1)); + return getFreeSpace(path); } /** - * Returns the free space for the working directory in kibibytes (1024 bytes) by invoking the command line. + * Gets the number of kibibytes (1024 bytes) available to this Java virtual machine on the current file store. * <p> * Identical to: + * </p> * * <pre> * freeSpaceKb(FileUtils.current().getAbsolutePath()) * </pre> * * @return the amount of free drive space on the drive or volume in kilobytes + * @throws IOException if an I/O error occurs. * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space * @since 2.0 * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}. */ @@ -167,18 +87,19 @@ public class FileSystemUtils { } /** - * Returns the free space for the working directory in kibibytes (1024 bytes) by invoking the command line. + * Gets the number of kibibytes (1024 bytes) available to this Java virtual machine on the current file store. * <p> * Identical to: + * </p> * * <pre> * freeSpaceKb(FileUtils.current().getAbsolutePath()) * </pre> * - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less + * @param timeout ignored. * @return the amount of free drive space on the drive or volume in kilobytes + * @throws IOException if an I/O error occurs. * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space * @since 2.0 * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}. */ @@ -188,24 +109,18 @@ public class FileSystemUtils { } /** - * Returns the free space on a drive or volume in kibibytes (1024 bytes) by invoking the command line. + * Gets the number of kibibytes (1024 bytes) available to this Java virtual machine on the given file store. * * <pre> * FileSystemUtils.freeSpaceKb("C:"); // Windows * FileSystemUtils.freeSpaceKb("/volume"); // *nix * </pre> * - * The free space is calculated via the command line. It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix. - * <p> - * In order to work, you must be running Windows, or have an implementation of UNIX df that supports GNU format when passed -k (or -kP). If you are going to - * rely on this code, please check that it works on your OS by running some simple tests to compare the command line with the output from this class. If - * your operating system isn't supported, please raise a JIRA call detailing the exact result from df -k and as much other detail as possible, thanks. - * * @param path the path to get free space for, not null, not empty on UNIX * @return the amount of free drive space on the drive or volume in kilobytes + * @throws IOException if an I/O error occurs. * @throws IllegalArgumentException if the path is invalid * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space * @since 1.2, enhanced OS support in 1.3 * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}. */ @@ -215,312 +130,59 @@ public class FileSystemUtils { } /** - * Returns the free space on a drive or volume in kibibytes (1024 bytes) by invoking the command line. + * Gets the number of kibibytes (1024 bytes) available to this Java virtual machine on the given file store. * * <pre> * FileSystemUtils.freeSpaceKb("C:"); // Windows * FileSystemUtils.freeSpaceKb("/volume"); // *nix * </pre> * - * The free space is calculated via the command line. It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix. - * <p> - * In order to work, you must be running Windows, or have an implementation of UNIX df that supports GNU format when passed -k (or -kP). If you are going to - * rely on this code, please check that it works on your OS by running some simple tests to compare the command line with the output from this class. If - * your operating system isn't supported, please raise a JIRA call detailing the exact result from df -k and as much other detail as possible, thanks. - * * @param path the path to get free space for, not null, not empty on UNIX - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less + * @param timeout ignored. * @return the amount of free drive space on the drive or volume in kilobytes + * @throws IOException if an I/O error occurs. * @throws IllegalArgumentException if the path is invalid * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space * @since 2.0 * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}. */ @Deprecated public static long freeSpaceKb(final String path, final long timeout) throws IOException { - return INSTANCE.freeSpaceOS(path, OS, true, Duration.ofMillis(timeout)); - } - - /** - * Instances should NOT be constructed in standard programming. - * - * @deprecated TODO Make private in 3.0. - */ - @Deprecated - public FileSystemUtils() { - // empty - } - - /** - * Checks that a path string is valid through NIO's {@link Paths#get(String, String...)}. - * - * @param pathStr string. - * @param allowEmpty allows empty paths. - * @return A checked normalized Path. - * @throws InvalidPathException if the path string cannot be converted to a {@code Path} - */ - private Path checkPath(final String pathStr, final boolean allowEmpty) { - Objects.requireNonNull(pathStr, "pathStr"); - if (!allowEmpty && pathStr.isEmpty()) { - throw new IllegalArgumentException("Path must not be empty"); - } - final Path normPath; - final String trimPathStr = pathStr.trim(); - if (trimPathStr.isEmpty() || trimPathStr.charAt(0) != '"') { - // Paths.get throws InvalidPathException if the path is bad before we pass it to a shell. - normPath = Paths.get(trimPathStr).normalize(); - } else { - // Paths.get throws InvalidPathException if the path is bad before we pass it to a shell. - normPath = Paths.get(trimPathStr.substring(1, trimPathStr.length() - 1)).normalize(); - } - return normPath; + return getFreeSpace(path) / FileUtils.ONE_KB; } /** - * Returns the free space on a drive or volume in a cross-platform manner. Note that some OS's are NOT currently supported, including OS/390. + * Gets the number of bytes available to this Java virtual machine on the given file store. * * <pre> * FileSystemUtils.freeSpace("C:"); // Windows * FileSystemUtils.freeSpace("/volume"); // *nix * </pre> * - * The free space is calculated via the command line. It uses 'dir /-c' on Windows and 'df' on *nix. - * * @param pathStr the path to get free space for, not null, not empty on UNIX - * @param os the operating system code - * @param kb whether to normalize to kilobytes - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less * @return the amount of free drive space on the drive or volume + * @throws IOException if an I/O error occurs. * @throws IllegalArgumentException if the path is invalid * @throws IllegalStateException if an error occurred in initialization - * @throws IOException if an error occurs when finding the free space - */ - long freeSpaceOS(final String pathStr, final int os, final boolean kb, final Duration timeout) throws IOException { - Objects.requireNonNull(pathStr, "path"); - switch (os) { - case WINDOWS: - return kb ? freeSpaceWindows(pathStr, timeout) / FileUtils.ONE_KB : freeSpaceWindows(pathStr, timeout); - case UNIX: - return freeSpaceUnix(pathStr, kb, false, timeout); - case POSIX_UNIX: - return freeSpaceUnix(pathStr, kb, true, timeout); - case OTHER: - throw new IllegalStateException("Unsupported operating system"); - default: - throw new IllegalStateException("Exception caught when determining operating system"); - } - } - - /** - * Finds free space on the *nix platform using the 'df' command. - * - * @param path the path to get free space for - * @param kb whether to normalize to kilobytes - * @param posix whether to use the POSIX standard format flag - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less - * @return the amount of free drive space on the volume - * @throws IOException If an I/O error occurs - */ - long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout) throws IOException { - final String pathStr = checkPath(path, false).toString(); - // build and run the 'dir' command - String flags = "-"; - if (kb) { - flags += "k"; - } - if (posix) { - flags += "P"; - } - final String[] cmdAttribs = flags.length() > 1 ? new String[] { DF, flags, pathStr } : new String[] { DF, pathStr }; - - // perform the command, asking for up to 3 lines (header, interesting, overflow) - final List<String> lines = performCommand(cmdAttribs, 3, timeout); - if (lines.size() < 2) { - // unknown problem, throw exception - throw new IOException("Command line '" + DF + "' did not return info as expected for path '" + pathStr + "'- response was " + lines); - } - final String line2 = lines.get(1); // the line we're interested in - - // Now, we tokenize the string. The fourth element is what we want. - StringTokenizer tok = new StringTokenizer(line2, " "); - if (tok.countTokens() < 4) { - // could be long Filesystem, thus data on third line - if (tok.countTokens() != 1 || lines.size() < 3) { - throw new IOException("Command line '" + DF + "' did not return data as expected for path '" + pathStr + "'- check path is valid"); - } - final String line3 = lines.get(2); // the line may be interested in - tok = new StringTokenizer(line3, " "); - } else { - tok.nextToken(); // Ignore Filesystem - } - tok.nextToken(); // Ignore 1K-blocks - tok.nextToken(); // Ignore Used - final String freeSpace = tok.nextToken(); - return parseBytes(freeSpace, path); - } - - /** - * Finds free space on the Windows platform using the 'dir' command. - * - * @param pathStr the path to get free space for, including the colon - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less - * @return the amount of free drive space on the drive - * @throws IOException If an I/O error occurs - */ - long freeSpaceWindows(final String pathStr, final Duration timeout) throws IOException { - final Path path = checkPath(pathStr, true); - // build and run the 'dir' command - // read in the output of the command to an ArrayList - final List<String> lines = performCommand(new String[] { "cmd.exe", "/C", "dir /a /-c \"" + path + "\"" }, Integer.MAX_VALUE, timeout); - - // now iterate over the lines we just read and find the LAST - // non-empty line (the free space bytes should be in the last element - // of the ArrayList anyway, but this will ensure it works even if it's - // not, still assuming it is on the last non-blank line) - for (int i = lines.size() - 1; i >= 0; i--) { - final String line = lines.get(i); - if (!line.isEmpty()) { - return parseDir(line, pathStr); - } - } - // all lines are blank - throw new IOException("Command 'dir' did not return any info for path '" + path + "'"); - } - - /** - * Opens the process to the operating system. - * <p> - * Package-private for tests. - * </p> - * @param cmdArray the command line parameters - * @return the process - * @throws IOException If an I/O error occurs */ - Process openProcess(final String[] cmdArray) throws IOException { - return Runtime.getRuntime().exec(cmdArray); - } - - /** - * Parses the bytes from a string. - * - * @param freeSpace the free space string - * @param path the path - * @return the number of bytes - * @throws IOException If an I/O error occurs - */ - private long parseBytes(final String freeSpace, final String path) throws IOException { - try { - final long bytes = Long.parseLong(freeSpace); - if (bytes < 0) { - throw new IOException("Command line '" + DF + "' did not find free space in response for path '" + path + "'- check path is valid"); - } - return bytes; - - } catch (final NumberFormatException ex) { - throw new IOException("Command line '" + DF + "' did not return numeric data as expected for path '" + path + "'- check path is valid", ex); - } - } - - /** - * Parses the Windows dir response last line. - * - * @param line the line to parse - * @param path the path that was sent - * @return the number of bytes - * @throws IOException If an I/O error occurs - */ - private long parseDir(final String line, final String path) throws IOException { - // read from the end of the line to find the last numeric - // character on the line, then continue until we find the first - // non-numeric character, and everything between that and the last - // numeric character inclusive is our free space bytes count - int bytesStart = 0; - int bytesEnd = 0; - int j = line.length() - 1; - innerLoop1: while (j >= 0) { - final char c = line.charAt(j); - if (Character.isDigit(c)) { - // found the last numeric character, this is the end of - // the free space bytes count - bytesEnd = j + 1; - break innerLoop1; - } - j--; - } - innerLoop2: while (j >= 0) { - final char c = line.charAt(j); - if (!Character.isDigit(c) && c != ',' && c != '.') { - // found the next non-numeric character, this is the - // beginning of the free space bytes count - bytesStart = j + 1; - break innerLoop2; - } - j--; + static long getFreeSpace(final String pathStr) throws IOException { + final Path path = Paths.get(Objects.requireNonNull(pathStr, "pathStr")); + if (Files.exists(path)) { + // Need an absolute path for input like "" to work + return Files.getFileStore(path.toAbsolutePath()).getUsableSpace(); + // return path.toAbsolutePath().toFile().getUsableSpace(); } - if (j < 0) { - throw new IOException("Command line 'dir /-c' did not return valid info for path '" + path + "'"); - } - - // remove commas and dots in the bytes count - final StringBuilder buf = new StringBuilder(line.substring(bytesStart, bytesEnd)); - for (int k = 0; k < buf.length(); k++) { - if (buf.charAt(k) == ',' || buf.charAt(k) == '.') { - buf.deleteCharAt(k--); - } - } - return parseBytes(buf.toString(), path); + throw new IllegalArgumentException(path.toString()); } /** - * Performs an OS command. + * Instances should NOT be constructed in standard programming. * - * @param cmdArray the command line parameters - * @param max The maximum limit for the lines returned - * @param timeout The timeout amount in milliseconds or no timeout if the value is zero or less - * @return the lines returned by the command, converted to lower-case - * @throws IOException if an error occurs + * @deprecated TODO Make private in 3.0. */ - private List<String> performCommand(final String[] cmdArray, final int max, final Duration timeout) throws IOException { - // - // This method does what it can to avoid the 'Too many open files' error - // based on trial and error and these links: - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4784692 - // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4801027 - // However, it's still not perfect as the JDK support is so poor. - // (See commons-exec or Ant for a better multithreaded multi-OS solution.) - // - final Process proc = openProcess(cmdArray); - final Thread monitor = ThreadMonitor.start(timeout); - try (InputStream in = proc.getInputStream(); - OutputStream out = proc.getOutputStream(); - // default Charset is most likely appropriate here - InputStream err = proc.getErrorStream(); - // If in is null here, InputStreamReader throws NullPointerException - BufferedReader inr = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()))) { - - final List<String> lines = inr.lines().limit(max).map(line -> line.toLowerCase(Locale.getDefault()).trim()).collect(Collectors.toList()); - proc.waitFor(); - ThreadMonitor.stop(monitor); - - if (proc.exitValue() != 0) { - // Command problem, throw exception - throw new IOException("Command line returned OS error code '" + proc.exitValue() + "' for command " + Arrays.asList(cmdArray)); - } - if (lines.isEmpty()) { - // Unknown problem, throw exception - throw new IOException("Command line did not return any info for command " + Arrays.asList(cmdArray)); - } - - return lines; - - } catch (final InterruptedException ex) { - throw new IOException("Command line threw an InterruptedException for command " + Arrays.asList(cmdArray) + " timeout=" + timeout, ex); - } finally { - if (proc != null) { - proc.destroy(); - } - } + @Deprecated + public FileSystemUtils() { + // empty } } diff --git a/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java b/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java index 4525ca35a..fff75e421 100644 --- a/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java +++ b/src/test/java/org/apache/commons/io/FileSystemUtilsTest.java @@ -16,23 +16,10 @@ */ package org.apache.commons.io; -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.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.time.Duration; -import java.util.Locale; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -42,479 +29,62 @@ import org.junit.jupiter.params.provider.MethodSource; @SuppressWarnings("deprecation") // testing deprecated class public class FileSystemUtilsTest { - static class MockFileSystemUtils extends FileSystemUtils { - private final int exitCode; - private final byte[] bytes; - private final String cmd; - - public MockFileSystemUtils(final int exitCode, final String lines) { - this(exitCode, lines, null); - } - - public MockFileSystemUtils(final int exitCode, final String lines, final String cmd) { - this.exitCode = exitCode; - this.bytes = lines.getBytes(); - this.cmd = cmd; - } - - @Override - Process openProcess(final String[] cmdArray) { - if (cmd != null) { - assertEquals(cmd, cmdArray[cmdArray.length - 1]); - } - return new Process() { - - @Override - public void destroy() { - // nnop - } - - @Override - public int exitValue() { - return exitCode; - } - - @Override - public InputStream getErrorStream() { - return null; - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(bytes); - } - - @Override - public OutputStream getOutputStream() { - return null; - } - - @Override - public int waitFor() throws InterruptedException { - return exitCode; - } - }; - } - } - - private static final Duration NEG_1_TIMEOUT = Duration.ofMillis(-1); - static char[] getIllegalFileNameChars() { return FileSystem.getCurrent().getIllegalFileNameChars(); } - @Test - public void testGetFreeSpace_String() throws Exception { - // test coverage, as we can't check value - if (File.separatorChar == '/') { - // have to figure out UNIX block size - final String[] cmd; - String osName = System.getProperty("os.name"); - osName = osName.toLowerCase(Locale.ENGLISH); - - if (osName.contains("hp-ux") || osName.contains("aix")) { - cmd = new String[]{"df", "-P", "/"}; - } else if (osName.contains("sunos") || osName.contains("sun os") - || osName.contains("solaris")) { - cmd = new String[]{"/usr/xpg4/bin/df", "-P", "/"}; - } else { - cmd = new String[]{"df", "/"}; - } - final Process proc = Runtime.getRuntime().exec(cmd); - boolean kilobyteBlock = true; - try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getInputStream()))){ - final String line = r.readLine(); - assertNotNull(line, "Unexpected null line"); - if (line.contains("512")) { - kilobyteBlock = false; - } - } - - // now perform the test - final long free = FileSystemUtils.freeSpace("/"); - final long kb = FileSystemUtils.freeSpaceKb("/"); - // Assume disk space does not fluctuate - // more than 1% between the above two calls; - // this is also small enough to verify freeSpaceKb uses - // kibibytes (1024) instead of SI kilobytes (1000) - final double acceptableDelta = kb * 0.01d; - if (kilobyteBlock) { - assertEquals(free, kb, acceptableDelta); - } else { - assertEquals(free / 2d, kb, acceptableDelta); - } - } else { - final long bytes = FileSystemUtils.freeSpace(""); - final long kb = FileSystemUtils.freeSpaceKb(""); - // Assume disk space does not fluctuate more than 1% - final double acceptableDelta = kb * 0.01d; - assertEquals((double) bytes / 1024, kb, acceptableDelta); - } - } - - @Test - public void testGetFreeSpaceOS_String_InitError() throws Exception { - final FileSystemUtils fsu = new FileSystemUtils(); - assertThrows(IllegalStateException.class, () -> fsu.freeSpaceOS("", -1, false, NEG_1_TIMEOUT)); - assertThrows(IllegalStateException.class, () -> fsu.freeSpaceOS("", -1, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceOS_String_NullPath() throws Exception { - final FileSystemUtils fsu = new FileSystemUtils(); - assertThrows(NullPointerException.class, () -> fsu.freeSpaceOS(null, 1, false, NEG_1_TIMEOUT)); - assertThrows(NullPointerException.class, () -> fsu.freeSpaceOS(null, 1, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceOS_String_Other() throws Exception { - final FileSystemUtils fsu = new FileSystemUtils(); - assertThrows(IllegalStateException.class, () -> fsu.freeSpaceOS("", 0, false, NEG_1_TIMEOUT)); - assertThrows(NullPointerException.class, () -> fsu.freeSpaceOS(null, 1, true, NEG_1_TIMEOUT)); - assertThrows(IllegalStateException.class, () -> fsu.freeSpaceOS("", 0, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceOS_String_Unix() throws Exception { - final FileSystemUtils fsu = new FileSystemUtils() { - @Override - protected long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout) throws IOException { - return kb ? 12345L : 54321; - } - }; - assertEquals(54321L, fsu.freeSpaceOS("", 2, false, NEG_1_TIMEOUT)); - assertEquals(12345L, fsu.freeSpaceOS("", 2, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceOS_String_Windows() throws Exception { - final FileSystemUtils fsu = new FileSystemUtils() { - @Override - protected long freeSpaceWindows(final String path, final Duration timeout) throws IOException { - return 12345L; - } - }; - assertEquals(12345L, fsu.freeSpaceOS("", 1, false, NEG_1_TIMEOUT)); - assertEquals(12345L / 1024, fsu.freeSpaceOS("", 1, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_EmptyPath() throws Exception { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx:/home/users/s 14428928 12956424 1472504 90% /home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IllegalArgumentException.class, () -> fsu.freeSpaceUnix("", false, false, NEG_1_TIMEOUT)); - assertThrows(IllegalArgumentException.class, () -> fsu.freeSpaceUnix("", true, false, NEG_1_TIMEOUT)); - assertThrows(IllegalArgumentException.class, () -> fsu.freeSpaceUnix("", true, true, NEG_1_TIMEOUT)); - assertThrows(IllegalArgumentException.class, () -> fsu.freeSpaceUnix("", false, true, NEG_1_TIMEOUT)); - } - - @Test - - public void testGetFreeSpaceUnix_String_EmptyResponse() { - final String lines = ""; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, true, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_InvalidResponse1() { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - " 14428928 12956424 100"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, true, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_InvalidResponse2() { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx:/home/users/s 14428928 12956424 nnnnnnn 90% /home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, true, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_InvalidResponse3() { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx:/home/users/s 14428928 12956424 -1 90% /home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, true, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_InvalidResponse4() { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx-yyyyyyy-zzz:/home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", false, true, NEG_1_TIMEOUT)); - assertThrows(IOException.class, () -> fsu.freeSpaceUnix("/home/users/s", true, true, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_LongResponse() throws Exception { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx-yyyyyyy-zzz:/home/users/s\n" + - " 14428928 12956424 1472504 90% /home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(1472504L, fsu.freeSpaceUnix("/home/users/s", false, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_LongResponseKb() throws Exception { - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "xxx-yyyyyyy-zzz:/home/users/s\n" + - " 14428928 12956424 1472504 90% /home/users/s"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(1472504L, fsu.freeSpaceUnix("/home/users/s", true, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_NormalResponseFreeBSD() throws Exception { - // from Apache 'FreeBSD 6.1-RELEASE (SMP-turbo)' - final String lines = - "Filesystem 1K-blocks Used Avail Capacity Mounted on\n" + - "/dev/xxxxxx 128990 102902 15770 87% /"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(15770L, fsu.freeSpaceUnix("/", false, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_NormalResponseKbFreeBSD() throws Exception { - // from Apache 'FreeBSD 6.1-RELEASE (SMP-turbo)' - // df and df -k are identical, but df -kP uses 512 blocks (not relevant as not used) - final String lines = - "Filesystem 1K-blocks Used Avail Capacity Mounted on\n" + - "/dev/xxxxxx 128990 102902 15770 87% /"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(15770L, fsu.freeSpaceUnix("/", true, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_NormalResponseKbLinux() throws Exception { - // from Sourceforge 'GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu)' - // df, df -k and df -kP are all identical - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "/dev/xxx 497944 308528 189416 62% /"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(189416L, fsu.freeSpaceUnix("/", true, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_NormalResponseKbSolaris() throws Exception { - // from IO-91 - ' SunOS et 5.10 Generic_118822-25 sun4u sparc SUNW,Ultra-4' - // non-kb response does not contain free space - see IO-91 - final String lines = - "Filesystem kbytes used avail capacity Mounted on\n" + - "/dev/dsk/x0x0x0x0 1350955 815754 481163 63%"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(481163L, fsu.freeSpaceUnix("/dev/dsk/x0x0x0x0", true, false, NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceUnix_String_NormalResponseLinux() throws Exception { - // from Sourceforge 'GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu)' - final String lines = - "Filesystem 1K-blocks Used Available Use% Mounted on\n" + - "/dev/xxx 497944 308528 189416 62% /"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(189416L, fsu.freeSpaceUnix("/", false, false, NEG_1_TIMEOUT)); - } - - @EnabledOnOs(value = OS.WINDOWS) @ParameterizedTest @MethodSource("getIllegalFileNameChars") - public void testGetFreeSpaceWindows_IllegalFileName(final char illegalFileNameChar) throws Exception { - assertThrows(IllegalArgumentException.class, () -> new FileSystemUtils().freeSpaceWindows("\\ \"" + illegalFileNameChar, NEG_1_TIMEOUT)); - } - - @EnabledOnOs(value = OS.WINDOWS) - @Test - public void testGetFreeSpaceWindows_IllegalFileNames() throws Exception { - assertThrows(IllegalArgumentException.class, () -> new FileSystemUtils().freeSpaceWindows("\\ \"", NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceWindows_String_EmptyMultiLineResponse() { - final String lines = "\n\n"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT)); + public void testGetFreeSpace_IllegalFileName(final char illegalFileNameChar) throws Exception { + assertThrows(IllegalArgumentException.class, () -> FileSystemUtils.freeSpace("\\ \"" + illegalFileNameChar)); } @Test - public void testGetFreeSpaceWindows_String_EmptyPath() throws Exception { - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180260 bytes\n" + - " 10 Dir(s) 41411551232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"\""); - assertEquals(41411551232L, fsu.freeSpaceWindows("", NEG_1_TIMEOUT)); + public void testGetFreeSpace_IllegalFileNames() throws Exception { + assertThrows(IllegalArgumentException.class, () -> FileSystemUtils.freeSpace("\\ \"")); } @Test - public void testGetFreeSpaceWindows_String_EmptyResponse() { - final String lines = ""; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceWindows_String_InvalidTextResponse() { - final String lines = "BlueScreenOfDeath"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT)); - } - @Test - public void testGetFreeSpaceWindows_String_NormalResponse() throws Exception { - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180260 bytes\n" + - " 10 Dir(s) 41411551232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"C:\""); - assertEquals(41411551232L, fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceWindows_String_NoSuchDirectoryResponse() { - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\empty" + - "\n"; - final FileSystemUtils fsu = new MockFileSystemUtils(1, lines); - assertThrows(IOException.class, () -> fsu.freeSpaceWindows("C:", NEG_1_TIMEOUT)); - } - - @Test - public void testGetFreeSpaceWindows_String_ParseCommaFormatBytes() throws Exception { - // this is the format of response when calling dir /c - // we have now switched to dir /-c, so we should never get this - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180,260 bytes\n" + - " 10 Dir(s) 41,411,551,232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(41411551232L, fsu.freeSpaceWindows("", NEG_1_TIMEOUT)); + public void testGetFreeSpace_String() throws Exception { + assertThrows(NullPointerException.class, () -> FileSystemUtils.freeSpace(null)); + assertThrows(IllegalArgumentException.class, () -> FileSystemUtils.freeSpace("this directory does not exist, at all.")); + // "" means current dir. + assertTrue(FileSystemUtils.freeSpace("") > 0); + assertTrue(FileSystemUtils.freeSpace("target") > 0); + // files worked as well in previous versions. + assertTrue(FileSystemUtils.freeSpace("pom.xml") > 0); } @Test - public void testGetFreeSpaceWindows_String_ParseCommaFormatBytes_Big() throws Exception { - // test with very large free space - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180,260 bytes\n" + - " 10 Dir(s) 141,411,551,232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(141411551232L, fsu.freeSpaceWindows("", NEG_1_TIMEOUT)); + public void testGetFreeSpaceKb() throws Exception { + assertTrue(FileSystemUtils.freeSpaceKb() > 0); } @Test - public void testGetFreeSpaceWindows_String_ParseCommaFormatBytes_Small() throws Exception { - // test with very large free space - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180,260 bytes\n" + - " 10 Dir(s) 1,232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines); - assertEquals(1232L, fsu.freeSpaceWindows("", NEG_1_TIMEOUT)); + public void testGetFreeSpaceKb_long() throws Exception { + assertTrue(FileSystemUtils.freeSpaceKb(0) > 0); } @Test - public void testGetFreeSpaceWindows_String_quoted() throws Exception { - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180260 bytes\n" + - " 10 Dir(s) 41411551232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"C:\\somedir\""); - assertEquals(41411551232L, fsu.freeSpaceWindows("\"C:\\somedir\"", NEG_1_TIMEOUT)); + public void testGetFreeSpaceKb_String() throws Exception { + assertThrows(NullPointerException.class, () -> FileSystemUtils.freeSpaceKb(null)); + assertThrows(IllegalArgumentException.class, () -> FileSystemUtils.freeSpaceKb("this directory does not exist, at all.")); + // "" means current dir. + assertTrue(FileSystemUtils.freeSpaceKb("") > 0); + assertTrue(FileSystemUtils.freeSpaceKb("target") > 0); + // files worked as well in previous versions. + assertTrue(FileSystemUtils.freeSpaceKb("pom.xml") > 0); } @Test - public void testGetFreeSpaceWindows_String_StripDrive() throws Exception { - final String lines = - " Volume in drive C is HDD\n" + - " Volume Serial Number is XXXX-YYYY\n" + - "\n" + - " Directory of C:\\Documents and Settings\\Xxxx\n" + - "\n" + - "19/08/2005 22:43 <DIR> .\n" + - "19/08/2005 22:43 <DIR> ..\n" + - "11/08/2005 01:07 81 build.properties\n" + - "17/08/2005 21:44 <DIR> Desktop\n" + - " 7 File(s) 180260 bytes\n" + - " 10 Dir(s) 41411551232 bytes free"; - final FileSystemUtils fsu = new MockFileSystemUtils(0, lines, "dir /a /-c \"C:\\somedir\""); - assertEquals(41411551232L, fsu.freeSpaceWindows("C:\\somedir", NEG_1_TIMEOUT)); + public void testGetFreeSpaceKb_String_long() throws Exception { + assertThrows(NullPointerException.class, () -> FileSystemUtils.freeSpaceKb(null, 0)); + assertThrows(IllegalArgumentException.class, () -> FileSystemUtils.freeSpaceKb("this directory does not exist, at all.", 0)); + // "" means current dir. + assertTrue(FileSystemUtils.freeSpaceKb("", 0) > 0); + assertTrue(FileSystemUtils.freeSpaceKb("target", 0) > 0); + // files worked as well in previous versions. + assertTrue(FileSystemUtils.freeSpaceKb("pom.xml", 0) > 0); } }