#!/usr/bin/env python2
#
# Copyright 2010 Google Inc. All Rights Reserved.
"""Script adapter used by automation client for testing dejagnu.

   This is not intended to be run on command line.
   To kick off a single dejagnu run, use ./dejagnu/run_dejagnu.py
"""

from __future__ import print_function

__author__ = 'shenhan@google.com (Han Shen)'

import argparse
import sys
import setup_chromeos
import build_tc

from dejagnu import run_dejagnu
from cros_utils import command_executer
from cros_utils import email_sender


class DejagnuAdapter(object):
  """Dejagnu Adapter class"""

  # TODO(shenhan): move these to constants.py.
  _CHROMIUM_GCC_GIT = ('https://chromium.googlesource.com/'
                       'chromiumos/third_party/gcc.git')
  _CHROMIUM_GCC_BRANCH = 'gcc.gnu.org/branches/google/gcc-4_7-mobile'

  _cmd_exec = command_executer.GetCommandExecuter()

  def __init__(self, board, remote, gcc_dir, chromeos_root, runtestflags,
               cleanup):
    self._board = board
    self._remote = remote
    self._gcc_dir = gcc_dir
    self._chromeos_root = chromeos_root
    self._runtestflags = runtestflags
    self._cleanup = cleanup

  def SetupChromeOS(self):
    cmd = [
        setup_chromeos.__file__, '--dir=' + self._chromeos_root, '--minilayout',
        '--jobs=8'
    ]
    ret = setup_chromeos.Main(cmd)
    if ret:
      raise RuntimeError('Failed to checkout chromeos')
    ## Do cros_sdk and setup_board, otherwise build_tc in next step will fail.
    cmd = 'cd {0} && cros_sdk --download'.format(self._chromeos_root)
    ret = self._cmd_exec.RunCommand(cmd, terminated_timeout=9000)
    if ret:
      raise RuntimeError('Failed to create chroot.')

  def SetupBoard(self):
    cmd = './setup_board --board=' + self._board
    ret = self._cmd_exec.ChrootRunCommand(
        self._chromeos_root, cmd, terminated_timeout=4000)
    if ret:
      raise RuntimeError('Failed to setup board.')

  def CheckoutGCC(self):
    cmd = 'git clone {0} {1} && cd {1} && git checkout {2}'.format(
        self._CHROMIUM_GCC_GIT, self._gcc_dir, self._CHROMIUM_GCC_BRANCH)

    ret = self._cmd_exec.RunCommand(cmd, terminated_timeout=300)
    if ret:
      raise RuntimeError('Failed to checkout gcc.')
    ## Handle build_tc bug.
    cmd = ('touch {0}/gcc/config/arm/arm-tune.md ' + \
        '{0}/gcc/config/arm/arm-tables.opt').format(self._gcc_dir)
    ret = self._cmd_exec.RunCommand(cmd)

  def BuildGCC(self):
    build_gcc_args = [
        build_tc.__file__, '--board=' + self._board,
        '--chromeos_root=' + self._chromeos_root, '--gcc_dir=' + self._gcc_dir
    ]
    ret = build_tc.Main(build_gcc_args)
    if ret:
      raise RuntimeError('Building gcc failed.')

  def CheckGCC(self):
    args = [
        run_dejagnu.__file__, '--board=' + self._board,
        '--chromeos_root=' + self._chromeos_root, '--mount=' + self._gcc_dir,
        '--remote=' + self._remote
    ]
    if self._cleanup:
      args.append('--cleanup=' + self._cleanup)
    if self._runtestflags:
      args.append('--flags=' + self._runtestflags)
    return run_dejagnu.Main(args)


