Skip to content

Commit

Permalink
WIP: resolver_spec
Browse files Browse the repository at this point in the history
  • Loading branch information
waj committed Jan 21, 2020
1 parent 836e060 commit c0a6302
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 27 deletions.
15 changes: 8 additions & 7 deletions spec/resolver_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module Molinillo
class TestCase
getter fixture : Fixture
getter name : String
@index : SpecificationProvider(Nil, Nil, Nil)?
@index : SpecificationProvider(Gem::Dependency, Nil)?
@requested : Array(Gem::Dependency)?
@result : DependencyGraph(TestSpecification?, TestSpecification?)?
@@all : Array(TestCase)?

Expand All @@ -24,10 +25,9 @@ module Molinillo
end

def requested
# @requested ||= @fixture['requested'].map do |(name, reqs)|
# Gem::Dependency.new name.delete("\x01"), reqs.split(',').map(&:chomp)
# end
@fixture.requested
@requested ||= @fixture.requested.map do |(name, reqs)|
Gem::Dependency.new name.delete("\x01"), reqs.split(',').map(&.chomp)
end
end

def add_dependencies_to_graph(graph, parent, hash, all_parents = Set(DependencyGraph::Vertex(TestSpecification?, TestSpecification?)).new)
Expand Down Expand Up @@ -56,7 +56,7 @@ module Molinillo
end

def base
DependencyGraph(Nil, Nil).new
DependencyGraph(Molinillo::Resolver::PosibilitySet(Gem::Dependency, Nil)?, Gem::Dependency).new
end

def self.all
Expand All @@ -65,14 +65,15 @@ module Molinillo

def resolve(index_class)
index = index_class.new(self.index.specs)
resolver = Resolver(Nil, Nil, Nil).new(index, TestUI.new)
resolver = Resolver(Gem::Dependency, Nil).new(index, TestUI.new)
resolver.resolve(requested, base)
end

def run(index_class)
it name do
# skip 'does not yet reliably pass' if test_case.ignore?(index_class)
if fixture.conflicts.any?
raise "tbd"
else
result = resolve(index_class)

Expand Down
9 changes: 9 additions & 0 deletions spec/spec_helper/gem.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Gem
class Dependency
property name : String
property requirements : Array(String)

def initialize(@name, @requirements)
end
end
end
2 changes: 1 addition & 1 deletion spec/spec_helper/index.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Molinillo

class TestIndex
getter specs : Hash(String, Array(TestSpecification))
include SpecificationProvider(Nil, Nil, Nil)
include SpecificationProvider(Gem::Dependency, Nil)

def self.from_fixture(fixture_name)
new(TestIndex.specs_from_fixture(fixture_name))
Expand Down
2 changes: 2 additions & 0 deletions spec/spec_helper/ui.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Molinillo
class TestUI
include UI

@output : IO?

def output
@output ||= if debug?
STDERR
Expand Down
27 changes: 27 additions & 0 deletions spec/state_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "./spec_helper"

module Molinillo
describe ResolutionState do
describe DependencyState do
it "pops a possibility state" do
state = DependencyState(Nil, String, String).new(
"name",
%w(requirement1 requirement2 requirement3),
DependencyGraph(Nil, String).new,
"requirement",
%w(possibility1 possibility),
0,
{} of String => Nil,
[] of Nil
)
possibility_state = state.pop_possibility_state
# %w(name requirements activated requirement conflicts).each do |attr|
# expect(possibility_state.send(attr)).to eq(state.send(attr))
# end
possibility_state.should be_a(PossibilityState(Nil, String, String))
possibility_state.depth.should eq(state.depth + 1)
possibility_state.possibilities.should eq(%w(possibility))
end
end
end
end
55 changes: 55 additions & 0 deletions src/molinillo/delegates/resolution_state.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Molinillo
# @!visibility private
module Delegates
# Delegates all {Molinillo::ResolutionState} methods to a `#state` property.
module ResolutionState(R, S)
# (see Molinillo::ResolutionState#name)
def name
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.name
end

