molinillo 0.5.7 → 0.6.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 +5 -5
- data/ARCHITECTURE.md +102 -0
- data/CHANGELOG.md +352 -0
- data/lib/molinillo.rb +2 -0
- data/lib/molinillo/compatibility.rb +26 -0
- data/lib/molinillo/delegates/resolution_state.rb +7 -0
- data/lib/molinillo/delegates/specification_provider.rb +1 -0
- data/lib/molinillo/dependency_graph.rb +3 -2
- data/lib/molinillo/dependency_graph/action.rb +1 -0
- data/lib/molinillo/dependency_graph/add_edge_no_circular.rb +1 -0
- data/lib/molinillo/dependency_graph/add_vertex.rb +1 -0
- data/lib/molinillo/dependency_graph/delete_edge.rb +1 -0
- data/lib/molinillo/dependency_graph/detach_vertex_named.rb +1 -0
- data/lib/molinillo/dependency_graph/log.rb +1 -0
- data/lib/molinillo/dependency_graph/set_payload.rb +1 -0
- data/lib/molinillo/dependency_graph/tag.rb +1 -0
- data/lib/molinillo/dependency_graph/vertex.rb +3 -2
- data/lib/molinillo/errors.rb +69 -6
- data/lib/molinillo/gem_metadata.rb +2 -1
- data/lib/molinillo/modules/specification_provider.rb +1 -0
- data/lib/molinillo/modules/ui.rb +3 -1
- data/lib/molinillo/resolution.rb +495 -145
- data/lib/molinillo/resolver.rb +1 -0
- data/lib/molinillo/state.rb +8 -4
- metadata +8 -30
- data/spec/dependency_graph/log_spec.rb +0 -29
- data/spec/dependency_graph_spec.rb +0 -74
- data/spec/errors_spec.rb +0 -26
- data/spec/fuzz_spec.rb +0 -94
- data/spec/resolver_integration_specs/index_from_rubygems.rb +0 -76
- data/spec/resolver_spec.rb +0 -235
- data/spec/spec_helper.rb +0 -44
- data/spec/spec_helper/equal_dependency_graph.rb +0 -16
- data/spec/spec_helper/index.rb +0 -186
- data/spec/spec_helper/naive_resolver.rb +0 -48
- data/spec/spec_helper/specification.rb +0 -28
- data/spec/spec_helper/ui.rb +0 -14
- data/spec/state_spec.rb +0 -30
    
        data/spec/spec_helper.rb
    DELETED
    
    | @@ -1,44 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'bundler/setup'
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            # Set up coverage analysis
         | 
| 5 | 
            -
            #-----------------------------------------------------------------------------#
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            if (ENV['CI'] || ENV['GENERATE_COVERAGE']) && RUBY_VERSION >= '2.0.0' && Bundler.current_ruby.mri?
         | 
| 8 | 
            -
              require 'simplecov'
         | 
| 9 | 
            -
              require 'codeclimate-test-reporter'
         | 
| 10 | 
            -
             | 
| 11 | 
            -
              if ENV['CI']
         | 
| 12 | 
            -
                SimpleCov.formatter = CodeClimate::TestReporter::Formatter
         | 
| 13 | 
            -
              elsif ENV['GENERATE_COVERAGE']
         | 
| 14 | 
            -
                SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
         | 
| 15 | 
            -
              end
         | 
| 16 | 
            -
              SimpleCov.start do
         | 
| 17 | 
            -
                add_filter '/vendor/'
         | 
| 18 | 
            -
                add_filter '/lib/molinillo/modules/'
         | 
| 19 | 
            -
              end
         | 
| 20 | 
            -
              CodeClimate::TestReporter.start
         | 
| 21 | 
            -
            end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
            # Set up
         | 
| 24 | 
            -
            #-----------------------------------------------------------------------------#
         | 
| 25 | 
            -
             | 
| 26 | 
            -
            require 'pathname'
         | 
| 27 | 
            -
            require 'json'
         | 
| 28 | 
            -
            ROOT = Pathname.new(File.expand_path('../../', __FILE__))
         | 
| 29 | 
            -
            $LOAD_PATH.unshift((ROOT + 'lib').to_s)
         | 
| 30 | 
            -
            $LOAD_PATH.unshift((ROOT + 'spec').to_s)
         | 
| 31 | 
            -
             | 
| 32 | 
            -
            require 'molinillo'
         | 
| 33 | 
            -
             | 
| 34 | 
            -
            require 'spec_helper/index'
         | 
| 35 | 
            -
            require 'spec_helper/specification'
         | 
