Introduce install --relaxed

It introduces a relaxed way to install packages with loose deps. Default
installation now will by default prefer up-to-date packages during
selection.

Also:
- Upgrade now it's used in install so it have to return the full system view also when there is nothing to upgrade
- Avoid checking upgrade upfront if relaxed is on
This commit is contained in:
Ettore Di Giacinto
2021-10-09 17:36:13 +02:00
parent 77b4c9a972
commit e64f68d36b
7 changed files with 329 additions and 64 deletions

View File

@@ -104,6 +104,7 @@ To force install a package:
PreserveSystemEssentialData: true, PreserveSystemEssentialData: true,
DownloadOnly: downloadOnly, DownloadOnly: downloadOnly,
Ask: !yes, Ask: !yes,
Relaxed: relax,
}) })
inst.Repositories(repos) inst.Repositories(repos)
@@ -125,6 +126,8 @@ func init() {
installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)") installCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
installCmd.Flags().Bool("relax", false, "Relax installation constraints")
installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies") installCmd.Flags().Bool("onlydeps", false, "Consider **only** package dependencies")
installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)") installCmd.Flags().Bool("force", false, "Skip errors and keep going (potentially harmful)")
installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)") installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")

View File

@@ -50,6 +50,7 @@ type LuetInstallerOptions struct {
SolverUpgrade, RemoveUnavailableOnUpgrade, UpgradeNewRevisions bool SolverUpgrade, RemoveUnavailableOnUpgrade, UpgradeNewRevisions bool
Ask bool Ask bool
DownloadOnly bool DownloadOnly bool
Relaxed bool
} }
type LuetInstaller struct { type LuetInstaller struct {
@@ -513,7 +514,7 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error {
return err return err
} }
if len(s.Database.World()) > 0 { if len(s.Database.World()) > 0 && !l.Options.Relaxed {
Info(":thinking: Checking for available upgrades") Info(":thinking: Checking for available upgrades")
if err := l.checkAndUpgrade(syncedRepos, s); err != nil { if err := l.checkAndUpgrade(syncedRepos, s); err != nil {
return errors.Wrap(err, "while checking upgrades before install") return errors.Wrap(err, "while checking upgrades before install")
@@ -684,7 +685,12 @@ func (l *LuetInstaller) computeInstall(o Option, syncedRepos Repositories, cp pk
if !o.NoDeps { if !o.NoDeps {
solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
if l.Options.Relaxed {
solution, err = solv.RelaxedInstall(p)
} else {
solution, err = solv.Install(p) solution, err = solv.Install(p)
}
/// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1) /// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1)
if err != nil && !o.Force { if err != nil && !o.Force {
return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package") return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package")

View File

@@ -72,6 +72,7 @@ type Package interface {
SetVersion(string) SetVersion(string)
RequiresContains(PackageDatabase, Package) (bool, error) RequiresContains(PackageDatabase, Package) (bool, error)
Matches(m Package) bool Matches(m Package) bool
AtomMatches(m Package) bool
BumpBuildVersion() error BumpBuildVersion() error
AddUse(use string) AddUse(use string)
@@ -536,6 +537,13 @@ func (p *DefaultPackage) Matches(m Package) bool {
return false return false
} }
func (p *DefaultPackage) AtomMatches(m Package) bool {
if p.GetName() == m.GetName() && p.GetCategory() == m.GetCategory() {
return true
}
return false
}
func (p *DefaultPackage) Mark() Package { func (p *DefaultPackage) Mark() Package {
marked := p.Clone() marked := p.Clone()
marked.SetName("@@" + marked.GetName()) marked.SetName("@@" + marked.GetName())
@@ -795,37 +803,38 @@ func (pack *DefaultPackage) buildFormula(definitiondb PackageDatabase, db Packag
required = requiredDef required = requiredDef
} else { } else {
var ALO, priorityConstraints, priorityALO []bf.Formula var ALO []bf.Formula // , priorityConstraints, priorityALO []bf.Formula
// Try to prio best match // Try to prio best match
// Force the solver to consider first our candidate (if does exists). // Force the solver to consider first our candidate (if does exists).
// Then builds ALO and AMO for the requires. // Then builds ALO and AMO for the requires.
c, candidateErr := definitiondb.FindPackageCandidate(requiredDef) // c, candidateErr := definitiondb.FindPackageCandidate(requiredDef)
var C bf.Formula // var C bf.Formula
if candidateErr == nil { // if candidateErr == nil {
// We have a desired candidate, try to look a solution with that included first // // We have a desired candidate, try to look a solution with that included first
for _, o := range packages { // for _, o := range packages {
encodedB, err := o.Encode(db) // encodedB, err := o.Encode(db)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
B := bf.Var(encodedB) // B := bf.Var(encodedB)
if !o.Matches(c) { // if !o.Matches(c) {
priorityConstraints = append(priorityConstraints, bf.Not(B)) // priorityConstraints = append(priorityConstraints, bf.Not(B))
priorityALO = append(priorityALO, B) // priorityALO = append(priorityALO, B)
} // }
} // }
encodedC, err := c.Encode(db) // encodedC, err := c.Encode(db)
if err != nil { // if err != nil {
return nil, err // return nil, err
} // }
C = bf.Var(encodedC) // C = bf.Var(encodedC)
// Or the Candidate is true, or all the others might be not true // // Or the Candidate is true, or all the others might be not true
// This forces the CDCL sat implementation to look first at a solution with C=true // // This forces the CDCL sat implementation to look first at a solution with C=true
formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.And(C, bf.Or(priorityConstraints...)), bf.And(bf.Not(C), bf.Or(priorityALO...))))) // //formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.And(C, bf.Or(priorityConstraints...)), bf.And(bf.Not(C), bf.Or(priorityALO...)))))
} // formulas = append(formulas, bf.Or(C, bf.Or(priorityConstraints...)))
// }
// AMO - At most one // AMO/ALO - At most/least one
for _, o := range packages { for _, o := range packages {
encodedB, err := o.Encode(db) encodedB, err := o.Encode(db)
if err != nil { if err != nil {

View File

@@ -415,6 +415,7 @@ var _ = Describe("Decoder", func() {
orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint()) orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint())
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(len(orderW) > 0).To(BeTrue())
Expect(orderW[0].Package.GetName()).To(Equal("X")) Expect(orderW[0].Package.GetName()).To(Equal("X"))
Expect(orderW[1].Package.GetName()).To(Equal("Y")) Expect(orderW[1].Package.GetName()).To(Equal("Y"))
Expect(orderW[2].Package.GetName()).To(Equal("Z")) Expect(orderW[2].Package.GetName()).To(Equal("Z"))

View File

@@ -550,6 +550,13 @@ func (s *Parallel) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAsse
// Upgrade compute upgrades of the package against the world definition. // Upgrade compute upgrades of the package against the world definition.
// It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade // It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade
func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) { func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
return s.upgrade(s.DefinitionDatabase, s.InstalledDatabase, checkconflicts, full)
}
// Upgrade compute upgrades of the package against the world definition.
// It accepts two boolean indicating if it has to check for conflicts or try to attempt a full upgrade
func (s *Parallel) upgrade(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase, checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
// First get candidates that needs to be upgraded.. // First get candidates that needs to be upgraded..
@@ -557,7 +564,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
toInstall := pkg.Packages{} toInstall := pkg.Packages{}
// we do this in memory so we take into account of provides // we do this in memory so we take into account of provides
universe, err := s.DefinitionDatabase.Copy() universe, err := defDB.Copy()
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "Could not copy def db") return nil, nil, errors.Wrap(err, "Could not copy def db")
} }
@@ -595,7 +602,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
} }
}() }()
for _, p := range s.InstalledDatabase.World() { for _, p := range installDB.World() {
all <- p all <- p
} }
@@ -604,7 +611,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss
close(results) close(results)
wg2.Wait() wg2.Wait()
s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: installedcopy, DefinitionDatabase: s.DefinitionDatabase, ParallelDatabase: pkg.NewInMemoryDatabase(false)} s2 := &Parallel{Concurrency: s.Concurrency, InstalledDatabase: installedcopy, DefinitionDatabase: defDB, ParallelDatabase: pkg.NewInMemoryDatabase(false)}
s2.SetResolver(s.Resolver) s2.SetResolver(s.Resolver)
if !full { if !full {
ass := PackagesAssertions{} ass := PackagesAssertions{}
@@ -855,6 +862,32 @@ func (s *Parallel) Install(c pkg.Packages) (PackagesAssertions, error) {
} }
return ass, nil return ass, nil
} }
assertions, err := s.Solve()
return s.Solve() if err != nil {
return nil, err
}
return s.upgradeAssertions(assertions)
}
func (s *Parallel) upgradeAssertions(assertions PackagesAssertions) (PackagesAssertions, error) {
systemAfterInstall := pkg.NewInMemoryDatabase(false)
for _, p := range assertions {
if p.Value {
systemAfterInstall.CreatePackage(p.Package)
}
}
_, assertions, err := s.upgrade(s.DefinitionDatabase, systemAfterInstall, false, false)
if err != nil {
return nil, err
}
// for _, u := range toUninstall {
// systemAfterInstall.RemovePackage()
// }
return assertions, nil
} }

View File

@@ -37,6 +37,8 @@ const (
type PackageSolver interface { type PackageSolver interface {
SetDefinitionDatabase(pkg.PackageDatabase) SetDefinitionDatabase(pkg.PackageDatabase)
Install(p pkg.Packages) (PackagesAssertions, error) Install(p pkg.Packages) (PackagesAssertions, error)
RelaxedInstall(p pkg.Packages) (PackagesAssertions, error)
Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error) Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error)
ConflictsWithInstalled(p pkg.Package) (bool, error) ConflictsWithInstalled(p pkg.Package) (bool, error)
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error) ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
@@ -51,6 +53,7 @@ type PackageSolver interface {
SetResolver(PackageResolver) SetResolver(PackageResolver)
Solve() (PackagesAssertions, error) Solve() (PackagesAssertions, error)
// BestInstall(c pkg.Packages) (PackagesAssertions, error)
} }
// Solver is the default solver for luet // Solver is the default solver for luet
@@ -493,34 +496,47 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert
return markedForRemoval, assertion, nil return markedForRemoval, assertion, nil
} }
func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) { func inPackage(list []pkg.Package, p pkg.Package) bool {
for _, l := range list {
// First get candidates that needs to be upgraded.. if l.AtomMatches(p) {
return true
}
}
return false
}
// Compute upgrade between packages if specified, or all if none is specified
func (s *Solver) computeUpgrade(pps ...pkg.Package) func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase) {
return func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase) {
toUninstall := pkg.Packages{} toUninstall := pkg.Packages{}
toInstall := pkg.Packages{} toInstall := pkg.Packages{}
// we do this in memory so we take into account of provides, and its faster // we do this in memory so we take into account of provides, and its faster
universe, err := s.DefinitionDatabase.Copy() universe, _ := defDB.Copy()
if err != nil {
return nil, nil, errors.Wrap(err, "failed creating db copy")
}
installedcopy := pkg.NewInMemoryDatabase(false) installedcopy := pkg.NewInMemoryDatabase(false)
for _, p := range s.InstalledDatabase.World() { for _, p := range installDB.World() {
installedcopy.CreatePackage(p) installedcopy.CreatePackage(p)
packages, err := universe.FindPackageVersions(p) packages, err := universe.FindPackageVersions(p)
if err == nil && len(packages) != 0 { if err == nil && len(packages) != 0 {
best := packages.Best(nil) best := packages.Best(nil)
if !best.Matches(p) { if !best.Matches(p) && len(pps) == 0 ||
len(pps) != 0 && inPackage(pps, p) {
toUninstall = append(toUninstall, p) toUninstall = append(toUninstall, p)
toInstall = append(toInstall, best) toInstall = append(toInstall, best)
} }
} }
} }
return toUninstall, toInstall, installedcopy
}
}
s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(false)) func (s *Solver) upgrade(fn func(defDB pkg.PackageDatabase, installDB pkg.PackageDatabase) (pkg.Packages, pkg.Packages, pkg.PackageDatabase), defDB pkg.PackageDatabase, installDB pkg.PackageDatabase, checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
toUninstall, toInstall, installedcopy := fn(defDB, installDB)
s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, defDB, pkg.NewInMemoryDatabase(false))
s2.SetResolver(s.Resolver) s2.SetResolver(s.Resolver)
if !full { if !full {
ass := PackagesAssertions{} ass := PackagesAssertions{}
@@ -541,15 +557,20 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser
} }
if len(toInstall) == 0 { if len(toInstall) == 0 {
return toUninstall, PackagesAssertions{}, nil ass := PackagesAssertions{}
for _, i := range installDB.World() {
ass = append(ass, PackageAssert{Package: i.(*pkg.DefaultPackage), Value: true})
}
return toUninstall, ass, nil
} }
assertions, err := s2.Install(toInstall.Unique()) assertions, err := s2.RelaxedInstall(toInstall.Unique())
return toUninstall, assertions, err return toUninstall, assertions, err
// To that tree, ask to install the versions that should be upgraded, and try to solve }
// Return the solution
func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) {
return s.upgrade(s.computeUpgrade(), s.DefinitionDatabase, s.InstalledDatabase, checkconflicts, full)
} }
// Uninstall takes a candidate package and return a list of packages that would be removed // Uninstall takes a candidate package and return a list of packages that would be removed
@@ -619,7 +640,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg
s2.SetResolver(s.Resolver) s2.SetResolver(s.Resolver)
// Get the requirements to install the candidate // Get the requirements to install the candidate
asserts, err := s2.Install(toRemove) asserts, err := s2.RelaxedInstall(toRemove)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -729,7 +750,7 @@ func (s *Solver) Solve() (PackagesAssertions, error) {
// Install given a list of packages, returns package assertions to indicate the packages that must be installed in the system in order // Install given a list of packages, returns package assertions to indicate the packages that must be installed in the system in order
// to statisfy all the constraints // to statisfy all the constraints
func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) { func (s *Solver) RelaxedInstall(c pkg.Packages) (PackagesAssertions, error) {
coll, err := s.getList(s.DefinitionDatabase, c) coll, err := s.getList(s.DefinitionDatabase, c)
if err != nil { if err != nil {
@@ -749,6 +770,59 @@ func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
} }
return ass, nil return ass, nil
} }
assertions, err := s.Solve()
return s.Solve() if err != nil {
return nil, err
}
return assertions, nil
}
// Install returns the assertions necessary in order to install the packages in
// a system.
// It calculates the best result possible, trying to maximize new packages.
func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) {
assertions, err := s.RelaxedInstall(c)
if err != nil {
return nil, err
}
systemAfterInstall := pkg.NewInMemoryDatabase(false)
toUpgrade := pkg.Packages{}
for _, p := range c {
if p.GetVersion() == ">=0" || p.GetVersion() == ">0" {
toUpgrade = append(toUpgrade, p)
}
}
for _, p := range assertions {
if p.Value {
systemAfterInstall.CreatePackage(p.Package)
if !inPackage(c, p.Package) {
toUpgrade = append(toUpgrade, p.Package)
}
}
}
if len(toUpgrade) == 0 {
return assertions, nil
}
// do partial upgrade based on input.
// IF there is no version specified in the input, or >=0 is specified,
// then compute upgrade for those
_, newassertions, err := s.upgrade(s.computeUpgrade(toUpgrade...), s.DefinitionDatabase, systemAfterInstall, false, false)
if err != nil {
// TODO: Emit warning.
// We were not able to compute upgrades (maybe for some pinned packages, or a conflict)
// so we return the relaxed result
return assertions, nil
}
// Protect if we return no assertion at all
if len(newassertions) == 0 && len(assertions) > 0 {
return assertions, nil
}
return newassertions, nil
} }

