1/* 2 * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 4313887 6907737 27 * @summary Tests that walkFileTree is consistent with the native find program 28 * @requires (os.family != "windows") 29 * @library /test/lib 30 * @build jdk.test.lib.Utils 31 * jdk.test.lib.Asserts 32 * jdk.test.lib.JDKToolFinder 33 * jdk.test.lib.JDKToolLauncher 34 * jdk.test.lib.Platform 35 * jdk.test.lib.process.* 36 * CreateFileTree 37 * @run testng/othervm -Djava.io.tmpdir=. FindTest 38 */ 39 40import java.io.IOException; 41import java.nio.file.FileSystemLoopException; 42import java.nio.file.FileVisitOption; 43import java.nio.file.FileVisitResult; 44import java.nio.file.FileVisitor; 45import java.nio.file.Files; 46import java.nio.file.Path; 47import java.nio.file.attribute.BasicFileAttributes; 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.HashSet; 51import java.util.List; 52import java.util.Random; 53import java.util.Set; 54import java.util.stream.Collectors; 55 56import jdk.test.lib.process.OutputAnalyzer; 57import jdk.test.lib.process.ProcessTools; 58 59import org.testng.annotations.BeforeClass; 60import org.testng.annotations.Test; 61 62import static org.testng.Assert.assertEquals; 63import static org.testng.Assert.assertTrue; 64 65public class FindTest { 66 67 private static final Random rand = new Random(); 68 private static final boolean isAIX = System.getProperty("os.name").equals("AIX"); 69 private static Path top; 70 private static String TOP; 71 72 @BeforeClass 73 public static void createFileTree() throws Exception { 74 top = CreateFileTree.create(); 75 TOP = top.toAbsolutePath().toString(); 76 } 77 78 @Test 79 public void printTreeTest() throws Throwable { 80 // print the file tree and compare output with find(1) 81 assertOutputEquals(printFileTree(top), runFind("find", TOP)); 82 } 83 84 @Test 85 public void printTreeFollowLinkTest() throws Throwable { 86 // print the file tree, following links, and compare output with find(1). 87 88 // On AIX "find -follow" may core dump on recursive links without '-L' 89 // see: http://www-01.ibm.com/support/docview.wss?uid=isg1IV28143 90 String[] cmds = isAIX 91 ? new String[]{"find", "-L", TOP, "-follow"} 92 : new String[]{"find", TOP, "-follow"}; 93 OutputAnalyzer expected = runFind(cmds); 94 95 // Some versions of find(1) output cycles (sym links to ancestor 96 // directories), other versions do not. For that reason we run 97 // PrintFileTree with the -printCycles option when the output without 98 // this option differs to find(1). 99 try { 100 assertOutputEquals(printFileTree(top, "-follow"), expected); 101 } catch (AssertionError x) { 102 assertOutputEquals(printFileTree(top, "-follow", "-printCycles"), expected); 103 } 104 } 105 106 private void assertOutputEquals(List<String> actual, OutputAnalyzer expected) 107 throws IOException { 108 List<String> expectedList = Arrays.asList(expected.getStdout() 109 .split(System.lineSeparator())); 110 assertEquals(actual.size(), expectedList.size()); 111 assertTrue(actual.removeAll(expectedList)); 112 } 113 114 private OutputAnalyzer runFind(String... cmds) throws Throwable { 115 return ProcessTools.executeCommand(cmds); 116 } 117 118 /** 119 * Invokes Files.walkFileTree to traverse a file tree and prints 120 * each of the directories and files. The -follow option causes symbolic 121 * links to be followed and the -printCycles option will print links 122 * where the target of the link is an ancestor directory. 123 */ 124 private static List<String> printFileTree(Path dir, String... opts) throws Exception { 125 List<Path> fileTreeList = new ArrayList<>(); 126 127 List<String> optsList = Arrays.asList(opts); 128 boolean followLinks = optsList.contains("-follow"); 129 boolean reportCycles = optsList.contains("-printCycles"); 130 131 Set<FileVisitOption> options = new HashSet<>(); 132 if (followLinks) 133 options.add(FileVisitOption.FOLLOW_LINKS); 134 135 Files.walkFileTree(dir, options, Integer.MAX_VALUE, new FileVisitor<Path>() { 136 @Override 137 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { 138 fileTreeList.add(dir); 139 return FileVisitResult.CONTINUE; 140 } 141 @Override 142 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { 143 fileTreeList.add(file); 144 return FileVisitResult.CONTINUE; 145 } 146 @Override 147 public FileVisitResult postVisitDirectory(Path dir, IOException exc) 148 throws IOException 149 { 150 if (exc != null) 151 throw exc; 152 return FileVisitResult.CONTINUE; 153 } 154 @Override 155 public FileVisitResult visitFileFailed(Path file, IOException exc) 156 throws IOException 157 { 158 if (followLinks && (exc instanceof FileSystemLoopException)) { 159 if (reportCycles) 160 fileTreeList.add(file); 161 return FileVisitResult.CONTINUE; 162 } else { 163 throw exc; 164 } 165 } 166 }); 167 168 return fileTreeList.stream() 169 .map(f -> f.toAbsolutePath().toString()) 170 .collect(Collectors.toCollection(ArrayList::new)); 171 } 172} 173