Skip to content

Commit

Permalink
Various improvements in AbstractWatchService.
Browse files Browse the repository at this point in the history
* Fixes bug introduced by eclipse-archived#1597 reverted with eclipse-archived#1754, that led to duplicated
directory names in FolderObserver
* Added directory registration after initialization.This change allows all
classes that extend AbstractWatchService to register directories to be watched,
after the WatchService is initialized. This will be possible with the
registerAllDirectoriesInternal() method.The implementaions can handle
the DIRECTORY_CREATED event, in order to do that.
* Added test cases for the registration of directories after service
initialization
* Test class is refactored
* Improved javadoc of AbstractWatchService and AbstractQueueReader

Signed-off-by: Svilen Valkanov <svilen.valkanov@musala.com>
  • Loading branch information
Svilen Valkanov committed Aug 5, 2016
1 parent e044a97 commit 38cc577
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 207 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,26 @@
package org.eclipse.smarthome.core.service

import static java.nio.file.StandardWatchEventKinds.*
import static org.hamcrest.CoreMatchers.*
import static org.junit.Assert.*
import static org.junit.matchers.JUnitMatchers.*

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.io.IOException;
import java.nio.file.Path
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent
import java.nio.file.WatchKey
import java.nio.file.WatchService
import java.nio.file.WatchEvent.Kind
import java.util.Map

import org.apache.commons.lang.SystemUtils;
import org.eclipse.smarthome.core.service.AbstractWatchServiceTest.FullEvent;
import org.apache.commons.lang.SystemUtils
import org.eclipse.smarthome.test.OSGiTest
import org.junit.After
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.Ignore;
import org.junit.Test;
import org.junit.Test

/**
* Test for {@link AbstractWatchService}.
*
*
* @author Dimitar Ivanov
*
*/
Expand Down Expand Up @@ -116,7 +108,8 @@ class AbstractWatchServiceTest extends OSGiTest {
isCreated = file.createNewFile()
assertThat "The file '$file.absolutePath' was not created successfully", isCreated, is(true)

waitForAssert({assertThat "Exactly two watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(2)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)
def expectedEvents = 2
waitForAssert({assertThat "Exactly two watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)

FullEvent innerFileEvent = watchService.allFullEvents[0]
assertThat "The inner file '$innerfile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents,innerFileEvent.eventKind,is(ENTRY_CREATE)
Expand All @@ -128,7 +121,109 @@ class AbstractWatchServiceTest extends OSGiTest {
}

@Test
void 'directory changes are watched correctly'(){
void 'subirs are registered and modifications are watched, folder is created after service activation'(){
// Watch subdirectories and their modifications
// RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation
watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,true)

watchService.activate()
def subDirName = "subDir"
def fileName = "innerFile"
def innerFileName = subDirName + File.separatorChar + fileName
File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName)

// Make subdirectories
innerFile.getParentFile().mkdirs()

assertDirectoryCraeteEventIsProcessed(subDirName)

watchService.allFullEvents.clear();

boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

assertAllEventsAreProcessed(subDirName,innerFile, innerFileName)
}

@Test
void 'subdirs are not registered and modifications are watched, folder is created after service activation'(){
// Do not watch subdirectories, but watch their modifications
// RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation
watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,true)
watchService.activate()

def subDirName = "subDir"
def fileName = "innerFile"
def innerFileName = subDirName + File.separatorChar + fileName

File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName)

// Make subdirectories
innerFile.getParentFile().mkdirs()

assertDirectoryCraeteEventIsProcessed(subDirName)

watchService.allFullEvents.clear();

boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

assertDirectoryModifyEventIsProcessed(subDirName)
}

@Test
void 'subdirs are not registered and modifications are not watched, folder is created after service activation' () {
// Do not watch subdirectories and do not watch their modifications
// RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation
watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,false)
watchService.activate()

def subDirName = "subDir"
def fileName = "innerFile"
def innerFileName = subDirName + File.separatorChar + fileName

File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName)

// Make subdirectories
innerFile.getParentFile().mkdirs()

// Consequent creation and deletion in order to generate any watch events for the subdirectory
boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

boolean isDeleted = innerFile.delete()
assertThat "Inner file is not deleted", isDeleted, is(true)

assertNoEventsAreProcessed()
}