| 36 | 
            -
            require 'spec_helper/ui'
         | 
| 37 | 
            -
            require 'spec_helper/equal_dependency_graph'
         | 
| 38 | 
            -
             | 
| 39 | 
            -
            RSpec.configure do |config|
         | 
| 40 | 
            -
              # Enable flags like --only-failures and --next-failure
         | 
| 41 | 
            -
              config.example_status_persistence_file_path = '.rspec_status'
         | 
| 42 | 
            -
              config.filter_run :focus => true
         | 
| 43 | 
            -
              config.run_all_when_everything_filtered = true
         | 
| 44 | 
            -
            end
         | 
| @@ -1,16 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            RSpec::Matchers.define :equal_dependency_graph do |expected|
         | 
| 3 | 
            -
              diffable
         | 
| 4 | 
            -
              attr_reader :actual, :expected
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              match do |actual|
         | 
| 7 | 
            -
                eql = actual == expected
         | 
| 8 | 
            -
                @expected = expected.to_dot(:edge_label => proc { |e| e.destination.payload.version })
         | 
| 9 | 
            -
                @actual = actual.to_dot(:edge_label => proc { |e| e.destination.payload.version })
         | 
| 10 | 
            -
                eql
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              failure_message do
         | 
| 14 | 
            -
                'Expected the two dependency graphs to be equal'
         | 
| 15 | 
            -
              end
         | 
| 16 | 
            -
            end
         | 
    
        data/spec/spec_helper/index.rb
    DELETED
    
    | @@ -1,186 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            module Molinillo
         | 
| 3 | 
            -
              FIXTURE_DIR = Pathname.new('spec/resolver_integration_specs')
         | 
| 4 | 
            -
              FIXTURE_INDEX_DIR = FIXTURE_DIR + 'index'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              class TestIndex
         | 
| 7 | 
            -
                attr_accessor :specs
         | 
| 8 | 
            -
                include SpecificationProvider
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                def self.from_fixture(fixture_name)
         | 
| 11 | 
            -
                  File.open(FIXTURE_INDEX_DIR + (fixture_name + '.json'), 'r') do |fixture|
         | 
| 12 | 
            -
                    sorted_specs = JSON.load(fixture).reduce(Hash.new([])) do |specs_by_name, (name, versions)|
         | 
| 13 | 
            -
                      specs_by_name.tap do |specs|
         | 
| 14 | 
            -
                        specs[name] = versions.map { |s| TestSpecification.new s }.sort_by(&:version)
         | 
| 15 | 
            -
                      end
         | 
| 16 | 
            -
                    end
         | 
| 17 | 
            -
                    return new(sorted_specs)
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                def initialize(specs_by_name)
         | 
| 22 | 
            -
                  self.specs = specs_by_name
         | 
| 23 | 
            -
                end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                def requirement_satisfied_by?(requirement, _activated, spec)
         | 
| 26 | 
            -
                  case requirement
         | 
| 27 | 
            -
                  when TestSpecification
         | 
| 28 | 
            -
                    requirement.version == spec.version
         | 
| 29 | 
            -
                  when Gem::Dependency
         | 
| 30 | 
            -
                    requirement.requirement.satisfied_by?(spec.version)
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                def search_for(dependency)
         | 
| 35 | 
            -
                  @search_for ||= {}
         | 
| 36 | 
            -
                  @search_for[dependency] ||= begin
         | 
| 37 | 
            -
                    prerelease = dependency_prerelease?(dependency)
         | 
| 38 | 
            -
                    Array(specs[dependency.name]).select do |spec|
         | 
| 39 | 
            -
                      (prerelease ? true : !spec.version.prerelease?) &&
         | 
| 40 | 
            -
                        dependency.requirement.satisfied_by?(spec.version)
         | 
| 41 | 
            -
                    end
         | 
| 42 | 
            -
                  end
         | 
| 43 | 
            -
                  @search_for[dependency].dup
         | 
| 44 | 
            -
                end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def name_for(dependency)
         | 
| 47 | 
            -
                  dependency.name
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                def dependencies_for(dependency)
         | 
| 51 | 
            -
                  dependency.dependencies
         | 
| 52 | 
            -
                end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                def sort_dependencies(dependencies, activated, conflicts)
         | 
| 55 | 
            -
                  dependencies.sort_by do |d|
         | 
