molinillo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +9 -0
- data/README.md +45 -0
- data/lib/molinillo.rb +5 -0
- data/lib/molinillo/dependency_graph.rb +243 -0
- data/lib/molinillo/errors.rb +69 -0
- data/lib/molinillo/gem_metadata.rb +3 -0
- data/lib/molinillo/modules/specification_provider.rb +90 -0
- data/lib/molinillo/modules/ui.rb +63 -0
- data/lib/molinillo/resolution.rb +323 -0
- data/lib/molinillo/resolver.rb +43 -0
- data/lib/molinillo/state.rb +43 -0
- data/spec/dependency_graph_spec.rb +79 -0
- data/spec/resolver_spec.rb +113 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/spec_helper/index.rb +59 -0
- data/spec/spec_helper/specification.rb +22 -0
- data/spec/spec_helper/ui.rb +9 -0
- data/spec/state_spec.rb +33 -0
- metadata +98 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Molinillo
|
5
|
+
describe DependencyGraph do
|
6
|
+
describe 'in general' do
|
7
|
+
before do
|
8
|
+
@graph = DependencyGraph.new
|
9
|
+
@root = @graph.add_root_vertex('Root', 'Root')
|
10
|
+
@root2 = @graph.add_root_vertex('Root2', 'Root2')
|
11
|
+
@child = @graph.add_child_vertex('Child', 'Child', %w(Root), 'Child')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns root vertices by name' do
|
15
|
+
@graph.root_vertex_named('Root').
|
16
|
+
should.equal @root
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns vertices by name' do
|
20
|
+
@graph.vertex_named('Root').
|
21
|
+
should.equal @root
|
22
|
+
@graph.vertex_named('Child').
|
23
|
+
should.equal @child
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns nil for non-existant root vertices' do
|
27
|
+
@graph.root_vertex_named('missing').
|
28
|
+
should.equal nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns nil for non-existant vertices' do
|
32
|
+
@graph.vertex_named('missing').
|
33
|
+
should.equal nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'detaching a node' do
|
38
|
+
before do
|
39
|
+
@graph = DependencyGraph.new
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'detaches a root vertex without successors' do
|
43
|
+
root = @graph.add_root_vertex('root', 'root')
|
44
|
+
@graph.detach_vertex_named(root.name)
|
45
|
+
@graph.vertex_named(root.name).
|
46
|
+
should.equal nil
|
47
|
+
@graph.vertices.count.
|
48
|
+
should.equal 0
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'detaches a root vertex with successors' do
|
52
|
+
root = @graph.add_root_vertex('root', 'root')
|
53
|
+
child = @graph.add_child_vertex('child', 'child', %w(root), 'child')
|
54
|
+
@graph.detach_vertex_named(root.name)
|
55
|
+
@graph.vertex_named(root.name).
|
56
|
+
should.equal nil
|
57
|
+
@graph.vertex_named(child.name).
|
58
|
+
should.equal nil
|
59
|
+
@graph.vertices.count.
|
60
|
+
should.equal 0
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'detaches a root vertex with successors with other parents' do
|
64
|
+
root = @graph.add_root_vertex('root', 'root')
|
65
|
+
root2 = @graph.add_root_vertex('root2', 'root2')
|
66
|
+
child = @graph.add_child_vertex('child', 'child', %w(root root2), 'child')
|
67
|
+
@graph.detach_vertex_named(root.name)
|
68
|
+
@graph.vertex_named(root.name).
|
69
|
+
should.equal nil
|
70
|
+
@graph.vertex_named(child.name).
|
71
|
+
should.equal child
|
72
|
+
child.predecessors.
|
73
|
+
should.equal Set[root2]
|
74
|
+
@graph.vertices.count.
|
75
|
+
should.equal 2
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'json'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Molinillo
|
6
|
+
FIXTURE_DIR = Pathname.new('spec/resolver_integration_specs')
|
7
|
+
FIXTURE_INDEX_DIR = FIXTURE_DIR + 'index'
|
8
|
+
FIXTURE_CASE_DIR = FIXTURE_DIR + 'case'
|
9
|
+
|
10
|
+
class TestCase
|
11
|
+
require File.expand_path('../spec_helper/index', __FILE__)
|
12
|
+
require File.expand_path('../spec_helper/specification', __FILE__)
|
13
|
+
require File.expand_path('../spec_helper/ui', __FILE__)
|
14
|
+
|
15
|
+
attr_accessor :name, :requested, :base, :conflicts, :resolver, :result, :index
|
16
|
+
|
17
|
+
# rubocop:disable Metrics/MethodLength
|
18
|
+
def initialize(fixture_path)
|
19
|
+
File.open(fixture_path) do |fixture|
|
20
|
+
JSON.load(fixture).tap do |test_case|
|
21
|
+
self.name = test_case['name']
|
22
|
+
self.index = TestIndex.new(test_case['index'] || 'awesome')
|
23
|
+
self.requested = test_case['requested'].map do |(name, reqs)|
|
24
|
+
VersionKit::Dependency.new name, reqs.split(',').map(&:chomp)
|
25
|
+
end
|
26
|
+
add_dependencies_to_graph = lambda do |graph, parent, hash|
|
27
|
+
name = hash['name']
|
28
|
+
version = VersionKit::Version.new(hash['version'])
|
29
|
+
dependency = index.specs[name].find { |s| s.version == version }
|
30
|
+
node = if parent
|
31
|
+
graph.add_vertex(name, dependency).tap do |v|
|
32
|
+
graph.add_edge(parent, v, dependency)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
graph.add_root_vertex(name, dependency)
|
36
|
+
end
|
37
|
+
hash['dependencies'].each do |dep|
|
38
|
+
add_dependencies_to_graph.call(graph, node, dep)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
self.result = test_case['resolved'].reduce(DependencyGraph.new) do |graph, r|
|
42
|
+
graph.tap do |g|
|
43
|
+
add_dependencies_to_graph.call(g, nil, r)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
self.base = test_case['base'].reduce(DependencyGraph.new) do |graph, r|
|
47
|
+
graph.tap do |g|
|
48
|
+
add_dependencies_to_graph.call(g, nil, r)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
self.conflicts = test_case['conflicts'].to_set
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
self.resolver = Resolver.new(index, TestUI.new)
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics/MethodLength
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Resolver do
|
61
|
+
|
62
|
+
describe 'dependency resolution' do
|
63
|
+
Dir.glob(FIXTURE_CASE_DIR + '**/*.json').map do |fixture|
|
64
|
+
test_case = TestCase.new(fixture)
|
65
|
+
it test_case.name do
|
66
|
+
resolve = lambda { test_case.resolver.resolve(test_case.requested, test_case.base) }
|
67
|
+
|
68
|
+
if test_case.conflicts.any?
|
69
|
+
error = should.raise ResolverError do
|
70
|
+
resolve.call
|
71
|
+
end
|
72
|
+
|
73
|
+
names = case error
|
74
|
+
when CircularDependencyError
|
75
|
+
error.dependencies.map(&:name)
|
76
|
+
when VersionConflict
|
77
|
+
error.conflicts.keys
|
78
|
+
end.to_set
|
79
|
+
names.should.equal test_case.conflicts
|
80
|
+
else
|
81
|
+
result = resolve.call
|
82
|
+
|
83
|
+
pretty_dependencies = lambda do |dg|
|
84
|
+
dg.vertices.values.map { |v| "#{v.payload.name} (#{v.payload.version})" }.sort
|
85
|
+
end
|
86
|
+
pretty_dependencies.call(result).should.
|
87
|
+
equal pretty_dependencies.call(test_case.result)
|
88
|
+
|
89
|
+
result.should.equal test_case.result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'in general' do
|
96
|
+
before do
|
97
|
+
@resolver = Resolver.new(TestIndex.new('awesome'), TestUI.new)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'can resolve a list of 0 requirements' do
|
101
|
+
@resolver.resolve([], DependencyGraph.new).
|
102
|
+
should.equal DependencyGraph.new
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'includes the source of a user-specified unsatisfied dependency' do
|
106
|
+
should.raise VersionConflict do
|
107
|
+
@resolver.resolve([VersionKit::Dependency.new('missing', '3.0')], DependencyGraph.new)
|
108
|
+
end.message.should.match /required by `user-specified dependency`/
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
# Set up coverage analysis
|
3
|
+
#-----------------------------------------------------------------------------#
|
4
|
+
|
5
|
+
if (ENV['CI'] || ENV['GENERATE_COVERAGE']) && RUBY_VERSION >= '2.0.0'
|
6
|
+
require 'simplecov'
|
7
|
+
require 'codeclimate-test-reporter'
|
8
|
+
|
9
|
+
if ENV['CI']
|
10
|
+
SimpleCov.formatter = CodeClimate::TestReporter::Formatter
|
11
|
+
elsif ENV['GENERATE_COVERAGE']
|
12
|
+
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
13
|
+
end
|
14
|
+
SimpleCov.start do
|
15
|
+
add_filter '/vendor/'
|
16
|
+
add_filter '/lib/molinillo/modules/'
|
17
|
+
end
|
18
|
+
CodeClimate::TestReporter.start
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set up
|
22
|
+
#-----------------------------------------------------------------------------#
|
23
|
+
|
24
|
+
require 'pathname'
|
25
|
+
ROOT = Pathname.new(File.expand_path('../../', __FILE__))
|
26
|
+
$LOAD_PATH.unshift((ROOT + 'lib').to_s)
|
27
|
+
$LOAD_PATH.unshift((ROOT + 'spec').to_s)
|
28
|
+
|
29
|
+
require 'bundler/setup'
|
30
|
+
require 'bacon'
|
31
|
+
require 'mocha-on-bacon'
|
32
|
+
require 'pretty_bacon'
|
33
|
+
require 'version_kit'
|
34
|
+
require 'molinillo'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Molinillo
|
2
|
+
class TestIndex
|
3
|
+
attr_accessor :specs
|
4
|
+
include SpecificationProvider
|
5
|
+
|
6
|
+
def initialize(fixture_name)
|
7
|
+
File.open(FIXTURE_INDEX_DIR + (fixture_name + '.json'), 'r') do |fixture|
|
8
|
+
self.specs = JSON.load(fixture).reduce(Hash.new([])) do |specs_by_name, (name, versions)|
|
9
|
+
specs_by_name.tap do |specs|
|
10
|
+
specs[name] = versions.map { |s| TestSpecification.new s }.sort { |x, y| x.version <=> y.version }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def requirement_satisfied_by?(requirement, _activated, spec)
|
17
|
+
case requirement
|
18
|
+
when TestSpecification
|
19
|
+
VersionKit::Dependency.new(requirement.name, requirement.version).satisfied_by?(spec.version)
|
20
|
+
when VersionKit::Dependency
|
21
|
+
requirement.satisfied_by?(spec.version)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def search_for(dependency)
|
26
|
+
pre_release = dependency_pre_release?(dependency)
|
27
|
+
specs[dependency.name].reject do |spec|
|
28
|
+
pre_release ? false : spec.version.pre_release?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def name_for(dependency)
|
33
|
+
dependency.name
|
34
|
+
end
|
35
|
+
|
36
|
+
def dependencies_for(dependency)
|
37
|
+
dependency.dependencies
|
38
|
+
end
|
39
|
+
|
40
|
+
def sort_dependencies(dependencies, activated, conflicts)
|
41
|
+
dependencies.sort_by do |d|
|
42
|
+
[
|
43
|
+
activated.vertex_named(d.name).payload ? 0 : 1,
|
44
|
+
dependency_pre_release?(d) ? 0 : 1,
|
45
|
+
conflicts[d.name] ? 0 : 1,
|
46
|
+
specs[d.name].count,
|
47
|
+
]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def dependency_pre_release?(dependency)
|
54
|
+
dependency.requirement_list.requirements.any? do |r|
|
55
|
+
VersionKit::Version.new(r.reference_version).pre_release?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Molinillo
|
2
|
+
class TestSpecification
|
3
|
+
attr_accessor :name, :version, :dependencies
|
4
|
+
def initialize(hash)
|
5
|
+
self.name = hash['name']
|
6
|
+
self.version = VersionKit::Version.new(hash['version'])
|
7
|
+
self.dependencies = hash['dependencies'].map do |(name, requirement)|
|
8
|
+
VersionKit::Dependency.new(name, requirement.split(',').map(&:chomp))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
name == other.name &&
|
14
|
+
version == other.version &&
|
15
|
+
dependencies == other.dependencies
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{name} (#{version})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/state_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
module Molinillo
|
4
|
+
describe ResolutionState do
|
5
|
+
describe DependencyState do
|
6
|
+
before do
|
7
|
+
@state = DependencyState.new(
|
8
|
+
'name',
|
9
|
+
%w(requirement1 requirement2 requirement3),
|
10
|
+
DependencyGraph.new,
|
11
|
+
'requirement',
|
12
|
+
%w(possibility1 possibility),
|
13
|
+
0,
|
14
|
+
{}
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'pops a possibility state' do
|
19
|
+
possibility_state = @state.pop_possibility_state
|
20
|
+
%w(name requirements activated requirement conflicts).each do |attr|
|
21
|
+
possibility_state.send(attr).
|
22
|
+
should.equal @state.send(attr)
|
23
|
+
end
|
24
|
+
possibility_state.is_a?(PossibilityState).
|
25
|
+
should.be.true?
|
26
|
+
possibility_state.depth.
|
27
|
+
should.equal @state.depth + 1
|
28
|
+
possibility_state.possibilities.
|
29
|
+
should.equal %w(possibility)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: molinillo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Samuel E. Giddins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- segiddins@segiddins.me
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/molinillo/dependency_graph.rb
|
49
|
+
- lib/molinillo/errors.rb
|
50
|
+
- lib/molinillo/gem_metadata.rb
|
51
|
+
- lib/molinillo/modules/specification_provider.rb
|
52
|
+
- lib/molinillo/modules/ui.rb
|
53
|
+
- lib/molinillo/resolution.rb
|
54
|
+
- lib/molinillo/resolver.rb
|
55
|
+
- lib/molinillo/state.rb
|
56
|
+
- lib/molinillo.rb
|
57
|
+
- README.md
|
58
|
+
- LICENSE
|
59
|
+
- spec/dependency_graph_spec.rb
|
60
|
+
- spec/resolver_spec.rb
|
61
|
+
- spec/spec_helper/index.rb
|
62
|
+
- spec/spec_helper/specification.rb
|
63
|
+
- spec/spec_helper/ui.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
- spec/state_spec.rb
|
66
|
+
homepage: https://github.com/CocoaPods/Molinillo
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.0.14
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Provides support for dependency resolution
|
90
|
+
test_files:
|
91
|
+
- spec/dependency_graph_spec.rb
|
92
|
+
- spec/resolver_spec.rb
|
93
|
+
- spec/spec_helper/index.rb
|
94
|
+
- spec/spec_helper/specification.rb
|
95
|
+
- spec/spec_helper/ui.rb
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
- spec/state_spec.rb
|
98
|
+
has_rdoc:
|