1#!/usr/bin/env python2
2#
3# Copyright 2010 Google Inc. All Rights Reserved.
4"""Script adapter used by automation client for testing dejagnu.
5
6   This is not intended to be run on command line.
7   To kick off a single dejagnu run, use ./dejagnu/run_dejagnu.py
8"""
9
10from __future__ import print_function
11
12__author__ = 'shenhan@google.com (Han Shen)'
13
14import argparse
15import sys
16import setup_chromeos
17import build_tc
18
19from dejagnu import run_dejagnu
20from cros_utils import command_executer
21from cros_utils import email_sender
22
23
24class DejagnuAdapter(object):
25  """Dejagnu Adapter class"""
26
27  # TODO(shenhan): move these to constants.py.
28  _CHROMIUM_GCC_GIT = ('https://chromium.googlesource.com/'
29                       'chromiumos/third_party/gcc.git')
30  _CHROMIUM_GCC_BRANCH = 'gcc.gnu.org/branches/google/gcc-4_7-mobile'
31
32  _cmd_exec = command_executer.GetCommandExecuter()
33
34  def __init__(self, board, remote, gcc_dir, chromeos_root, runtestflags,
35               cleanup):
36    self._board = board
37    self._remote = remote
38    self._gcc_dir = gcc_dir
39    self._chromeos_root = chromeos_root
40    self._runtestflags = runtestflags
41    self._cleanup = cleanup
42
43  def SetupChromeOS(self):
44    cmd = [
45        setup_chromeos.__file__, '--dir=' + self._chromeos_root, '--minilayout',
46        '--jobs=8'
47    ]
48    ret = setup_chromeos.Main(cmd)
49    if ret:
50      raise RuntimeError('Failed to checkout chromeos')
51    ## Do cros_sdk and setup_board, otherwise build_tc in next step will fail.
52    cmd = 'cd {0} && cros_sdk --download'.format(self._chromeos_root)
53    ret = self._cmd_exec.RunCommand(cmd, terminated_timeout=9000)
54    if ret:
55      raise RuntimeError('Failed to create chroot.')
56
57  def SetupBoard(self):
58    cmd = './setup_board --board=' + self._board
59    ret = self._cmd_exec.ChrootRunCommand(
60        self._chromeos_root, cmd, terminated_timeout=4000)
61    if ret:
62      raise RuntimeError('Failed to setup board.')
63
64  def CheckoutGCC(self):
65    cmd = 'git clone {0} {1} && cd {1} && git checkout {2}'.format(
66        self._CHROMIUM_GCC_GIT, self._gcc_dir, self._CHROMIUM_GCC_BRANCH)
67
68    ret = self._cmd_exec.RunCommand(cmd, terminated_timeout=300)
69    if ret:
70      raise RuntimeError('Failed to checkout gcc.')
71    ## Handle build_tc bug.
72    cmd = ('touch {0}/gcc/config/arm/arm-tune.md ' + \
73        '{0}/gcc/config/arm/arm-tables.opt').format(self._gcc_dir)
74    ret = self._cmd_exec.RunCommand(cmd)
75
76  def BuildGCC(self):
77    build_gcc_args = [
78        build_tc.__file__, '--board=' + self._board,
79        '--chromeos_root=' + self._chromeos_root, '--gcc_dir=' + self._gcc_dir
80    ]
81    ret = build_tc.Main(build_gcc_args)
82    if ret:
83      raise RuntimeError('Building gcc failed.')
84
85  def CheckGCC(self):
86    args = [
87        run_dejagnu.__file__, '--board=' + self._board,
88        '--chromeos_root=' + self._chromeos_root, '--mount=' + self._gcc_dir,
89        '--remote=' + self._remote
90    ]
91    if self._cleanup:
92      args.append('--cleanup=' + self._cleanup)
93    if self._runtestflags:
94      args.append('--flags=' + self._runtestflags)
95    return run_dejagnu.Main(args)
96
97
98# Parse the output log to determine how many failures we have.
99# Return -1 if parse output log failed.
100def GetNumNewFailures(input_str):
101  if not input_str:
102    return 0
103  start_counting = False
104  n_failures = 0
105  for l in input_str.splitlines():
106    print(l)
107    if not start_counting and 'Build results not in the manifest' in l:
108      start_counting = True
109    elif start_counting and l and (l.find('UNRESOLVED:') == 0 or
110                                   l.find('FAIL:') == 0 or l.find('XFAIL:') == 0
111                                   or l.find('XPASS:') == 0):
112      n_failures = n_failures + 1
113  if not start_counting:
114    return -1
115  return n_failures
116
117
118# Do not throw any exception in this function!
119def EmailResult(result):
120  email_to = ['c-compiler-chrome@google.com']
121  if len(result) == 4:
122    subject = 'Job failed: dejagnu test didn\'t finish'
123    email_text = 'Job failed prematurely, check exception below.\n' + \
124        result[3]
125  elif result[0]:
126    subject = 'Job finished: dejagnu test failed'
127    num_new_failures = GetNumNewFailures(result[1])
128    if num_new_failures >= 0:
129      summary = '{0} new fail(s), check log below.'.format(num_new_failures)
130    else:
131      summary = 'At least 1 new fail found, check log below.'
132    email_text = summary + \
133        ('\nStdout ====\n'
134         '{0}\n'
135         '\nStderr ===\n'
136         '{1}\n').format(result[1], result[2])
137  else:
138    subject = 'Job finished: dejagnu test passed'
139    email_text = ('Cool! No new fail found.\n'
140                  '\nStdout ====\n'
141                  '{0}\n'
142                  '\nStderr ====\n'
143                  '{1}\n').format(result[1], result[2])
144
145  try:
146    email_sender.EmailSender().SendEmail(email_to, subject, email_text)
147    print('Email sent.')
148  except Exception as e:
149    # Do not propagate this email sending exception, you want to email an
150    # email exception? Just log it on console.
151    print('Sending email failed - {0}'
152          'Subject: {1}'
153          'Text: {2}').format(str(e), subject, email_text)
154
155
156def ProcessArguments(argv):
157  """Processing script arguments."""
158  parser = argparse.ArgumentParser(
159      description=('This script is used by nightly client to test gcc. '
160                   'DO NOT run it unless you know what you are doing.'),
161      usage='test_gcc_dejagnu.py options')
162  parser.add_argument(
163      '-b',
164      '--board',
165      dest='board',
166      help=('Required. Specify board type. For example '
167            '\'lumpy\' and \'daisy\''))
168  parser.add_argument(
169      '-r',
170      '--remote',
171      dest='remote',
172      help=('Required. Specify remote board address'))
173  parser.add_argument(
174      '-g',
175      '--gcc_dir',
176      dest='gcc_dir',
177      default='gcc.live',
178      help=('Optional. Specify gcc checkout directory.'))
179  parser.add_argument(
180      '-c',
181      '--chromeos_root',
182      dest='chromeos_root',
183      default='chromeos.live',
184      help=('Optional. Specify chromeos checkout directory.'))
185  parser.add_argument(
186      '--cleanup',
187      dest='cleanup',
188      default=None,
189      help=('Optional. Do cleanup after the test.'))
190  parser.add_argument(
191      '--runtestflags',
192      dest='runtestflags',
193      default=None,
194      help=('Optional. Options to RUNTESTFLAGS env var '
195            'while invoking make check. '
196            '(Mainly used for testing purpose.)'))
197
198  options = parser.parse_args(argv[1:])
199
200  if not options.board or not options.remote:
201    raise SyntaxError('--board and --remote are mandatory options.')
202
203  return options
204
205
206def Main(argv):
207  opt = ProcessArguments(argv)
208  adapter = DejagnuAdapter(opt.board, opt.remote, opt.gcc_dir,
209                           opt.chromeos_root, opt.runtestflags, opt.cleanup)
210  try:
211    adapter.SetupChromeOS()
212    adapter.SetupBoard()
213    adapter.CheckoutGCC()
214    adapter.BuildGCC()
215    ret = adapter.CheckGCC()
216  except Exception as e:
217    print(e)
218    ret = (1, '', '', str(e))
219  finally:
220    EmailResult(ret)
221
222  return ret
223
224
225if __name__ == '__main__':
226  retval = Main(sys.argv)
227  sys.exit(retval[0])
228