molinillo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|