| 56 | 
            -
                    [
         | 
| 57 | 
            -
                      activated.vertex_named(d.name).payload ? 0 : 1,
         | 
| 58 | 
            -
                      dependency_prerelease?(d) ? 0 : 1,
         | 
| 59 | 
            -
                      conflicts[d.name] ? 0 : 1,
         | 
| 60 | 
            -
                      activated.vertex_named(d.name).payload ? 0 : search_for(d).count,
         | 
| 61 | 
            -
                    ]
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
                end
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                private
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                def dependency_prerelease?(dependency)
         | 
| 68 | 
            -
                  dependency.prerelease?
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
              end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
              class BundlerIndex < TestIndex
         | 
| 73 | 
            -
                # Some bugs we want to write a regression test for only occurs when
         | 
| 74 | 
            -
                # Molinillo processes dependencies in a specific order for the given
         | 
| 75 | 
            -
                # index and demands. This sorting logic ensures we hit the repro case
         | 
| 76 | 
            -
                def sort_dependencies(dependencies, activated, conflicts)
         | 
| 77 | 
            -
                  dependencies.sort_by do |dependency|
         | 
| 78 | 
            -
                    name = name_for(dependency)
         | 
| 79 | 
            -
                    [
         | 
| 80 | 
            -
                      activated.vertex_named(name).payload ? 0 : 1,
         | 
| 81 | 
            -
                      amount_constrained(dependency),
         | 
| 82 | 
            -
                      conflicts[name] ? 0 : 1,
         | 
| 83 | 
            -
                      activated.vertex_named(name).payload ? 0 : search_for(dependency).count,
         | 
| 84 | 
            -
                    ]
         | 
| 85 | 
            -
                  end
         | 
| 86 | 
            -
                end
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                def amount_constrained(dependency)
         | 
| 89 | 
            -
                  @amount_constrained ||= {}
         | 
| 90 | 
            -
                  @amount_constrained[dependency.name] ||= begin
         | 
| 91 | 
            -
                    all = specs[dependency.name].size
         | 
| 92 | 
            -
                    if all <= 1
         | 
| 93 | 
            -
                      all - all_leq_one_penalty
         | 
| 94 | 
            -
                    else
         | 
| 95 | 
            -
                      search = search_for(dependency).size
         | 
| 96 | 
            -
                      search - all
         | 
| 97 | 
            -
                    end
         | 
| 98 | 
            -
                  end
         | 
| 99 | 
            -
                end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                def all_leq_one_penalty
         | 
| 102 | 
            -
                  1_000_000
         | 
| 103 | 
            -
                end
         | 
| 104 | 
            -
              end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
              class BundlerSingleAllNoPenaltyIndex < BundlerIndex
         | 
| 107 | 
            -
                def all_leq_one_penalty
         | 
| 108 | 
            -
                  0
         | 
| 109 | 
            -
                end
         | 
| 110 | 
            -
              end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
              class ReverseBundlerIndex < BundlerIndex
         | 
| 113 | 
            -
                def sort_dependencies(*)
         | 
| 114 | 
            -
                  super.reverse
         | 
| 115 | 
            -
                end
         | 
| 116 | 
            -
              end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
              class RandomSortIndex < TestIndex
         | 
| 119 | 
            -
                def sort_dependencies(dependencies, _activated, _conflicts)
         | 
| 120 | 
            -
                  dependencies.shuffle
         | 
| 121 | 
            -
                end
         | 
| 122 | 
            -
              end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
              class CocoaPodsIndex < TestIndex
         | 
| 125 | 
            -
                def sort_dependencies(dependencies, activated, conflicts)
         | 
| 126 | 
            -
                  dependencies.sort_by do |d|
         | 
| 127 | 
            -
                    [
         | 
| 128 | 
            -
                      activated.vertex_named(d.name).payload ? 0 : 1,
         | 
| 129 | 
            -
                      dependency_prerelease?(d) ? 0 : 1,
         | 
| 130 | 
            -
                      conflicts[d.name] ? 0 : 1,
         | 
| 131 | 
            -
                      search_for(d).count,
         | 
| 132 | 
            -
                    ]
         | 
| 133 | 
            -
                  end
         | 
| 134 | 
            -
                end
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                def requirement_satisfied_by?(requirement, activated, spec)
         | 
| 137 | 
            -
                  requirement = case requirement
         | 
| 138 | 
            -
                                when TestSpecification
         | 
| 139 | 
            -
                                  Gem::Dependency.new(requirement.name, requirement.version)
         | 
| 140 | 
            -
                                when Gem::Dependency
         | 
| 141 | 
            -
                                  requirement
         | 
| 142 | 
            -
                                end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                  existing_vertices = activated.vertices.values.select do |v|
         | 
