BrowserLocalChrome.prototype.startWdServer = function(browserCaps) {
  'use strict';
  var requestedBrowserName = browserCaps[webdriver.Capability.BROWSER_NAME];
  if (webdriver.Browser.CHROME !== requestedBrowserName) {
    throw new Error('BrowserLocalChrome called with unexpected browser ' +
        requestedBrowserName);
  }
  if (!this.chromedriver_) {
    throw new Error('Must set chromedriver before starting it');
  }

  var serverCommand = this.chromedriver_;
  var serverArgs = ['--port=' + this.serverPort_];
  if (logger.isLogging('extra')) {
    serverArgs.push('--verbose');
  }
  browserCaps.chromeOptions = {args: CHROME_FLAGS.slice()};
  if (this.chrome_) {
    browserCaps.chromeOptions.binary = this.chrome_;
  }
  this.startChildProcess(serverCommand, serverArgs, 'WD server');
  // Make sure we set serverUrl_ only after the child process start success.
  this.app_.schedule('Set WD server URL', function() {
    this.serverUrl_ = 'http://localhost:' + this.serverPort_;
  }.bind(this));
};
Example #2
0
/**
 * Adds "logger.extra" level logging to all process scheduler tasks.
 *
 * @param {string=} appName log message prefix.
 * @param {webdriver.promise.ControlFlow} app The app to inject logging into.
 */
function injectWdAppLogging(appName, app) {
  'use strict';
  if (!app.isLoggingInjected) {
    var realExecute = app.execute;
    if (logger.isLogging('extra')) {
      var realGetNextTask = app.getNextTask_;
      app.getNextTask_ = function() {
        var task = realGetNextTask.apply(app, arguments);
        if (task) {
          logger.extra('(%s) %s', appName, task.getDescription());
        } else {
          logger.extra('(%s) no next task', appName);
        }
        return task;
      };

      app.execute = function(fn, opt_description) {
        logger.extra('(%s) %s', appName,
            opt_description || 'function ' + (fn.name || '<unnamed>'));
        return realExecute.apply(app, arguments);
      };
    }

    // TODO(klm): Migrate all code to execute().
    // Monkey-patching the old schedule(desc,fn) API just for gradual migration.
    app.schedule = function(description, fn) {
      logger.extra('(%s) %s', appName, description);
      return realExecute.call(app, fn, description);
    };

    app.isLoggingInjected = true;
  }
}
Example #3
0
WebDriverServer.prototype.onDevToolsMessage_ = function(message) {
  'use strict';
  if (this.driver_) {
    throw new Error('Internal error: DevTools callback called with WebDriver');
  }
  var level = DEVTOOLS_METHOD_TO_LEVEL[message.method] || 'debug';
  if (logger.isLogging(level)) {
    logger[level].apply(undefined, ['%sDevTools message: %s',
        (this.isRecordingDevTools_ ? '' : '#'), JSON.stringify(message)]);
  }
  if (message.method && 0 === message.method.indexOf('Tracing.')) {
    this.onTracingMessage_(message);
    return;  // Don't clutter the DevTools log with tracing events.
  }
  if (this.isRecordingDevTools_) {
    this.devToolsMessages_.push(message);
  }
  // If abortTimer_ is set, it means we received an 'Inspector.detached'
  // message, as noted below, so ignore messages until our abortTimer fires.
  if (!this.abortTimer_) {
    if ('Page.loadEventFired' === message.method) {
      if (this.isRecordingDevTools_) {
        this.onPageLoad_();
      }
    } else if ('Inspector.detached' === message.method) {
      if (this.pageLoadDonePromise_ && this.pageLoadDonePromise_.isPending()) {
        // This message typically means that the browser has crashed.
        // Instead of waiting for the timeout, we'll give the browser a couple
        // seconds to paint an error message (for our screenshot) and then fail
        // the page load.
        var err = new Error('Inspector detached on run ' + this.runNumber_ +
            ', did the browser crash?', this.runNumber_);
        this.abortTimer_ = global.setTimeout(
            this.onPageLoad_.bind(this, err), DETACH_TIMEOUT_MS_);
      } else {
        // TODO detach during coalesce?
        logger.warn('%s after Page.loadEventFired?', message.method);
      }
    }
    // We might be able to detect timeouts via Network.loadingFailed and
    // Page.frameStoppedLoading messages. For now we'll let our timeoutTimer
    // handle this.
  }
};
 this.scheduleNeedsXvfb_().then(function(useXvfb) {
   var cmd = this.chromedriver_;
   var args = ['-port=' + this.serverPort_];
   if (logger.isLogging('extra')) {
     args.push('--verbose');
   }
   if (useXvfb) {
     // Use a fake X display, otherwise a scripted "sendKeys" fails with:
     //   an X display is required for keycode conversions, consider using Xvfb
     // TODO(wrightt) submit a crbug; Android shouldn't use the host's keymap!
     args.splice(0, 0, '-a', cmd);
     cmd = 'xvfb-run';
   }
   this.startChildProcess(cmd, args, 'WD server');
   // Make sure we set serverUrl_ only after the child process start success.
   this.app_.schedule('Set DevTools URL', function() {
     this.serverUrl_ = 'http://localhost:' + this.serverPort_;
   }.bind(this));
 }.bind(this));
