diff --git a/cmd/install.go b/cmd/install.go index 0792494e..7c9e747b 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -104,6 +104,7 @@ To force install a package: PreserveSystemEssentialData: true, DownloadOnly: downloadOnly, Ask: !yes, + Relaxed: relax, }) inst.Repositories(repos) @@ -125,6 +126,8 @@ func init() { installCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") installCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") 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("force", false, "Skip errors and keep going (potentially harmful)") installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)") diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go index 3f9dbea7..b930b8c5 100644 --- a/pkg/installer/installer.go +++ b/pkg/installer/installer.go @@ -50,6 +50,7 @@ type LuetInstallerOptions struct { SolverUpgrade, RemoveUnavailableOnUpgrade, UpgradeNewRevisions bool Ask bool DownloadOnly bool + Relaxed bool } type LuetInstaller struct { @@ -513,7 +514,7 @@ func (l *LuetInstaller) Install(cp pkg.Packages, s *System) error { return err } - if len(s.Database.World()) > 0 { + if len(s.Database.World()) > 0 && !l.Options.Relaxed { Info(":thinking: Checking for available upgrades") if err := l.checkAndUpgrade(syncedRepos, s); err != nil { 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 { solv := solver.NewResolver(solver.Options{Type: l.Options.SolverOptions.Implementation, Concurrency: l.Options.Concurrency}, s.Database, allRepos, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver()) - solution, err = solv.Install(p) + + if l.Options.Relaxed { + solution, err = solv.RelaxedInstall(p) + } else { + solution, err = solv.Install(p) + } /// TODO: PackageAssertions needs to be a map[fingerprint]pack so lookup is in O(1) if err != nil && !o.Force { return toInstall, p, solution, allRepos, errors.Wrap(err, "Failed solving solution for package") diff --git a/pkg/package/package.go b/pkg/package/package.go index 6985fcf9..d7d80171 100644 --- a/pkg/package/package.go +++ b/pkg/package/package.go @@ -72,6 +72,7 @@ type Package interface { SetVersion(string) RequiresContains(PackageDatabase, Package) (bool, error) Matches(m Package) bool + AtomMatches(m Package) bool BumpBuildVersion() error AddUse(use string) @@ -536,6 +537,13 @@ func (p *DefaultPackage) Matches(m Package) bool { 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 { marked := p.Clone() marked.SetName("@@" + marked.GetName()) @@ -795,37 +803,38 @@ func (pack *DefaultPackage) buildFormula(definitiondb PackageDatabase, db Packag required = requiredDef } else { - var ALO, priorityConstraints, priorityALO []bf.Formula + var ALO []bf.Formula // , priorityConstraints, priorityALO []bf.Formula // Try to prio best match // Force the solver to consider first our candidate (if does exists). // Then builds ALO and AMO for the requires. - c, candidateErr := definitiondb.FindPackageCandidate(requiredDef) - var C bf.Formula - if candidateErr == nil { - // We have a desired candidate, try to look a solution with that included first - for _, o := range packages { - encodedB, err := o.Encode(db) - if err != nil { - return nil, err - } - B := bf.Var(encodedB) - if !o.Matches(c) { - priorityConstraints = append(priorityConstraints, bf.Not(B)) - priorityALO = append(priorityALO, B) - } - } - encodedC, err := c.Encode(db) - if err != nil { - return nil, err - } - C = bf.Var(encodedC) - // 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 - formulas = append(formulas, bf.Or(bf.Not(A), bf.Or(bf.And(C, bf.Or(priorityConstraints...)), bf.And(bf.Not(C), bf.Or(priorityALO...))))) - } + // c, candidateErr := definitiondb.FindPackageCandidate(requiredDef) + // var C bf.Formula + // if candidateErr == nil { + // // We have a desired candidate, try to look a solution with that included first + // for _, o := range packages { + // encodedB, err := o.Encode(db) + // if err != nil { + // return nil, err + // } + // B := bf.Var(encodedB) + // if !o.Matches(c) { + // priorityConstraints = append(priorityConstraints, bf.Not(B)) + // priorityALO = append(priorityALO, B) + // } + // } + // encodedC, err := c.Encode(db) + // if err != nil { + // return nil, err + // } + // C = bf.Var(encodedC) + // // 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 + // //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 { encodedB, err := o.Encode(db) if err != nil { diff --git a/pkg/solver/decoder_test.go b/pkg/solver/decoder_test.go index 204d598f..f7e114cf 100644 --- a/pkg/solver/decoder_test.go +++ b/pkg/solver/decoder_test.go @@ -415,6 +415,7 @@ var _ = Describe("Decoder", func() { orderW, err := solution.Order(dbDefinitions, W.GetFingerPrint()) Expect(err).ToNot(HaveOccurred()) + Expect(len(orderW) > 0).To(BeTrue()) Expect(orderW[0].Package.GetName()).To(Equal("X")) Expect(orderW[1].Package.GetName()).To(Equal("Y")) Expect(orderW[2].Package.GetName()).To(Equal("Z")) diff --git a/pkg/solver/parallel.go b/pkg/solver/parallel.go index 358de7a6..565d353e 100644 --- a/pkg/solver/parallel.go +++ b/pkg/solver/parallel.go @@ -550,6 +550,13 @@ func (s *Parallel) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAsse // 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(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.. @@ -557,7 +564,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss toInstall := pkg.Packages{} // we do this in memory so we take into account of provides - universe, err := s.DefinitionDatabase.Copy() + universe, err := defDB.Copy() if err != nil { 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 } @@ -604,7 +611,7 @@ func (s *Parallel) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAss close(results) 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) if !full { ass := PackagesAssertions{} @@ -855,6 +862,32 @@ func (s *Parallel) Install(c pkg.Packages) (PackagesAssertions, error) { } return ass, nil } + assertions, err := s.Solve() + if err != nil { + return nil, err + } - return s.Solve() + 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 } diff --git a/pkg/solver/solver.go b/pkg/solver/solver.go index 399c4211..8cf1a9f4 100644 --- a/pkg/solver/solver.go +++ b/pkg/solver/solver.go @@ -37,6 +37,8 @@ const ( type PackageSolver interface { SetDefinitionDatabase(pkg.PackageDatabase) Install(p pkg.Packages) (PackagesAssertions, error) + RelaxedInstall(p pkg.Packages) (PackagesAssertions, error) + Uninstall(checkconflicts, full bool, candidate ...pkg.Package) (pkg.Packages, error) ConflictsWithInstalled(p pkg.Package) (bool, error) ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error) @@ -51,6 +53,7 @@ type PackageSolver interface { SetResolver(PackageResolver) Solve() (PackagesAssertions, error) + // BestInstall(c pkg.Packages) (PackagesAssertions, error) } // Solver is the default solver for luet @@ -493,34 +496,47 @@ func (s *Solver) UpgradeUniverse(dropremoved bool) (pkg.Packages, PackagesAssert return markedForRemoval, assertion, nil } -func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAssertions, error) { - - // First get candidates that needs to be upgraded.. - - toUninstall := pkg.Packages{} - toInstall := pkg.Packages{} - - // we do this in memory so we take into account of provides, and its faster - universe, err := s.DefinitionDatabase.Copy() - if err != nil { - return nil, nil, errors.Wrap(err, "failed creating db copy") - } - - installedcopy := pkg.NewInMemoryDatabase(false) - - for _, p := range s.InstalledDatabase.World() { - installedcopy.CreatePackage(p) - packages, err := universe.FindPackageVersions(p) - if err == nil && len(packages) != 0 { - best := packages.Best(nil) - if !best.Matches(p) { - toUninstall = append(toUninstall, p) - toInstall = append(toInstall, best) - } +func inPackage(list []pkg.Package, p pkg.Package) bool { + for _, l := range list { + if l.AtomMatches(p) { + return true } } + return false +} - s2 := NewSolver(Options{Type: SingleCoreSimple}, installedcopy, s.DefinitionDatabase, pkg.NewInMemoryDatabase(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{} + toInstall := pkg.Packages{} + + // we do this in memory so we take into account of provides, and its faster + universe, _ := defDB.Copy() + + installedcopy := pkg.NewInMemoryDatabase(false) + + for _, p := range installDB.World() { + installedcopy.CreatePackage(p) + packages, err := universe.FindPackageVersions(p) + if err == nil && len(packages) != 0 { + best := packages.Best(nil) + if !best.Matches(p) && len(pps) == 0 || + len(pps) != 0 && inPackage(pps, p) { + toUninstall = append(toUninstall, p) + toInstall = append(toInstall, best) + } + } + } + return toUninstall, toInstall, installedcopy + } +} + +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) if !full { ass := PackagesAssertions{} @@ -541,15 +557,20 @@ func (s *Solver) Upgrade(checkconflicts, full bool) (pkg.Packages, PackagesAsser } 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 - // 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 @@ -619,7 +640,7 @@ func (s *Solver) Uninstall(checkconflicts, full bool, packs ...pkg.Package) (pkg s2.SetResolver(s.Resolver) // Get the requirements to install the candidate - asserts, err := s2.Install(toRemove) + asserts, err := s2.RelaxedInstall(toRemove) if err != nil { 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 // 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) if err != nil { @@ -749,6 +770,59 @@ func (s *Solver) Install(c pkg.Packages) (PackagesAssertions, error) { } return ass, nil } + assertions, err := s.Solve() + if err != nil { + return nil, err + } - return s.Solve() + 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 } diff --git a/pkg/solver/solver_test.go b/pkg/solver/solver_test.go index 96ee2bc4..cec95be2 100644 --- a/pkg/solver/solver_test.go +++ b/pkg/solver/solver_test.go @@ -16,6 +16,8 @@ package solver_test import ( + "fmt" + pkg "github.com/mudler/luet/pkg/package" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -36,6 +38,143 @@ var _ = Describe("Solver", func() { dbDefinitions = pkg.NewInMemoryDatabase(false) 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() { It("Solves correctly if the selected package has no requirements or conflicts and we have nothing installed yet", func() {