Dependency resolution engine with tests.

This commit is contained in:
Andrey Smirnov
2014-01-09 13:12:32 +04:00
parent 5dbb771ba8
commit e37fe33203
3 changed files with 138 additions and 3 deletions
+57 -3
View File
@@ -65,11 +65,13 @@ const (
// VerifyDependencies looks for missing dependencies in package list.
//
// Analysis would be peformed for each architecture
// Analysis would be peformed for each architecture, in specified sources
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageIndexedList) ([]Dependency, error) {
missing := make([]Dependency, 0, 100)
missing := make([]Dependency, 0, 128)
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packages {
if !p.MatchesArchitecture(arch) {
continue
@@ -83,8 +85,17 @@ func (l *PackageList) VerifyDependencies(options int, architectures []string, so
dep.Architecture = arch
if sources.Search(dep) != nil {
hash := dep.Hash()
_, ok := cache[hash]
if ok {
continue
}
if sources.Search(dep) == nil {
missing = append(missing, dep)
cache[hash] = false
} else {
cache[hash] = true
}
}
}
@@ -154,6 +165,49 @@ func (l *PackageIndexedList) Append(pl *PackageList) {
// Search searches package index for specified package
func (l *PackageIndexedList) Search(dep Dependency) *Package {
if dep.Relation == VersionDontCare {
for _, p := range l.providesList[dep.Pkg] {
if p.MatchesArchitecture(dep.Architecture) {
return p
}
}
}
i := sort.Search(len(l.packages), func(j int) bool { return l.packages[j].Name >= dep.Pkg })
for i < len(l.packages) && l.packages[i].Name == dep.Pkg {
p := l.packages[i]
if p.MatchesArchitecture(dep.Architecture) {
if dep.Relation == VersionDontCare {
return p
}
r := CompareVersions(p.Version, dep.Version)
switch dep.Relation {
case VersionEqual:
if r == 0 {
return p
}
case VersionLess:
if r < 0 {
return p
}
case VersionGreater:
if r > 0 {
return p
}
case VersionLessOrEqual:
if r <= 0 {
return p
}
case VersionGreaterOrEqual:
if r >= 0 {
return p
}
}
}
i++
}
return nil
}
+76
View File
@@ -119,3 +119,79 @@ func (s *PackageListSuite) TestPackageRefListForeach(c *C) {
c.Check(err, Equals, e)
}
type PackageIndexedListSuite struct {
packages []*Package
pl *PackageList
list *PackageIndexedList
}
var _ = Suite(&PackageIndexedListSuite{})
func (s *PackageIndexedListSuite) SetUpTest(c *C) {
s.pl = NewPackageList()
s.packages = []*Package{
&Package{Name: "lib", Version: "1.0", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"mail-agent"}},
&Package{Name: "dpkg", Version: "1.7", Architecture: "i386"},
&Package{Name: "data", Version: "1.1~bp1", Architecture: "all", PreDepends: []string{"dpkg (>= 1.6)"}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "mailer", Version: "3.5.8", Architecture: "i386", Provides: "mail-agent"},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "amd64", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "app", Version: "1.1~bp1", Architecture: "arm", PreDepends: []string{"dpkg (>= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "app", Version: "1.0", Architecture: "s390", PreDepends: []string{"dpkg >= 1.6)"}, Depends: []string{"lib (>> 0.9)", "data (>= 1.0)"}},
&Package{Name: "aa", Version: "2.0-1", Architecture: "i386", PreDepends: []string{"dpkg (>= 1.6)"}},
&Package{Name: "dpkg", Version: "1.6.1-3", Architecture: "amd64"},
}
for _, p := range s.packages {
s.pl.Add(p)
}
s.list = NewPackageIndexedList()
s.list.Append(s.pl)
s.list.PrepareIndex()
}
func (s *PackageIndexedListSuite) TestIndex(c *C) {
c.Check(len(s.list.providesList), Equals, 1)
c.Check(len(s.list.providesList["mail-agent"]), Equals, 1)
c.Check(s.list.packages[0], Equals, s.packages[8])
}
func (s *PackageIndexedListSuite) TestSearch(c *C) {
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "mail-agent"}), Equals, s.packages[4])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "puppy"}), IsNil)
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionEqual, Version: "1.1~bp2"}), IsNil)
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLess, Version: "1.1~~"}), IsNil)
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionLessOrEqual, Version: "1.1~~"}), IsNil)
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.0"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreater, Version: "1.2"}), IsNil)
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.1~bp1"}), Equals, s.packages[3])
c.Check(s.list.Search(Dependency{Architecture: "i386", Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.2"}), IsNil)
}
func (s *PackageIndexedListSuite) TestVerifyDependencies(c *C) {
missing, err := s.pl.VerifyDependencies(0, []string{"i386"}, s.list)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{})
missing, err = s.pl.VerifyDependencies(0, []string{"i386", "amd64"}, s.list)
c.Check(err, IsNil)
c.Check(missing, DeepEquals, []Dependency{Dependency{Pkg: "lib", Relation: VersionGreater, Version: "0.9", Architecture: "amd64"}})
_, err = s.pl.VerifyDependencies(0, []string{"i386", "amd64", "s390"}, s.list)
c.Check(err, ErrorMatches, "unable to process package app-1.0_s390:.*")
}
+5
View File
@@ -188,6 +188,11 @@ type Dependency struct {
Architecture string
}
// Hash calculates some predefined unique ID of Dependency
func (d *Dependency) Hash() string {
return fmt.Sprintf("%s:%s:%d:%s", d.Architecture, d.Pkg, d.Relation, d.Version)
}
// parseDependency parses dependency in format "pkg (>= 1.35)" into parts
func parseDependency(dep string) (d Dependency, err error) {
if !strings.HasSuffix(dep, ")") {