Ejemplo n.º 1
0
def _run_test_unit(fullname, task_key, conf):
  """Runs a single test unit based on a RunTestUnitTask.

  The test identified by the task is run and the result is stored in the
  RunTestUnitTask.

  Args:
    fullname: The full name of the test unit to run.
    task_key: The key of the RunTestUnitTask to run.
    conf: The configuration to use.
  """
  ctx_options = models.get_ctx_options(conf)
  task = models.RunTestUnitTask(key=task_key, fullname=fullname)
  load_errors = []
  if _this_task_has_failed_before():
    # This will appear to the user as a "load error" in the test.
    msg = 'Unknown error running test %s.  See log for details.' % fullname
    load_errors.append((fullname, msg))
    try:
      task.set_test_result(load_errors, unittest.TestResult(), '', conf)
      task.put(**ctx_options)
    # pylint: disable-msg=W0703
    except:
      msg = 'Error writing message about the test %s that failed!' % fullname
      logging.exception(msg)
    return
  test = logic.get_requested_object(fullname, conf)
  suite = test.get_suite(conf, load_errors)
  # Since the test is a TestSuite, its run method will handle all the
  # administrative work involved in setUpModule, setUpClass, skipping, etc.
  result, output = _run_test_and_capture_output(suite)
  task.set_test_result(load_errors, result, output, conf)
  task.put(**ctx_options)
Ejemplo n.º 2
0
def start_batch(fullname, conf):
  """Creates a TestBatch for all the given tests and returns it.

  Eventually, all tests will automatically run in the background.

  Args:
    fullname: The full name of the group of tests to run.  This should be the
        period-separated name of a test package, module, class, or method, or
        the empty string to run all tests.
    conf: The configuration to use.
    run_immediately: If set to True, tests will be run immediately rather than
        in the task queue.  They will be finished before this function returns.

  Returns:
    The TestBatch created for the run.

  Raises:
    TypeError: Wrong input arguments.
  """
  utils.check_type(fullname, 'fullname', str)
  utils.check_type(conf, 'conf', config.Config)
  ctx_options = models.get_ctx_options(conf)
  # It's necessary to set the key because if ctx_options['use_datastore'] ==
  # False, the key will not be set to something reasonable automatically.
  batch_key = ndb.Key(models.TestBatch, utils.rand_unique_id())
  batch = models.TestBatch(fullname=fullname, key=batch_key)
  batch.put(**ctx_options)
  call = deferred.DeferredCall(_initialize_batch, fullname, batch_key, conf)
  if conf.storage == 'immediate':
    call.run()
    # _initialize_batch() should have updated batch data
    return batch.key.get(**ctx_options)
  else:
    deferred.defer_multi([call], queue=conf.test_queue)
    return batch
Ejemplo n.º 3
0
def _delete_batch(batch_key, prev_done, conf):
  """Deletes the given batch and its tasks if no progress has been made.

  Otherwise, it will check back in _DELETE_TIME_SECS seconds.

  Note that any blobs used in this batch will eventually be deleted after their
  respective TestBatch/RunTestUnitTask objects are deleted; see
  models._delete_blob_if_done().

  Args:
    batch_key: The key to the TestBatch to delete.
    prev_done: How many tests were done _DELETE_TIME_SECS seconds ago.  If the
        number of completed tests has not increased, the batch is deleted.
    conf: The configuration to use.
  """
  ctx_options = models.get_ctx_options(conf)
  batch = batch_key.get(**ctx_options)
  if batch is None: return  # For idempotency.
  utils.check_type(batch, 'batch', models.TestBatch)
  tasks = [task for task in batch.get_tasks(conf) if task]
  num_done = len(tasks)
  if num_done == prev_done:
    task_keys = [task.key for task in tasks]
    ndb.delete_multi([batch_key] + task_keys, **ctx_options)
  else:
    deferred.defer(_delete_batch, batch_key, num_done, conf,
                   _queue=conf.test_queue, _countdown=_DELETE_TIME_SECS)
Ejemplo n.º 4
0
def _initialize_batch(fullname, batch_key, conf):
  """Initializes a TestBatch to start the tests running.

  This function creates a RunTestUnitTask for every test unit in the batch and
  starts running them in the background.

  Args:
    batch_key: The ndb.Key of the batch to initialize.
    conf: The configuration to use.
    run_immediately: If set to True, tests will be run immediately rather than
        in the task queue.  They will be finished before this function returns.
  """
  ctx_options = models.get_ctx_options(conf)
  errors_out = []
  batch = models.TestBatch(key=batch_key, fullname=fullname)
  if _this_task_has_failed_before():
    # This will appear to the user as a "load error" in the batch.
    msg = ('Unknown error initializing batch %s.  See log for details.' %
           fullname)
    errors_out.append((fullname, msg))
    try:
      batch.set_info(errors_out, {}, conf)
      batch.put(**ctx_options)
    # pylint: disable-msg=W0703
    except:
      msg = 'Error writing message about the batch %s that failed!' % fullname
      logging.exception(msg)
    return
  test = logic.get_requested_object(fullname, conf)
  test_units = test.get_units(conf, errors_out)
  test_unit_methods = {}
  tasks = []
  defer_calls = []
  for (i, unit) in enumerate(test_units):
    # Ignore loading errors for now.  _run_test_unit will detect loading errors
    # when its task is executed.
    method_names = [method.fullname for method in unit.get_methods(conf)]
    test_unit_methods[unit.fullname] = method_names
    task_key = models.RunTestUnitTask.get_key(batch_key, i)
    tasks.append(models.RunTestUnitTask(key=task_key, fullname=unit.fullname))
  batch.set_info(errors_out, test_unit_methods, conf)
  # Put batch after tasks, so that we don't see that the batch has tasks before
  # they exist.
  ndb.put_multi(tasks + [batch], **ctx_options)
  for task in tasks:
    call = deferred.DeferredCall(_run_test_unit, str(task.fullname), task.key,
                                 conf)
    if conf.storage == 'immediate':
      call.run()
    else:
      defer_calls.append(call)
  if ctx_options.get('use_datastore', True):
    defer_calls.append(deferred.DeferredCall(_delete_batch, batch.key, 0, conf,
                                             _countdown=_DELETE_TIME_SECS))
  deferred.defer_multi(defer_calls, queue=conf.test_queue)