#!/usr/bin/env ruby

#
# Copyright (c) 2019-2023 Felipe Contreras
#
# This script runs the tests for all versions of hg.
#
# You can run it without arguments, in which case it runs the tests for all
# versions in `versions.txt`.
#
# Or you can specify a single version manually:
#
#   ./check-versions 6.3
#

require 'fileutils'
require 'tmpdir'

$tests = %w[main.t bidi.t hg-git.t]
$workdir = "#{Dir.home}/.cache/git-remote-hg"
$builddir = Dir.mktmpdir("git-remote-hg-build-")
$testoutdir = Dir.mktmpdir("git-remote-hg-tests-")

at_exit {
  FileUtils.remove_entry($builddir)
  FileUtils.remove_entry($testoutdir)
}

QUIET, LOW, HIGH = (1..3).to_a
$verbosity = LOW

# Util {{{1

def section(text)
  puts [nil, text, '=' * text.size]
end

def title(text)
  puts [nil, text, '-' * text.size] unless $verbosity < HIGH
end

def run_cmd(cmd, fatal: true)
  puts cmd.join(' ') unless $verbosity < HIGH
  result = system(*cmd)
  unless result or not fatal
    STDERR.puts "Failed to run command '%s'" % cmd.join(' ')
    exit -1
  end
  result
end

def check_version(a, b)
  return true if a == '@'
  a = a.split('.').map(&:to_i)
  b = b.split('.').map(&:to_i)
  (a <=> b) >= 0
end

# Hg {{{1

class Hg

  def initialize
    @url = 'https://www.mercurial-scm.org/repo/hg'
  end

  def dir
    "#{$workdir}/hg"
  end

  def clone
    run_cmd %w[hg clone -q] + [@url, dir]
  end

  def checkout(version)
    Dir.chdir(dir) do
      run_cmd %w[hg update --clean -q] << version
      checkout_fix(version)
    end
  end

  def build
    Dir.chdir(dir) do
      targets = %w[build_py build_ext].map { |e| [e, '--build-lib', "#{$builddir}/python"] }
      run_cmd %w[python setup.py --quiet] + targets.flatten
    end
  end

  def checkout_fix(version)
    FileUtils.cp('hg', "#{$builddir}/bin/")

    return if check_version(version, '4.3')

    if run_cmd %W[hg import -q --no-commit #{__dir__}/hg_setup_hack_2.4.patch], fatal: false
      File.write('.hg_force_version', "%s\n" % version)
    else
      File.write('mercurial/__version__.py', "version = \"%s\"\n" % version)
    end
  end

end

# Functions {{{1

def setup
  dirs = %w[bin python]
  FileUtils.mkdir_p(dirs.map { |e| "#{$builddir}/#{e}" })
  FileUtils.mkdir_p($workdir)

  return if File.exist?($hg.dir)

  if $verbosity < HIGH
    puts "Cloning hg"
  else
    title "Cloning hg"
  end
  $hg.clone
end

def test_env(paths: nil)
  old = ENV.to_h
  paths.each do |id, path|
    name = id.to_s
    ENV[name] = "#{path}:#{ENV[name]}"
  end
  r = yield
  ENV.replace(old)
  return r
end

def run_tests(tests)
  title "Running tests"

  Dir.chdir("#{__dir__}/../test") do
    case $verbosity
    when QUIET
      tests_opt = tests.join(' ')
      cmd = "prove -q #{tests_opt} :: -i"
    when LOW
      tests_opt = "T='%s'" % tests.join(' ')
      cmd = "make -j1 #{tests_opt}"
    else
      tests_opt = "T='%s'" % tests.join(' ')
      cmd = "TEST_OPTS='-v -i' make -j1 #{tests_opt}"
    end
    system(cmd)
  end
end

def check(version)
  section version

  title "Checking out hg #{version}"
  $hg.checkout(version)

  title "Building hg"
  $hg.build

  paths = {
    PATH: "#{$builddir}/bin",
    PYTHONPATH: "#{$builddir}/python",
  }

  test_env(paths: paths) do
    ENV['SHARNESS_TEST_OUTPUT_DIRECTORY'] = $testoutdir
    run_tests($tests)
  end
end

$hg = Hg.new()

# Main {{{1

setup

$checks = []

$version = ARGV.first
$checks = File.readlines(__dir__ + '/versions.txt', chomp: true)
$results = File.open(__dir__ + '/results.txt', 'w')

if $version
  $verbosity = HIGH

  exit check($version) ? 0 : 1
else
  $verbosity = QUIET

  failures = 0

  $checks.each do |version|
    result = check(version)
    failures += 1 unless result
    $results.puts '%s # %s' % [version, result ? 'OK' : 'FAIL']
  end

  exit 1 unless failures == 0
end
