iOSのplistを黒魔術で操作しているところで文字化けしてた(Appiumの使っているライブラリの話)

現在のAppium(Appium1.3.6)は、Non-ASCII文字列をタイトルに持つアプリケーションは文字化けして表示されます。通常、iOSアプリはplist内のデータを読み込んで、そのメタ情報をアプリ名などとしてシミュレータ/実機上で表示します。なのですが、Non-ASCIIをファイル名に持つアプリで、Appiumを使ってインストールしたアプリではアプリ名が文字化けするという現象にでくわしていました。

原因を把握するためにAppiumのコードを読んでみましょう。

iOSアプリ起動の下準備

Appiumのコードを追っていると、以下でiOSデバイスに対する下準備を行っていると分かります。

  • appium/lib/devices/ios/ios.js

この中で、以下箇所で何やらplistを生成している模様。plistはbinaryとxmlがあるのですが、今回問題になっている文字化けはbinaryのplist。なので、bplistCreated(obj)が怪しそう。

IOS.prototype.setDeviceTypeInInfoPlist = function (cb) {
  var plist = path.resolve(this.args.app, "Info.plist");
  var dString = this.getDeviceString();
  var isiPhone = dString.toLowerCase().indexOf("ipad") === -1;
  var deviceTypeCode = isiPhone ? 1 : 2;
  parsePlistFile(plist, function (err, obj) {
    if (err) {
      logger.error("Could not set the device type in Info.plist");
      return cb(err, null);
    } else {
      var newPlist;
      obj.UIDeviceFamily = [deviceTypeCode];
      if (binaryPlist) {
        newPlist = bplistCreate(obj);
      } else {
        newPlist = xmlplist.build(obj);
      }
      fs.writeFile(plist, newPlist, function (err) {
        if (err) {
          logger.error("Could not save new Info.plist");
          cb(err);
        } else {
          logger.debug("Wrote new app Info.plist with device type");
          cb();
        }
      }.bind(this));
    }
  }.bind(this));
};

このpblistCreatedはどのライブラリで処理されているのか?というと、以下の模様。

この中で、Stringを書き込んでいる箇所が。

  function writeString(entry) {
    if (debug) {
      console.log('0x' + buffer.size().toString(16), 'writeString', entry.value, '(id: ' + entry.id + ')');
    }
    if (entry.type === 'string-utf16') {
      var utf16 = new Buffer(entry.value, 'ucs2');
      writeIntHeader(0x6, utf16.length / 2);
      // needs to be big endian so swap the bytes
      for (var i = 0; i < utf16.length; i += 2) {
        var t = utf16[i + 0];
        utf16[i + 0] = utf16[i + 1];
        utf16[i + 1] = t;
      }
      buffer.write(utf16);
    } else {
      var utf8 = new Buffer(entry.value, 'utf8');
      writeIntHeader(0x5, utf8.length);
      buffer.write(utf8);
    }
  }

ここを覗いて、writeIntHeaderに渡している0x6と0x5が何かにおいます。

によると、

kCFBinaryPlistMarkerASCIIString = 0x50,
kCFBinaryPlistMarkerUnicode16String = 0x60

らしい。なるほど。UTF-16として処理されなければいけない所を、ASCIIとして処理するようにしてしまっていたので、再現していたのですね。

この問題に対する修正は過去に既にマージさえていていたのだけれど、nodeに公開されていなかった模様。同僚が公開してくれると嬉しいな〜ってコメントしたら、昨日のうちに0.0.5を公開された。めでたい。

無事、0.0.5が公開されたので、このPRがマージされれば次回公開版からは修正されるでしょう。
https://github.com/appium/appium/pull/4714

binaryファイルを葬る黒魔術に出会ったわけでした。

蛇足

以下を見てみると、find_element(:accessibility_id ‘なにか’)とかで渡す要素が、何を受けつけられるのか見えますね。
今はxpath、id、name、class nameの他、ネイティブアプリに対してはuiautomationやuiautomator、accessibilityを、Webアプリに対してはlink text、css selector、tag name、partial link textが与えることができる模様。

  • appium/lib/devices/common.js
exports.checkValidLocStrat = function (strat, includeWeb, cb) {
  if (typeof includeWeb === "undefined") {
    includeWeb = false;
  }
  var validStrats = [
    'xpath',
    'id',
    'name',
    'class name'
  ];
  var nativeStrats = [
    '-ios uiautomation',
    'accessibility id',
    '-android uiautomator'
  ];
  var webStrats = [
    'link text',
    'css selector',
    'tag name',
    'partial link text'
  ];

なるほど。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中