@Test
void 'subdirs are registered, but dirs modifications are not watched, folder is created after service activation'() {
// Watch subdirectories, but not their modifications
// RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation
watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,false)

watchService.activate()

def subDirName = "subDir"
def fileName = "innerFile"
def innerFileName = subDirName + File.separatorChar + fileName

File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName)

// Make subdirectories
innerFile.getParentFile().mkdirs()

//Create file in the subdirectory
boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

//The subdirectory is created after the service initialization. As no modifications are watched the new subdir can not be registered
assertNoEventsAreProcessed()
}

@Test
void 'subdirs are registered and modifications watched'(){
// Watch subdirectories and their modifications
watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,true)

Expand All @@ -146,23 +241,11 @@ class AbstractWatchServiceTest extends OSGiTest {
boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

//This could vary across different platforms. For more information see "Platform dependencies" section in the WatchService documentation
def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 2 : 1
waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)

FullEvent fileEvent = watchService.allFullEvents[0]
assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE)
assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName)

if(SystemUtils.IS_OS_WINDOWS){
FullEvent dirEvent = watchService.allFullEvents[1]
assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, dirEvent.eventKind, is(ENTRY_MODIFY)
assertThat "Subdirectory was not found in the modified event", dirEvent.eventPath.toString(), is(subDirName)
}
assertAllEventsAreProcessed(subDirName,innerFile,innerFileName)
}

@Test
void 'subdirs are not registered and not watched'(){
void 'subdirs are not registered and modifications are not watched'(){
// Do not watch the subdirectories of the root directory
watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,false)

Expand All @@ -180,14 +263,33 @@ class AbstractWatchServiceTest extends OSGiTest {
boolean isDeleted = innerFile.delete()
assertThat "Inner file is not deleted", isDeleted, is(true)

// Wait for a possible event for the maximum timeout
sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000
assertNoEventsAreProcessed()
}

assertThat "No watch events are expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0)
@Test
void 'subdirs are registered, but dirs modifications are not watched'() {
// Do watch the subdirectories of the root directory, but do not watch directory modifications
watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,false)

def innerFileName = "watchRequestSubDir"+ File.separatorChar + "watchRequestInnerFile"
File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName)
// Make all the subdirectories before running the service
innerFile.getParentFile().mkdirs()

watchService.activate()

boolean isCreated = innerFile.createNewFile()
assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true)

assertFileCreateEventIsProcessed(innerFile,innerFileName)

watchService.allFullEvents.clear();

assertNoEventsAreProcessed()
}

@Test
void 'subdirs are not watched, but dirs modifications are watched'(){
void 'subdirs are not registered, but dirs modifications are watched'(){
// Do not watch the subdirectories of the root directory, but watch directory modifications
watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,true)

Expand All @@ -205,26 +307,69 @@ class AbstractWatchServiceTest extends OSGiTest {
// Wait for possible watch event for the maximum timeout
sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000

if(SystemUtils.IS_OS_WINDOWS){
// This behavior could vary depending on the Platform (see the WatchService documentation)
assertThat "Exactly one event is expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(1)
FullEvent event = watchService.allFullEvents[0]
assertThat "Modification event for subdir '$subDirName' on file creation within it is expected", event.eventKind , is(ENTRY_MODIFY)
assertThat "The watch event does not contain the path of the modified directory", event.eventPath.toString() , is(subDirName)
}else {
assertThat "No events are expected if not Windows OS" + watchService.allFullEvents, watchService.allFullEvents.size(), is(0)
}
assertDirectoryModifyEventIsProcessed(subDirName)

// Clear the asserted event
watchService.allFullEvents.clear()

boolean isDeleted = innerFile.delete()
assertThat "Inner file '$innerFile.absolutePath' is not deleted", isDeleted, is(true)

assertNoEventsAreProcessed()
}
void assertNoEventsAreProcessed(){
// Wait for a possible event for the maximum timeout
sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000

assertThat "No event is expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0)
assertThat "No watch events are expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0)
}

void assertAllEventsAreProcessed(def subDirName,def innerFile, def innerFileName){
//This could vary across different platforms. For more information see "Platform dependencies" section in the WatchService documentation
def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 2 : 1
waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)

FullEvent fileEvent = watchService.allFullEvents[0]
assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE)
assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName)

