1 package com.atlassian.plugin.manager;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.regex.Pattern;
9
10 import com.atlassian.plugin.MockPlugin;
11 import com.atlassian.plugin.ModuleDescriptorFactory;
12 import com.atlassian.plugin.Plugin;
13 import com.atlassian.plugin.event.PluginEventManager;
14 import com.atlassian.plugin.loaders.PluginLoader;
15
16 import com.google.common.base.Function;
17 import com.google.common.base.Predicates;
18 import com.google.common.collect.Collections2;
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.Maps;
23 import com.google.common.collect.Sets;
24
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.contrib.java.lang.system.RestoreSystemProperties;
30 import org.mockito.Mockito;
31
32 import static com.atlassian.plugin.manager.DefaultPluginManager.getPluginSortModeProperty;
33 import static org.hamcrest.MatcherAssert.assertThat;
34 import static org.hamcrest.Matchers.contains;
35 import static org.hamcrest.Matchers.containsInAnyOrder;
36 import static org.hamcrest.Matchers.instanceOf;
37 import static org.hamcrest.Matchers.is;
38
39 public class TestDefaultPluginManagerDependentPlugins
40 {
41 @Rule
42 public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(getPluginSortModeProperty());
43
44 private Collection<Plugin> enabledPlugins;
45 private ImmutableMap<String, Plugin> pluginsByKey;
46 private DefaultPluginManager pluginManager;
47
48 private static Function<Plugin, String> GET_KEY = new Function<Plugin, String>()
49 {
50 @Override
51 public String apply(Plugin input)
52 {
53 return input.getKey();
54 }
55 };
56
57 @Before
58 public void setUp() throws Exception
59 {
60 enabledPlugins = ImmutableList.<Plugin>of(
61
62 new MockDepPlugin("root", Collections.<String>emptySet()),
63
64 new MockDepPlugin("a", ImmutableSet.of("root")),
65 new MockDepPlugin("b", ImmutableSet.of("root", "a")),
66 new MockDepPlugin("c", ImmutableSet.of("root")),
67
68 new MockDepPlugin("aa", ImmutableSet.of("a")),
69 new MockDepPlugin("ba", ImmutableSet.of("b")),
70 new MockDepPlugin("bb", ImmutableSet.of("b")),
71 new MockDepPlugin("ca", ImmutableSet.of("c")),
72 new MockDepPlugin("cb", ImmutableSet.of("c", "ca")),
73
74 new MockDepPlugin("aaa", ImmutableSet.of("aa")),
75 new MockDepPlugin("caa", ImmutableSet.of("c", "ca", "cb")),
76
77 new MockDepPlugin("disconnected.a", ImmutableSet.<String>of()),
78 new MockDepPlugin("disconnected.b", ImmutableSet.of("disconnected.a")),
79 new MockDepPlugin("disconnected.c", ImmutableSet.of("disconnected.b"))
80 );
81
82 pluginsByKey = Maps.uniqueIndex(enabledPlugins, GET_KEY);
83
84 pluginManager = new DefaultPluginManager(Mockito.mock(PluginPersistentStateStore.class), Collections.<PluginLoader>emptyList(),
85 Mockito.mock(ModuleDescriptorFactory.class), Mockito.mock(PluginEventManager.class));
86 }
87
88 private Set<String> getDependentPluginKeys(String parentKey)
89 {
90 return getDependentPluginKeys(pluginsByKey.get(parentKey), enabledPlugins);
91 }
92
93 private Set<String> getDependentPluginKeys(Plugin root, Collection<Plugin> enabledPlugins)
94 {
95 return ImmutableSet.copyOf(Collections2.transform(
96 pluginManager.getDependentPlugins(root.getKey(), enabledPlugins), GET_KEY));
97 }
98
99 @Test
100 public void testNoDependencies() throws Exception
101 {
102 Assert.assertEquals(ImmutableSet.<String>of(), getDependentPluginKeys("aaa"));
103 }
104
105 @Test
106 public void testSingleDependency() throws Exception
107 {
108 Assert.assertEquals(ImmutableSet.of("aaa"), getDependentPluginKeys("aa"));
109 }
110
111 @Test
112 public void testSimpleTransitiveDependency() throws Exception
113 {
114 Assert.assertEquals(ImmutableSet.of("disconnected.b", "disconnected.c"), getDependentPluginKeys("disconnected.a"));
115 }
116
117 @Test
118 public void testTransitiveDependency() throws Exception
119 {
120 Assert.assertEquals(ImmutableSet.of("aa", "aaa", "b", "ba", "bb"), getDependentPluginKeys("a"));
121 }
122
123 @Test
124 public void testCrossLayerDependency() throws Exception
125 {
126 Assert.assertEquals(ImmutableSet.of("ca", "cb", "caa"), getDependentPluginKeys("c"));
127 }
128
129 @Test
130 public void testFullTreeDependency() throws Exception
131 {
132 final Set<String> notDisconnected = Sets.filter(pluginsByKey.keySet(), Predicates.not(Predicates.contains(
133 Pattern.compile("disconnected|root"))));
134 Assert.assertEquals(notDisconnected, getDependentPluginKeys("root"));
135 }
136
137 @Test (timeout = 1000)
138 public void testCircularDependency() throws Exception
139 {
140 final ImmutableList<Plugin> plugins = ImmutableList.<Plugin>of(
141 new MockDepPlugin("a", ImmutableSet.of("c")),
142 new MockDepPlugin("b", ImmutableSet.of("a")),
143 new MockDepPlugin("c", ImmutableSet.of("b"))
144 );
145 Assert.assertEquals(ImmutableSet.of("b", "c"), getDependentPluginKeys(plugins.get(0), plugins));
146 }
147
148 @Test (timeout = 1000)
149 public void testVeryDeepDependencyTreePerformance() throws Exception
150 {
151 final Map<String, Plugin> plugins = Maps.newHashMap();
152 plugins.put("root", new MockDepPlugin("root", ImmutableSet.<String>of()));
153
154 for (int lvl = 0; lvl < 10; lvl++)
155 {
156 final Set<String> existingKeys = ImmutableSet.copyOf(plugins.keySet());
157 for (int i = 0; i < 10; i++)
158 {
159 final String key = lvl + "." + i;
160 plugins.put(key, new MockDepPlugin(key, existingKeys));
161 }
162 }
163
164 final Set<String> allButRoot = Sets.difference(plugins.keySet(), ImmutableSet.of("root"));
165 Assert.assertEquals(allButRoot, getDependentPluginKeys(plugins.get("root"), plugins.values()));
166 }
167
168 @Test
169 public void sortPluginsForEnableSorts() throws Exception
170 {
171 Plugin a = new MockDepPlugin("a", Collections.<String>emptySet());
172 Plugin b = new MockDepPlugin("b", ImmutableSet.of("a"));
173 Plugin c = new MockDepPlugin("c", ImmutableSet.of("b"));
174
175 final ImmutableList<Plugin> plugins = ImmutableList.<Plugin>of(b, c, a);
176 final ImmutableMap<String, Plugin> pluginsByKey = Maps.uniqueIndex(plugins, GET_KEY);
177
178 List<Plugin> sortedList = pluginManager.sortPluginsForEnable(plugins, pluginsByKey);
179
180 assertThat(sortedList, contains(a, b, c));
181 }
182
183 @Test (timeout = 1000)
184 public void sortPluginsForEnableSortsCycle() throws Exception
185 {
186 Plugin a = new MockDepPlugin("a", Collections.<String>emptySet());
187 Plugin b = new MockDepPlugin("b", ImmutableSet.of("a", "d"));
188 Plugin c = new MockDepPlugin("c", ImmutableSet.of("b"));
189 Plugin d = new MockDepPlugin("d", ImmutableSet.of("c"));
190
191 final ImmutableList<Plugin> plugins = ImmutableList.<Plugin>of(b, c, a, d);
192 final ImmutableMap<String, Plugin> pluginsByKey = Maps.uniqueIndex(plugins, GET_KEY);
193
194 List<Plugin> sortedList = pluginManager.sortPluginsForEnable(plugins, pluginsByKey);
195
196 assertThat(sortedList, containsInAnyOrder(a, b, c, d));
197 assertThat(sortedList.get(0), is(a));
198 }
199
200 @Test
201 public void sortPluginsForEnableIgnoresNonPluginRequirements()
202 {
203 Plugin a = new MockDepPlugin("a", Collections.<String>emptySet());
204 Plugin b = new MockDepPlugin("b", ImmutableSet.of("a", "framework"));
205 Plugin c = new MockDepPlugin("c", ImmutableSet.of("b", "host"));
206
207 final ImmutableList<Plugin> plugins = ImmutableList.<Plugin>of(b, c, a);
208 final ImmutableMap<String, Plugin> pluginsByKey = Maps.uniqueIndex(plugins, GET_KEY);
209
210 List<Plugin> sortedList = pluginManager.sortPluginsForEnable(plugins, pluginsByKey);
211
212 assertThat(sortedList, contains(a, b, c));
213 }
214
215 @Test
216 public void sortPluginsForEnableTraversesButHidesUnrequestedPlugins()
217 {
218 Plugin a = new MockDepPlugin("a", Collections.<String>emptySet());
219 Plugin b = new MockDepPlugin("b", ImmutableSet.of("a"));
220 Plugin c = new MockDepPlugin("c", ImmutableSet.of("b"));
221
222 final ImmutableList<Plugin> pluginsToEnable = ImmutableList.<Plugin>of(c, a);
223 final ImmutableList<Plugin> allPlugins = ImmutableList.<Plugin>of(a, b, c);
224 final ImmutableMap<String, Plugin> pluginsByKey = Maps.uniqueIndex(allPlugins, GET_KEY);
225
226 List<Plugin> sortedList = pluginManager.sortPluginsForEnable(pluginsToEnable, pluginsByKey);
227
228 assertThat(sortedList, contains(a, c));
229
230 }
231
232 @Test
233 public void getDependentPluginsReturnsListByDefault()
234 {
235 final Collection<Plugin> collection = pluginManager.getDependentPlugins("a", enabledPlugins);
236 assertThat(collection, instanceOf(List.class));
237 }
238
239 @Test
240 public void getDependentPluginsReturnsSetInLegacyMode()
241 {
242 System.setProperty(getPluginSortModeProperty(), DefaultPluginManager.PluginSortMode.LEGACY.toString());
243 final Collection<Plugin> collection = pluginManager.getDependentPlugins("a", enabledPlugins);
244 assertThat(collection, instanceOf(Set.class));
245 }
246 }
247
248 class MockDepPlugin extends MockPlugin
249 {
250 final Set<String> requiredPlugins;
251
252 public MockDepPlugin(String key, Set<String> requiredPlugins)
253 {
254 super(key, null);
255 this.requiredPlugins = requiredPlugins;
256 }
257
258 @Override
259 public Set<String> getRequiredPlugins()
260 {
261 return requiredPlugins;
262 }
263 }