Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / native_client / tools / test_lib.py
1 #!/usr/bin/python
2 # Copyright (c) 2011 The Native Client 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 """Testing Library For Nacl.
7
8 """
9
10 import atexit
11 import difflib
12 import os
13 import re
14 import shutil
15 import signal
16 import subprocess
17 import sys
18 import tempfile
19
20
21
22 # Windows does not fully implement os.times functionality.  If
23 # _GetTimesPosix were used, the fields for CPU time used in user and
24 # in kernel mode by the process will both be zero.  Instead, we must
25 # use the ctypes module and access windll's kernel32 interface to
26 # extract the CPU usage information.
27
28 if sys.platform[:3] == 'win':
29   import ctypes
30
31
32 class SubprocessCpuTimer:
33   """Timer used to measure user and kernel CPU time expended by a subprocess.
34
35   A new object of this class should be instantiated just before the
36   subprocess is created, and after the subprocess is finished and
37   waited for (via the wait method of the Popen object), the elapsed
38   time can be obtained by invoking the ElapsedCpuTime of the
39   SubprocessCpuTimer instance.
40
41   """
42
43   WIN32_PROCESS_TIMES_TICKS_PER_SECOND = 1.0e7
44
45   # use a class variable to avoid slicing at run-time
46   _use_proc_handle = sys.platform[:3] == 'win'
47
48
49   @staticmethod
50   def _GetTimePosix():
51     try:
52       t = os.times()
53     except OSError:
54       # This works around a bug in the calling conventions for the
55       # times() system call on Linux.  This syscall returns a number
56       # of clock ticks since an arbitrary time in the past, but if
57       # this happens to be between -4095 and -1, it is interpreted as
58       # an errno value, and we get an exception here.
59       # Returning 0 as a dummy value may result in ElapsedCpuTime()
60       # below returning a negative value.  This is OK for our use
61       # because a test that takes too long is likely to be caught
62       # elsewhere.
63       if sys.platform == "linux2":
64         return 0
65       raise
66     else:
67       return t[2] + t[3]
68
69
70   @staticmethod
71   def _GetTimeWindows(proc_handle):
72     if proc_handle is None:
73       return 0
74     creation_time = ctypes.c_ulonglong()
75     exit_time = ctypes.c_ulonglong()
76     kernel_time = ctypes.c_ulonglong()
77     user_time = ctypes.c_ulonglong()
78     rc = ctypes.windll.kernel32.GetProcessTimes(
79         int(proc_handle._handle),
80         ctypes.byref(creation_time),
81         ctypes.byref(exit_time),
82         ctypes.byref(kernel_time),
83         ctypes.byref(user_time))
84     if not rc:
85       print >>sys.stderr, 'Could not obtain process time'
86       return 0
87     return ((kernel_time.value + user_time.value)
88             / SubprocessCpuTimer.WIN32_PROCESS_TIMES_TICKS_PER_SECOND)
89
90
91   @staticmethod
92   def _GetTime(proc_handle):
93     if SubprocessCpuTimer._use_proc_handle:
94       return SubprocessCpuTimer._GetTimeWindows(proc_handle)
95     return SubprocessCpuTimer._GetTimePosix()
96
97
98   def __init__(self):
99     self._start_time = self._GetTime(None)
100
101
102   def ElapsedCpuTime(self, proc_handle):
103     return self._GetTime(proc_handle) - self._start_time
104
105 def PopenBufSize():
106   return 1000 * 1000
107
108 def RunTestWithInput(cmd, input_data):
109   """Run a test where we only care about the return code."""
110   assert type(cmd) == list
111   failed = 0
112   timer = SubprocessCpuTimer()
113   p = None
114   try:
115     sys.stdout.flush() # Make sure stdout stays in sync on the bots.
116     if type(input_data) == str:
117       p = subprocess.Popen(cmd,
118                            bufsize=PopenBufSize(),
119                            stdin=subprocess.PIPE)
120       p.communicate(input_data)
121     else:
122       p = subprocess.Popen(cmd,
123                            bufsize=PopenBufSize(),
124                            stdin=input_data)
125       p.communicate()
126     retcode = p.wait()
127   except OSError:
128     print 'exception: ' + str(sys.exc_info()[1])
129     retcode = 0
130     failed = 1
131
132   if p is None:
133     return (0, 0, 1)
134   return (timer.ElapsedCpuTime(p), retcode, failed)
135
136
137 def RunTestWithInputOutput(cmd, input_data, capture_stderr=True):
138   """Run a test where we also care about stdin/stdout/stderr.
139
140   NOTE: this function may have problems with arbitrarily
141         large input or output, especially on windows
142   NOTE: input_data can be either a string or or a file like object,
143         file like objects may be better for large input/output
144   """
145   assert type(cmd) == list
146   stdout = ''
147   stderr = ''
148   failed = 0
149
150   p = None
151   timer = SubprocessCpuTimer()
152   try:
153     # Python on windows does not include any notion of SIGPIPE.  On
154     # Linux and OSX, Python installs a signal handler for SIGPIPE that
155     # sets the handler to SIG_IGN so that syscalls return -1 with
156     # errno equal to EPIPE, and translates those to exceptions;
157     # unfortunately, the subprocess module fails to reset the handler
158     # for SIGPIPE to SIG_DFL, and the SIG_IGN behavior is inherited.
159     # subprocess.Popen's preexec_fn is apparently okay to use on
160     # Windows, as long as its value is None.
161
162     if hasattr(signal, 'SIGPIPE'):
163       no_pipe = lambda : signal.signal(signal.SIGPIPE, signal.SIG_DFL)
164     else:
165       no_pipe = None
166
167     # Only capture stderr if capture_stderr is true
168     stderr = subprocess.PIPE if capture_stderr else None
169
170     if type(input_data) == str:
171       p = subprocess.Popen(cmd,
172                            bufsize=PopenBufSize(),
173                            stdin=subprocess.PIPE,
174                            stderr=stderr,
175                            stdout=subprocess.PIPE,
176                            preexec_fn = no_pipe)
177       stdout, stderr = p.communicate(input_data)
178     else:
179       # input_data is a file like object
180       p = subprocess.Popen(cmd,
181                            bufsize=PopenBufSize(),
182                            stdin=input_data,
183                            stderr=stderr,
184                            stdout=subprocess.PIPE,
185                            preexec_fn = no_pipe)
186       stdout, stderr = p.communicate()
187     retcode = p.wait()
188   except OSError, x:
189     if x.errno == 10:
190       print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
191       print 'ignoring exception', str(sys.exc_info()[1])
192       print 'return code NOT checked'
193       print 'this seems to be a windows issue'
194       print '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
195       failed = 0
196       retcode = 0
197     else:
198       print 'exception: ' + str(sys.exc_info()[1])
199       retcode = 0
200       failed = 1
201   if p is None:
202     cpu_time_consumed = 0
203   else:
204     cpu_time_consumed = timer.ElapsedCpuTime(p)
205   return (cpu_time_consumed, retcode, failed, stdout, stderr)
206
207
208 def DiffStringsIgnoringWhiteSpace(a, b):
209   a = a.splitlines()
210   b = b.splitlines()
211   # NOTE: the whitespace stuff seems to be broken in python
212   cruncher = difflib.SequenceMatcher(lambda x: x in ' \t\r', a, b)
213
214   for group in cruncher.get_grouped_opcodes():
215     eq = True
216     for tag, i1, i2, j1, j2 in group:
217       if tag != 'equal':
218         eq = False
219         break
220     if eq: continue
221     i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4]
222     yield '@@ -%d,%d +%d,%d @@\n' % (i1+1, i2-i1, j1+1, j2-j1)
223
224     for tag, i1, i2, j1, j2 in group:
225       if tag == 'equal':
226         for line in a[i1:i2]:
227           yield ' [' + line + ']'
228         continue
229       if tag == 'replace' or tag == 'delete':
230         for line in a[i1:i2]:
231           yield '-[' + repr(line) + ']'
232       if tag == 'replace' or tag == 'insert':
233         for line in b[j1:j2]:
234           yield '+[' + repr(line) + ']'
235
236
237 def RegexpFilterLines(regexp, inverse, group_only, lines):
238   """Apply regexp to filter lines of text, keeping only those lines
239   that match.
240
241   Any carriage return / newline sequence is turned into a newline.
242
243   Args:
244     regexp: A regular expression, only lines that match are kept
245     inverse: Only keep lines that do not match
246     group_only: replace matching lines with the regexp groups,
247                 text outside the groups are omitted, useful for
248                 eliminating file names that might change, etc).
249
250     lines: A string containing newline-separated lines of text
251
252   Return:
253     Filtered lines of text, newline separated.
254   """
255
256   result = []
257   nfa = re.compile(regexp)
258   for line in lines.split('\n'):
259     if line.endswith('\r'):
260       line = line[:-1]
261     mobj = nfa.search(line)
262     if mobj and inverse:
263       continue
264     if not mobj and not inverse:
265       continue
266
267     if group_only:
268       matched_strings = []
269       for s in mobj.groups():
270         if s is not None:
271           matched_strings.append(s)
272       result.append(''.join(matched_strings))
273     else:
274       result.append(line)
275
276   return '\n'.join(result)
277
278
279 def MakeTempDir(env, **kwargs):
280   """Create a temporary directory and arrange to clean it up on exit.
281
282   Passes arguments through to tempfile.mkdtemp
283   """
284   temporary_dir = tempfile.mkdtemp(**kwargs)
285   def Cleanup():
286     try:
287       # Try to remove the dir but only if it exists. Some tests may clean up
288       # after themselves.
289       if os.path.exists(temporary_dir):
290         shutil.rmtree(temporary_dir)
291     except BaseException as e:
292       sys.stderr.write('Unable to delete dir %s on exit: %s\n' % (
293         temporary_dir, e))
294   atexit.register(Cleanup)
295   return temporary_dir
296
297 def MakeTempFile(env, **kwargs):
298   """Create a temporary file and arrange to clean it up on exit.
299
300   Passes arguments through to tempfile.mkstemp
301   """
302   handle, path = tempfile.mkstemp()
303   def Cleanup():
304     try:
305       # Try to remove the file but only if it exists. Some tests may clean up
306       # after themselves.
307       if os.path.exists(path):
308         os.unlink(path)
309     except BaseException as e:
310       sys.stderr.write('Unable to delete file %s on exit: %s\n' % (
311         path, e))
312   atexit.register(Cleanup)
313   return handle, path