1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Script for a testing an existing SDK.
7
8This script is normally run immediately after build_sdk.py.
9"""
10
11import optparse
12import os
13import subprocess
14import sys
15
16import buildbot_common
17import build_projects
18import build_sdk
19import build_version
20import parse_dsc
21
22SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
23SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR)
24SDK_LIBRARY_DIR = os.path.join(SDK_SRC_DIR, 'libraries')
25SDK_DIR = os.path.dirname(SDK_SRC_DIR)
26SRC_DIR = os.path.dirname(SDK_DIR)
27OUT_DIR = os.path.join(SRC_DIR, 'out')
28
29sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
30import getos
31
32def StepBuildExamples(pepperdir):
33  for config in ('Debug', 'Release'):
34    build_sdk.BuildStepMakeAll(pepperdir, 'getting_started',
35                               'Build Getting Started (%s)' % config,
36                               deps=False, config=config)
37
38    build_sdk.BuildStepMakeAll(pepperdir, 'examples',
39                               'Build Examples (%s)' % config,
40                               deps=False, config=config)
41
42
43def StepCopyTests(pepperdir, toolchains, build_experimental):
44  buildbot_common.BuildStep('Copy Tests')
45
46  # Update test libraries and test apps
47  filters = {
48    'DEST': ['tests']
49  }
50  if not build_experimental:
51    filters['EXPERIMENTAL'] = False
52
53  tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
54  build_projects.UpdateHelpers(pepperdir, clobber=False)
55  build_projects.UpdateProjects(pepperdir, tree, clobber=False,
56                                toolchains=toolchains)
57
58
59def StepBuildTests(pepperdir):
60  for config in ('Debug', 'Release'):
61    build_sdk.BuildStepMakeAll(pepperdir, 'tests',
62                                   'Build Tests (%s)' % config,
63                                   deps=False, config=config)
64
65
66def StepRunSelLdrTests(pepperdir, sanitizer):
67  filters = {
68    'SEL_LDR': True
69  }
70
71  tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
72
73  def RunTest(test, toolchain, config, arch=None):
74    args = ['STANDALONE=1', 'TOOLCHAIN=%s' % toolchain]
75    if arch is not None:
76      args.append('NACL_ARCH=%s' % arch)
77    deps = False
78
79    if sanitizer is not None:
80      # For sanitizer builds we pass extra argument for make, and do
81      # full clean build to make sure everything is rebuilt with the
82      # correct flags
83      deps = True
84      if sanitizer == 'valgrind':
85        args += ['RUN_UNDER=valgrind']
86      else:
87        args += ['CC=clang', 'CXX=clang++',
88                 'LDFLAGS=-pie -fsanitize=' + sanitizer,
89                 'CFLAGS=-fPIC -fsanitize=' + sanitizer]
90      build_projects.BuildProjectsBranch(pepperdir, 'src', clean=False,
91                                         deps=deps, config=config,
92                                         args=args + ['clean'])
93      build_projects.BuildProjectsBranch(pepperdir, 'tests', clean=False,
94                                         deps=deps, config=config,
95                                         args=args + ['clean'])
96
97    build_projects.BuildProjectsBranch(pepperdir, test, clean=False,
98                                       deps=deps, config=config,
99                                       args=args + ['run'])
100
101  if getos.GetPlatform() == 'win':
102    # On win32 we only support running on the system
103    # arch
104    archs = (getos.GetSystemArch('win'),)
105  elif getos.GetPlatform() == 'mac':
106    # We only ship 32-bit version of sel_ldr on mac.
107    archs = ('x86_32',)
108  else:
109    # On linux we can run both 32 and 64-bit
110    archs = ('x86_64', 'x86_32')
111
112  for root, projects in tree.iteritems():
113    for project in projects:
114      if sanitizer:
115        sanitizer_name = '[sanitizer=%s]'  % sanitizer
116      else:
117        sanitizer_name = ''
118      title = 'standalone test%s: %s' % (sanitizer_name,
119                                         os.path.basename(project['NAME']))
120      location = os.path.join(root, project['NAME'])
121      buildbot_common.BuildStep(title)
122      configs = ('Debug', 'Release')
123
124      # On linux we can run the standalone tests natively using the host
125      # compiler.
126      if getos.GetPlatform() == 'linux':
127        if sanitizer:
128          configs = ('Debug',)
129        for config in configs:
130          RunTest(location, 'linux', config)
131
132      if sanitizer:
133        continue
134
135      for toolchain in ('newlib', 'glibc'):
136        for arch in archs:
137          for config in configs:
138            RunTest(location, toolchain, config, arch)
139
140
141def StepRunBrowserTests(toolchains, experimental):
142  buildbot_common.BuildStep('Run Tests')
143
144  args = [
145    sys.executable,
146    os.path.join(SCRIPT_DIR, 'test_projects.py'),
147    '--retry-times=3',
148  ]
149
150  if experimental:
151    args.append('-x')
152  for toolchain in toolchains:
153    args.extend(['-t', toolchain])
154
155  try:
156    subprocess.check_call(args)
157  except subprocess.CalledProcessError:
158    buildbot_common.ErrorExit('Error running tests.')
159
160
161def main(args):
162  usage = '%prog [<options>] [<phase...>]'
163  parser = optparse.OptionParser(description=__doc__, usage=usage)
164  parser.add_option('--experimental', help='build experimental tests',
165                    action='store_true')
166  parser.add_option('--sanitizer',
167                    help='Run sanitizer (asan/tsan/valgrind) tests',
168                    action='store_true')
169  parser.add_option('--verbose', '-v', help='Verbose output',
170                    action='store_true')
171
172  if 'NACL_SDK_ROOT' in os.environ:
173    # We don't want the currently configured NACL_SDK_ROOT to have any effect
174    # of the build.
175    del os.environ['NACL_SDK_ROOT']
176
177  # To setup bash completion for this command first install optcomplete
178  # and then add this line to your .bashrc:
179  #  complete -F _optcomplete test_sdk.py
180  try:
181    import optcomplete
182    optcomplete.autocomplete(parser)
183  except ImportError:
184    pass
185
186  options, args = parser.parse_args(args[1:])
187
188  pepper_ver = str(int(build_version.ChromeMajorVersion()))
189  pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
190  toolchains = ['newlib', 'glibc', 'pnacl']
191  toolchains.append(getos.GetPlatform())
192
193  if options.verbose:
194    build_projects.verbose = True
195
196  phases = [
197    ('build_examples', StepBuildExamples, pepperdir),
198    ('copy_tests', StepCopyTests, pepperdir, toolchains, options.experimental),
199    ('build_tests', StepBuildTests, pepperdir),
200    ('sel_ldr_tests', StepRunSelLdrTests, pepperdir, None),
201    ('browser_tests', StepRunBrowserTests, toolchains, options.experimental),
202  ]
203
204  if options.sanitizer:
205    if getos.GetPlatform() != 'linux':
206      buildbot_common.ErrorExit('sanitizer tests only run on linux.')
207    phases += [
208      ('sel_ldr_tests_asan', StepRunSelLdrTests, pepperdir, 'address'),
209      ('sel_ldr_tests_tsan', StepRunSelLdrTests, pepperdir, 'thread'),
210      ('sel_ldr_tests_valgrind', StepRunSelLdrTests, pepperdir, 'valgrind')
211    ]
212
213  if args:
214    phase_names = [p[0] for p in phases]
215    for arg in args:
216      if arg not in phase_names:
217        msg = 'Invalid argument: %s\n' % arg
218        msg += 'Possible arguments:\n'
219        for name in phase_names:
220          msg += '   %s\n' % name
221        parser.error(msg.strip())
222
223  for phase in phases:
224    phase_name = phase[0]
225    if args and phase_name not in args:
226      continue
227    phase_func = phase[1]
228    phase_args = phase[2:]
229    phase_func(*phase_args)
230
231  return 0
232
233
234if __name__ == '__main__':
235  try:
236    sys.exit(main(sys.argv))
237  except KeyboardInterrupt:
238    buildbot_common.ErrorExit('test_sdk: interrupted')
239