1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.plugin.MockPlugin;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.event.PluginEventManager;
7 import com.atlassian.plugin.loaders.PluginLoader;
8 import com.google.common.base.Function;
9 import com.google.common.base.Predicates;
10 import com.google.common.collect.Collections2;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Maps;
15 import com.google.common.collect.Sets;
16 import org.junit.Assert;
17 import org.junit.Before;
18 import org.junit.Test;
19 import org.mockito.Mockito;
20
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.Future;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31 import java.util.regex.Pattern;
32
33 public class TestDefaultPluginManagerDependentPlugins
34 {
35
36 private Collection<Plugin> enabledPlugins;
37 private ImmutableMap<String, Plugin> pluginsByKey;
38 private DefaultPluginManager pluginManager;
39
40 private static Function<Plugin, String> GET_KEY = new Function<Plugin, String>()
41 {
42 @Override
43 public String apply(Plugin input)
44 {
45 return input.getKey();
46 }
47 };
48
49 @Before
50 public void setUp() throws Exception
51 {
52 enabledPlugins = ImmutableList.<Plugin>of(
53
54 new MockDepPlugin("root", Collections.<String>emptySet()),
55
56 new MockDepPlugin("a", ImmutableSet.of("root")),
57 new MockDepPlugin("b", ImmutableSet.of("root", "a")),
58 new MockDepPlugin("c", ImmutableSet.of("root")),
59
60 new MockDepPlugin("aa", ImmutableSet.of("a")),
61 new MockDepPlugin("ba", ImmutableSet.of("b")),
62 new MockDepPlugin("bb", ImmutableSet.of("b")),
63 new MockDepPlugin("ca", ImmutableSet.of("c")),
64 new MockDepPlugin("cb", ImmutableSet.of("c", "ca")),
65
66 new MockDepPlugin("aaa", ImmutableSet.of("aa")),
67 new MockDepPlugin("caa", ImmutableSet.of("c", "ca", "cb")),
68
69 new MockDepPlugin("disconnected.a", ImmutableSet.<String>of()),
70 new MockDepPlugin("disconnected.b", ImmutableSet.of("disconnected.a")),
71 new MockDepPlugin("disconnected.c", ImmutableSet.of("disconnected.b"))
72 );
73
74 pluginsByKey = Maps.uniqueIndex(enabledPlugins, GET_KEY);
75
76 pluginManager = new DefaultPluginManager(Mockito.mock(PluginPersistentStateStore.class), Collections.<PluginLoader>emptyList(),
77 Mockito.mock(ModuleDescriptorFactory.class), Mockito.mock(PluginEventManager.class));
78 }
79
80 private Set<String> getDependentPluginKeys(String parentKey)
81 {
82 return getDependentPluginKeys(pluginsByKey.get(parentKey), enabledPlugins);
83 }
84
85 private Set<String> getDependentPluginKeys(Plugin root, Collection<Plugin> enabledPlugins)
86 {
87 return ImmutableSet.copyOf(Collections2.transform(
88 pluginManager.getDependentPlugins(root, enabledPlugins), GET_KEY));
89 }
90
91 @Test
92 public void testNoDependencies() throws Exception
93 {
94 Assert.assertEquals(ImmutableSet.<String>of(), getDependentPluginKeys("aaa"));
95 }
96
97 @Test
98 public void testSingleDependency() throws Exception
99 {
100 Assert.assertEquals(ImmutableSet.of("aaa"), getDependentPluginKeys("aa"));
101 }
102
103 @Test
104 public void testSimpleTransitiveDependency() throws Exception
105 {
106 Assert.assertEquals(ImmutableSet.of("disconnected.b", "disconnected.c"), getDependentPluginKeys("disconnected.a"));
107 }
108
109 @Test
110 public void testTransitiveDependency() throws Exception
111 {
112 Assert.assertEquals(ImmutableSet.of("aa", "aaa", "b", "ba", "bb"), getDependentPluginKeys("a"));
113 }
114
115 @Test
116 public void testCrossLayerDependency() throws Exception
117 {
118 Assert.assertEquals(ImmutableSet.of("ca", "cb", "caa"), getDependentPluginKeys("c"));
119 }
120
121 @Test
122 public void testFullTreeDependency() throws Exception
123 {
124 final Set<String> notDisconnected = Sets.filter(pluginsByKey.keySet(), Predicates.not(Predicates.contains(
125 Pattern.compile("disconnected|root"))));
126 Assert.assertEquals(notDisconnected, getDependentPluginKeys("root"));
127 }
128
129 @Test(timeout = 1000)
130 public void testCircularDependency() throws Exception
131 {
132 final ImmutableList<Plugin> plugins = ImmutableList.<Plugin>of(
133 new MockDepPlugin("a", ImmutableSet.of("c")),
134 new MockDepPlugin("b", ImmutableSet.of("a")),
135 new MockDepPlugin("c", ImmutableSet.of("b"))
136 );
137 Assert.assertEquals(ImmutableSet.of("b", "c"), getDependentPluginKeys(plugins.get(0), plugins));
138 }
139
140 @Test(timeout = 1000)
141 public void testVeryDeepDependencyTreePerformance() throws Exception
142 {
143 final Map<String, Plugin> plugins = Maps.newHashMap();
144 plugins.put("root", new MockDepPlugin("root", ImmutableSet.<String>of()));
145
146 for (int lvl = 0; lvl < 10; lvl++)
147 {
148 final Set<String> existingKeys = ImmutableSet.copyOf(plugins.keySet());
149 for (int i = 0; i < 10; i++)
150 {
151 final String key = lvl + "." + i;
152 plugins.put(key, new MockDepPlugin(key, existingKeys));
153 }
154 }
155
156 final Set<String> allButRoot = Sets.difference(plugins.keySet(), ImmutableSet.of("root"));
157 Assert.assertEquals(allButRoot, getDependentPluginKeys(plugins.get("root"), plugins.values()));
158 }
159 }
160
161 class MockDepPlugin extends MockPlugin
162 {
163 final Set<String> requiredPlugins;
164
165 public MockDepPlugin(String key, Set<String> requiredPlugins)
166 {
167 super(key, null);
168 this.requiredPlugins = requiredPlugins;
169 }
170
171 @Override
172 public Set<String> getRequiredPlugins()
173 {
174 return requiredPlugins;
175 }
176 }