# Parse the output log to determine how many failures we have.
# Return -1 if parse output log failed.
def GetNumNewFailures(input_str):
  if not input_str:
    return 0
  start_counting = False
  n_failures = 0
  for l in input_str.splitlines():
    print(l)
    if not start_counting and 'Build results not in the manifest' in l:
      start_counting = True
    elif start_counting and l and (l.find('UNRESOLVED:') == 0 or
                                   l.find('FAIL:') == 0 or l.find('XFAIL:') == 0
                                   or l.find('XPASS:') == 0):
      n_failures = n_failures + 1
  if not start_counting:
    return -1
  return n_failures


# Do not throw any exception in this function!
def EmailResult(result):
  email_to = ['c-compiler-chrome@google.com']
  if len(result) == 4:
    subject = 'Job failed: dejagnu test didn\'t finish'
    email_text = 'Job failed prematurely, check exception below.\n' + \
        result[3]
  elif result[0]:
    subject = 'Job finished: dejagnu test failed'
    num_new_failures = GetNumNewFailures(result[1])
    if num_new_failures >= 0:
      summary = '{0} new fail(s), check log below.'.format(num_new_failures)
    else:
      summary = 'At least 1 new fail found, check log below.'
    email_text = summary + \
        ('\nStdout ====\n'
         '{0}\n'
         '\nStderr ===\n'
         '{1}\n').format(result[1], result[2])
  else:
    subject = 'Job finished: dejagnu test passed'
    email_text = ('Cool! No new fail found.\n'
                  '\nStdout ====\n'
                  '{0}\n'
                  '\nStderr ====\n'
                  '{1}\n').format(result[1], result[2])

  try:
    email_sender.EmailSender().SendEmail(email_to, subject, email_text)
    print('Email sent.')
  except Exception as e:
    # Do not propagate this email sending exception, you want to email an
    # email exception? Just log it on console.
    print('Sending email failed - {0}'
          'Subject: {1}'
          'Text: {2}').format(str(e), subject, email_text)


def ProcessArguments(argv):
  """Processing script arguments."""
  parser = argparse.ArgumentParser(
      description=('This script is used by nightly client to test gcc. '
                   'DO NOT run it unless you know what you are doing.'),
      usage='test_gcc_dejagnu.py options')
  parser.add_argument(
      '-b',
      '--board',
      dest='board',
      help=('Required. Specify board type. For example '
            '\'lumpy\' and \'daisy\''))
  parser.add_argument(
      '-r',
      '--remote',
      dest='remote',
      help=('Required. Specify remote board address'))
  parser.add_argument(
      '-g',
      '--gcc_dir',
      dest='gcc_dir',
      default='gcc.live',
      help=('Optional. Specify gcc checkout directory.'))
  parser.add_argument(
      '-c',
      '--chromeos_root',
      dest='chromeos_root',
      default='chromeos.live',
      help=('Optional. Specify chromeos checkout directory.'))
  parser.add_argument(
      '--cleanup',
      dest='cleanup',
      default=None,
      help=('Optional. Do cleanup after the test.'))
  parser.add_argument(
      '--runtestflags',
      dest='runtestflags',
      default=None,
      help=('Optional. Options to RUNTESTFLAGS env var '
            'while invoking make check. '
            '(Mainly used for testing purpose.)'))

  options = parser.parse_args(argv[1:])

  if not options.board or not options.remote:
    raise SyntaxError('--board and --remote are mandatory options.')

  return options


def Main(argv):
  opt = ProcessArguments(argv)
  adapter = DejagnuAdapter(opt.board, opt.remote, opt.gcc_dir,
                           opt.chromeos_root, opt.runtestflags, opt.cleanup)
  try:
    adapter.SetupChromeOS()
    adapter.SetupBoard()
    adapter.CheckoutGCC()
    adapter.BuildGCC()
    ret = adapter.CheckGCC()
  except Exception as e:
    print(e)
    ret = (1, '', '', str(e))
  finally:
    EmailResult(ret)

  return ret


if __name__ == '__main__':
  retval = Main(sys.argv)
  sys.exit(retval[0])