# (see Molinillo::ResolutionState#requirements)
def requirements
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.requirements
end

# (see Molinillo::ResolutionState#activated)
def activated
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.activated
end

# (see Molinillo::ResolutionState#requirement)
def requirement
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.requirement
end

# (see Molinillo::ResolutionState#possibilities)
def possibilities
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.possibilities
end

# (see Molinillo::ResolutionState#depth)
def depth
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.depth
end

# (see Molinillo::ResolutionState#conflicts)
def conflicts
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.conflicts
end

# (see Molinillo::ResolutionState#unused_unwind_options)
def unused_unwind_options
current_state = state || Molinillo::ResolutionState(R, S).empty
current_state.unused_unwind_options
end
end
end
end
78 changes: 78 additions & 0 deletions src/molinillo/delegates/specification_provider.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
module Molinillo
module Delegates
# Delegates all {Molinillo::SpecificationProvider} methods to a
# `#specification_provider` property.
module SpecificationProvider
# (see Molinillo::SpecificationProvider#search_for)
def search_for(dependency)
with_no_such_dependency_error_handling do
specification_provider.search_for(dependency)
end
end

# (see Molinillo::SpecificationProvider#dependencies_for)
def dependencies_for(specification)
with_no_such_dependency_error_handling do
specification_provider.dependencies_for(specification)
end
end

# (see Molinillo::SpecificationProvider#requirement_satisfied_by?)
def requirement_satisfied_by?(requirement, activated, spec)
with_no_such_dependency_error_handling do
specification_provider.requirement_satisfied_by?(requirement, activated, spec)
end
end

# (see Molinillo::SpecificationProvider#name_for)
def name_for(dependency)
with_no_such_dependency_error_handling do
specification_provider.name_for(dependency)
end
end

# (see Molinillo::SpecificationProvider#name_for_explicit_dependency_source)
def name_for_explicit_dependency_source
with_no_such_dependency_error_handling do
specification_provider.name_for_explicit_dependency_source
end
end

# (see Molinillo::SpecificationProvider#name_for_locking_dependency_source)
def name_for_locking_dependency_source
with_no_such_dependency_error_handling do
specification_provider.name_for_locking_dependency_source
end
end

# (see Molinillo::SpecificationProvider#sort_dependencies)
def sort_dependencies(dependencies, activated, conflicts)
with_no_such_dependency_error_handling do
specification_provider.sort_dependencies(dependencies, activated, conflicts)
end
end

# (see Molinillo::SpecificationProvider#allow_missing?)
def allow_missing?(dependency)
with_no_such_dependency_error_handling do
specification_provider.allow_missing?(dependency)
end
end

# Ensures any raised {NoSuchDependencyError} has its
# {NoSuchDependencyError#required_by} set.
# @yield
private def with_no_such_dependency_error_handling
yield
rescue error # TODO : NoSuchDependencyError
if state
# TODO
# vertex = activated.vertex_named(name_for(error.dependency))
# error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
# error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
end
raise error
end
end
end
end
2 changes: 1 addition & 1 deletion src/molinillo/dependency_graph.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Molinillo::DependencyGraph(P, R)
# Tags the current state of the dependency as the given tag
# @param [Object] tag an opaque tag for the current state of the graph
# @return [Void]
def tag(tag : Reference)
def tag(tag : Symbol | Reference)
log.tag(self, tag)
end

Expand Down
5 changes: 3 additions & 2 deletions src/molinillo/dependency_graph/log.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Molinillo::DependencyGraph::Log(P, R)
@first_action : Action(P, R)?

def tag(graph, tag)
push_action(graph, Tag(P, R).new(tag.object_id))
push_action(graph, Tag(P, R).new(tag))
end

def add_vertex(graph, name : String, payload : P, root)
Expand Down Expand Up @@ -42,10 +42,11 @@ class Molinillo::DependencyGraph::Log(P, R)
end