if(SystemUtils.IS_OS_WINDOWS){
FullEvent dirEvent = watchService.allFullEvents[1]
assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, dirEvent.eventKind, is(ENTRY_MODIFY)
assertThat "Subdirectory was not found in the modified event", dirEvent.eventPath.toString(), is(subDirName)
}
}

void assertDirectoryCraeteEventIsProcessed(def subDirName) {
//Single event for directory creation is present
def expectedEvents = 1
waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)
FullEvent event = watchService.allFullEvents[0]
assertThat "Directory $subDirName craetion was not detected. All events detected: " + watchService.allFullEvents, event.eventKind, is(ENTRY_CREATE)
assertThat "Subdirectory was not found in the creation event", event.eventPath.toString(), is(subDirName)
}

void assertFileCreateEventIsProcessed(def innerFile, def innerFileName) {
//Single event for file creation is present
def expectedEvents = 1
waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)
FullEvent fileEvent = watchService.allFullEvents[0]
assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE)
assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName)
}

void assertDirectoryModifyEventIsProcessed(def subDirName) {
//Create file is not detected, only the modification event is detected
def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 1 : 0
waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000)

if(SystemUtils.IS_OS_WINDOWS){
FullEvent fileEvent = watchService.allFullEvents[0]
assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_MODIFY)
assertThat "Subdirectory was not found in the modified event", fileEvent.eventPath.toString(), is(subDirName)
} else {
assertThat "No events are expected if not Windows OS" + watchService.allFullEvents, watchService.allFullEvents.size(), is(0)
}
}

void assertByRelativePath(String fileName) {
Expand Down Expand Up @@ -254,8 +399,8 @@ class AbstractWatchServiceTest extends OSGiTest {
void fullEventAssertionsByKind(String fileName, kind, osSpecific){
waitForAssert({
assertThat "At least one watch event is expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds.",
watchService.allFullEvents.size() >= 1,
is(true)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000);
watchService.allFullEvents.size() >= 1,
is(true)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000);

if(osSpecific && kind.equals(ENTRY_DELETE)){
// There is possibility that one more modify event is triggered on some OS
Expand Down Expand Up @@ -311,16 +456,23 @@ class AbstractWatchServiceTest extends OSGiTest {


@Override
protected AbstractWatchQueueReader buildWatchQueueReader(WatchService watchService, Path toWatch,Map<WatchKey, Path> registeredKeys) {
def queueReader = new AbstractWatchQueueReader(watchService, toWatch,registeredKeys) {
@Override
protected void processWatchEvent(WatchEvent<?> event, Kind<?> kind, Path path) {
FullEvent fullEvent = new FullEvent(event,kind,path)
allFullEvents << fullEvent
protected AbstractWatchQueueReader buildWatchQueueReader(WatchService watchServiceImpl, Path toWatch,Map<WatchKey, Path> registeredKeys) {
def queueReader = new AbstractWatchQueueReader(watchServiceImpl, toWatch,registeredKeys) {
@Override
protected void processWatchEvent(WatchEvent<?> event, Kind<?> kind, Path path) {
FullEvent fullEvent = new FullEvent(event,kind,path)
if (kind.equals(ENTRY_CREATE)) {
Path relativePath = baseWatchedDir.resolve(path);
if (relativePath.toFile().isDirectory() && watchSubDirectories()) {
registerAllDirectoriesInternal(relativePath, registeredKeys);
}
};
}
allFullEvents << fullEvent
}
};
queueReader.setWatchingDirectoryChanges(watchDirectoryChanges)
return queueReader

}

@Override
Expand Down
Loading

0 comments on commit 38cc577

Please sign in to comment.