require 'rbconfig'

# use Debian-specific Gemfile
ENV['BUNDLE_GEMFILE'] = 'debian/ruby-tests.gemfile'

# generic class for running tests which require a service
class TestService
  require 'tmpdir'
  require 'socket'

  DATADIR = Dir.mktmpdir('moneta')
  @@port = nil

  def initialize
    (1025..3600).to_a.shuffle.each do |port|
      begin
        s = TCPSocket.new '127.0.0.1', port
        s.close
      rescue Errno::ECONNREFUSED
        @@port = port
        break
      end
    end

    unless @@port
      $stderr.puts "Unable to find a free TCP port to start the service."
      exit 1
    end
  end

  def teardown
    if File.exist? DATADIR
      FileUtils.remove_entry_secure DATADIR
    end
  end

end

# prepare mysql and run tests
class TestMysql < TestService
  def initialize
    super

    started = system(File.expand_path('../setup-mysql.sh', __FILE__),
                     @@port.to_s, DATADIR, 'start')

    unless started
      $stderr.puts "Unable to start the test MySQL server."
      exit 1
    end

    at_exit do
      self.teardown
    end
  end

  def run
    ENV['MYSQL_SOCKET'] = File.join(DATADIR, 'mysql.sock')
    result = system("bundle exec rspec -t mysql -- spec/moneta")
    self.teardown
    return result
  end

  def teardown
    system File.expand_path('../setup-mysql.sh', __FILE__),
      @@port.to_s, DATADIR, 'stop'
    super
  end
end

# prepare postgresql and run tests
class TestPostgres < TestService
  def initialize
    super

    started = system(File.expand_path('../setup-pgsql.sh', __FILE__),
                     DATADIR, @@port.to_s)

    unless started
      $stderr.puts "Unable to start the test PostgreSQL server."
      exit 1
    end

    at_exit do
      self.teardown
    end
  end

  def run
    ENV['PGUSER'] = 'moneta'
    ENV['PGPORT'] = @@port.to_s
    result = system("bundle exec rspec -t postgres -- spec/moneta")
    self.teardown
    return result
  end

  def teardown
    system("pg_ctl stop -D #{DATADIR} -m immediate")
    super
  end
end

# run most tests which do not require service setup/teardown
class TestParallel
  def run
    include_tests = []
    include_tests += Dir.glob('spec/moneta/*_spec.rb')
    include_tests += Dir.glob('spec/moneta/adapters/*/').reject do |f|
      # bindings missing in Debian
      f.match(%r{(cassandra|couch|daybreak|fog|gdbm|hbase|leveldb|lmdb|localmemcache|memcached|riak|sdbm|tdb|tokyotyrant)/$}) or
        # needs external database to run
        f.match(%r{(activerecord|activesupportcache|mongo|redis|sequel)/$}) or
        # specific exclusions below
        f.match(%r{(memory|client)/$})
    end
    include_tests += Dir.glob('spec/moneta/adapters/client/*_spec.rb').reject do |f|
      # too flaky on ci.debian.net
      f.match(%r{standard_client_tcp})
    end
    include_tests += Dir.glob('spec/moneta/adapters/memory/*_spec.rb').reject do |f|
      # ruby snappy binding missing
      f.match(%r{with_snappy_compress})
    end
    include_tests += Dir.glob('spec/moneta/proxies/*/').reject do |f|
      # missing gems fog/aws and gdbm gems in Debian
      f.match(%r{(shared|weak_create|weak_each_key|weak_increment)/$}) or
        # exclusions below
        f.match(%r{transformer/$})
    end
    include_tests += Dir.glob('spec/moneta/proxies/transformer/*_spec.rb').reject do |f|
      # missing bindings in Debian
      f.match(%r{(bencode|bzip2|city[0-9]+|lz4|lzma|lzo|php|snappy|tnet)_spec}) or
        # the yaml specs here are broken currently w/ Ruby 3.1
        f.match(%r{yaml_spec})
    end
    include_tests += Dir.glob('test/*/').reject do |f|
      # not packaged in Debian
      f.match(%r{action_dispatch/$})
    end
    # ruby-rack-cache needs updating
    #include_tests += Dir.glob('spec/rack/*_spec.rb')
    ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
    return system("#{ruby} script/parallel-tests -- #{include_tests.join(' ')}")
  end
end

class TestSqlite
  def run
    return system("bundle exec rspec -t sqlite -- spec/moneta")
  end
end

rc = 0

# TODO: re-enable mysql and postgresql tests
#
for test in [TestParallel, TestSqlite] do
  t = test.new
  unless t.run
    rc = 1
  end
end

exit rc
