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
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)