#!/usr/bin/python

# Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.

import os
import re
import shutil
import subprocess
import sys
import tempfile

# We are deliberately not using bot utils from the dart repo.

PACKAGES_BUILDER = r'packages-(windows|linux|mac)-(.*)'

class BotInfo(object):
  """
  Stores the info extracted from the bot name
  - system: windows, linux, mac
  - package-name
  """
  def __init__(self, system, package_name):
      self.system = system
      self.package_name = package_name

  def __str__(self):
    return "System: %s, Package-name: %s" % (self.system, self.package_name)

def GetBotInfo():
  name = os.environ.get('BUILDBOT_BUILDERNAME')
  builder_pattern = re.match(PACKAGES_BUILDER, name)
  if builder_pattern:
    return BotInfo(builder_pattern.group(1),
                   builder_pattern.group(2))

class BuildStep(object):
  """
  A context manager for handling build steps.

  When the context manager is entered, it prints the "@@@BUILD_STEP __@@@"
  message. If it exits from an error being raised it displays the
  "@@@STEP_FAILURE@@@" message.

  If swallow_error is True, then this will catch and discard any OSError that
  is thrown. This lets you run later BuildSteps if the current one fails.
  """
  def __init__(self, name, swallow_error=False):
    self.name = name
    self.swallow_error = swallow_error

  def __enter__(self):
    print '@@@BUILD_STEP %s@@@' % self.name
    sys.stdout.flush()

  def __exit__(self, type, value, traceback):
    if value:
      print '@@@STEP_FAILURE@@@'
      sys.stdout.flush()
      if self.swallow_error and isinstance(value, OSError):
        return True

class TempDir(object):
  def __init__(self, prefix=''):
    self._temp_dir = None
    self._prefix = prefix

  def __enter__(self):
    self._temp_dir = tempfile.mkdtemp(self._prefix)
    return self._temp_dir

  def __exit__(self, *_):
    shutil.rmtree(self._temp_dir, ignore_errors=True)

class ChangedWorkingDirectory(object):
  def __init__(self, working_directory):
    self._working_directory = working_directory

  def __enter__(self):
    self._old_cwd = os.getcwd()
    print "Enter directory = ", self._working_directory
    os.chdir(self._working_directory)

  def __exit__(self, *_):
    print "Enter directory = ", self._old_cwd
    os.chdir(self._old_cwd)

def RunProcess(command):
  """
  Runs command.

  If a non-zero exit code is returned, raises an OSError with errno as the exit
  code.
  """
  no_color_env = dict(os.environ)
  no_color_env['TERM'] = 'nocolor'
  print "Running: %s" % ' '.join(command)
  sys.stdout.flush()
  exit_code = subprocess.call(command, env=no_color_env)
  if exit_code != 0:
    raise OSError(exit_code)

def BuildSDK(bot_info):
  with BuildStep('Build sdk'):
    args = [sys.executable, 'tools/build.py',
            '-mrelease',
            'create_sdk']
    RunProcess(args)

def GetPackagePath(bot_info):
  return os.path.join('third_party', 'pkg', bot_info.package_name)

def GetPackageCopy(bot_info, tempdir):
  package_path = GetPackagePath(bot_info)
  copy_path = os.path.join(tempdir, bot_info.package_name)
  shutil.copytree(package_path, copy_path, symlinks=False)
  RunProcess(['cat', os.path.join(copy_path, '.status')])
  return copy_path

def RunPub(path):
  pub = os.path.join(os.getcwd(), 'out', 'ReleaseIA32',
                     'dart-sdk', 'bin', 'pub')

  # For now, assume pub
  with ChangedWorkingDirectory(path):
    args = [pub, 'upgrade']
    RunProcess(args)

# Major hack
def FixupTestControllerJS(package_path):
  test_controller = os.path.join(package_path, 'packages', 'unittest',
                                 'test_controller.js')
  dart_controller = os.path.join('tools', 'testing', 'dart',
                                 'test_controller.js')
  print 'Hack test controller by copying of  %s to %s' % (dart_controller,
                                                          test_controller)
  shutil.copy(dart_controller, test_controller)


def RunPackageTesting(bot_info, package_path):
  package_root = os.path.join(package_path, 'packages')
  standard_args = ['--suite-dir=%s' % package_path,
                   '--use-sdk', '--report', '--progress=buildbot',
                   '--clear_browser_cache',
                   '--package-root=%s' % package_root]

  with BuildStep('Test vm release mode', swallow_error=True):
    args = [sys.executable, 'tools/test.py',
            '-mrelease', '-rvm', '-cnone'] + standard_args
    RunProcess(args)
  with BuildStep('Test vm debug mode', swallow_error=True):
    args = [sys.executable, 'tools/test.py',
            '-mrelease', '-rvm', '-cnone'] + standard_args
    RunProcess(args)
  with BuildStep('Test dartium', swallow_error=True):
    args = [sys.executable, 'tools/test.py',
            '-mrelease', '-rdartium', '-cnone'] + standard_args
    RunProcess(args)


  # TODO(ricow): add mac/windows and generalize to top level list
  runtimes = ['d8', 'jsshell', 'ff', 'drt']

  for runtime in runtimes:
    with BuildStep('dart2js-%s' % runtime, swallow_error=True):
      args = [sys.executable, 'tools/test.py',
              '-mrelease', '-r%s' % runtime, '-cdart2js', '-j1',
              '--dart2js-batch'] + standard_args
      RunProcess(args)


if __name__ == '__main__':
  bot_info = GetBotInfo()
  print 'Bot info: %s' % bot_info
  BuildSDK(bot_info)
  # TODO(ricow): generalize
  tempdir = os.path.join('out', 'ReleaseIA32', 'package_copy')
  shutil.rmtree(tempdir, ignore_errors=True)
  copy_path = GetPackageCopy(bot_info, tempdir)
  print 'Running testing in copy of package in %s' % copy_path
  RunPub(copy_path)
  FixupTestControllerJS(copy_path)
  RunPackageTesting(bot_info, copy_path)

@@ -3337,66 +3337,8 @@
 se)%0A
-  RunProcess(%5B'cat', os.path.join(copy_path, '.status')%5D)%0A
   re

Remove debug line from annotated steps