1#!/usr/bin/python
2#
3# Copyright 2010-2012 Google Inc. All Rights Reserved.
4
5"""Renderscript Compiler Test.
6
7Runs subdirectories of tests for the Renderscript compiler.
8"""
9
10import filecmp
11import glob
12import os
13import re
14import shutil
15import subprocess
16import sys
17
18__author__ = 'Android'
19
20
21class Options(object):
22  def __init__(self):
23    return
24  verbose = 0
25  cleanup = 1
26  updateCTS = 0
27
28
29def CompareFiles(actual, expect):
30  """Compares actual and expect for equality."""
31  if not os.path.isfile(actual):
32    if Options.verbose:
33      print 'Could not find %s' % actual
34    return False
35  if not os.path.isfile(expect):
36    if Options.verbose:
37      print 'Could not find %s' % expect
38    return False
39
40  return filecmp.cmp(actual, expect, False)
41
42
43def UpdateFiles(src, dst):
44  """Update dst if it is different from src."""
45  if not CompareFiles(src, dst):
46    print 'Copying from %s to %s' % (src, dst)
47    shutil.copyfile(src, dst)
48
49
50def GetCommandLineArgs(filename):
51  """Extracts command line arguments from first comment line in a file."""
52  f = open(filename, 'r')
53  line = f.readline()
54  if line[0] == '/' and line [1] == '/':
55    return line[2:].strip()
56  else:
57    return ''
58
59
60def ExecTest(dirname):
61  """Executes an llvm-rs-cc test from dirname."""
62  passed = True
63
64  if Options.verbose != 0:
65    print 'Testing %s' % dirname
66
67  os.chdir(dirname)
68  stdout_file = open('stdout.txt', 'w+')
69  stderr_file = open('stderr.txt', 'w+')
70
71  out_dir = os.environ['ANDROID_HOST_OUT']
72  cmd_string = ('%s/bin/llvm-rs-cc -o tmp/ -p tmp/ -MD '
73                '-I ../../../../../frameworks/rs/scriptc/ '
74                '-I ../../../../../external/clang/lib/Headers/') % out_dir
75  base_args = cmd_string.split()
76  rs_files = glob.glob('*.rs')
77  fs_files = glob.glob('*.fs')
78  rs_files += fs_files;
79  rs_files.sort()
80
81  # Extra command line arguments can be placed as // comments at the start of
82  # any .rs file. We automatically bundle up all of these extra args and invoke
83  # llvm-rs-cc with them.
84  extra_args_str = ''
85  for rs_file in rs_files:
86    extra_args_str += GetCommandLineArgs(rs_file)
87  extra_args = extra_args_str.split()
88
89  args = base_args + extra_args + rs_files
90
91  if Options.verbose > 1:
92    print 'Executing:',
93    for arg in args:
94      print arg,
95    print
96
97  # Execute the command and check the resulting shell return value.
98  # All tests that are expected to FAIL have directory names that
99  # start with 'F_'. Other tests that are expected to PASS have
100  # directory names that start with 'P_'.
101  ret = 0
102  try:
103    ret = subprocess.call(args, stdout=stdout_file, stderr=stderr_file)
104  except:
105    passed = False
106
107  stdout_file.flush()
108  stderr_file.flush()
109
110  if Options.verbose > 1:
111    stdout_file.seek(0)
112    stderr_file.seek(0)
113    for line in stdout_file:
114      print 'STDOUT>', line,
115    for line in stderr_file:
116      print 'STDERR>', line,
117
118  stdout_file.close()
119  stderr_file.close()
120
121  if dirname[0:2] == 'F_':
122    if ret == 0:
123      passed = False
124      if Options.verbose:
125        print 'Command passed on invalid input'
126  elif dirname[0:2] == 'P_':
127    if ret != 0:
128      passed = False
129      if Options.verbose:
130        print 'Command failed on valid input'
131  else:
132    passed = (ret == 0)
133    if Options.verbose:
134      print 'Test Directory name should start with an F or a P'
135
136  if not CompareFiles('stdout.txt', 'stdout.txt.expect'):
137    passed = False
138    if Options.verbose:
139      print 'stdout is different'
140  if not CompareFiles('stderr.txt', 'stderr.txt.expect'):
141    passed = False
142    if Options.verbose:
143      print 'stderr is different'
144  java_expect = glob.glob('Script*.java.expect');
145  for expect in java_expect:
146    expect_base = expect[:-7] # strip ".expect" suffix
147    if Options.verbose:
148      print 'Comparing ' + expect_base
149    find = 'tmp/*/' + expect_base
150    found = glob.glob(find)
151    if len(found) != 1:
152      passed = False
153      if Options.verbose:
154        print 'unique ' + find + ' not found'
155    elif not CompareFiles(found[0], expect):
156      passed = False
157      if Options.verbose:
158        print expect_base + ' is different'
159
160  if Options.updateCTS:
161    # Copy resulting files to appropriate CTS directory (if different).
162    if passed and glob.glob('IN_CTS'):
163      cts_path = '../../../../../cts/'
164      cts_res_raw_path = cts_path + 'tests/res/raw/'
165      cts_src_path = cts_path + 'tests/tests/renderscript/src/'
166      for bc_src in glob.glob('tmp/*.bc'):
167        bc_dst = re.sub('tmp\/', cts_res_raw_path, bc_src, 1)
168        UpdateFiles(bc_src, bc_dst)
169      for java_src in glob.glob('tmp/android/renderscript/cts/*.java'):
170        java_dst = re.sub('tmp\/', cts_src_path, java_src, 1)
171        UpdateFiles(java_src, java_dst)
172
173  if Options.cleanup:
174    try:
175      os.remove('stdout.txt')
176      os.remove('stderr.txt')
177      shutil.rmtree('tmp/')
178    except:
179      pass
180
181  os.chdir('..')
182  return passed
183
184
185def Usage():
186  """Print out usage information."""
187  print ('Usage: %s [OPTION]... [TESTNAME]...'
188         'Renderscript Compiler Test Harness\n'
189         'Runs TESTNAMEs (all tests by default)\n'
190         'Available Options:\n'
191         '  -h, --help          Help message\n'
192         '  -n, --no-cleanup    Don\'t clean up after running tests\n'
193         '  -u, --update-cts    Update CTS test versions\n'
194         '  -v, --verbose       Verbose output.  Enter multiple -v to get more verbose.\n'
195        ) % (sys.argv[0]),
196  return
197
198
199def main():
200  passed = 0
201  failed = 0
202  files = []
203  failed_tests = []
204
205  for arg in sys.argv[1:]:
206    if arg in ('-h', '--help'):
207      Usage()
208      return 0
209    elif arg in ('-n', '--no-cleanup'):
210      Options.cleanup = 0
211    elif arg in ('-u', '--update-cts'):
212      Options.updateCTS = 1
213    elif arg in ('-v', '--verbose'):
214      Options.verbose += 1
215    else:
216      # Test list to run
217      if os.path.isdir(arg):
218        files.append(arg)
219      else:
220        print >> sys.stderr, 'Invalid test or option: %s' % arg
221        return 1
222
223  if not files:
224    tmp_files = os.listdir('.')
225    # Only run tests that are known to PASS or FAIL
226    # Disabled tests can be marked D_ and invoked explicitly
227    for f in tmp_files:
228      if os.path.isdir(f) and (f[0:2] == 'F_' or f[0:2] == 'P_'):
229        files.append(f)
230    files.sort()
231
232  for f in files:
233    if os.path.isdir(f):
234      if ExecTest(f):
235        passed += 1
236      else:
237        failed += 1
238        failed_tests.append(f)
239
240  print 'Tests Passed: %d\n' % passed,
241  print 'Tests Failed: %d\n' % failed,
242  if failed:
243    print 'Failures:',
244    for t in failed_tests:
245      print t,
246
247  return failed != 0
248
249
250if __name__ == '__main__':
251  sys.exit(main())
252