def rewind_to(graph, tag)
tag_value = Tag::Value.new(tag)
loop do
action = pop!(graph)
raise "No tag #{tag.inspect} found" unless action
break if action.is_a?(Tag(P, R)) && action.tag == tag.object_id
break if action.is_a?(Tag(P, R)) && action.tag == tag_value
end
end

Expand Down
30 changes: 28 additions & 2 deletions src/molinillo/dependency_graph/tag.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,41 @@ require "./action"

class Molinillo::DependencyGraph
class Tag(P, R) < Action(P, R)
getter tag : UInt64
abstract struct Value
def self.new(value : Reference)
ReferenceValue.new(value)
end

def self.new(value)
OtherValue.new(value)
end
end

struct ReferenceValue < Value
@value : UInt64

def initialize(value : Reference)
@value = value.object_id
end
end

struct OtherValue < Value
@value : Symbol

def initialize(@value)
end
end

getter tag : Value

def up(graph)
end

def down(graph)
end

def initialize(@tag)
def initialize(tag)
@tag = Value.new(tag)
end
end
end
2 changes: 2 additions & 0 deletions src/molinillo/dependency_graph/vertex.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ class Molinillo::DependencyGraph::Vertex(P, R)
property root = false
property name : String
property payload : P
getter explicit_requirements : Array(R)
getter outgoing_edges : Array(Edge(P, R))
getter incoming_edges : Array(Edge(P, R))

def initialize(@name, @payload : P)
@explicit_requirements = Array(R).new
@outgoing_edges = Array(Edge(P, R)).new
@incoming_edges = Array(Edge(P, R)).new
end
Expand Down
8 changes: 4 additions & 4 deletions src/molinillo/modules/specification_provider.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Molinillo
#
# This module contains the methods that users of Molinillo must to implement,
# using knowledge of their own model classes.
module SpecificationProvider(P, R, S)
module SpecificationProvider(R, S)
# Search for the specifications that match the given dependency.
# The specifications in the returned array will be considered in reverse
# order, so the latest version ought to be last.
Expand Down Expand Up @@ -39,7 +39,7 @@ module Molinillo
# @param [Object] spec
# @return [Boolean] whether `requirement` is satisfied by `spec` in the
# context of the current `activated` dependency graph.
def requirement_satisfied_by?(requirement : R, activated : DependencyGraph(P, R), spec : S)
def requirement_satisfied_by?(requirement : R, activated : DependencyGraph, spec : S)
true
end

Expand Down Expand Up @@ -77,11 +77,11 @@ module Molinillo
# resolution process.
# @param [{String => Array<Conflict>}] conflicts
# @return [Array<Object>] a sorted copy of `dependencies`.
def sort_dependencies(dependencies : R, activated : DependencyGraph(P, R), conflicts)
def sort_dependencies(dependencies : Array(R), activated : DependencyGraph, conflicts)
dependencies.sort_by do |dependency|
name = name_for(dependency)
{
activated.vertex_named(name).payload ? 0 : 1,
activated.vertex_named!(name).payload ? 0 : 1,
conflicts[name] ? 0 : 1,
}
end
Expand Down
10 changes: 8 additions & 2 deletions src/molinillo/modules/ui.cr
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,20 @@ module Molinillo
end
end

@debug_mode : Bool?

# Whether or not debug messages should be printed.
# By default, whether or not the `MOLINILLO_DEBUG` environment variable is
# set.
#
# @return [Boolean]
def debug?
return @debug_mode if defined?(@debug_mode)
@debug_mode = ENV["MOLINILLO_DEBUG"]
debug_mode = @debug_mode
if debug_mode == nil
@debug_mode = ENV.has_key?("MOLINILLO_DEBUG")
else
debug_mode
end
end
end
end
Loading

0 comments on commit c0a6302

Please sign in to comment.