def CompleteAtLeastOneTest(executing_tests):
  """Blocks until at least one task is removed from executing_tests.

  This function removes completed tests from executing_tests, logging failures
  and output.  If no tests can be removed, it will enter a poll-loop until one
  test finishes or times out.  On a timeout, this function is responsible for
  terminating the process in the appropriate fashion.

  Args:
    executing_tests: A dict mapping a string containing the test name to the
                     test dict return from StartTest().

  Returns:
    A list of tests that have finished.
  """
  finished_tests = []
  busy_loop_timeout = time.time() + BUSY_LOOP_MAX_TIME_SEC
  while len(finished_tests) == 0:
    # If we don't make progress for too long, assume the code is just dead.
    assert busy_loop_timeout > time.time()

    # Select on the output files to block until we have something to
    # do. We ignore the return value from select and just poll all
    # processes.
    read_set = []
    for test in executing_tests.values():
      read_set.extend([test['stdout'], test['stderr']])
    select.select(read_set, [], read_set, NCTEST_TERMINATE_TIMEOUT_SEC)

    # Now attempt to process results.
    now = time.time()
    for test in executing_tests.values():
      proc = test['proc']
      if proc.poll() is not None:
        test['finished_at'] = now
        finished_tests.append(test)
      elif test['terminate_timeout'] < now:
        proc.terminate()
        test['aborted_at'] = now
      elif test['kill_timeout'] < now:
        proc.kill()
        test['aborted_at'] = now

    if len(finished_tests) == 0:
      # We had output from some process but no process had
      # finished. To avoid busy looping while waiting for a process to
      # finish, insert a small 100 ms delay here.
      time.sleep(0.1)

  for test in finished_tests:
    del executing_tests[test['name']]
  return finished_tests