001/** 002 * Powerunit - A JDK1.8 test framework 003 * Copyright (C) 2014 Mathieu Boretti. 004 * 005 * This file is part of Powerunit 006 * 007 * Powerunit is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * Powerunit is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>. 019 */ 020package ch.powerunit.extensions.async.lang; 021 022import static ch.powerunit.extensions.async.lang.WaitResult.callableWithToString; 023import static ch.powerunit.extensions.async.lang.WaitResult.of; 024import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; 025import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; 026import static java.util.Objects.requireNonNull; 027import static java.util.stream.Collectors.collectingAndThen; 028import static java.util.stream.Collectors.toList; 029 030import java.nio.file.Path; 031import java.nio.file.WatchEvent; 032import java.nio.file.WatchEvent.Kind; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.Objects; 036import java.util.concurrent.Callable; 037 038import ch.powerunit.extensions.async.impl.FilePool; 039 040/** 041 * This class provides methods to wait for fileSystem events. 042 * <p> 043 * The goal is to wait for filesystem event, by using the 044 * {@link java.nio.file.WatchService} functionality. 045 * 046 * @since 1.1.0 047 * @see java.nio.file.WatchService 048 */ 049public final class WaitFile { 050 private WaitFile() { 051 } 052 053 /** 054 * Wait for a folder to have some event. 055 * <p> 056 * The wait starts at the first try to get the result. 057 * <p> 058 * For example : 059 * 060 * <pre> 061 * CompletableFuture<Optional<Collection<WatchEvent<Path>>>> wait = WaitFile 062 * .eventIn(test, StandardWatchEventKinds.ENTRY_CREATE) 063 * .expecting(l -> l.stream().map(WatchEvent::context).map(Path::getFileName).map(Path::toString) 064 * .anyMatch(n -> n.equals("test"))) 065 * .repeat(3).every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec(); 066 * </pre> 067 * 068 * Defines a 3 tries with a wait time of 250ms, for a creation event, containing 069 * at least one event with last part of a path named test. 070 * 071 * @param directory 072 * the directory to be verified. 073 * @param events 074 * the events to wait for. 075 * @return {@link WaitResultBuilder1} the next step of the builder. 076 */ 077 @SafeVarargs 078 public static WaitResultBuilder1<Collection<WatchEvent<Path>>> eventIn(Path directory, Kind<Path>... events) { 079 requireNonNull(directory, "directory can't be null"); 080 FilePool filePool = new FilePool(directory, events); 081 return of(filePool, filePool::close); 082 } 083 084 /** 085 * Wait for a folder to contains new entry. 086 * <p> 087 * The wait starts at the first try to get the result. 088 * <p> 089 * For example : 090 * 091 * <pre> 092 * CompletableFuture<Optional<Collection<Path>>> wait = WaitFile.newFileIn(test) 093 * .expecting(l -> l.stream().map(Path::getFileName).map(Path::toString).anyMatch(n -> n.equals("test"))) 094 * .repeat(3).every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec(); 095 * </pre> 096 * 097 * Defines a 3 tries with a wait time of 250ms, for a list of new file, 098 * containing at least one file with last part of a path named test. 099 * 100 * 101 * @param directory 102 * the directory to be verified. 103 * @return {@link WaitResultBuilder1} the next step of the builder. 104 */ 105 public static WaitResultBuilder1<Collection<Path>> newFileIn(Path directory) { 106 requireNonNull(directory, "directory can't be null"); 107 FilePool filePool = new FilePool(directory, ENTRY_CREATE); 108 return of(callableWithToString(toPathCollection(filePool), () -> "new file in " + filePool), filePool::close); 109 } 110 111 /** 112 * Wait for a folder to contains new entry based on his name. 113 * <p> 114 * The wait starts at the first try to get the result. 115 * <p> 116 * For example : 117 * 118 * <pre> 119 * CompletableFuture<Optional<Path>> wait = WaitFile.newFileNamedIn(test, "test").expectingNotNull().repeat(3) 120 * .every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec(); 121 * </pre> 122 * 123 * Defines a 3 tries with a wait time of 25ms, for a file named "test". 124 * 125 * @param directory 126 * the directory to be verified. 127 * @param name 128 * the expected name 129 * @return {@link WaitResultBuilder1} the next step of the builder. 130 */ 131 public static WaitResultBuilder1<Path> newFileNamedIn(Path directory, String name) { 132 requireNonNull(directory, "directory can't be null"); 133 requireNonNull(name, "name can't be null"); 134 FilePool filePool = new FilePool(directory, ENTRY_CREATE); 135 return of(callableWithToString(toPathByName(toPathCollection(filePool), name), 136 () -> String.format("New file named %s in %s", name, filePool)), filePool::close); 137 } 138 139 /** 140 * Wait for a folder to have entry removed. 141 * <p> 142 * The wait starts at the first try to get the result. 143 * 144 * @param directory 145 * the directory to be verified. 146 * @return {@link WaitResultBuilder1} the next step of the builder. 147 */ 148 public static WaitResultBuilder1<Collection<Path>> removeFileFrom(Path directory) { 149 requireNonNull(directory, "directory can't be null"); 150 FilePool filePool = new FilePool(directory, ENTRY_DELETE); 151 return of(callableWithToString(toPathCollection(filePool), () -> "Removed file from " + directory), 152 filePool::close); 153 } 154 155 private static Callable<Collection<Path>> toPathCollection(Callable<Collection<WatchEvent<Path>>> callable) { 156 return () -> callable.call().stream().map(WatchEvent::context) 157 .collect(collectingAndThen(toList(), Collections::unmodifiableList)); 158 } 159 160 private static Callable<Path> toPathByName(Callable<Collection<Path>> callable, String name) { 161 return () -> callable.call().stream() 162 .filter(p -> Objects.equals(p.getName(p.getNameCount() - 1).toString(), name)).findFirst().orElse(null); 163 } 164 165}