| 145 | 
            -
                    v.name.split('/').first == requirement.name.split('/').first
         | 
| 146 | 
            -
                  end
         | 
| 147 | 
            -
                  existing = existing_vertices.map(&:payload).compact.first
         | 
| 148 | 
            -
                  if existing
         | 
| 149 | 
            -
                    existing.version == spec.version && requirement.requirement.satisfied_by?(spec.version)
         | 
| 150 | 
            -
                  else
         | 
| 151 | 
            -
                    requirement.requirement.satisfied_by? spec.version
         | 
| 152 | 
            -
                  end
         | 
| 153 | 
            -
                end
         | 
| 154 | 
            -
              end
         | 
| 155 | 
            -
             | 
| 156 | 
            -
              class BerkshelfIndex < TestIndex
         | 
| 157 | 
            -
                # The bug we want to write a regression test for only occurs when
         | 
| 158 | 
            -
                # Molinillo processes dependencies in a specific order for the given
         | 
| 159 | 
            -
                # index and demands. This sorting logic ensures we hit the repro case
         | 
| 160 | 
            -
                # when using the index file "swap_child_with_successors"
         | 
| 161 | 
            -
                def sort_dependencies(dependencies, activated, conflicts)
         | 
| 162 | 
            -
                  dependencies.sort_by do |dependency|
         | 
| 163 | 
            -
                    name = name_for(dependency)
         | 
| 164 | 
            -
                    [
         | 
| 165 | 
            -
                      activated.vertex_named(name).payload ? 0 : 1,
         | 
| 166 | 
            -
                      conflicts[name] ? 0 : 1,
         | 
| 167 | 
            -
                      activated.vertex_named(name).payload ? 0 : versions_of(name),
         | 
| 168 | 
            -
                    ]
         | 
| 169 | 
            -
                  end
         | 
| 170 | 
            -
                end
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                def versions_of(dependency_name)
         | 
| 173 | 
            -
                  Array(specs[dependency_name]).count
         | 
| 174 | 
            -
                end
         | 
| 175 | 
            -
              end
         | 
| 176 | 
            -
             | 
| 177 | 
            -
              INDICES = [
         | 
| 178 | 
            -
                TestIndex,
         | 
| 179 | 
            -
                BundlerIndex,
         | 
| 180 | 
            -
                ReverseBundlerIndex,
         | 
| 181 | 
            -
                BundlerSingleAllNoPenaltyIndex,
         | 
| 182 | 
            -
                # RandomSortIndex, this isn't yet always passing
         | 
| 183 | 
            -
                CocoaPodsIndex,
         | 
| 184 | 
            -
                BerkshelfIndex,
         | 
| 185 | 
            -
              ].freeze
         | 
| 186 | 
            -
            end
         | 
| @@ -1,48 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 4 | 
            -
            # rubocop:disable Metrics/LineLength
         | 
| 5 | 
            -
            # rubocop:disable Style/Semicolon
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            module Molinillo
         | 
| 8 | 
            -
              class NaiveResolver
         | 
| 9 | 
            -
                def self.resolve(index, dependencies)
         | 
| 10 | 
            -
                  activated = Molinillo::DependencyGraph.new
         | 
| 11 | 
            -
                  level = 0
         | 
| 12 | 
            -
                  dependencies.each { |d| activated.add_child_vertex(d.name, nil, [nil], d) }
         | 
| 13 | 
            -
                  activated.tag(level)
         | 
| 14 | 
            -
                  possibilities_by_level = {}
         | 
| 15 | 
            -
                  loop do
         | 
| 16 | 
            -
                    vertex = activated.find { |a| !a.requirements.empty? && a.payload.nil? }
         | 
| 17 | 
            -
                    break unless vertex
         | 
| 18 | 
            -
                    possibilities = possibilities_by_level[level] ||= index.search_for(Gem::Dependency.new(vertex.name, '>= 0.0.0-a'))
         | 
| 19 | 
            -
                    possibilities.select! do |spec|
         | 
| 20 | 
            -
                      vertex.requirements.all? { |r| r.requirement.satisfied_by?(spec.version) && (!spec.version.prerelease? || index.send(:dependency_prerelease?, r)) } &&
         | 
| 21 | 
            -
                        spec.dependencies.all? { |d| v = activated.vertex_named(d.name); !v || !v.payload || d.satisfied_by?(v.payload.version) }
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
                    warn "level = #{level} possibilities = #{possibilities.map(&:to_s)} requirements = #{vertex.requirements.map(&:to_s)}"
         | 
