Browse Source

Enforce dependencies versions (#150)

pull/154/head
Decebal Suiu 8 years ago committed by GitHub
parent
commit
617508ffb5
  1. 47
      pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java
  2. 269
      pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java
  3. 1
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java
  4. 32
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java
  5. 2
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java
  6. 37
      pf4j/src/main/java/ro/fortsoft/pf4j/PluginNotFoundException.java
  7. 38
      pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java
  8. 19
      pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java
  9. 141
      pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java

47
pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java

@ -50,7 +50,7 @@ public abstract class AbstractPluginManager implements PluginManager {
protected Map<String, PluginWrapper> plugins;
/*
* A map of plugin class loaders (he key is the 'pluginId').
* A map of plugin class loaders (the key is the 'pluginId').
*/
private Map<String, ClassLoader> pluginClassLoaders;
@ -60,7 +60,7 @@ public abstract class AbstractPluginManager implements PluginManager {
private List<PluginWrapper> unresolvedPlugins;
/**
* A list with resolved plugins (resolved dependency).
* A list with all resolved plugins (resolved dependency).
*/
private List<PluginWrapper> resolvedPlugins;
@ -83,7 +83,7 @@ public abstract class AbstractPluginManager implements PluginManager {
/*
* The system version used for comparisons to the plugin requires attribute.
*/
private Version systemVersion = Version.forIntegers(0, 0, 0);
private Version systemVersion = Version.forIntegers(0);
private PluginRepository pluginRepository;
private PluginFactory pluginFactory;
@ -394,7 +394,7 @@ public abstract class AbstractPluginManager implements PluginManager {
}
/**
* Stop the specified plugin and it's dependencies.
* Stop the specified plugin and it's dependents.
*/
@Override
public PluginState stopPlugin(String pluginId) {
@ -612,7 +612,7 @@ public abstract class AbstractPluginManager implements PluginManager {
}
}
return (version != null) ? Version.valueOf(version) : Version.forIntegers(0, 0, 0);
return (version != null) ? Version.valueOf(version) : Version.forIntegers(0);
}
protected abstract PluginRepository createPluginRepository();
@ -700,7 +700,7 @@ public abstract class AbstractPluginManager implements PluginManager {
// If exact versions are not allowed in requires, rewrite to >= expression
requires = ">=" + requires;
}
if (systemVersion.equals(Version.forIntegers(0,0,0)) || systemVersion.satisfies(requires)) {
if (systemVersion.equals(Version.forIntegers(0)) || systemVersion.satisfies(requires)) {
return true;
}
@ -718,15 +718,36 @@ public abstract class AbstractPluginManager implements PluginManager {
}
protected void resolvePlugins() throws PluginException {
resolveDependencies();
// extract plugins descriptors from "unresolvedPlugins" list
List<PluginDescriptor> descriptors = new ArrayList<>();
for (PluginWrapper plugin : unresolvedPlugins) {
descriptors.add(plugin.getDescriptor());
}
protected void resolveDependencies() throws PluginException {
dependencyResolver.resolve(unresolvedPlugins);
resolvedPlugins = dependencyResolver.getSortedPlugins();
for (PluginWrapper pluginWrapper : resolvedPlugins) {
DependencyResolver.Result result = dependencyResolver.resolve(descriptors);
if (result.hasCyclicDependency()) {
throw new DependencyResolver.CyclicDependencyException();
}
List<String> notFoundDependencies = result.getNotFoundDependencies();
if (!notFoundDependencies.isEmpty()) {
throw new DependencyResolver.DependenciesNotFoundException(notFoundDependencies);
}
List<DependencyResolver.WrongDependencyVersion> wrongVersionDependencies = result.getWrongVersionDependencies();
if (!wrongVersionDependencies.isEmpty()) {
throw new DependencyResolver.DependenciesWrongVersionException(wrongVersionDependencies);
}
List<String> sortedPlugins = result.getSortedPlugins();
// move plugins from "unresolved" to "resolved"
for (String pluginId : sortedPlugins) {
PluginWrapper pluginWrapper = plugins.get(pluginId);
unresolvedPlugins.remove(pluginWrapper);
log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId());
resolvedPlugins.add(pluginWrapper);
log.info("Plugin '{}' resolved", pluginId);
}
}
@ -800,6 +821,7 @@ public abstract class AbstractPluginManager implements PluginManager {
return plugin.getPluginId();
}
}
return null;
}
@ -841,4 +863,5 @@ public abstract class AbstractPluginManager implements PluginManager {
public void setExactVersionAllowed(boolean exactVersionAllowed) {
this.exactVersionAllowed = exactVersionAllowed;
}
}

269
pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java

@ -15,13 +15,16 @@
*/
package ro.fortsoft.pf4j;
import com.github.zafarkhaja.semver.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.fortsoft.pf4j.util.DirectedGraph;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Decebal Suiu
@ -30,89 +33,263 @@ public class DependencyResolver {
private static final Logger log = LoggerFactory.getLogger(DependencyResolver.class);
private List<PluginWrapper> plugins;
private DirectedGraph<String> dependenciesGraph;
private DirectedGraph<String> dependentsGraph;
private DirectedGraph<String> dependenciesGraph; // the value is 'pluginId'
private DirectedGraph<String> dependentsGraph; // the value is 'pluginId'
private boolean resolved;
public void resolve(List<PluginWrapper> plugins) {
this.plugins = plugins;
public Result resolve(List<PluginDescriptor> plugins) {
// create graphs
dependenciesGraph = new DirectedGraph<>();
dependentsGraph = new DirectedGraph<>();
// populate graphs
Map<String, PluginDescriptor> pluginByIds = new HashMap<>();
for (PluginDescriptor plugin : plugins) {
addPlugin(plugin);
pluginByIds.put(plugin.getPluginId(), plugin);
}
log.debug("Graph: {}", dependenciesGraph);
// get a sorted list of dependencies
List<String> sortedPlugins = dependenciesGraph.reverseTopologicalSort();
log.debug("Plugins order: {}", sortedPlugins);
initGraph();
// create the result object
Result result = new Result(sortedPlugins);
resolved = true;
if (sortedPlugins != null) { // no cyclic dependency
// detect not found dependencies
for (String pluginId : sortedPlugins) {
if (!pluginByIds.containsKey(pluginId)) {
result.addNotFoundDependency(pluginId);
}
}
}
public List<String> getDependecies(String pluginsId) {
if (!resolved) {
return Collections.emptyList();
// check dependencies versions
for (PluginDescriptor plugin : plugins) {
String pluginId = plugin.getPluginId();
Version existingVersion = plugin.getVersion();
List<String> dependents = getDependents(pluginId);
while (!dependents.isEmpty()) {
String dependentId = dependents.remove(0);
PluginDescriptor dependent = pluginByIds.get(dependentId);
String requiredVersion = getDependencyVersionSupport(dependent, pluginId);
boolean ok = checkDependencyVersion(requiredVersion, existingVersion);
if (!ok) {
result.addWrongDependencyVersion(new WrongDependencyVersion(pluginId, dependentId, existingVersion, requiredVersion));
}
}
}
return dependenciesGraph.getNeighbors(pluginsId);
return result;
}
public List<String> getDependents(String pluginsId) {
if (!resolved) {
return Collections.emptyList();
/**
* Retrieves the plugins ids that the given plugin id directly depends on.
*
* @param pluginId
* @return
*/
public List<String> getDependencies(String pluginId) {
checkResolved();
return dependenciesGraph.getNeighbors(pluginId);
}
return dependentsGraph.getNeighbors(pluginsId);
/**
* Retrieves the plugins ids that the given content is a direct dependency of.
*
* @param pluginId
* @return
*/
public List<String> getDependents(String pluginId) {
checkResolved();
return dependentsGraph.getNeighbors(pluginId);
}
/**
* Get the list of plugins in dependency sorted order.
* Check if an existing version of dependency is compatible with the required version (from plugin descriptor).
*
* @param requiredVersion
* @param existingVersion
* @return
*/
public List<PluginWrapper> getSortedPlugins() throws PluginException {
protected boolean checkDependencyVersion(String requiredVersion, Version existingVersion) {
return existingVersion.satisfies(requiredVersion);
}
private void addPlugin(PluginDescriptor descriptor) {
String pluginId = descriptor.getPluginId();
List<PluginDependency> dependencies = descriptor.getDependencies();
if (dependencies.isEmpty()) {
dependenciesGraph.addVertex(pluginId);
dependentsGraph.addVertex(pluginId);
} else {
for (PluginDependency dependency : dependencies) {
dependenciesGraph.addEdge(pluginId, dependency.getPluginId());
dependentsGraph.addEdge(dependency.getPluginId(), pluginId);
}
}
}
private void checkResolved() {
if (!resolved) {
return Collections.emptyList();
throw new IllegalStateException("Call 'resolve' method first");
}
}
log.debug("Graph: {}", dependenciesGraph);
List<String> pluginsId = dependenciesGraph.reverseTopologicalSort();
private String getDependencyVersionSupport(PluginDescriptor dependent, String dependencyId) {
List<PluginDependency> dependencies = dependent.getDependencies();
for (PluginDependency dependency : dependencies) {
if (dependencyId.equals(dependency.getPluginId())) {
return dependency.getPluginVersionSupport();
}
}
throw new IllegalStateException("Cannot find a dependency with id '" + dependencyId +
"' for plugin '" + dependent.getPluginId() + "'");
}
public static class Result {
private boolean cyclicDependency;
private List<String> notFoundDependencies; // value is "pluginId"
private List<String> sortedPlugins; // value is "pluginId"
private List<WrongDependencyVersion> wrongVersionDependencies;
Result(List<String> sortedPlugins) {
if (sortedPlugins == null) {
cyclicDependency = true;
this.sortedPlugins = Collections.emptyList();
} else {
this.sortedPlugins = new ArrayList<>(sortedPlugins);
}
notFoundDependencies = new ArrayList<>();
wrongVersionDependencies = new ArrayList<>();
}
/**
* Returns true is a cyclic dependency was detected.
*/
public boolean hasCyclicDependency() {
return cyclicDependency;
}
if (pluginsId == null) {
throw new PluginException("Cyclic dependencies !!! {}", dependenciesGraph.toString());
/**
* Returns a list with dependencies required that were not found.
*/
public List<String> getNotFoundDependencies() {
return notFoundDependencies;
}
log.debug("Plugins order: {}", pluginsId);
List<PluginWrapper> sortedPlugins = new ArrayList<>();
for (String pluginId : pluginsId) {
sortedPlugins.add(getPlugin(pluginId));
/**
* Returns a list with dependencies with wrong version.
*/
public List<WrongDependencyVersion> getWrongVersionDependencies() {
return wrongVersionDependencies;
}
/**
* Get the list of plugins in dependency sorted order.
*/
public List<String> getSortedPlugins() {
return sortedPlugins;
}
private void initGraph() {
// create graph
dependenciesGraph = new DirectedGraph<>();
dependentsGraph = new DirectedGraph<>();
void addNotFoundDependency(String pluginId) {
notFoundDependencies.add(pluginId);
}
// populate graph
for (PluginWrapper pluginWrapper : plugins) {
PluginDescriptor descriptor = pluginWrapper.getDescriptor();
String pluginId = descriptor.getPluginId();
List<PluginDependency> dependencies = descriptor.getDependencies();
if (!dependencies.isEmpty()) {
for (PluginDependency dependency : dependencies) {
dependenciesGraph.addEdge(pluginId, dependency.getPluginId());
dependentsGraph.addEdge(dependency.getPluginId(), pluginId);
void addWrongDependencyVersion(WrongDependencyVersion wrongDependencyVersion) {
wrongVersionDependencies.add(wrongDependencyVersion);
}
} else {
dependenciesGraph.addVertex(pluginId);
dependentsGraph.addVertex(pluginId);
}
public static class WrongDependencyVersion {
private String dependencyId; // value is "pluginId"
private String dependentId; // value is "pluginId"
private Version existingVersion;
private String requiredVersion;
WrongDependencyVersion(String dependencyId, String dependentId, Version existingVersion, String requiredVersion) {
this.dependencyId = dependencyId;
this.dependentId = dependentId;
this.existingVersion = existingVersion;
this.requiredVersion = requiredVersion;
}
public String getDependencyId() {
return dependencyId;
}
public String getDependentId() {
return dependentId;
}
private PluginWrapper getPlugin(String pluginId) throws PluginNotFoundException {
for (PluginWrapper pluginWrapper : plugins) {
if (pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) {
return pluginWrapper;
public Version getExistingVersion() {
return existingVersion;
}
public String getRequiredVersion() {
return requiredVersion;
}
}
/**
* It will be thrown if a cyclic dependency is detected.
*/
public static class CyclicDependencyException extends PluginException {
public CyclicDependencyException() {
super("Cyclic dependencies");
}
}
/**
* Indicates that the dependencies required were not found.
*/
public static class DependenciesNotFoundException extends PluginException {
private List<String> dependencies;
public DependenciesNotFoundException(List<String> dependencies) {
super("Dependencies '{}' not found", dependencies);
this.dependencies = dependencies;
}
public List<String> getDependencies() {
return dependencies;
}
}
/**
* Indicates that some dependencies have wrong version.
*/
public static class DependenciesWrongVersionException extends PluginException {
private List<WrongDependencyVersion> dependencies;
public DependenciesWrongVersionException(List<WrongDependencyVersion> dependencies) {
super("Dependencies '{}' have wrong version", dependencies);
this.dependencies = dependencies;
}
public List<WrongDependencyVersion> getDependencies() {
return dependencies;
}
throw new PluginNotFoundException(pluginId);
}
}

1
pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java

@ -28,7 +28,6 @@ public class PluginDependency {
if (index == -1) {
this.pluginId = dependency;
} else {
this.pluginId = dependency.substring(0, index);
if (dependency.length() > index + 1) {
this.pluginVersionSupport = dependency.substring(index + 1);

32
pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java

@ -109,31 +109,43 @@ public class PluginDescriptor {
+ license + "]";
}
void setPluginId(String pluginId) {
PluginDescriptor setPluginId(String pluginId) {
this.pluginId = pluginId;
return this;
}
void setPluginDescription(String pluginDescription) {
PluginDescriptor setPluginDescription(String pluginDescription) {
this.pluginDescription = pluginDescription;
return this;
}
void setPluginClass(String pluginClassName) {
PluginDescriptor setPluginClass(String pluginClassName) {
this.pluginClass = pluginClassName;
return this;
}
void setPluginVersion(Version version) {
PluginDescriptor setPluginVersion(Version version) {
this.version = version;
return this;
}
void setProvider(String provider) {
PluginDescriptor setProvider(String provider) {
this.provider = provider;
return this;
}
void setRequires(String requires) {
PluginDescriptor setRequires(String requires) {
this.requires = requires;
return this;
}
void setDependencies(String dependencies) {
PluginDescriptor setDependencies(String dependencies) {
if (dependencies != null) {
dependencies = dependencies.trim();
if (dependencies.isEmpty()) {
@ -154,10 +166,14 @@ public class PluginDescriptor {
} else {
this.dependencies = Collections.emptyList();
}
return this;
}
public void setLicense(String license) {
public PluginDescriptor setLicense(String license) {
this.license = license;
return this;
}
}

2
pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java

@ -24,8 +24,6 @@ import ro.fortsoft.pf4j.util.StringUtils;
*/
public class PluginException extends Exception {
private static final long serialVersionUID = 1L;
public PluginException() {
super();
}

37
pf4j/src/main/java/ro/fortsoft/pf4j/PluginNotFoundException.java

@ -1,37 +0,0 @@
/*
* Copyright 2012 Decebal Suiu
*
* 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.
*/
package ro.fortsoft.pf4j;
/**
* @author Decebal Suiu
*/
class PluginNotFoundException extends PluginException {
private static final long serialVersionUID = 1L;
private String pluginId;
public PluginNotFoundException(String pluginId) {
super("Plugin '" + pluginId + "' not found.");
this.pluginId = pluginId;
}
public String getPluginId() {
return pluginId;
}
}

38
pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java

@ -23,6 +23,8 @@ import java.util.Map;
import java.util.Stack;
/**
* See <a href="https://en.wikipedia.org/wiki/Directed_graph">Wikipedia</a> for more information.
*
* @author Decebal Suiu
*/
public class DirectedGraph<V> {
@ -38,7 +40,7 @@ public class DirectedGraph<V> {
* Add a vertex to the graph. Nothing happens if vertex is already in graph.
*/
public void addVertex(V vertex) {
if (neighbors.containsKey(vertex)) {
if (containsVertex(vertex)) {
return;
}
@ -52,38 +54,42 @@ public class DirectedGraph<V> {
return neighbors.containsKey(vertex);
}
public void removeVertex(V vertex) {
neighbors.remove(vertex);
}
/**
* Add an edge to the graph; if either vertex does not exist, it's added.
* This implementation allows the creation of multi-edges and self-loops.
*/
public void addEdge(V from, V to) {
this.addVertex(from);
this.addVertex(to);
addVertex(from);
addVertex(to);
neighbors.get(from).add(to);
}
/**
* Remove an edge from the graph. Nothing happens if no such edge.
* @throws IllegalArgumentException if either vertex doesn't exist.
* @throws {@link IllegalArgumentException} if either vertex doesn't exist.
*/
public void remove(V from, V to) {
if (!(this.containsVertex(from) && this.containsVertex(to))) {
throw new IllegalArgumentException("Nonexistent vertex");
public void removeEdge(V from, V to) {
if (!containsVertex(from)) {
throw new IllegalArgumentException("Nonexistent vertex " + from);
}
neighbors.get(from).remove(to);
if (!containsVertex(to)) {
throw new IllegalArgumentException("Nonexistent vertex " + to);
}
public List<V> getNeighbors(V vertex) {
if (!neighbors.containsKey(vertex)) {
return new ArrayList<V>();
neighbors.get(from).remove(to);
}
return neighbors.get(vertex);
public List<V> getNeighbors(V vertex) {
return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList<V>();
}
/**
* Report (as a Map) the out-degree of each vertex.
* Report (as a Map) the out-degree (the number of tail ends adjacent to a vertex) of each vertex.
*/
public Map<V, Integer> outDegree() {
Map<V, Integer> result = new HashMap<>();
@ -95,9 +101,9 @@ public class DirectedGraph<V> {
}
/**
* Report (as a Map) the in-degree of each vertex.
* Report (as a Map) the in-degree (the number of head ends adjacent to a vertex) of each vertex.
*/
public Map<V,Integer> inDegree() {
public Map<V, Integer> inDegree() {
Map<V, Integer> result = new HashMap<>();
for (V vertex : neighbors.keySet()) {
result.put(vertex, 0); // all in-degrees are 0
@ -113,6 +119,7 @@ public class DirectedGraph<V> {
/**
* Report (as a List) the topological sort of the vertices; null for no such sort.
* See <a href="https://en.wikipedia.org/wiki/Topological_sorting">this</a> for more information.
*/
public List<V> topologicalSort() {
Map<V, Integer> degree = inDegree();
@ -156,6 +163,7 @@ public class DirectedGraph<V> {
if (list == null) {
return null;
}
Collections.reverse(list);
return list;

19
pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java

@ -18,7 +18,13 @@ package ro.fortsoft.pf4j.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@ -26,7 +32,6 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@ -34,9 +39,8 @@ import java.util.List;
* @author Decebal Suiu
*/
public class FileUtils {
private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
private static final List<String> ZIP_EXTENSIONS = Arrays.asList(".zip", ".ZIP", ".Zip");
private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
public static List<String> readLines(File file, boolean ignoreComments) throws IOException {
if (!file.exists() || !file.isFile()) {
@ -86,19 +90,23 @@ public class FileUtils {
*/
public static void delete(Path path) throws IOException {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (!attrs.isSymbolicLink()) {
Files.delete(path);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
@ -140,6 +148,7 @@ public class FileUtils {
return newPath;
}
}
return null;
}
@ -151,6 +160,7 @@ public class FileUtils {
if (path == null) {
return;
}
try {
Files.delete(path);
} catch (IOException ignored) { }
@ -202,4 +212,5 @@ public class FileUtils {
public static boolean isZipFile(Path path) {
return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".zip");
}
}

141
pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java

@ -0,0 +1,141 @@
/*
* Copyright 2017 Decebal Suiu
*
* 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.
*/
package ro.fortsoft.pf4j;
import com.github.zafarkhaja.semver.Version;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Decebal Suiu
*/
public class DependencyResolverTest {
@Test
public void sortedPlugins() {
// create incomplete plugin descriptor (ignore some attributes)
PluginDescriptor pd1 = new PluginDescriptor()
.setPluginId("p1")
.setDependencies("p2");
PluginDescriptor pd2 = new PluginDescriptor()
.setPluginId("p2")
.setPluginVersion(Version.forIntegers(0)); // needed in "checkDependencyVersion" method
List<PluginDescriptor> plugins = new ArrayList<>();
plugins.add(pd1);
plugins.add(pd2);
DependencyResolver resolver = new DependencyResolver();
DependencyResolver.Result result = resolver.resolve(plugins);
assertTrue(result.getNotFoundDependencies().isEmpty());
assertEquals(result.getSortedPlugins(), Arrays.asList("p2", "p1"));
}
@Test
public void notFoundDependencies() throws Exception {
PluginDescriptor pd1 = new PluginDescriptor()
.setPluginId("p1")
.setDependencies("p2, p3");
List<PluginDescriptor> plugins = new ArrayList<>();
plugins.add(pd1);
DependencyResolver resolver = new DependencyResolver();
DependencyResolver.Result result = resolver.resolve(plugins);
assertFalse(result.getNotFoundDependencies().isEmpty());
assertEquals(result.getNotFoundDependencies(), Arrays.asList("p2", "p3"));
}
@Test
public void cyclicDependencies() {
PluginDescriptor pd1 = new PluginDescriptor()
.setPluginId("p1")
.setPluginVersion(Version.forIntegers(0))
.setDependencies("p2");
PluginDescriptor pd2 = new PluginDescriptor()
.setPluginId("p2")
.setPluginVersion(Version.forIntegers(0))
.setDependencies("p3");
PluginDescriptor pd3 = new PluginDescriptor()
.setPluginId("p3")
.setPluginVersion(Version.forIntegers(0))
.setDependencies("p1");
List<PluginDescriptor> plugins = new ArrayList<>();
plugins.add(pd1);
plugins.add(pd2);
plugins.add(pd3);
DependencyResolver resolver = new DependencyResolver();
DependencyResolver.Result result = resolver.resolve(plugins);
assertTrue(result.hasCyclicDependency());
}
@Test
public void wrongDependencyVersion() {
PluginDescriptor pd1 = new PluginDescriptor()
.setPluginId("p1")
// .setDependencies("p2@2.0.0"); // simple version
.setDependencies("p2@>=1.5.0 & <1.6.0"); // range version
PluginDescriptor pd2 = new PluginDescriptor()
.setPluginId("p2")
.setPluginVersion(Version.forIntegers(1, 4));
List<PluginDescriptor> plugins = new ArrayList<>();
plugins.add(pd1);
plugins.add(pd2);
DependencyResolver resolver = new DependencyResolver();
DependencyResolver.Result result = resolver.resolve(plugins);
assertFalse(result.getWrongVersionDependencies().isEmpty());
}
@Test
public void goodDependencyVersion() {
PluginDescriptor pd1 = new PluginDescriptor()
.setPluginId("p1")
.setDependencies("p2@2.0.0");
PluginDescriptor pd2 = new PluginDescriptor()
.setPluginId("p2")
.setPluginVersion(Version.forIntegers(2));
List<PluginDescriptor> plugins = new ArrayList<>();
plugins.add(pd1);
plugins.add(pd2);
DependencyResolver resolver = new DependencyResolver();
DependencyResolver.Result result = resolver.resolve(plugins);
assertTrue(result.getWrongVersionDependencies().isEmpty());
}
}
Loading…
Cancel
Save