docs: function(configFile, additionalConfig) {
  
    var runnerErrorCount = 0; 
  
    var configParser = new ConfigParser();
    if (configFile) {
      configParser.addFileConfig(configFile);
    }
    if (additionalConfig) {
      configParser.addConfig(additionalConfig);
    }
    var config = configParser.getConfig();
    var scheduler = new TaskScheduler(config);
  
    /**
     * A fork of a runner for running a specified task. The RunnerFork will
     * start a new process that calls on '/runFromLauncher.js' and report the
     * result to a reporter.
     *
     * @constructor
     * @param {object} task Task to run.
     */
    var RunnerFork = function(task) {
      this.capability = task.capability;
      this.specs = task.specs;
      this.process = child.fork(
        __dirname + '/runFromLauncher.js',
        process.argv.slice(2), {
          cwd: process.cwd(),
          silent: true
        }
      );
      this.reporter = reporter.addTaskReporter(task, this.process.pid);
    };
  
    /**
     * Add handlers for the RunnerFork for events like stdout, stderr, testsDone,
     * testPass, testFail, error, and exit. Optionally, you can pass in a 
     * callback function to be called when a test completes.
     *
     * @param {function()} testsDoneCallback Callback function for testsDone events.
     */
    RunnerFork.prototype.addEventHandlers = function(testsDoneCallback) {
      var self = this;
  
      // stdout pipe
      this.process.stdout.on('data', function(chunk) {
        self.reporter.logStdout(chunk);
      });
  
      // stderr pipe
      this.process.stderr.on('data', function(chunk) {
        self.reporter.logStderr(chunk);
      });
  
      this.process.on('message', function(m) {
        switch (m.event) {
          case 'testPass':
            process.stdout.write('.');
            break;
          case 'testFail':
            process.stdout.write('F');
            break;
          case 'testsDone':
            self.reporter.testsDone(m.failedCount);
            break;
        }
      });
  
      this.process.on('error', function(err) {
        self.reporter.flush();
        log_('Runner Process(' + self.process.pid + ') Error: ' + err);
        runnerErrorCount += 1;
      });
  
      this.process.on('exit', function(code) {
        self.reporter.flush();
        if (code) {
          log_('Runner Process Exited With Error Code: ' + code);
          runnerErrorCount += 1;
        }
        self.reporter.exitCode = code;
        if (typeof testsDoneCallback === 'function') {
          testsDoneCallback();
        }
        log_(scheduler.countActiveTasks() + 
          ' instance(s) of WebDriver still running');
      }); 
    };
  
    /**
     * Sends the run command.
     */
    RunnerFork.prototype.run = function() {
      this.process.send({
        command: 'run',
        configFile: configFile,
        additionalConfig: additionalConfig,
        capability: this.capability,
        specs: this.specs
      });
      this.reporter.reportHeader_();
    };
  
    // Don't start new process if there is only 1 task.
    var totalTasks = scheduler.numTasksRemaining(); 
    if (totalTasks === 1) {
      var Runner = require('./runner');
      var task = scheduler.nextTask();
      config.capabilities = task.capability; 
      config.specs = task.specs;
  
      var runner = new Runner(config);
      runner.run().then(function(exitCode) {
        process.exit(exitCode);
      }).catch(function(err) {
        log_('Error: ' + err.message);
        process.exit(1);
      });
    } else {
      if (config.debug) {
        throw new Error('Cannot run in debug mode with ' + 
          'multiCapabilities, count > 1, or sharding');
      }
      for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) {
        var createNextRunnerFork = function() {
          var task = scheduler.nextTask();
          if (task) {
            var done = function() {
              task.done();
              createNextRunnerFork();
            };
            var runnerFork = new RunnerFork(task);
            runnerFork.addEventHandlers(done);
            runnerFork.run();
          }
        };
        createNextRunnerFork();
      }
      log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver');
  
      process.on('exit', function(code) {
        if (code) {
          log_('Process exited with error code ' + code);
          process.exit(code);
        } else if (runnerErrorCount > 0) {
          reporter.reportSummary();
          process.exit(RUNNERS_FAILED_EXIT_CODE);
        } else {
          if (scheduler.numTasksRemaining() > 0) {
            throw new Error('BUG: launcher exited with ' + 
                scheduler.numTasksRemaining() + ' tasks remaining');
          }
          reporter.reportSummary();
          process.exit(0);
        }
      });
    }
  }