nocompile_driver.py revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Implements a simple "negative compile" test for C++ on linux.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Sometimes a C++ API needs to ensure that various usages cannot compile. To
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enable unittesting of these assertions, we use this python script to
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)invoke gcc on a source file and assert that compilation fails.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)For more info, see:
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  http://dev.chromium.org/developers/testing/no-compile-tests
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import ast
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import locale
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import select
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shlex
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Matches lines that start with #if and have the substring TEST in the
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# conditional. Also extracts the comment.  This allows us to search for
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# lines like the following:
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #ifdef NCTEST_NAME_OF_TEST  // [r'expected output']
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #if defined(NCTEST_NAME_OF_TEST)  // [r'expected output']
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #if NCTEST_NAME_OF_TEST  // [r'expected output']
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #elif NCTEST_NAME_OF_TEST  // [r'expected output']
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #elif DISABLED_NCTEST_NAME_OF_TEST  // [r'expected output']
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# inside the unittest file.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NCTEST_CONFIG_RE = re.compile(r'^#(?:el)?if.*\s+(\S*NCTEST\S*)\s*(//.*)?')
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Matches and removes the defined() preprocesor predicate. This is useful
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# for test cases that use the preprocessor if-statement form:
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   #if defined(NCTEST_NAME_OF_TEST)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Should be used to post-process the results found by NCTEST_CONFIG_RE.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)STRIP_DEFINED_RE = re.compile(r'defined\((.*)\)')
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Used to grab the expectation from comment at the end of an #ifdef.  See
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# NCTEST_CONFIG_RE's comment for examples of what the format should look like.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The extracted substring should be a python array of regular expressions.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EXTRACT_EXPECTATION_RE = re.compile(r'//\s*(\[.*\])')
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The header for the result file so that it can be compiled.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RESULT_FILE_HEADER = """
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file is generated by the no compile test from:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   %s
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The GUnit test function to output on a successful test completion.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SUCCESS_GUNIT_TEMPLATE = """
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(%s, %s) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LOG(INFO) << "Took %f secs. Started at %f, ended at %f";
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The GUnit test function to output for a disabled test.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DISABLED_GUNIT_TEMPLATE = """
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(%s, %s) { }
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Timeout constants.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NCTEST_TERMINATE_TIMEOUT_SEC = 60
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NCTEST_KILL_TIMEOUT_SEC = NCTEST_TERMINATE_TIMEOUT_SEC + 2
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BUSY_LOOP_MAX_TIME_SEC = NCTEST_KILL_TIMEOUT_SEC * 2
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ValidateInput(parallelism, sourcefile_path, cflags, resultfile_path):
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Make sure the arguments being passed in are sane."""
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert parallelism >= 1
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert type(sourcefile_path) is str
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert type(cflags) is str
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert type(resultfile_path) is str
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ParseExpectation(expectation_string):
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Extracts expectation definition from the trailing comment on the ifdef.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  See the comment on NCTEST_CONFIG_RE for examples of the format we are parsing.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expectation_string: A string like "// [r'some_regex']"
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A list of compiled regular expressions indicating all possible valid
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compiler outputs.  If the list is empty, all outputs are considered valid.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert expectation_string is not None
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  match = EXTRACT_EXPECTATION_RE.match(expectation_string)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert match
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  raw_expectation = ast.literal_eval(match.group(1))
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  assert type(raw_expectation) is list
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expectation = []
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for regex_str in raw_expectation:
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert type(regex_str) is str
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expectation.append(re.compile(regex_str))
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return expectation
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ExtractTestConfigs(sourcefile_path):
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Parses the soruce file for test configurations.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Each no-compile test in the file is separated by an ifdef macro.  We scan
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the source file with the NCTEST_CONFIG_RE to find all ifdefs that look like
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  they demark one no-compile test and try to extract the test configuration
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  from that.
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sourcefile_path: The path to the source file.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A list of test configurations. Each test configuration is a dictionary of
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the form:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      { name: 'NCTEST_NAME'
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        suite_name: 'SOURCE_FILE_NAME'
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expectations: [re.Pattern, re.Pattern] }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The |suite_name| is used to generate a pretty gtest output on successful
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    completion of the no compile test.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The compiled regexps in |expectations| define the valid outputs of the
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compiler.  If any one of the listed patterns matches either the stderr or
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stdout from the compilation, and the compilation failed, then the test is
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    considered to have succeeded.  If the list is empty, than we ignore the
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    compiler output and just check for failed compilation. If |expectations|
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is actually None, then this specifies a compiler sanity check test, which
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    should expect a SUCCESSFUL compilation.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sourcefile = open(sourcefile_path, 'r')
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Convert filename from underscores to CamelCase.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  words = os.path.splitext(os.path.basename(sourcefile_path))[0].split('_')
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  words = [w.capitalize() for w in words]
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  suite_name = 'NoCompile' + ''.join(words)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Start with at least the compiler sanity test.  You need to always have one
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # sanity test to show that compiler flags and configuration are not just
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # wrong.  Otherwise, having a misconfigured compiler, or an error in the
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # shared portions of the .nc file would cause all tests to erroneously pass.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_configs = [{'name': 'NCTEST_SANITY',
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   'suite_name': suite_name,
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   'expectations': None}]
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for line in sourcefile:
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    match_result = NCTEST_CONFIG_RE.match(line)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not match_result:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    groups = match_result.groups()
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Grab the name and remove the defined() predicate if there is one.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name = groups[0]
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strip_result = STRIP_DEFINED_RE.match(name)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if strip_result:
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name = strip_result.group(1)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read expectations if there are any.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_configs.append({'name': name,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         'suite_name': suite_name,
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         'expectations': ParseExpectation(groups[1])})
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sourcefile.close()
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return test_configs
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def StartTest(sourcefile_path, cflags, config):
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Start one negative compile test.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sourcefile_path: The path to the source file.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cflags: A string with all the CFLAGS to give to gcc. This string will be
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            split by shelex so be careful with escaping.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    config: A dictionary describing the test.  See ExtractTestConfigs
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for a description of the config format.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A dictionary containing all the information about the started test. The
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fields in the dictionary are as follows:
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      { 'proc': A subprocess object representing the compiler run.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'cmdline': The exectued command line.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'name': The name of the test.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'suite_name': The suite name to use when generating the gunit test
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      result.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'terminate_timeout': The timestamp in seconds since the epoch after
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             which the test should be terminated.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'kill_timeout': The timestamp in seconds since the epoch after which
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        the test should be given a hard kill signal.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'started_at': A timestamp in seconds since the epoch for when this test
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      was started.
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'aborted_at': A timestamp in seconds since the epoch for when this test
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      was aborted.  If the test completed successfully,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      this value is 0.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'finished_at': A timestamp in seconds since the epoch for when this
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       test was successfully complete.  If the test is aborted,
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       or running, this value is 0.
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'expectations': A dictionary with the test expectations. See
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        ParseExpectation() for the structure.
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # TODO(ajwong): Get the compiler from gyp.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cmdline = ['g++']
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cmdline.extend(shlex.split(cflags))
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  name = config['name']
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  expectations = config['expectations']
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if expectations is not None:
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cmdline.append('-D%s' % name)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cmdline.extend(['-o', '/dev/null', '-c', '-x', 'c++', sourcefile_path])
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  process = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             stderr=subprocess.PIPE)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  now = time.time()
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {'proc': process,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'cmdline': ' '.join(cmdline),
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'name': name,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'suite_name': config['suite_name'],
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'terminate_timeout': now + NCTEST_TERMINATE_TIMEOUT_SEC,
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'kill_timeout': now + NCTEST_KILL_TIMEOUT_SEC,
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'started_at': now,
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'aborted_at': 0,
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'finished_at': 0,
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'expectations': expectations}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def PassTest(resultfile, test):
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Logs the result of a test started by StartTest(), or a disabled test
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  configuration.
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile: File object for .cc file that results are written to.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test: An instance of the dictionary returned by StartTest(), a
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          configuration from ExtractTestConfigs().
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The 'started_at' key is only added if a test has been started.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if 'started_at' in test:
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile.write(SUCCESS_GUNIT_TEMPLATE % (
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['suite_name'], test['name'],
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['finished_at'] - test['started_at'],
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['started_at'], test['finished_at']))
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile.write(DISABLED_GUNIT_TEMPLATE % (
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['suite_name'], test['name']))
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def FailTest(resultfile, test, error, stdout=None, stderr=None):
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Logs the result of a test started by StartTest()
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile: File object for .cc file that results are written to.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test: An instance of the dictionary returned by StartTest()
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error: The printable reason for the failure.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stdout: The test's output to stdout.
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stderr: The test's output to stderr.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.write('#error %s Failed: %s\n' % (test['name'], error))
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.write('#error compile line: %s\n' % test['cmdline'])
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if stdout and len(stdout) != 0:
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile.write('#error %s stdout:\n' % test['name'])
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line in stdout.split('\n'):
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resultfile.write('#error   %s\n' % line)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if stderr and len(stderr) != 0:
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile.write('#error %s stderr:\n' % test['name'])
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line in stderr.split('\n'):
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resultfile.write('#error   %s\n' % line)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.write('\n')
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def WriteStats(resultfile, suite_name, timings):
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Logs the peformance timings for each stage of the script into a fake test.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile: File object for .cc file that results are written to.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suite_name: The name of the GUnit suite this test belongs to.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    timings: Dictionary with timestamps for each stage of the script run.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  stats_template = ("Started %f, Ended %f, Total %fs, Extract %fs, "
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    "Compile %fs, Process %fs")
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  total_secs = timings['results_processed'] - timings['started']
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extract_secs = timings['extract_done'] - timings['started']
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compile_secs = timings['compile_done'] - timings['extract_done']
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  process_secs = timings['results_processed'] - timings['compile_done']
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.write('TEST(%s, Stats) { LOG(INFO) << "%s"; }\n' % (
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suite_name, stats_template % (
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          timings['started'], timings['results_processed'], total_secs,
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          extract_secs, compile_secs, process_secs)))
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ProcessTestResult(resultfile, test):
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Interprets and logs the result of a test started by StartTest()
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resultfile: File object for .cc file that results are written to.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test: The dictionary from StartTest() to process.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Snap a copy of stdout and stderr into the test dictionary immediately
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # cause we can only call this once on the Popen object, and lots of stuff
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # below will want access to it.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  proc = test['proc']
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (stdout, stderr) = proc.communicate()
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if test['aborted_at'] != 0:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FailTest(resultfile, test, "Compile timed out. Started %f ended %f." %
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             (test['started_at'], test['aborted_at']))
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if test['expectations'] is None:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # This signals a compiler sanity check test. Fail iff compilation failed.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if proc.poll() == 0:
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PassTest(resultfile, test)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FailTest(resultfile, test, 'Sanity compile failed. Is compiler borked?',
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               stdout, stderr)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif proc.poll() == 0:
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Handle failure due to successful compile.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FailTest(resultfile, test,
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'Unexpected successful compilation.',
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             stdout, stderr)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Check the output has the right expectations.  If there are no
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # expectations, then we just consider the output "matched" by default.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(test['expectations']) == 0:
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PassTest(resultfile, test)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Otherwise test against all expectations.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for regexp in test['expectations']:
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (regexp.search(stdout) is not None or
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          regexp.search(stderr) is not None):
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        PassTest(resultfile, test)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expectation_str = ', '.join(
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ["r'%s'" % regexp.pattern for regexp in test['expectations']])
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FailTest(resultfile, test,
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             'Expectations [%s] did not match output.' % expectation_str,
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             stdout, stderr)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CompleteAtLeastOneTest(resultfile, executing_tests):
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Blocks until at least one task is removed from executing_tests.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  This function removes completed tests from executing_tests, logging failures
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  and output.  If no tests can be removed, it will enter a poll-loop until one
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test finishes or times out.  On a timeout, this function is responsible for
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  terminating the process in the appropriate fashion.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    executing_tests: A dict mapping a string containing the test name to the
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     test dict return from StartTest().
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    A list of tests that have finished.
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finished_tests = []
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  busy_loop_timeout = time.time() + BUSY_LOOP_MAX_TIME_SEC
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while len(finished_tests) == 0:
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If we don't make progress for too long, assume the code is just dead.
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert busy_loop_timeout > time.time()
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Select on the output pipes.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    read_set = []
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for test in executing_tests.values():
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read_set.extend([test['proc'].stderr, test['proc'].stdout])
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = select.select(read_set, [], read_set, NCTEST_TERMINATE_TIMEOUT_SEC)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Now attempt to process results.
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    now = time.time()
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for test in executing_tests.values():
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      proc = test['proc']
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if proc.poll() is not None:
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['finished_at'] = now
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        finished_tests.append(test)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif test['terminate_timeout'] < now:
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        proc.terminate()
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['aborted_at'] = now
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif test['kill_timeout'] < now:
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        proc.kill()
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test['aborted_at'] = now
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for test in finished_tests:
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    del executing_tests[test['name']]
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return finished_tests
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(sys.argv) != 5:
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print ('Usage: %s <parallelism> <sourcefile> <cflags> <resultfile>' %
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           sys.argv[0])
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sys.exit(1)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Force us into the "C" locale so the compiler doesn't localize its output.
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # In particular, this stops gcc from using smart quotes when in english UTF-8
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # locales.  This makes the expectation writing much easier.
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  os.environ['LC_ALL'] = 'C'
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parallelism = int(sys.argv[1])
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sourcefile_path = sys.argv[2]
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cflags = sys.argv[3]
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile_path = sys.argv[4]
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timings = {'started': time.time()}
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ValidateInput(parallelism, sourcefile_path, cflags, resultfile_path)
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_configs = ExtractTestConfigs(sourcefile_path)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timings['extract_done'] = time.time()
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile = open(resultfile_path, 'w')
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.write(RESULT_FILE_HEADER % sourcefile_path)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Run the no-compile tests, but ensure we do not run more than |parallelism|
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # tests at once.
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timings['header_written'] = time.time()
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  executing_tests = {}
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finished_tests = []
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for config in test_configs:
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # CompleteAtLeastOneTest blocks until at least one test finishes. Thus, this
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # acts as a semaphore.  We cannot use threads + a real semaphore because
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # subprocess forks, which can cause all sorts of hilarity with threads.
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(executing_tests) >= parallelism:
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      finished_tests.extend(CompleteAtLeastOneTest(resultfile, executing_tests))
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if config['name'].startswith('DISABLED_'):
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PassTest(resultfile, config)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      test = StartTest(sourcefile_path, cflags, config)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      assert test['name'] not in executing_tests
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      executing_tests[test['name']] = test
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If there are no more test to start, we still need to drain the running
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ones.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while len(executing_tests) > 0:
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finished_tests.extend(CompleteAtLeastOneTest(resultfile, executing_tests))
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timings['compile_done'] = time.time()
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for test in finished_tests:
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProcessTestResult(resultfile, test)
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timings['results_processed'] = time.time()
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # We always know at least a sanity test was run.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WriteStats(resultfile, finished_tests[0]['suite_name'], timings)
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resultfile.close()
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  main()
473