#!/usr/bin/env ruby
#
# Copyright (C) 2017 Harald Sitter <sitter@kde.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License or (at your option) version 3 or any later version
# accepted by the membership of KDE e.V. (or its successor approved
# by the membership of KDE e.V.), which shall act as a proxy
# defined in Section 14 of version 3 of the license.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

require 'fileutils'
require 'optparse'
require 'tmpdir'

STDOUT.sync = true # force immediate flushing without internal caching

class Isolator
  def mangle_env(tmpdir)
    ENV['HOME'] = tmpdir
    ENV.keys.each { |k| ENV.delete(k) if k.start_with?('XDG_') }
  end

  private

  def dbus_run(cmd)
    warn dbus_run_cmd(cmd)
    system(dbus_run_cmd(cmd))
  end

  def dbus_run_cmd(cmd)
    "dbus-run-session -- #{cmd}"
  end
end

# Isolates GUI via xephyr
class XephyrIsolator < Isolator
  def run(cmd)
    ephemeral('Xephyr -screen 1024x768x24+32 :666') do
      ENV['DISPLAY'] = ':666'
      dbus_run(cmd) || raise
    end
  end

  private

  def ephemeral(*args)
    pid = spawn(*args)
    yield
  ensure
    Process.kill('KILL', pid) if pid
    Process.waitpid2(pid) if pid
  end
end

# Isolates GUI via xvfb-run
class XvfbIsolator < Isolator
  def run(cmd)
    warn "xvfb-run -a --server-args=\"-screen 0 1024x768x24\" #{dbus_run_cmd(cmd)}"
    system("xvfb-run -a --server-args=\"-screen 0 1024x768x24\" #{dbus_run_cmd(cmd)}") || raise
  end
end

class NoIsolator < Isolator
  def mangle_env(*)
    # noop without isolation
  end

  def run(cmd)
    # No X11 and no DBus isolation.
    warn cmd.to_s
    system(cmd) || raise
  end
end

def new_isolator
  return NoIsolator.new if ENV['JENKINS_SERVER_COOKIE'] || ENV['NO_ISOLATOR']
  return XephyrIsolator.new if ENV['XEPHYR']
  XvfbIsolator.new
end

OptionParser.new do |opts|
  opts.banner = "Usage: #{$0} ARGS"

  opts.separator('')

  opts.on('--drkonqi PATH', 'Path to drkonqi bin to test.') do |v|
    ENV['DRKONQI_PATH'] = v
  end

  opts.on('--at-spi-bus-launcher PATH',
          'Path to --at-spi-bus-launcher bin to use for testing.') do |v|
    ENV['AT_SPI_BUS_LAUNCHER_PATH'] = v
  end

  opts.on('--at-spi-registryd PATH',
          'Path to registry bin to use for testing.') do |v|
    ENV['AT_SPI_REGISTRY_PATH'] = v
  end
end.parse!

ENV['DRKONQI_PATH'] ||= '/usr/lib/x86_64-linux-gnu/libexec/drkonqi'
ENV['AT_SPI_BUS_LAUNCHER_PATH'] ||= '/usr/lib/at-spi2-core/at-spi-bus-launcher'
ENV['AT_SPI_REGISTRY_PATH'] ||= '/usr/lib/at-spi2-core/at-spi2-registryd'
ENV['KDE_FORK_SLAVES'] = '1' # fork slaves so they inherit env
ENV['KIO_DISABLE_CACHE_CLEANER'] = '1' # do not start the cleaner, it'll dangle
ENV['DRKONQI_IGNORE_QUALITY'] = '1'

# Isolate ourselves by forcing into a separate home and unsetting the XDG path
# variables. Then spin up a suitable virtual X, run a new dbus session bus
# and our test in that environment.
Dir.mktmpdir do |tmpdir|
  Dir.glob("#{__dir__}/*_test.rb").each do |test|
    isolator = new_isolator
    isolator.mangle_env(tmpdir)
    isolator.run("ruby '#{test}' -p")
  end
  sleep 2 # Wait a bit to make sure all children are dead.
end

warn 'all done'
system 'ps -ejH'
exit 0
