View Javadoc

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                  // Draw it on paper first if you want to modify it, the more letters in the key the lower row
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 }