| 24 | 
            -
                    if spec = possibilities.pop
         | 
| 25 | 
            -
                      warn "trying #{spec}"
         | 
| 26 | 
            -
                      activated.set_payload(vertex.name, spec)
         | 
| 27 | 
            -
                      spec.dependencies.each do |d|
         | 
| 28 | 
            -
                        activated.add_child_vertex(d.name, nil, [spec.name], d)
         | 
| 29 | 
            -
                      end
         | 
| 30 | 
            -
                      level += 1
         | 
| 31 | 
            -
                      warn "tagging level #{level}"
         | 
| 32 | 
            -
                      activated.tag(level)
         | 
| 33 | 
            -
                      next
         | 
| 34 | 
            -
                    end
         | 
| 35 | 
            -
                    level = possibilities_by_level.reverse_each.find(proc { [-1, nil] }) { |_l, p| !p.empty? }.first
         | 
| 36 | 
            -
                    warn "going back to level #{level}"
         | 
| 37 | 
            -
                    possibilities_by_level.reject! { |l, _| l > level }
         | 
| 38 | 
            -
                    return nil if level < 0
         | 
| 39 | 
            -
                    activated.rewind_to(level)
         | 
| 40 | 
            -
                    activated.tag(level)
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                  activated
         | 
| 44 | 
            -
                end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def self.warn(*); end
         | 
| 47 | 
            -
              end
         | 
| 48 | 
            -
            end
         | 
| @@ -1,28 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            module Molinillo
         | 
| 3 | 
            -
              class TestSpecification
         | 
| 4 | 
            -
                attr_accessor :name, :version, :dependencies
         | 
| 5 | 
            -
                def initialize(hash)
         | 
| 6 | 
            -
                  self.name = hash['name']
         | 
| 7 | 
            -
                  self.version = Gem::Version.new(hash['version'])
         | 
| 8 | 
            -
                  self.dependencies = hash.fetch('dependencies') { Hash.new }.map do |(name, requirement)|
         | 
| 9 | 
            -
                    requirements = requirement.split(',').map(&:chomp)
         | 
| 10 | 
            -
                    Gem::Dependency.new(name, requirements)
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
                end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                def ==(other)
         | 
| 15 | 
            -
                  name == other.name &&
         | 
| 16 | 
            -
                    version == other.version &&
         | 
| 17 | 
            -
                    dependencies == other.dependencies
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                def to_s
         | 
| 21 | 
            -
                  "#{name} (#{version})"
         | 
| 22 | 
            -
                end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                def inspect
         | 
| 25 | 
            -
                  "#<#{self.class} name=#{name} version=#{version} dependencies=[#{dependencies.join(', ')}]>"
         | 
| 26 | 
            -
                end
         | 
| 27 | 
            -
              end
         | 
| 28 | 
            -
            end
         | 
    
        data/spec/spec_helper/ui.rb
    DELETED
    
    
    
        data/spec/state_spec.rb
    DELETED
    
    | @@ -1,30 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require File.expand_path('../spec_helper', __FILE__)
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            module Molinillo
         | 
| 5 | 
            -
              describe ResolutionState do
         | 
| 6 | 
            -
                describe DependencyState do
         | 
| 7 | 
            -
                  before do
         | 
| 8 | 
            -
                    @state = described_class.new(
         | 
| 9 | 
            -
                      'name',
         | 
| 10 | 
            -
                      %w(requirement1 requirement2 requirement3),
         | 
| 11 | 
            -
                      DependencyGraph.new,
         | 
| 12 | 
            -
                      'requirement',
         | 
| 13 | 
            -
                      %w(possibility1 possibility),
         | 
| 14 | 
            -
                      0,
         | 
| 15 | 
            -
                      {}
         | 
| 16 | 
            -
                    )
         | 
| 17 | 
            -
                  end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                  it 'pops a possibility state' do
         | 
| 20 | 
            -
                    possibility_state = @state.pop_possibility_state
         | 
| 21 | 
            -
                    %w(name requirements activated requirement conflicts).each do |attr|
         | 
| 22 | 
            -
                      expect(possibility_state.send(attr)).to eq(@state.send(attr))
         | 
| 23 | 
            -
                    end
         | 
| 24 | 
            -
                    expect(possibility_state).to be_a(PossibilityState)
         | 
| 25 | 
            -
                    expect(possibility_state.depth).to eq(@state.depth + 1)
         | 
| 26 | 
            -
                    expect(possibility_state.possibilities).to eq(%w(possibility))
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
            end
         |