8205563: modules/AnnotationProcessing.java failed testGenerateSingleModule
authorjjg
Mon, 02 Jul 2018 17:54:36 -0700
changeset 50967 1e24c7152e47
parent 50924 012ab74e9802
child 50968 14708e1acdc3
8205563: modules/AnnotationProcessing.java failed testGenerateSingleModule Reviewed-by: darcy
test/langtools/tools/javac/modules/AnnotationProcessing.java
test/langtools/tools/lib/toolbox/ToolBox.java
--- a/test/langtools/tools/javac/modules/AnnotationProcessing.java	Mon Jul 02 16:28:09 2018 -0400
+++ b/test/langtools/tools/javac/modules/AnnotationProcessing.java	Mon Jul 02 17:54:36 2018 -0700
@@ -95,6 +95,7 @@
 public class AnnotationProcessing extends ModuleTestBase {
 
     public static void main(String... args) throws Exception {
+        System.out.println(System.getProperties());
         new AnnotationProcessing().runTests();
     }
 
@@ -568,7 +569,7 @@
                             "--module-source-path", moduleSrc.toString());
                 assertFileExists(classes, "m1x", "api1", "Impl.class");
 
-                deleteFile(m1.resolve("test").resolve("Test.java"));
+                tb.deleteFiles(m1.resolve("test").resolve("Test.java"));
 
                 //resource class output:
                 runCompiler(base,
@@ -623,7 +624,7 @@
             assertFileExists(classes, "m1x", pack, "Pass.class");
             assertFileNotExists(classes, "m2x", pack, "Pass.class");
 
-            deleteFile(m1.resolve("test").resolve("Test.java"));
+            tb.deleteFiles(m1.resolve("test").resolve("Test.java"));
 
             runCompiler(base,
                         moduleSrc,
@@ -694,36 +695,6 @@
         }
     }
 
-    private void deleteFile(Path file) throws IOException {
-        long startTime = System.currentTimeMillis();
-
-        do {
-            Files.delete(file);
-            if (!Files.exists(file)) {
-                return;
-            }
-            System.err.println("!! File not deleted !!");
-            System.gc(); // allow finalizers and cleaners to run
-            try {
-                Thread.sleep(RETRY_DELETE_MILLIS);
-            } catch (InterruptedException e) {
-                throw new IOException("Interrupted while deleting " + file, e);
-            }
-        } while ((System.currentTimeMillis() - startTime) <= MAX_RETRY_DELETE_MILLIS);
-
-        throw new IOException("Can't delete " + file);
-    }
-
-
-    private static final int RETRY_DELETE_MILLIS;
-    private static final int MAX_RETRY_DELETE_MILLIS;
-
-    static {
-        boolean isWindows = System.getProperty("os.name").startsWith("Windows");
-        RETRY_DELETE_MILLIS = isWindows ? 500 : 0;
-        MAX_RETRY_DELETE_MILLIS = isWindows ? 15 * 1000 : 0;
-    }
-
     public static abstract class GeneratingAP extends AbstractProcessor {
 
         public void createSource(CreateFileObject file, String name, String content) {
@@ -872,7 +843,7 @@
                                 options);
                     assertFileExists(classes, modulePath, "impl", "Impl.class");
 
-                    deleteFile(m1.resolve("test").resolve("Test.java"));
+                    tb.deleteFiles(m1.resolve("test").resolve("Test.java"));
 
                     //resource class output:
                     runCompiler(base,
@@ -899,7 +870,7 @@
                     "expectFilerException(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"m1x/impl\", \"resource\"))",
                     "-sourcepath", m1.toString());
 
-        deleteFile(m1.resolve("impl").resolve("resource"));
+        tb.deleteFiles(m1.resolve("impl").resolve("resource"));
 
         //can read resources from the system module path if module name given:
         runCompiler(base,
@@ -960,7 +931,7 @@
                         "-sourcepath", m1.toString());
         }
 
-        deleteFile(m1.resolve("module-info.java"));
+        tb.deleteFiles(m1.resolve("module-info.java"));
         tb.writeJavaFiles(m1,
                           "package test; class Test { }");
 
--- a/test/langtools/tools/lib/toolbox/ToolBox.java	Mon Jul 02 16:28:09 2018 -0400
+++ b/test/langtools/tools/lib/toolbox/ToolBox.java	Mon Jul 02 17:54:36 2018 -0700
@@ -34,8 +34,10 @@
 import java.io.Writer;
 import java.net.URI;
 import java.nio.charset.Charset;
+import java.nio.file.DirectoryNotEmptyException;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
@@ -43,8 +45,11 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -98,6 +103,12 @@
     public static final String testSrc = System.getProperty("test.src");
     /** The location of the test JDK for this test, or null if not set. */
     public static final String testJDK = System.getProperty("test.jdk");
+    /** The timeout factor for slow systems. */
+    public static final float timeoutFactor;
+    static {
+        String ttf = System.getProperty("test.timeout.factor");
+        timeoutFactor = (ttf == null) ? 1.0f : Float.valueOf(ttf);
+    }
 
     /** The current directory. */
     public static final Path currDir = Paths.get(".");
@@ -109,7 +120,7 @@
      * Checks if the host OS is some version of Windows.
      * @return true if the host OS is some version of Windows
      */
