Browse Source

Enforce dependencies versions (#150)

tags/release-2.0.0
Decebal Suiu 7 years ago
parent
commit
617508ffb5

+ 36
- 13
pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java View File

@@ -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;
}

}

+ 235
- 58
pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java View File

@@ -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);

initGraph();
// get a sorted list of dependencies
List<String> sortedPlugins = dependenciesGraph.reverseTopologicalSort();
log.debug("Plugins order: {}", sortedPlugins);

// create the result object
Result result = new Result(sortedPlugins);

resolved = true;
}

public List<String> getDependecies(String pluginsId) {
if (!resolved) {
return Collections.emptyList();
if (sortedPlugins != null) { // no cyclic dependency
// detect not found dependencies
for (String pluginId : sortedPlugins) {
if (!pluginByIds.containsKey(pluginId)) {
result.addNotFoundDependency(pluginId);
}
}
}

return dependenciesGraph.getNeighbors(pluginsId);
}
// check dependencies versions
for (PluginDescriptor plugin : plugins) {
String pluginId = plugin.getPluginId();
Version existingVersion = plugin.getVersion();

public List<String> getDependents(String pluginsId) {
if (!resolved) {
return Collections.emptyList();
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 dependentsGraph.getNeighbors(pluginsId);
return result;
}

/**
* Get the list of plugins in dependency sorted order.
*/
public List<PluginWrapper> getSortedPlugins() throws PluginException {
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);
}

/**
* 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);
}

/**
* Check if an existing version of dependency is compatible with the required version (from plugin descriptor).
*
* @param requiredVersion
* @param existingVersion
* @return
*/
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);
}
}
}

log.debug("Graph: {}", dependenciesGraph);
List<String> pluginsId = dependenciesGraph.reverseTopologicalSort();
private void checkResolved() {
if (!resolved) {
throw new IllegalStateException("Call 'resolve' method first");
}
}

if (pluginsId == null) {
throw new PluginException("Cyclic dependencies !!! {}", dependenciesGraph.toString());
}
private String getDependencyVersionSupport(PluginDescriptor dependent, String dependencyId) {
List<PluginDependency> dependencies = dependent.getDependencies();
for (PluginDependency dependency : dependencies) {
if (dependencyId.equals(dependency.getPluginId())) {
return dependency.getPluginVersionSupport();
}
}

log.debug("Plugins order: {}", pluginsId);
List<PluginWrapper> sortedPlugins = new ArrayList<>();
for (String pluginId : pluginsId) {
sortedPlugins.add(getPlugin(pluginId));
}
throw new IllegalStateException("Cannot find a dependency with id '" + dependencyId +
"' for plugin '" + dependent.getPluginId() + "'");
}

return sortedPlugins;
}
public static class Result {

private void initGraph() {
// create graph
dependenciesGraph = new DirectedGraph<>();
dependentsGraph = new DirectedGraph<>();
private boolean cyclicDependency;
private List<String> notFoundDependencies; // value is "pluginId"
private List<String> sortedPlugins; // value is "pluginId"
private List<WrongDependencyVersion> wrongVersionDependencies;

// 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);
}
Result(List<String> sortedPlugins) {
if (sortedPlugins == null) {
cyclicDependency = true;
this.sortedPlugins = Collections.emptyList();
} else {
dependenciesGraph.addVertex(pluginId);
dependentsGraph.addVertex(pluginId);
this.sortedPlugins = new ArrayList<>(sortedPlugins);
}

notFoundDependencies = new ArrayList<>();
wrongVersionDependencies = new ArrayList<>();
}

/**
* Returns true is a cyclic dependency was detected.
*/
public boolean hasCyclicDependency() {
return cyclicDependency;
}

/**
* Returns a list with dependencies required that were not found.
*/
public List<String> getNotFoundDependencies() {
return notFoundDependencies;
}

/**
* 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;
}

void addNotFoundDependency(String pluginId) {
notFoundDependencies.add(pluginId);
}

void addWrongDependencyVersion(WrongDependencyVersion wrongDependencyVersion) {
wrongVersionDependencies.add(wrongDependencyVersion);
}

}

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;
}

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;
}

}

private PluginWrapper getPlugin(String pluginId) throws PluginNotFoundException {
for (PluginWrapper pluginWrapper : plugins) {
if (pluginId.equals(pluginWrapper.getDescriptor().getPluginId())) {
return pluginWrapper;
}
}
/**
* 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);

throw new PluginNotFoundException(pluginId);
}
this.dependencies = dependencies;
}

public List<WrongDependencyVersion> getDependencies() {
return dependencies;
}

}

}

+ 0
- 1
pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java View File

@@ -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);

+ 24
- 8
pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java View File

@@ -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;
}

}

+ 0
- 2
pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java View File

@@ -24,8 +24,6 @@ import ro.fortsoft.pf4j.util.StringUtils;
*/
public class PluginException extends Exception {

private static final long serialVersionUID = 1L;

public PluginException() {
super();
}

+ 0
- 37
pf4j/src/main/java/ro/fortsoft/pf4j/PluginNotFoundException.java View File

@@ -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;
}

}

+ 23
- 15
pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java View File

@@ -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);
}

if (!containsVertex(to)) {
throw new IllegalArgumentException("Nonexistent vertex " + to);
}

neighbors.get(from).remove(to);
}

public List<V> getNeighbors(V vertex) {
if (!neighbors.containsKey(vertex)) {
return new ArrayList<V>();
}

return neighbors.get(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;

+ 15
- 4
pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java View File

@@ -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
- 0
pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java View File

@@ -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