VIXL Release 1.0

Refer to the README.md and LICENCE files for details.
diff --git a/tools/test.py b/tools/test.py
new file mode 100755
index 0000000..cf20385
--- /dev/null
+++ b/tools/test.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python2.7
+
+# Copyright 2013, ARM Limited
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   * Redistributions of source code must retain the above copyright notice,
+#     this list of conditions and the following disclaimer.
+#   * Redistributions in binary form must reproduce the above copyright notice,
+#     this list of conditions and the following disclaimer in the documentation
+#     and/or other materials provided with the distribution.
+#   * Neither the name of ARM Limited nor the names of its contributors may be
+#     used to endorse or promote products derived from this software without
+#     specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import sys
+import argparse
+import re
+import subprocess
+import threading
+import time
+import util
+
+
+def BuildOptions():
+  result = argparse.ArgumentParser(description = 'Unit test tool')
+  result.add_argument('name_filters', metavar='name filters', nargs='*',
+                      help='Tests matching any of the regexp filters will be run.')
+  result.add_argument('--mode', action='store', choices=['release', 'debug', 'coverage'],
+                      default='release', help='Build mode')
+  result.add_argument('--simulator', action='store', choices=['on', 'off'],
+                      default='on', help='Use the builtin a64 simulator')
+  result.add_argument('--timeout', action='store', type=int, default=5,
+                      help='Timeout (in seconds) for each cctest (5sec default).')
+  result.add_argument('--nobuild', action='store_true',
+                      help='Do not (re)build the tests')
+  result.add_argument('--jobs', '-j', metavar='N', type=int, default=1,
+                      help='Allow N jobs at once.')
+  return result.parse_args()
+
+
+def BuildRequiredObjects(arguments):
+  status, output = util.getstatusoutput('scons ' +
+                                        'mode=' + arguments.mode + ' ' +
+                                        'simulator=' + arguments.simulator + ' ' +
+                                        'target=cctest ' +
+                                        '--jobs=' + str(arguments.jobs))
+
+  if status != 0:
+    print(output)
+    util.abort('Failed bulding cctest')
+
+
+# Display the run progress:
+# [time| progress|+ passed|- failed]
+def UpdateProgress(start_time, passed, failed, card):
+  minutes, seconds = divmod(time.time() - start_time, 60)
+  progress = float(passed + failed) / card * 100
+  passed_colour = '\x1b[32m' if passed != 0 else ''
+  failed_colour = '\x1b[31m' if failed != 0 else ''
+  indicator = '\r[%02d:%02d| %3d%%|' + passed_colour + '+ %d\x1b[0m|' + failed_colour + '- %d\x1b[0m]'
+  sys.stdout.write(indicator % (minutes, seconds, progress, passed, failed))
+
+
+def PrintError(s):
+  # Print the error on a new line.
+  sys.stdout.write('\n')
+  print(s)
+  sys.stdout.flush()
+
+
+# List all tests matching any of the provided filters.
+def ListTests(cctest, filters):
+  status, output = util.getstatusoutput(cctest +  ' --list')
+  if status != 0: util.abort('Failed to list all tests')
+
+  available_tests = output.split()
+  if filters:
+    filters = map(re.compile, filters)
+    def is_selected(test_name):
+      for e in filters:
+        if e.search(test_name):
+          return True
+      return False
+
+    return filter(is_selected, available_tests)
+  else:
+    return available_tests
+
+
+# A class representing a cctest.
+class CCtest:
+  cctest = None
+
+  def __init__(self, name, options = None):
+    self.name = name
+    self.options = options
+    self.process = None
+    self.stdout = None
+    self.stderr = None
+
+  def Command(self):
+    command = '%s %s' % (CCtest.cctest, self.name)
+    if self.options is not None:
+      command = '%s %s' % (commnad, ' '.join(options))
+
+    return command
+
+  # Run the test.
+  # Use a thread to be able to control the test.
+  def Run(self, arguments):
+    command = [CCtest.cctest, self.name]
+    if self.options is not None:
+      command += self.options
+
+    def execute():
+      self.process = subprocess.Popen(command,
+                                      stdout=subprocess.PIPE,
+                                      stderr=subprocess.PIPE)
+      self.stdout, self.stderr = self.process.communicate()
+
+    thread = threading.Thread(target=execute)
+    retcode = -1
+    # Append spaces to hide the previous test name if longer.
+    sys.stdout.write('  ' + self.name + ' ' * 20)
+    sys.stdout.flush()
+    # Start the test with a timeout.
+    thread.start()
+    thread.join(arguments.timeout)
+    if thread.is_alive():
+      # Too slow! Terminate.
+      PrintError('### TIMEOUT %s\nCOMMAND:\n%s' % (self.name, self.Command()))
+      # If timeout was too small the thread may not have run and self.process
+      # is still None. Therefore check.
+      if (self.process):
+        self.process.terminate()
+      # Allow 1 second to terminate. Else, exterminate!
+      thread.join(1)
+      if thread.is_alive():
+        thread.kill()
+        thread.join()
+      # retcode is already set for failure.
+    else:
+      # Check the return status of the test.
+      retcode = self.process.poll()
+      if retcode != 0:
+        PrintError('### FAILED %s\nSTDERR:\n%s\nSTDOUT:\n%s\nCOMMAND:\n%s'
+                   % (self.name, self.stderr.decode(), self.stdout.decode(),
+                      self.Command()))
+
+    return retcode
+
+
+# Run all tests in the list 'tests'.
+def RunTests(cctest, tests, arguments):
+  CCtest.cctest = cctest
+  card = len(tests)
+  passed = 0
+  failed = 0
+
+  if card == 0:
+    print('No test to run')
+    return 0
+
+  # When the simulator is on the tests are ran twice: with and without the
+  # debugger.
+  if arguments.simulator:
+    card *= 2
+
+  print('Running %d tests... (timeout = %ds)' % (card, arguments.timeout))
+  start_time = time.time()
+
+  # Initialize the progress indicator.
+  UpdateProgress(start_time, 0, 0, card)
+  for e in tests:
+    variants = [CCtest(e)]
+    if arguments.simulator: variants.append(CCtest(e, ['--debugger']))
+    for v in variants:
+      retcode = v.Run(arguments)
+      # Update the counters and progress indicator.
+      if retcode == 0:
+        passed += 1
+      else:
+        failed += 1
+    UpdateProgress(start_time, passed, failed, card)
+
+  return failed
+
+
+if __name__ == '__main__':
+  original_dir = os.path.abspath('.')
+  # $ROOT/tools/test.py
+  root_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+  os.chdir(root_dir)
+
+  # Parse the arguments and build the executable.
+  args = BuildOptions()
+  if not args.nobuild:
+    BuildRequiredObjects(args)
+
+  # The test binary.
+  cctest = os.path.join(root_dir, 'cctest')
+  if args.simulator == 'on':
+    cctest += '_sim'
+  if args.mode == 'debug':
+    cctest += '_g'
+  elif args.mode == 'coverage':
+    cctest += '_gcov'
+
+  # List available tests.
+  tests = ListTests(cctest, args.name_filters)
+
+  # Delete coverage data files.
+  if args.mode == 'coverage':
+    status, output = util.getstatusoutput('find obj/coverage -name "*.gcda" -exec rm {} \;')
+
+  # Run the tests.
+  status = RunTests(cctest, tests, args)
+  sys.stdout.write('\n')
+
+  # Print coverage information.
+  if args.mode == 'coverage':
+    cmd = 'tggcov -R summary_all,untested_functions_per_file obj/coverage/src/aarch64'
+    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    stdout, stderr = p.communicate()
+    print(stdout)
+
+  # Restore original directory.
+  os.chdir(original_dir)
+
+  sys.exit(status)
+