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  cmd_string = ('../../../../../out/host/linux-x86/bin/llvm-rs-cc '
72                '-o tmp/ -p tmp/ '
73                '-MD '
74                '-I ../../../../../frameworks/rs/scriptc/ '
75                '-I ../../../../../external/clang/lib/Headers/')
76  base_args = cmd_string.split()
77  rs_files = glob.glob('*.rs')
78  fs_files = glob.glob('*.fs')
79  rs_files += fs_files;
80  rs_files.sort()
81
82  # Extra command line arguments can be placed as // comments at the start of
83  # any .rs file. We automatically bundle up all of these extra args and invoke
84  # llvm-rs-cc with them.
85  extra_args_str = ''
86  for rs_file in rs_files:
87    extra_args_str += GetCommandLineArgs(rs_file)
88  extra_args = extra_args_str.split()
89
90  args = base_args + extra_args + rs_files
91
92  if Options.verbose > 1:
93    print 'Executing:',
94    for arg in args:
95      print arg,
96    print
97
98  # Execute the command and check the resulting shell return value.
99  # All tests that are expected to FAIL have directory names that
100  # start with 'F_'. Other tests that are expected to PASS have
101  # directory names that start with 'P_'.
102  ret = 0
103  try:
104    ret = subprocess.call(args, stdout=stdout_file, stderr=stderr_file)
105  except:
106    passed = False
107
108  stdout_file.flush()
109  stderr_file.flush()
110
111  if Options.verbose > 1:
112    stdout_file.seek(0)
113    stderr_file.seek(0)
114    for line in stdout_file:
115      print 'STDOUT>', line,
116    for line in stderr_file:
117      print 'STDERR>', line,
118
119  stdout_file.close()
120  stderr_file.close()
121
122  if dirname[0:2] == 'F_':
123    if ret == 0:
124      passed = False
125      if Options.verbose:
126        print 'Command passed on invalid input'
127  elif dirname[0:2] == 'P_':
128    if ret != 0:
129      passed = False
130      if Options.verbose:
131        print 'Command failed on valid input'
132  else:
133    passed = (ret == 0)
134    if Options.verbose:
135      print 'Test Directory name should start with an F or a P'
136
137  if not CompareFiles('stdout.txt', 'stdout.txt.expect'):
138    passed = False
139    if Options.verbose:
140      print 'stdout is different'
141  if not CompareFiles('stderr.txt', 'stderr.txt.expect'):
142    passed = False
143    if Options.verbose:
144      print 'stderr is different'
145
146  if Options.updateCTS:
147    # Copy resulting files to appropriate CTS directory (if different).
148    if passed and glob.glob('IN_CTS'):
149      cts_path = '../../../../../cts/'
150      cts_res_raw_path = cts_path + 'tests/res/raw/'
151      cts_src_path = cts_path + 'tests/tests/renderscript/src/'
152      for bc_src in glob.glob('tmp/*.bc'):
153        bc_dst = re.sub('tmp\/', cts_res_raw_path, bc_src, 1)
154        UpdateFiles(bc_src, bc_dst)
155      for java_src in glob.glob('tmp/android/renderscript/cts/*.java'):
156        java_dst = re.sub('tmp\/', cts_src_path, java_src, 1)
157        UpdateFiles(java_src, java_dst)
158
159  if Options.cleanup:
160    try:
161      os.remove('stdout.txt')
162      os.remove('stderr.txt')
163      shutil.rmtree('tmp/')
164    except:
165      pass
166
167  os.chdir('..')
168  return passed
169
170
171def Usage():
172  """Print out usage information."""
173  print ('Usage: %s [OPTION]... [TESTNAME]...'
174         'Renderscript Compiler Test Harness\n'
175         'Runs TESTNAMEs (all tests by default)\n'
176         'Available Options:\n'
177         '  -h, --help          Help message\n'
178         '  -n, --no-cleanup    Don\'t clean up after running tests\n'
179         '  -u, --update-cts    Update CTS test versions\n'
180         '  -v, --verbose       Verbose output\n'
181        ) % (sys.argv[0]),
182  return
183
184
185def main():
186  passed = 0
187  failed = 0
188  files = []
189  failed_tests = []
190
191  for arg in sys.argv[1:]:
192    if arg in ('-h', '--help'):
193      Usage()
194      return 0
195    elif arg in ('-n', '--no-cleanup'):
196      Options.cleanup = 0
197    elif arg in ('-u', '--update-cts'):
198      Options.updateCTS = 1
199    elif arg in ('-v', '--verbose'):
200      Options.verbose += 1
201    else:
202      # Test list to run
203      if os.path.isdir(arg):
204        files.append(arg)
205      else:
206        print >> sys.stderr, 'Invalid test or option: %s' % arg
207        return 1
208
209  if not files:
210    tmp_files = os.listdir('.')
211    # Only run tests that are known to PASS or FAIL
212    # Disabled tests can be marked D_ and invoked explicitly
213    for f in tmp_files:
214      if os.path.isdir(f) and (f[0:2] == 'F_' or f[0:2] == 'P_'):
215        files.append(f)
216
217  for f in files:
218    if os.path.isdir(f):
219      if ExecTest(f):
220        passed += 1
221      else:
222        failed += 1
223        failed_tests.append(f)
224
225  print 'Tests Passed: %d\n' % passed,
226  print 'Tests Failed: %d\n' % failed,
227  if failed:
228    print 'Failures:',
229    for t in failed_tests:
230      print t,
231
232  return failed != 0
233
234
235if __name__ == '__main__':
236  sys.exit(main())
237