From 14451897b2bdfca21e82b1a2bb31852811b4340a Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Mon, 16 Sep 2019 11:42:26 +0200 Subject: [PATCH] Introduce PluginDependencyTracker --- .../scm/plugin/PluginDependencyTracker.java | 42 ++++++++++ .../plugin/PluginDependencyTrackerTest.java | 81 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginDependencyTracker.java create mode 100644 scm-webapp/src/test/java/sonia/scm/plugin/PluginDependencyTrackerTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginDependencyTracker.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDependencyTracker.java new file mode 100644 index 0000000000..bc5cd1dc6d --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginDependencyTracker.java @@ -0,0 +1,42 @@ +package sonia.scm.plugin; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import static sonia.scm.ScmConstraintViolationException.Builder.doThrow; + +class PluginDependencyTracker { + + private final Map> plugins = new HashMap<>(); + + void addInstalled(PluginDescriptor plugin) { + plugin.getDependencies().forEach(dependency -> addDependency(plugin.getInformation().getName(), dependency)); + } + + void removeInstalled(PluginDescriptor plugin) { + doThrow() + .violation("Plugin is needed as a dependency for other plugins", "plugin") + .when(!mayUninstall(plugin.getInformation().getName())); + plugin.getDependencies().forEach(dependency -> removeDependency(plugin.getInformation().getName(), dependency)); + } + + boolean mayUninstall(String name) { + return plugins.computeIfAbsent(name, x -> new HashSet<>()).isEmpty(); + } + + private void addDependency(String from, String to) { + plugins.computeIfAbsent(to, name -> createInitialDependencyCollection(from)); + } + + private void removeDependency(String from, String to) { + plugins.get(to).remove(from); + } + + private Collection createInitialDependencyCollection(String from) { + Collection dependencies = new HashSet<>(); + dependencies.add(from); + return dependencies; + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginDependencyTrackerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginDependencyTrackerTest.java new file mode 100644 index 0000000000..c38409eb51 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginDependencyTrackerTest.java @@ -0,0 +1,81 @@ +package sonia.scm.plugin; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import sonia.scm.ScmConstraintViolationException; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static sonia.scm.plugin.PluginTestHelper.createInstalled; + +class PluginDependencyTrackerTest { + + @Test + void simpleInstalledPluginWithoutDependingPluginsCanBeUninstalled() { + PluginDescriptor mail = createInstalled("scm-mail-plugin").getDescriptor(); + when(mail.getDependencies()).thenReturn(emptySet()); + + PluginDependencyTracker pluginDependencyTracker = new PluginDependencyTracker(); + pluginDependencyTracker.addInstalled(mail); + + boolean mayUninstall = pluginDependencyTracker.mayUninstall("scm-mail-plugin"); + assertThat(mayUninstall).isTrue(); + } + + @Test + void installedPluginWithDependingPluginCannotBeUninstalled() { + PluginDescriptor review = createInstalled("scm-review-plugin").getDescriptor(); + when(review.getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + PluginDependencyTracker pluginDependencyTracker = new PluginDependencyTracker(); + pluginDependencyTracker.addInstalled(review); + + boolean mayUninstall = pluginDependencyTracker.mayUninstall("scm-mail-plugin"); + assertThat(mayUninstall).isFalse(); + } + + @Test + void uninstallOfRequiredPluginShouldThrowException() { + PluginDescriptor review = createInstalled("scm-review-plugin").getDescriptor(); + when(review.getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + PluginDependencyTracker pluginDependencyTracker = new PluginDependencyTracker(); + pluginDependencyTracker.addInstalled(review); + + Assertions.assertThrows( + ScmConstraintViolationException.class, + () -> pluginDependencyTracker.removeInstalled(createInstalled("scm-mail-plugin").getDescriptor()) + ); + } + + @Test + void installedPluginWithDependingPluginCanBeUninstalledAfterDependingPluginIsUninstalled() { + PluginDescriptor review = createInstalled("scm-review-plugin").getDescriptor(); + when(review.getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + PluginDependencyTracker pluginDependencyTracker = new PluginDependencyTracker(); + pluginDependencyTracker.addInstalled(review); + pluginDependencyTracker.removeInstalled(review); + + boolean mayUninstall = pluginDependencyTracker.mayUninstall("scm-mail-plugin"); + assertThat(mayUninstall).isTrue(); + } + + @Test + void installedPluginWithMultipleDependingPluginCannotBeUninstalledAfterOnlyOneDependingPluginIsUninstalled() { + PluginDescriptor review = createInstalled("scm-review-plugin").getDescriptor(); + when(review.getDependencies()).thenReturn(singleton("scm-mail-plugin")); + PluginDescriptor jira = createInstalled("scm-jira-plugin").getDescriptor(); + when(jira.getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + PluginDependencyTracker pluginDependencyTracker = new PluginDependencyTracker(); + pluginDependencyTracker.addInstalled(review); + pluginDependencyTracker.addInstalled(jira); + pluginDependencyTracker.removeInstalled(jira); + + boolean mayUninstall = pluginDependencyTracker.mayUninstall("scm-mail-plugin"); + assertThat(mayUninstall).isFalse(); + } +}