Example #5
0
Client.prototype.postResultFile_ = function(job, resultFile, fields, callback) {
  'use strict';
  logger.extra('postResultFile: job=%s resultFile=%s fields=%j callback=%s',
      job.id, (resultFile ? 'present' : null), fields, callback);
  var servlet = WORK_DONE_SERVLET;
  var mp = new multipart.Multipart();
  mp.addPart('id', job.id, ['Content-Type: text/plain']);
  mp.addPart('location', this.location_);
  if (this.apiKey) {
    mp.addPart('key', this.apiKey);
  }
  if (fields) {
    fields.forEach(function(nameValue) {
      mp.addPart(nameValue[0], nameValue[1]);
    });
  }
  if (resultFile) {
    // A part with name="resultType" and then the content with name="file"
    if (exports.ResultFile.ResultType.IMAGE === resultFile.resultType) {
      // Images go to a different servlet and don't need the resultType part
      servlet = RESULT_IMAGE_SERVLET;
    } else {
      if (resultFile.resultType) {
        mp.addPart(resultFile.resultType, '1');
      }
      mp.addPart('_runNumber', String(job.runNumber));
      mp.addPart('_cacheWarmed', job.isCacheWarm ? '1' : '0');
    }
    var fileName = job.runNumber + (job.isCacheWarm ? '_Cached_' : '_') +
        resultFile.fileName;
    mp.addFilePart(
        'file', fileName, resultFile.contentType, resultFile.content);
    if (logger.isLogging('debug')) {
      logger.debug('Writing a local copy of %s', fileName);
      var body = resultFile.content;
      var bodyBuffer = (body instanceof Buffer ? body : new Buffer(body));
      fs.mkdir('results/', parseInt('0755', 8), function(e) {
        if (!e || 'EEXIST' === e.code) {
          var subdir = 'results/' + job.id + '/';
          fs.mkdir(subdir, parseInt('0755', 8), function(e) {
            if (!e || 'EEXIST' === e.code) {
              fs.writeFile(subdir + fileName, bodyBuffer);
            }
          });
        }
      });
    }
  }
  // TODO(klm): change body to chunked request.write().
  // Only makes sense if done for file content, the rest is peanuts.
  var mpResponse = mp.getHeadersAndBody();

  var options = {
      method: 'POST',
      host: this.baseUrl_.hostname,
      port: this.baseUrl_.port,
      path: path.join(this.baseUrl_.path, servlet),
      headers: mpResponse.headers
    };
  var request = http.request(options, function(res) {
    exports.processResponse(res, callback);
  });
  request.on('error', function(e) {
    logger.warn('Unable to post result: ' + e.message);
    if (callback) {
      callback(e, '');
    }
  });
  request.end(mpResponse.bodyBuffer, 'UTF-8');
};
Example #6
0
Client.prototype.postResultFile_ = function(job, resultFile, fields, callback) {
  'use strict';
  logger.extra('postResultFile: job=%s resultFile=%s fields=%j callback=%s',
      job.id, (resultFile ? 'present' : null), fields, callback);
  var servlet = WORK_DONE_SERVLET;
  var mp = new multipart.Multipart();
  mp.addPart('id', job.id, ['Content-Type: text/plain']);
  mp.addPart('location', this.location_);
  if (this.apiKey_) {
    mp.addPart('key', this.apiKey_);
  }
  if (this.name_) {
    mp.addPart('pc', this.name_);
  } else if (this.deviceSerial_) {
    mp.addPart('pc', this.deviceSerial_);
  }
  if (fields) {
    fields.forEach(function(nameValue) {
      mp.addPart(nameValue[0], nameValue[1]);
    });
  }
  if (resultFile) {
    if (exports.ResultFile.ResultType.IMAGE === resultFile.resultType ||
        exports.ResultFile.ResultType.PCAP === resultFile.resultType) {
      // Images and pcaps must be uploaded to the RESULT_IMAGE_SERVLET, with no
      // resultType or run/cache parts.
      //
      // If we submit the pcap via the regular servlet, it would be used for
      // the waterfall instead of the DevTools trace, which we don't want.
      servlet = RESULT_IMAGE_SERVLET;
    } else {
      if (resultFile.resultType) {
        mp.addPart(resultFile.resultType, '1');
      }
      mp.addPart('_runNumber', String(job.runNumber));
      mp.addPart('_cacheWarmed', job.isCacheWarm ? '1' : '0');
    }
    var fileName = createFileName(job, resultFile.fileName);
    mp.addFilePart(
        'file', fileName, resultFile.contentType, resultFile.content);
    if (logger.isLogging('debug')) {
      logger.debug('Writing a local copy of %s', fileName);
      var body = resultFile.content;
      var bodyBuffer = (body instanceof Buffer ? body : new Buffer(body));
      fs.mkdir('results', parseInt('0755', 8), function(e) {
        if (!e || 'EEXIST' === e.code) {
          var subdir = path.join('results', job.id);
          fs.mkdir(subdir, parseInt('0755', 8), function(e) {
            if (!e || 'EEXIST' === e.code) {
              fs.writeFile(path.join(subdir, fileName), bodyBuffer);
            }
          });
        }
      });
    }
  }
  // TODO(klm): change body to chunked request.write().
  // Only makes sense if done for file content, the rest is peanuts.
  var mpResponse = mp.getHeadersAndBody();

  var options = {
      method: 'POST',
      host: this.baseUrl_.hostname,
      port: this.baseUrl_.port,
      path: this.baseUrl_.path.replace(/\/+$/, '') + '/' + servlet,
      headers: mpResponse.headers
    };
  var request = http.request(options, function(res) {
    exports.processResponse(res, callback);
  });
  request.on('error', function(e) {
    logger.warn('Unable to post result: ' + e.message);
    if (callback) {
      callback(e, '');
    }
  });
  request.end(mpResponse.bodyBuffer, 'UTF-8');
};