/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.plugin.infra.monitor;
import java.io.File;
import java.util.Random;
import com.googlecode.junit.ext.checkers.OSChecker;
import com.thoughtworks.go.util.SystemEnvironment;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import static com.thoughtworks.go.util.FileUtil.recreateDirectory;
import static com.thoughtworks.go.util.SystemEnvironment.PLUGIN_EXTERNAL_PROVIDED_PATH;
import static com.thoughtworks.go.util.SystemEnvironment.PLUGIN_LOCATION_MONITOR_INTERVAL_IN_SECONDS;
import static com.thoughtworks.go.util.SystemEnvironment.PLUGIN_GO_PROVIDED_PATH;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class DefaultExternalPluginJarLocationMonitorTest extends AbstractDefaultPluginJarLocationMonitorTest {
private static OSChecker WINDOWS = new OSChecker(OSChecker.WINDOWS);
private static final Random RANDOM = new Random();
private File PLUGIN_BUNDLED_DIR;
private File PLUGIN_EXTERNAL_DIR;
private DefaultPluginJarLocationMonitor monitor;
private PluginJarChangeListener changeListener;
private SystemEnvironment systemEnvironment;
@Before
public void setUp() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
String pluginBundledDirName = "./tmp-bundled-DEPJLMT" + RANDOM.nextInt();
PLUGIN_BUNDLED_DIR = new File(pluginBundledDirName);
recreateDirectory(PLUGIN_BUNDLED_DIR);
String pluginExternalDirName = "./tmp-external-DEPJLMT" + RANDOM.nextInt();
PLUGIN_EXTERNAL_DIR = new File(pluginExternalDirName);
recreateDirectory(PLUGIN_EXTERNAL_DIR);
systemEnvironment = mock(SystemEnvironment.class);
when(systemEnvironment.get(PLUGIN_LOCATION_MONITOR_INTERVAL_IN_SECONDS)).thenReturn(1);
when(systemEnvironment.get(PLUGIN_GO_PROVIDED_PATH)).thenReturn(pluginBundledDirName);
when(systemEnvironment.get(PLUGIN_EXTERNAL_PROVIDED_PATH)).thenReturn(pluginExternalDirName);
changeListener = mock(PluginJarChangeListener.class);
monitor = new DefaultPluginJarLocationMonitor(systemEnvironment);
monitor.initialize();
}
@After
public void tearDown() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.stop();
FileUtils.deleteQuietly(PLUGIN_BUNDLED_DIR);
FileUtils.deleteQuietly(PLUGIN_EXTERNAL_DIR);
}
@Test
public void shouldCreateExternalPluginDirectoryIfItDoesNotExist() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
PLUGIN_EXTERNAL_DIR.delete();
new DefaultPluginJarLocationMonitor(systemEnvironment).initialize();
assertThat(PLUGIN_EXTERNAL_DIR.exists(), is(true));
}
@Test
public void shouldThrowUpWhenExternalPluginDirectoryCreationFails() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
when(systemEnvironment.get(PLUGIN_EXTERNAL_PROVIDED_PATH)).thenReturn("/xyz");
try {
new DefaultPluginJarLocationMonitor(systemEnvironment).initialize();
fail("should have failed for missing external plugin folder");
} catch (RuntimeException e) {
assertThat(e.getMessage(),is("Failed to create external plugins folder in location /xyz"));
}
}
@Test
public void shouldDetectNewlyAddedPluginJar() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldDetectOnlyJarsAsNewPlugins() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin.something-other-than-jar.zip");
waitAMoment();
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldDetectRemovedPluginJar() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar", false));
FileUtils.deleteQuietly(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar"));
waitAMoment();
verify(changeListener).pluginJarRemoved(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-plugin-2.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldNotifyListenerOfMultiplePluginFilesAdded() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar");
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-3.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar", false));
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-3.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldNotifyListenerOfMultiplePluginFilesRemoved() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar");
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar");
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-3.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar", false));
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-3.jar", false));
FileUtils.deleteQuietly(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar"));
FileUtils.deleteQuietly(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar"));
waitAMoment();
verify(changeListener).pluginJarRemoved(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
verify(changeListener).pluginJarRemoved(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-2.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldNotifyRemoveEventBeforeAddEventInCaseOfFileRename() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar");
waitAMoment();
PluginFileDetails orgExternalFile = pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false);
verify(changeListener).pluginJarAdded(orgExternalFile);
PluginFileDetails newExternalFile = pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1-new.jar", false);
FileUtils.moveFile(orgExternalFile.file(), newExternalFile.file());
waitAMoment();
InOrder inOrder = inOrder(changeListener);
inOrder.verify(changeListener).pluginJarRemoved(orgExternalFile);
inOrder.verify(changeListener).pluginJarAdded(newExternalFile);
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldNotifyListenersOfUpdatesToPluginJars() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin.jar");
waitAMoment();
verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin.jar", false));
updateFileContents(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin.jar"));
waitAMoment();
verify(changeListener).pluginJarUpdated(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldAlwaysHandleBundledPluginsAheadOfExternalPlugins() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar");
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar");
waitAMoment();
InOrder jarAddedOrder = inOrder(changeListener);
jarAddedOrder.verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar", true));
jarAddedOrder.verify(changeListener).pluginJarAdded(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
updateFileContents(new File(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar"));
updateFileContents(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar"));
waitAMoment();
InOrder jarUpdatedOrder = inOrder(changeListener);
jarUpdatedOrder.verify(changeListener).pluginJarUpdated(pluginFileDetails(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar", true));
jarUpdatedOrder.verify(changeListener).pluginJarUpdated(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
FileUtils.deleteQuietly(new File(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar"));
FileUtils.deleteQuietly(new File(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar"));
waitAMoment();
InOrder jarRemovedOrder = inOrder(changeListener);
jarRemovedOrder.verify(changeListener).pluginJarRemoved(pluginFileDetails(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar", true));
jarRemovedOrder.verify(changeListener).pluginJarRemoved(pluginFileDetails(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar", false));
verifyNoMoreInteractions(changeListener);
}
@Test
public void shouldSpecifyIfPluginIsBundledOrExternalWhenAdded() throws Exception {
if (WINDOWS.satisfy()) {
return;
}
monitor.addPluginJarChangeListener(changeListener);
monitor.start();
copyPluginToThePluginDirectory(PLUGIN_BUNDLED_DIR, "descriptor-aware-test-bundled-plugin-1.jar");
copyPluginToThePluginDirectory(PLUGIN_EXTERNAL_DIR, "descriptor-aware-test-external-plugin-1.jar");
ArgumentCaptor<PluginFileDetails> pluginFileDetailsArgumentCaptor = ArgumentCaptor.forClass(PluginFileDetails.class);
waitAMoment();
verify(changeListener, times(2)).pluginJarAdded(pluginFileDetailsArgumentCaptor.capture());
assertThat(pluginFileDetailsArgumentCaptor.getAllValues().get(0).isBundledPlugin(), is(true));
assertThat(pluginFileDetailsArgumentCaptor.getAllValues().get(1).isBundledPlugin(), is(false));
verifyNoMoreInteractions(changeListener);
}
}