/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package org.codice.ddf.configuration.admin;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ConfigurationFilesPollerTest {
private static final String FILE_EXT = ".config";
private static final String INVALID_FILE_EXT = ".invalid";
private static final String PID = "my.pid";
@Mock
private ChangeListener mockChangeListener;
@Mock
private Path configFilePath;
@Mock(answer = RETURNS_DEEP_STUBS)
private Path directoryPath;
@Mock(answer = RETURNS_DEEP_STUBS)
private Path nonConfigFilePath;
private File tempFile;
@Before
public void setup() throws IOException {
File configFile = mock(File.class);
when(configFilePath.toFile()).thenReturn(configFile);
when(configFile.isDirectory()).thenReturn(false);
when(configFilePath.getFileName()).thenReturn(configFilePath);
when(configFilePath.endsWith(FILE_EXT)).thenReturn(true);
when(configFilePath.toAbsolutePath()).thenReturn(Paths.get("foo"));
when(directoryPath.toFile()
.isDirectory()).thenReturn(true);
when(nonConfigFilePath.toFile()
.isDirectory()).thenReturn(true);
when(nonConfigFilePath.endsWith(FILE_EXT)).thenReturn(false);
tempFile = Files.createTempFile("foo", "bar")
.toFile();
}
/**
* Verify that an IllegalArgumentException is thrown when attempting to
* register a null listener.
*/
@Test(expected = IllegalArgumentException.class)
public void testRegisterNullParameter() throws Exception {
// Setup
Path mockPath = getMockPath(PID, FILE_EXT);
Path mockConfigurationDirectory = getMockConfigurationDirectoryPath(mockPath,
mock(Path.class));
ConfigurationFilesPoller configurationFilesPoller = new ConfigurationFilesPoller(
mockConfigurationDirectory,
FILE_EXT);
// Perform Test
configurationFilesPoller.register(null);
}
/**
* Verify that the ChangeListener is notified on file create events.
*/
@Test
public void testRunFileCreatedChangeListenerNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onFileCreate(tempFile);
// Verify
verify(mockChangeListener).notify(Paths.get(tempFile.toURI()));
}
/**
* Verify that the ChangeListener is not notified on file change events.
*/
@Test
public void testRunFileChangedChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onFileChange(tempFile);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the ChangeListener is not notified on file delete events.
*/
@Test
public void testRunFileDeletedChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onFileDelete(tempFile);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the ChangeListener is not notified on directory create events.
*/
@Test
public void testRunDirectoryCreatedChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onDirectoryCreate(tempFile);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the ChangeListener is not notified on directory change events.
*/
@Test
public void testRunDirectoryChangedChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onDirectoryChange(tempFile);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the ChangeListener is not notified on directory delete events.
*/
@Test
public void testRunDirectoryDeletedChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideWait();
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onDirectoryDelete(tempFile);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the ChangeListener is not notified if a RuntimeException is thrown.
*/
@Test
public void testRuntimeExceptionThrownChangeListenerNotNotified() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller = new ConfigurationFilesPoller(
configFilePath,
FILE_EXT);
File file = mock(File.class);
when(file.length()).thenThrow(new RuntimeException());
// Perform Test
configurationFilesPoller.register(mockChangeListener);
configurationFilesPoller.onFileCreate(file);
// Verify
verify(mockChangeListener, never()).notify(any());
}
/**
* Verify that the Poller waits once for a typical file length
*/
@Test
public void testWaitForFileToBeCompletelyWritten() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideSleepWithSpy();
File file = mock(File.class);
when(file.length()).thenReturn(1234L);
// Perform Test
configurationFilesPoller.waitForFileToBeCompletelyWritten(file);
// Verify
verify(configurationFilesPoller, times(1)).doSleep();
}
/**
* Verify that the Poller does an extra wait to give typical buffered writers time to finish
*/
@Test
public void testWaitForFileToBeCompletelyWrittenByBufferedWriter() throws Exception {
// Setup
ConfigurationFilesPoller configurationFilesPoller =
getConfigurationFilesPollerOverrideSleepWithSpy();
File file = mock(File.class);
when(file.length()).thenReturn(1024L);
// Perform Test
configurationFilesPoller.waitForFileToBeCompletelyWritten(file);
// Verify
verify(configurationFilesPoller, times(2)).doSleep();
}
private ConfigurationFilesPoller getConfigurationFilesPollerOverrideWait() {
return new ConfigurationFilesPoller(configFilePath, FILE_EXT) {
@Override
void waitForFileToBeCompletelyWritten(File file) throws InterruptedException {
return;
}
};
}
protected ConfigurationFilesPoller getConfigurationFilesPollerOverrideSleepWithSpy() {
return Mockito.spy(new ConfigurationFilesPoller(configFilePath, FILE_EXT) {
@Override
void doSleep() throws InterruptedException {
return;
}
});
}
private Path getMockPath(String baseFileName, String extension) {
Path mockPath = mock(Path.class);
when(mockPath.toString()).thenReturn(baseFileName + extension);
return mockPath;
}
private Path getMockConfigurationDirectoryPath(Path mockPath, Path mockResolvedPath) {
Path mockConfigurationDirectory = mock(Path.class);
when(mockConfigurationDirectory.resolve(mockPath)).thenReturn(mockResolvedPath);
File mockFile = mock(File.class);
when(mockConfigurationDirectory.toFile()).thenReturn(mockFile);
when(mockFile.list(any(FilenameFilter.class))).thenReturn(new String[0]);
return mockConfigurationDirectory;
}
}