8156595: java/io/pathNames/GeneralWin32.java fail intermittently on windows-x64
Reviewed-by: psandoz
/*
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
@summary Common definitions for general exhaustive pathname tests
@author Mark Reinhold
*/
import java.io.*;
import java.util.*;
import java.nio.file.*;
public class General {
public static boolean debug = false;
private static boolean win32 = (File.separatorChar == '\\');
private static int gensymCounter = 0;
protected static final String userDir = System.getProperty("user.dir");
protected static final String workSubDir = "tmp";
protected static String baseDir = null;
protected static String relative = null;
/* Generate a filename unique to this run */
private static String gensym() {
return "x." + ++gensymCounter;
}
/**
* Create files and folders in the test working directory.
* The purpose is to make sure the test will not go out of
* its user dir when walking the file tree.
*
* @param depth The number of directory levels to be created under
* the user directory. It should be the maximum value
* of the depths passed to checkNames method (including
* direct or indirect calling) in a whole test.
*/
protected static void initTestData(int depth) throws IOException {
File parent = new File(userDir + File.separator + workSubDir);
if (!parent.mkdir()) {
throw new IOException("Fail to create directory: " + parent);
}
for (int i = 0; i < depth; i++) {
File tmp = new File(parent, gensym());
tmp.createNewFile();
tmp = new File(parent, gensym());
if (tmp.mkdir())
parent = tmp;
else
throw new IOException("Fail to create directory, " + tmp);
}
baseDir = parent.getAbsolutePath();
relative = baseDir.substring(userDir.length() + 1);
}
/**
* Find a file in the given subdirectory, or descend into further
* subdirectories, if any, if no file is found here. Return null if no
* file can be found anywhere beneath the given subdirectory.
* @param dir Directory at which we started
* @param subdir Subdirectory that we're exploring
* @param dl Listing of subdirectory
*/
private static String findSomeFile(String dir, String subdir, String[] dl) {
for (int i = 0; i < dl.length; i++) {
File f = new File(subdir, dl[i]);
File df = new File(dir, f.getPath());
if (Files.isRegularFile(df.toPath(), LinkOption.NOFOLLOW_LINKS)) {
return f.getPath();
}
}
for (int i = 0; i < dl.length; i++) {
File f = (subdir.length() == 0) ? new File(dl[i])
: new File(subdir, dl[i]);
File df = new File(dir, f.getPath());
if (Files.isDirectory(df.toPath(), LinkOption.NOFOLLOW_LINKS)) {
String[] dl2 = df.list();
if (dl2 != null) {
String ff = findSomeFile(dir, f.getPath(), dl2);
if (ff != null) return ff;
}
}
}
return null;
}
/**
* Construct a string that names a file in the given directory. If create
* is true, then create a file if none is found, and throw an exception if
* that is not possible; otherwise, return null if no file can be found.
*/
private static String findSomeFile(String dir, boolean create) {
File d = new File(dir);
String[] dl = d.list();
if (dl == null) {
throw new RuntimeException("Can't list " + dir);
}
for (int i = 0; i < dl.length; i++) {
File f = new File(dir, dl[i]);
if (Files.isRegularFile(f.toPath(), LinkOption.NOFOLLOW_LINKS)) {
return dl[i];
}
}
String f = findSomeFile(dir, "", dl);
if (f != null) {
return f;
}
if (create) {
File nf = new File(d, gensym());
OutputStream os;
try {
os = new FileOutputStream(nf);
os.close();
} catch (IOException x) {
throw new RuntimeException("Can't create a file in " + dir);
}
return nf.getName();
}
return null;
}
/**
* Construct a string that names a subdirectory of the given directory.
* If create is true, then create a subdirectory if none is found, and
* throw an exception if that is not possible; otherwise, return null if
* no subdirectory can be found.
*/
private static String findSomeDir(String dir, boolean create) {
File d = new File(dir);
String[] dl = d.list();
if (dl == null) {
throw new RuntimeException("Can't list " + dir);
}
for (int i = 0; i < dl.length; i++) {
File f = new File(d, dl[i]);
if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS)) {
String[] dl2 = f.list();
if (dl2 == null || dl2.length >= 250) {
/* Heuristic to avoid scanning huge directories */
continue;
}
return dl[i];
}
}
if (create) {
File sd = new File(d, gensym());
if (sd.mkdir()) return sd.getName();
}
return null;
}
/** Construct a string that does not name a file in the given directory */
private static String findNon(String dir) {
File d = new File(dir);
String[] x = new String[] { "foo", "bar", "baz" };
for (int i = 0; i < x.length; i++) {
File f = new File(d, x[i]);
if (!f.exists()) {
return x[i];
}
}
for (int i = 0; i < 1024; i++) {
String n = "xx" + Integer.toString(i);
File f = new File(d, n);
if (!f.exists()) {
return n;
}
}
throw new RuntimeException("Can't find a non-existent file in " + dir);
}
/** Ensure that the named file does not exist */
public static void ensureNon(String fn) {
if ((new File(fn)).exists()) {
throw new RuntimeException("Test path " + fn + " exists");
}
}
/** Tell whether the given character is a "slash" on this platform */
private static boolean isSlash(char x) {
if (x == File.separatorChar) return true;
if (win32 && (x == '/')) return true;
return false;
}
/**
* Trim trailing slashes from the given string, but leave singleton slashes
* alone (they denote root directories)
*/
private static String trimTrailingSlashes(String s) {
int n = s.length();
if (n == 0) return s;
n--;
while ((n > 0) && isSlash(s.charAt(n))) {
if ((n >= 1) && s.charAt(n - 1) == ':') break;
n--;
}
return s.substring(0, n + 1);
}
/** Concatenate two paths, trimming slashes as needed */
private static String pathConcat(String a, String b) {
if (a.length() == 0) return b;
if (b.length() == 0) return a;
if (isSlash(a.charAt(a.length() - 1))
|| isSlash(b.charAt(0))
|| (win32 && (a.charAt(a.length() - 1) == ':'))) {
return a + b;
} else {
return a + File.separatorChar + b;
}
}
/** Hash table of input pathnames, used to detect duplicates */
private static Hashtable<String, String> checked = new Hashtable<>();
/**
* Check the given pathname. Its canonical pathname should be the given
* answer. If the path names a file that exists and is readable, then
* FileInputStream and RandomAccessFile should both be able to open it.
*/
public static void check(String answer, String path) throws IOException {
String ans = trimTrailingSlashes(answer);
if (path.length() == 0) return;
if (checked.get(path) != null) {
System.err.println("DUP " + path);
return;
}
checked.put(path, path);
String cpath;
try {
File f = new File(path);
cpath = f.getCanonicalPath();
if (f.exists() && f.isFile() && f.canRead()) {
InputStream in = new FileInputStream(path);
in.close();
RandomAccessFile raf = new RandomAccessFile(path, "r");
raf.close();
}
} catch (IOException x) {
System.err.println(ans + " <-- " + path + " ==> " + x);
if (debug) return;
else throw x;
}
if (cpath.equals(ans)) {
System.err.println(ans + " <== " + path);
} else {
System.err.println(ans + " <-- " + path + " ==> " + cpath + " MISMATCH");
if (!debug) {
throw new RuntimeException("Mismatch: " + path + " ==> " + cpath +
", should be " + ans);
}
}
}
/*
* The following three mutually-recursive methods generate and check a tree
* of filenames of arbitrary depth. Each method has (at least) these
* arguments:
*
* int depth Remaining tree depth
* boolean create Controls whether test files and directories
* will be created as needed
* String ans Expected answer for the check method (above)
* String ask Input pathname to be passed to the check method
*/
/** Check a single slash case, plus its children */
private static void checkSlash(int depth, boolean create,
String ans, String ask, String slash)
throws Exception
{
check(ans, ask + slash);
checkNames(depth, create,
ans.endsWith(File.separator) ? ans : ans + File.separator,
ask + slash);
}
/** Check slash cases for the given ask string */
public static void checkSlashes(int depth, boolean create,
String ans, String ask)
throws Exception
{
check(ans, ask);
if (depth == 0) return;
checkSlash(depth, create, ans, ask, "/");
checkSlash(depth, create, ans, ask, "//");
checkSlash(depth, create, ans, ask, "///");
if (win32) {
checkSlash(depth, create, ans, ask, "\\");
checkSlash(depth, create, ans, ask, "\\\\");
checkSlash(depth, create, ans, ask, "\\/");
checkSlash(depth, create, ans, ask, "/\\");
checkSlash(depth, create, ans, ask, "\\\\\\");
}
}
/** Check name cases for the given ask string */
public static void checkNames(int depth, boolean create,
String ans, String ask)
throws Exception
{
int d = depth - 1;
File f = new File(ans);
String n;
/* Normal name */
if (f.exists()) {
if (Files.isDirectory(f.toPath(), LinkOption.NOFOLLOW_LINKS) && f.list() != null) {
if ((n = findSomeFile(ans, create)) != null)
checkSlashes(d, create, ans + n, ask + n);
if ((n = findSomeDir(ans, create)) != null)
checkSlashes(d, create, ans + n, ask + n);
}
n = findNon(ans);
checkSlashes(d, create, ans + n, ask + n);
} else {
n = "foo" + depth;
checkSlashes(d, create, ans + n, ask + n);
}
/* "." */
checkSlashes(d, create, trimTrailingSlashes(ans), ask + ".");
/* ".." */
if ((n = f.getParent()) != null) {
String n2;
if (win32
&& ((n2 = f.getParentFile().getParent()) != null)
&& n2.equals("\\\\")) {
/* Win32 resolves \\foo\bar\.. to \\foo\bar */
checkSlashes(d, create, ans, ask + "..");
} else {
checkSlashes(d, create, n, ask + "..");
}
}
else {
if (win32)
checkSlashes(d, create, ans, ask + "..");
else {
// Fix for 4237875. We must ensure that we are sufficiently
// deep in the path hierarchy to test parents this high up
File thisPath = new File(ask);
File nextPath = new File(ask + "..");
if (!thisPath.getCanonicalPath().equals(nextPath.getCanonicalPath()))
checkSlashes(d, create, ans + "..", ask + "..");
}
}
}
}