View File

@@ -16,6 +16,8 @@
package solver_test package solver_test
import ( import (
"fmt"
pkg "github.com/mudler/luet/pkg/package" pkg "github.com/mudler/luet/pkg/package"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
@@ -36,6 +38,143 @@ var _ = Describe("Solver", func() {
dbDefinitions = pkg.NewInMemoryDatabase(false) dbDefinitions = pkg.NewInMemoryDatabase(false)
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db) s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
}) })
Context("Select of best available package", func() {
It("picks the best versions available for each package, excluding the ones manually specified while installing", func() {
B1 := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B2 := pkg.NewPackage("B", "1.2", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B3 := pkg.NewPackage("B", "1.3", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B4 := pkg.NewPackage("B", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
A1 := pkg.NewPackage("A", "1.1", []*pkg.DefaultPackage{
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
A2 := pkg.NewPackage("A", "1.2", []*pkg.DefaultPackage{
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "1.0", []*pkg.DefaultPackage{
pkg.NewPackage("A", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
C := pkg.NewPackage("C", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A1, A2, B1, B2, B3, B4, C, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err := s.(*Solver).Install([]pkg.Package{D})
fmt.Println(solution)
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(8))
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err = s.(*Solver).Install([]pkg.Package{D, B2})
fmt.Println(solution)
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(8))
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
})
It("picks the best available excluding those manually input. In this case we the input is a selector >=0", func() {
B1 := pkg.NewPackage("B", "1.1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B2 := pkg.NewPackage("B", "1.2", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B3 := pkg.NewPackage("B", "1.3", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
B4 := pkg.NewPackage("B", "1.4", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
A1 := pkg.NewPackage("A", "1.1", []*pkg.DefaultPackage{
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
A2 := pkg.NewPackage("A", "1.2", []*pkg.DefaultPackage{
pkg.NewPackage("B", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
D := pkg.NewPackage("D", "1.0", []*pkg.DefaultPackage{
pkg.NewPackage("A", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}),
}, []*pkg.DefaultPackage{})
C := pkg.NewPackage("C", "1", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})
for _, p := range []pkg.Package{A1, A2, B1, B2, B3, B4, C, D} {
_, err := dbDefinitions.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
for _, p := range []pkg.Package{C} {
_, err := dbInstalled.CreatePackage(p)
Expect(err).ToNot(HaveOccurred())
}
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err := s.(*Solver).Install([]pkg.Package{pkg.NewPackage("D", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{})})
fmt.Println(solution)
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(8))
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
s = NewSolver(Options{Type: SingleCoreSimple}, dbInstalled, dbDefinitions, db)
solution, err = s.(*Solver).Install([]pkg.Package{pkg.NewPackage("D", ">=0", []*pkg.DefaultPackage{}, []*pkg.DefaultPackage{}), B2})
fmt.Println(solution)
Expect(err).ToNot(HaveOccurred())
Expect(len(solution)).To(Equal(8))
// Expect(solution).To(ContainElement(PackageAssert{Package: B, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: C, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: D, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: B2, Value: true}))
Expect(solution).To(ContainElement(PackageAssert{Package: A1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B1, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B4, Value: false}))
Expect(solution).To(ContainElement(PackageAssert{Package: B3, Value: false}))
})
})
Context("Simple set", func() { Context("Simple set", func() {
It("Solves correctly if the selected package has no requirements or conflicts and we have nothing installed yet", func() { It("Solves correctly if the selected package has no requirements or conflicts and we have nothing installed yet", func() {