-    public boolean isWindows() {
+    public static boolean isWindows() {
         return osName.toLowerCase(Locale.ENGLISH).startsWith("windows");
     }
 
@@ -238,21 +249,50 @@
     }
 
     /**
-     * Deletes one or more files.
-     * Any directories to be deleted must be empty.
+     * Deletes one or more files, awaiting confirmation that the files
+     * no longer exist. Any directories to be deleted must be empty.
      * <p>Similar to the shell command: {@code rm files}.
-     * @param files the files to be deleted
+     * @param files the names of the files to be deleted
      * @throws IOException if an error occurred while deleting the files
      */
     public void deleteFiles(String... files) throws IOException {
-        if (files.length == 0)
-            throw new IllegalArgumentException("no files specified");
-        for (String file : files)
-            Files.delete(Paths.get(file));
+        deleteFiles(List.of(files).stream().map(Paths::get).collect(Collectors.toList()));
+    }
+
+    /**
+     * Deletes one or more files, awaiting confirmation that the files
+     * no longer exist. Any directories to be deleted must be empty.
+     * <p>Similar to the shell command: {@code rm files}.
+     * @param paths the paths for the files to be deleted
+     * @throws IOException if an error occurred while deleting the files
+     */
+    public void deleteFiles(Path... paths) throws IOException {
+        deleteFiles(List.of(paths));
     }
 
     /**
-     * Deletes all content of a directory (but not the directory itself).
+     * Deletes one or more files, awaiting confirmation that the files
+     * no longer exist. Any directories to be deleted must be empty.
+     * <p>Similar to the shell command: {@code rm files}.
+     * @param paths the paths for the files to be deleted
+     * @throws IOException if an error occurred while deleting the files
+     */
+    public void deleteFiles(List<Path> paths) throws IOException {
+        if (paths.isEmpty())
+            throw new IllegalArgumentException("no files specified");
+        IOException ioe = null;
+        for (Path path : paths) {
+            ioe = deleteFile(path, ioe);
+        }
+        if (ioe != null) {
+            throw ioe;
+        }
+        ensureDeleted(paths);
+    }
+
+    /**
+     * Deletes all content of a directory (but not the directory itself),
+     * awaiting confirmation that the content has been deleted.
      * @param root the directory to be cleaned
      * @throws IOException if an error occurs while cleaning the directory
      */
@@ -261,9 +301,23 @@
             throw new IOException(root + " is not a directory");
         }
         Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
+            private IOException ioe = null;
+            // for each directory we visit, maintain a list of the files that we try to delete
+            private Deque<List<Path>> dirFiles = new LinkedList<>();
+
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes a) throws IOException {
-                Files.delete(file);
+                ioe = deleteFile(file, ioe);
+                dirFiles.peekFirst().add(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes a) throws IOException {
+                if (!dir.equals(root)) {
+                    dirFiles.peekFirst().add(dir);
+                }
+                dirFiles.addFirst(new ArrayList<>());
                 return FileVisitResult.CONTINUE;
             }
 
@@ -272,8 +326,12 @@
                 if (e != null) {
                     throw e;
                 }
+                if (ioe != null) {
+                    throw ioe;
+                }
+                ensureDeleted(dirFiles.removeFirst());
                 if (!dir.equals(root)) {
-                    Files.delete(dir);
+                    ioe = deleteFile(dir, ioe);
                 }
                 return FileVisitResult.CONTINUE;
             }
@@ -281,6 +339,67 @@
     }
 
     /**
+     * Internal method to delete a file, using {@code Files.delete}.
+     * It does not wait to confirm deletion, nor does it retry.
+     * If an exception occurs it is either returned or added to the set of
+     * suppressed exceptions for an earlier exception.
+     * @param path the path for the file to be deleted
+     * @param ioe the earlier exception, or null
+     * @return the earlier exception or an exception that occurred while
+     *  trying to delete the file
+     */
+    private IOException deleteFile(Path path, IOException ioe) {
+        try {
+            Files.delete(path);
+        } catch (IOException e) {
+            if (ioe == null) {
+                ioe = e;
+            } else {
+                ioe.addSuppressed(e);
+            }
+        }
+        return ioe;
+    }
+
+    /**
+     * Wait until it is confirmed that a set of files have been deleted.
+     * @param paths the paths for the files to be deleted
+     * @throws IOException if a file has not been deleted
+     */
+    private void ensureDeleted(Collection<Path> paths)
+            throws IOException {
+        for (Path path : paths) {
+            ensureDeleted(path);
+        }
+    }
+
+    /**
+     * Wait until it is confirmed that a file has been deleted.
+     * @param path the path for the file to be deleted
+     * @throws IOException if problems occur while deleting the file
+     */
+    private void ensureDeleted(Path path) throws IOException {
+        long startTime = System.currentTimeMillis();
+        do {
+            // Note: Files.notExists is not the same as !Files.exists
+            if (Files.notExists(path)) {
+                return;
+            }
+            System.gc(); // allow finalizers and cleaners to run
+            try {
+                Thread.sleep(RETRY_DELETE_MILLIS);
+            } catch (InterruptedException e) {
+                throw new IOException("Interrupted while waiting for file to be deleted: " + path, e);
+            }
+        } while ((System.currentTimeMillis() - startTime) <= MAX_RETRY_DELETE_MILLIS);
+
+        throw new IOException("File not deleted: " + path);
+    }
+
+    private static final int RETRY_DELETE_MILLIS = isWindows() ? (int)(500 * timeoutFactor): 0;
+    private static final int MAX_RETRY_DELETE_MILLIS = isWindows() ? (int)(15 * 1000 * timeoutFactor) : 0;
+
+    /**
      * Moves a file.
      * If the given destination exists and is a directory, the file will be moved
      * to that directory.  Otherwise, the file will be moved to the destination,