diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index 5befe464..a64f6955 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -31,6 +31,8 @@ type PackageSolver interface { Uninstall(candidate pkg.Package, checkconflicts bool) (pkg.Packages, error) ConflictsWithInstalled(p pkg.Package) (bool, error) ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error) + Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error) + World() pkg.Packages Upgrade(checkconflicts bool) (pkg.Packages, PackagesAssertions, error) @@ -149,6 +151,35 @@ func (s *Solver) getList(db pkg.PackageDatabase, lsp pkg.Packages) (pkg.Packages return ls, nil } +// Conflicts acts like ConflictsWith, but uses package's reverse dependencies to +// determine if it conflicts with the given set +func (s *Solver) Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error) { + p, err := s.DefinitionDatabase.FindPackage(pack) + if err != nil { + p = pack + } + + ls, err := s.getList(s.DefinitionDatabase, lsp) + if err != nil { + return false, errors.Wrap(err, "Package not found in definition db") + } + + if s.noRulesWorld() { + return false, nil + } + + temporarySet := pkg.NewInMemoryDatabase(false) + for _, p := range ls { + temporarySet.CreatePackage(p) + } + visited := make(map[string]interface{}) + revdeps := p.ExpandedRevdeps(temporarySet, visited) + + return len(revdeps) != 0, nil +} + +// ConflictsWith return true if a package is part of the requirement set of a list of package +// return false otherwise (and thus it is NOT relevant to the given list) func (s *Solver) ConflictsWith(pack pkg.Package, lsp pkg.Packages) (bool, error) { p, err := s.DefinitionDatabase.FindPackage(pack) if err != nil { diff --git a/pkg/solver/solver_test.go b/pkg/solver/solver_test.go index af507c6e..3f88b925 100644 --- a/pkg/solver/solver_test.go +++ b/pkg/solver/solver_test.go @@ -672,6 +672,99 @@ var _ = Describe("Solver", func() { Expect(err).ToNot(HaveOccurred()) Expect(val).ToNot(BeTrue()) }) + + It("Find conflicts using revdeps", func() { + + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{A}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + val, err := s.Conflicts(A, dbInstalled.World()) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(BeTrue()) + + }) + + It("Find nested conflicts with revdeps", func() { + + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{}) + + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{A}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + val, err := s.Conflicts(D, dbInstalled.World()) + Expect(err).ToNot(HaveOccurred()) + Expect(val).To(BeTrue()) + }) + + It("Doesn't find nested conflicts with revdeps", func() { + + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{D}, []*pkg.DefaultPackage{}) + + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{A}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + val, err := s.Conflicts(C, dbInstalled.World()) + Expect(err).ToNot(HaveOccurred()) + Expect(val).ToNot(BeTrue()) + }) + + It("Doesn't find conflicts with revdeps", func() { + + C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + D := pkg.NewPackage("D", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + A := pkg.NewPackage("A", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + B := pkg.NewPackage("B", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}) + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbDefinitions.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + + for _, p := range []pkg.Package{A, B, C, D} { + _, err := dbInstalled.CreatePackage(p) + Expect(err).ToNot(HaveOccurred()) + } + val, err := s.Conflicts(C, dbInstalled.World()) + Expect(err).ToNot(HaveOccurred()) + Expect(val).ToNot(BeTrue()) + }) + It("Uninstalls simple packages not in world correctly", func() { C := pkg.NewPackage("C", "", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})