Appium Server源码分析之作为Bootstrap客户端

发表于:2014-12-16 14:14

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:天地会珠海分舵    来源:51Testing软件测试网采编

分享:
  configure首先会去调用Appium类的getDeviceType这个方法,而这个方法最终又会去调用getDeviceTypeFromPlatform这个方法:
  Appium.prototype.getDeviceTypeFromPlatform = function (caps) {
  var device = null;
  switch (caps) {
  case 'ios':
  device = DT_IOS;
  break;
  case 'android':
  device = DT_ANDROID;
  break;
  case 'firefoxos':
  device = DT_FIREFOX_OS;
  break;
  }
  return device;
  };
  可以看到我们支持的platform就三个,所以我们在测试脚本设置capabilities选项的时候别填错了:
  ios
  android
  firefox
  最终返回的device定义如下,其实就是一些对应的字串:
  var DT_IOS = "ios"
  , DT_SAFARI = "safari"
  , DT_ANDROID = "android"
  , DT_CHROME = "chrome"
  , DT_SELENDROID = "selendroid"
  , DT_FIREFOX_OS = "firefoxos";
  但是别小看这些字串,我们下面会看到就是通过他们来实例化对应的设备类的。
  在获得deviceType后,configure方法下一个重要的步骤就是去根据这个deviceType字串去调用getNewDevice这个方法获得或者叫做创建一个对应的设备对象了:
Appium.prototype.getNewDevice = function (deviceType) {
var DeviceClass = (function () {
switch (deviceType) {
case DT_IOS:
return IOS;
case DT_SAFARI:
return Safari;
case DT_ANDROID:
return Android;
case DT_CHROME:
return Chrome;
case DT_SELENDROID:
return Selendroid;
case DT_FIREFOX_OS:
return FirefoxOs;
default:
throw new Error("Tried to start a device that doesn't exist: " +
deviceType);
}
})();
return new DeviceClass();
};
  DeviceClass这个变量是通过匿名函数返回的一个别的地方export出来的一个对象,比如以DT_ANDROID这个deviceType为例子,它返回的是Android,而Android的定义是:
  , Android = require('./devices/android/android.js')
  而android.js导出来的其实就是Android这个类:
  var Android = function () {
  this.init();
  };
  ...
  module.exports = Android;
  最终getNewDevice这个方法通过new DeviceClass()对设备类进行实例化,事实上就是相当于new Android(),在我们这个例子中。那么在实例化Android这个设备类的时候其构造函数调用init方法又做了什么事情呢?
  Android.prototype.init = function () {
  ...
  this.args.devicePort = 4724;
  ...
  this.initQueue();
  ...
  this.adb = null;
  ...
  this.uiautomator = null;
  ...
  }
  Android类的init方法会初始化一大堆成员变量,在这里我们列出几个我们这篇文章需要关注的:
  args.devicePort:指定我们pc端forward到bootstrap的端口号4724
  adb:Android Debug Bridge实例,初始化为null,往后很进行设置
  uiautomator:初始化为空,往后会设置成uiautomator类的实例,转本处理往bootstrap发送接收命令的事情
  当中还调用了一个initQueue方法来把Appium的Work Queue给初始化了,这个Work Queue其实就是nodejs的async这个库的queue这个流程控制对象。首先,我们要搞清楚我们为什么需要用到这个queue呢?我们知道nodejs是异步执行框架的,如果不做特别的处理的话,我们一下子来了几个命令如“1.点击按钮打开新页面;2.读取新页面读取目标控件内容和预期结果比较”,那么nodejs就会两个命令同时执行,但不保证谁先占用了cpu完成操作,那么问题就来了,如果在准备执行1之前,cpu调度切换到2,那么我们的脚本就会失败,因为我们1还没有执行完,新页面还没有打开!
  而async这个库的不同对象就是专门针对这些问题提供的解决办法,比如waterfals,auto,serials和queue等,其他的我暂时没有碰到,所以不清楚,至于queue是怎么运作的,我们摘录下网上的一个解析:
  queue: 是一个串行的消息队列,通过限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。
  这里worker决定了我们一次过能并行处理queue里面的task的数量,我们看下Appium的Work Queue的worker是多少:
Android.prototype.initQueue = function () {
this.queue = async.queue(function (task, cb) {
var action = task.action,
params = task.params;
this.cbForCurrentCmd = cb;
if (this.adb && !this.shuttingDown) {
this.uiautomator.sendAction(action, params, function (response) {
this.cbForCurrentCmd = null;
if (typeof cb === 'function') {
this.respond(response, cb);
}
}.bind(this));
} else {
this.cbForCurrentCmd = null;
var msg = "Tried to send command to non-existent Android device, " +
"maybe it shut down?";
if (this.shuttingDown) {
msg = "We're in the middle of shutting down the Android device, " +
"so your request won't be executed. Sorry!";
}
this.respond({
status: status.codes.UnknownError.code
, value: msg
}, cb);
}
}.bind(this), 1);
};
  从倒数第2行我们可以看到worker是1,也就是一次过appium只会处理一个task,其他push进去的task只能等待第一个task处理完。那么这样就清楚了,我们刚才提到的两个命令,只要保证1先于2入队列,那么在异步执行的nodejs框架中就能保证1会先于2而执行。
53/5<12345>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号