<commit_before>class BundlerCase
  extend Forwardable

  def self.define(options={}, &block)
    c = BundlerCase.new(options)
    c.instance_eval(&block)
    c
  end

  attr_reader :out_dir, :repo_dir, :failures

  def initialize(options={})
    @reuse_out_dir = options[:reuse_out_dir]
    recreate_out_dir
    make_repo_dir

    # prolly should only support top-level step for non-step default
    # behavior OR nested ... but ... we'll see how this plays out
    @nested = []
    @step = Step.new(self)
  end

  def step(description=nil, &block)
    Step.new(self, description).tap { |c| c.instance_eval(&block) }.tap { |s| @nested << s }
  end

  def repeat_step(step)
    @nested << step
  end

  def copy_setup(src)
    Dir["#{src}/*"].each { |src_fn| FileUtils.cp src_fn, out_dir }
  end

  def test
    @failures = []
    steps = @nested.empty? ? Array(@step) : @nested
    steps.each do |s|
      if s.description
        puts '#' * s.description.length
        puts s.description
        puts '#' * s.description.length
      end
      @failures = s.test
      break unless @failures.empty?
      puts
    end
    @failures.empty?
  end

  def gem_filename
    File.join(@out_dir, 'Gemfile')
  end

  def lock_filename
    File.join(@out_dir, 'Gemfile.lock')
  end

  private

  def recreate_out_dir
    @out_dir = File.expand_path('../out', __dir__)
    FileUtils.remove_entry_secure(@out_dir) if File.exist?(@out_dir) unless @reuse_out_dir
    FileUtils.makedirs @out_dir
  end

  def make_repo_dir
    @repo_dir = File.join(out_dir, 'repo')
    FileUtils.makedirs @repo_dir

    gems_dir = File.join(@repo_dir, 'gems')
    FileUtils.makedirs gems_dir
  end

  class Step
    attr_reader :description

    def initialize(bundler_case, description=nil)
      @bundler_case = bundler_case
      @description = description
      @failures = []
      @expected_specs = []
      @expected_not_specs = []
      @cmd = 'bundle install --path .bundle'
      @procs = []
    end

    def given_gems(&block)
      @procs << -> {
        instance_eval(&block)
        Dir.chdir(@bundler_case.repo_dir) do
          system 'gem generate_index'
        end
      }
    end

    def given_gemfile(opts={}, &block)
      contents = block.call.outdent
      swap_in_fake_repo(contents)
      @procs << -> { GemfileFixture.new(@bundler_case, contents, opts).build }
    end

    def given_lockfile(opts={}, &block)
      contents = block.call.outdent
      swap_in_fake_repo(contents)
      @procs << -> { File.open(@bundler_case.lock_filename, 'w') { |f| f.print contents } }
    end

    def given_gemspec
      # TODO
    end

    def given_bundler_version(&block)
      @version = block.call
      @procs << -> {
        installed = `gem list bundler`.scan(/\Abundler \((.*)\)/).join.split(/, /)
        unless installed.include?(@version)
          puts "Installing bundler #{@version}..."
          `gem install bundler --version #{@version}`
        end
      }
    end

    def execute_bundler(&block)
      @cmd = block.call
    end

    def expect_locked(&block)
      @expected_specs.concat(block.call.map do |name, ver|
        if ver.nil? && name.split(/ /).length == 2
          Gem::Specification.new(*name.split(/ /))
        else
          Gem::Specification.new(name, ver)
        end
      end)
    end

    def expect_not_locked(&block)
      @expected_not_specs.concat(block.call.map do |name, ver|
        Gem::Specification.new(name, ver)
      end)
    end

    def test
      Bundler.with_clean_env do
        ENV['BUNDLE_GEMFILE'] = @bundler_case.gem_filename
        Dir.chdir(@bundler_case.out_dir) do
          @procs.map(&:call)
          _execute_bundler
        end
      end

      if @expected_specs.empty?
        []
      else
        lockfile = File.join(@bundler_case.out_dir, 'Gemfile.lock')
        parser = Bundler::LockfileParser.new(Bundler.read_file(lockfile))
        ExpectedSpecs.new.failures(@expected_specs, parser.specs)
      end
    end

    private

    def _execute_bundler
      cmd = @version ? @cmd.gsub(/(?<!\.)(bundle )/, "bundle _#{@version}_ ") : @cmd
      puts "=> #{cmd}"<commit_msg>Add bundler exit code to pass/fail<commit_after>class BundlerCase
  extend Forwardable

  def self.define(options={}, &block)
    c = BundlerCase.new(options)
    c.instance_eval(&block)
    c
  end

  attr_reader :out_dir, :repo_dir, :failures

  def initialize(options={})
    @reuse_out_dir = options[:reuse_out_dir]
    recreate_out_dir
    make_repo_dir

    # prolly should only support top-level step for non-step default
    # behavior OR nested ... but ... we'll see how this plays out
    @nested = []
    @step = Step.new(self)
  end

  def step(description=nil, &block)
    Step.new(self, description).tap { |c| c.instance_eval(&block) }.tap { |s| @nested << s }
  end

  def repeat_step(step)
    @nested << step
  end

  def copy_setup(src)
    Dir["#{src}/*"].each { |src_fn| FileUtils.cp src_fn, out_dir }
  end

  def test
    @failures = []
    steps = @nested.empty? ? Array(@step) : @nested
    steps.each do |s|
      if s.description
        puts '#' * s.description.length
        puts s.description
        puts '#' * s.description.length
      end
      @failures = s.test
      break unless @failures.empty?
      puts
    end
    @failures.empty?
  end

  def gem_filename
    File.join(@out_dir, 'Gemfile')
  end

  def lock_filename
    File.join(@out_dir, 'Gemfile.lock')
  end

  private

  def recreate_out_dir
    @out_dir = File.expand_path('../out', __dir__)
    FileUtils.remove_entry_secure(@out_dir) if File.exist?(@out_dir) unless @reuse_out_dir
    FileUtils.makedirs @out_dir
  end

  def make_repo_dir
    @repo_dir = File.join(out_dir, 'repo')
    FileUtils.makedirs @repo_dir

    gems_dir = File.join(@repo_dir, 'gems')
    FileUtils.makedirs gems_dir
  end

  class Step
    attr_reader :description

    def initialize(bundler_case, description=nil)
      @bundler_case = bundler_case
      @description = description
      @failures = []
      @expected_specs = []
      @expected_not_specs = []
      @cmd = 'bundle install --path .bundle'
      @procs = []
    end

    def given_gems(&block)
      @procs << -> {
        instance_eval(&block)
        Dir.chdir(@bundler_case.repo_dir) do
          system 'gem generate_index'
        end
      }
    end

    def given_gemfile(opts={}, &block)
      contents = block.call.outdent
      swap_in_fake_repo(contents)
      @procs << -> { GemfileFixture.new(@bundler_case, contents, opts).build }
    end

    def given_lockfile(opts={}, &block)
      contents = block.call.outdent
      swap_in_fake_repo(contents)
      @procs << -> { File.open(@bundler_case.lock_filename, 'w') { |f| f.print contents } }
    end

    def given_gemspec
      # TODO
    end

    def given_bundler_version(&block)
      @version = block.call
      @procs << -> {
        installed = `gem list bundler`.scan(/\Abundler \((.*)\)/).join.split(/, /)
        unless installed.include?(@version)
          puts "Installing bundler #{@version}..."
          `gem install bundler --version #{@version}`
        end
      }
    end

    def execute_bundler(&block)
      @cmd = block.call
    end

    def expect_locked(&block)
      @expected_specs.concat(block.call.map do |name, ver|
        if ver.nil? && name.split(/ /).length == 2
          Gem::Specification.new(*name.split(/ /))
        else
          Gem::Specification.new(name, ver)
        end
      end)
    end

    def expect_not_locked(&block)
      @expected_not_specs.concat(block.call.map do |name, ver|
        Gem::Specification.new(name, ver)
      end)
    end

    def test
      bundler_result = true

      Bundler.with_clean_env do
        ENV['BUNDLE_GEMFILE'] = @bundler_case.gem_filename
        Dir.chdir(@bundler_case.out_dir) do
          @procs.map(&:call)
          bundler_result = _execute_bundler
        end
      end

      test_failures = []

      unless @expected_specs.empty?
        lockfile = File.join(@bundler_case.out_dir, 'Gemfile.lock')
        parser = Bundler::LockfileParser.new(Bundler.read_file(lockfile))
        test_failures.concat(ExpectedSpecs.new.failures(@expected_specs, parser.specs))
      end

      unless bundler_result
        unless @expect_bundler_failure
          test_failures << 'Bundle command failed'
        end
      end

      test_failures
    end

    private

    def _execute_bundler
      cmd = @version ? @cmd.gsub(/(?<!\.)(bundle )/, "bundle _#{@version}_ ") : @cmd
      puts "=> #{cmd}"
{'fraction_non_alphanumeric': 0.12290764368488821, 'fraction_numerical': 0.00023410979749502515, 'mean_word_length': 2.4507269789983845, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3055838', 'n_tokens_mistral': 2921, 'n_tokens_neox': 2859, 'n_words': 590}