3030import java .lang .management .ThreadInfo ;
3131import java .lang .management .ThreadMXBean ;
3232import java .lang .reflect .InvocationTargetException ;
33+ import java .util .ArrayList ;
3334import java .util .Arrays ;
35+ import java .util .List ;
3436import java .util .Locale ;
3537import java .util .Objects ;
3638import java .util .Random ;
3739import java .util .Set ;
3840import java .util .Enumeration ;
41+ import java .util .concurrent .CompletableFuture ;
3942import java .util .concurrent .CountDownLatch ;
43+ import java .util .concurrent .TimeUnit ;
4044import java .util .concurrent .TimeoutException ;
4145import java .util .concurrent .atomic .AtomicInteger ;
4246import java .util .function .Supplier ;
4650import org .apache .commons .lang3 .RandomStringUtils ;
4751import org .apache .commons .logging .Log ;
4852import org .apache .commons .logging .impl .Log4JLogger ;
53+ import org .apache .hadoop .fs .FileSystem ;
4954import org .apache .hadoop .fs .FileUtil ;
5055import org .apache .hadoop .fs .Path ;
56+ import org .apache .hadoop .util .BlockingThreadPoolExecutorService ;
57+ import org .apache .hadoop .util .DurationInfo ;
5158import org .apache .hadoop .util .StringUtils ;
5259import org .apache .hadoop .util .Time ;
5360import org .apache .log4j .Appender ;
6168import org .junit .Assume ;
6269import org .mockito .invocation .InvocationOnMock ;
6370import org .mockito .stubbing .Answer ;
71+ import org .slf4j .LoggerFactory ;
6472
73+ import org .apache .hadoop .thirdparty .com .google .common .base .Charsets ;
6574import org .apache .hadoop .thirdparty .com .google .common .base .Joiner ;
6675import org .apache .hadoop .thirdparty .com .google .common .collect .Sets ;
6776
77+ import static org .apache .hadoop .fs .contract .ContractTestUtils .createFile ;
78+ import static org .apache .hadoop .util .functional .CommonCallableSupplier .submit ;
79+ import static org .apache .hadoop .util .functional .CommonCallableSupplier .waitForCompletion ;
80+
6881/**
6982 * Test provides some very generic helpers which might be used across the tests
7083 */
7184public abstract class GenericTestUtils {
7285
86+ public static final int EXECUTOR_THREAD_COUNT = 64 ;
87+
88+ private static final org .slf4j .Logger LOG =
89+ LoggerFactory .getLogger (GenericTestUtils .class );
90+
91+ public static final String PREFIX = "file-" ;
92+
7393 private static final AtomicInteger sequence = new AtomicInteger ();
7494
7595 /**
@@ -896,5 +916,132 @@ public static int getTestsThreadCount() {
896916 }
897917 return threadCount ;
898918 }
919+ /**
920+ * Write the text to a file asynchronously. Logs the operation duration.
921+ * @param fs filesystem
922+ * @param path path
923+ * @return future to the patch created.
924+ */
925+ private static CompletableFuture <Path > put (FileSystem fs ,
926+ Path path , String text ) {
927+ return submit (EXECUTOR , () -> {
928+ try (DurationInfo ignore =
929+ new DurationInfo (LOG , false , "Creating %s" , path )) {
930+ createFile (fs , path , true , text .getBytes (Charsets .UTF_8 ));
931+ return path ;
932+ }
933+ });
934+ }
935+
936+ /**
937+ * Build a set of files in a directory tree.
938+ * @param fs filesystem
939+ * @param destDir destination
940+ * @param depth file depth
941+ * @param fileCount number of files to create.
942+ * @param dirCount number of dirs to create at each level
943+ * @return the list of files created.
944+ */
945+ public static List <Path > createFiles (final FileSystem fs ,
946+ final Path destDir ,
947+ final int depth ,
948+ final int fileCount ,
949+ final int dirCount ) throws IOException {
950+ return createDirsAndFiles (fs , destDir , depth , fileCount , dirCount ,
951+ new ArrayList <Path >(fileCount ),
952+ new ArrayList <Path >(dirCount ));
953+ }
954+
955+ /**
956+ * Build a set of files in a directory tree.
957+ * @param fs filesystem
958+ * @param destDir destination
959+ * @param depth file depth
960+ * @param fileCount number of files to create.
961+ * @param dirCount number of dirs to create at each level
962+ * @param paths [out] list of file paths created
963+ * @param dirs [out] list of directory paths created.
964+ * @return the list of files created.
965+ */
966+ public static List <Path > createDirsAndFiles (final FileSystem fs ,
967+ final Path destDir ,
968+ final int depth ,
969+ final int fileCount ,
970+ final int dirCount ,
971+ final List <Path > paths ,
972+ final List <Path > dirs ) throws IOException {
973+ buildPaths (paths , dirs , destDir , depth , fileCount , dirCount );
974+ List <CompletableFuture <Path >> futures = new ArrayList <>(paths .size ()
975+ + dirs .size ());
976+
977+ // create directories. With dir marker retention, that adds more entries
978+ // to cause deletion issues
979+ try (DurationInfo ignore =
980+ new DurationInfo (LOG , "Creating %d directories" , dirs .size ())) {
981+ for (Path path : dirs ) {
982+ futures .add (submit (EXECUTOR , () ->{
983+ fs .mkdirs (path );
984+ return path ;
985+ }));
986+ }
987+ waitForCompletion (futures );
988+ }
989+
990+ try (DurationInfo ignore =
991+ new DurationInfo (LOG , "Creating %d files" , paths .size ())) {
992+ for (Path path : paths ) {
993+ futures .add (put (fs , path , path .getName ()));
994+ }
995+ waitForCompletion (futures );
996+ return paths ;
997+ }
998+ }
899999
900- }
1000+ /**
1001+ * Recursive method to build up lists of files and directories.
1002+ * @param filePaths list of file paths to add entries to.
1003+ * @param dirPaths list of directory paths to add entries to.
1004+ * @param destDir destination directory.
1005+ * @param depth depth of directories
1006+ * @param fileCount number of files.
1007+ * @param dirCount number of directories.
1008+ */
1009+ public static void buildPaths (final List <Path > filePaths ,
1010+ final List <Path > dirPaths , final Path destDir , final int depth ,
1011+ final int fileCount , final int dirCount ) {
1012+ if (depth <= 0 ) {
1013+ return ;
1014+ }
1015+ // create the file paths
1016+ for (int i = 0 ; i < fileCount ; i ++) {
1017+ String name = filenameOfIndex (i );
1018+ Path p = new Path (destDir , name );
1019+ filePaths .add (p );
1020+ }
1021+ for (int i = 0 ; i < dirCount ; i ++) {
1022+ String name = String .format ("dir-%03d" , i );
1023+ Path p = new Path (destDir , name );
1024+ dirPaths .add (p );
1025+ buildPaths (filePaths , dirPaths , p , depth - 1 , fileCount , dirCount );
1026+ }
1027+ }
1028+
1029+ /**
1030+ * Given an index, return a string to use as the filename.
1031+ * @param i index
1032+ * @return name
1033+ */
1034+ public static String filenameOfIndex (final int i ) {
1035+ return String .format ("%s%03d" , PREFIX , i );
1036+ }
1037+
1038+ /**
1039+ * For submitting work.
1040+ */
1041+ private static final BlockingThreadPoolExecutorService EXECUTOR =
1042+ BlockingThreadPoolExecutorService .newInstance (
1043+ EXECUTOR_THREAD_COUNT ,
1044+ EXECUTOR_THREAD_COUNT * 2 ,
1045+ 30 , TimeUnit .SECONDS ,
1046+ "test-operations" );
1047+ }
0 commit comments