зеркало из https://github.com/microsoft/appium.git
Support for iOS logs for devices and simulators
Fix after comments by @jlipps Fix require section
This commit is contained in:
Родитель
1ddc5f4e19
Коммит
caf7d789e9
|
@ -25,3 +25,6 @@
|
|||
[submodule "submodules/SafariLauncher"]
|
||||
path = submodules/SafariLauncher
|
||||
url = https://github.com/snevesbarros/SafariLauncher.git
|
||||
[submodule "submodules/libimobiledevice-macosx"]
|
||||
path = submodules/libimobiledevice-macosx
|
||||
url = https://github.com/benvium/libimobiledevice-macosx.git
|
||||
|
|
|
@ -71,6 +71,7 @@ module.exports = function(grunt) {
|
|||
reporter: 'spec'
|
||||
}
|
||||
}
|
||||
, maxBuffer: 2000*1024
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-mocha-test');
|
||||
|
|
|
@ -15,6 +15,11 @@ var errors = require('../../server/errors.js')
|
|||
|
||||
var androidController = {};
|
||||
|
||||
|
||||
var logTypesSupported = {
|
||||
'logcat' : 'Logs for Android applications on real device and emulators via ADB'
|
||||
};
|
||||
|
||||
androidController.keyevent = function(keycode, metastate, cb) {
|
||||
this.proxy(["pressKeyCode", {keycode: keycode, metastate: metastate}], cb);
|
||||
};
|
||||
|
@ -661,8 +666,43 @@ androidController.installApp = function(appPackage, cb) {
|
|||
deviceCommon.installApp(installationCommand, this.udid, appPackage, cb);
|
||||
};
|
||||
|
||||
androidController.getLog = deviceCommon.getLog;
|
||||
androidController.getLogTypes = deviceCommon.getLogTypes;
|
||||
androidController.getLog = function(logType, cb) {
|
||||
// Check if passed logType is supported
|
||||
if (!_.has(logTypesSupported, logType)) {
|
||||
return cb(null, {
|
||||
status: status.codes.UnknownError.code
|
||||
, value: "Unsupported log type '" + logType + "', supported types : " + JSON.stringify(logTypesSupported)
|
||||
});
|
||||
}
|
||||
var logs;
|
||||
// Check that current logType and instance is compatible
|
||||
if (logType == 'logcat') {
|
||||
try {
|
||||
logs = this.adb.getLogcatLogs();
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
}
|
||||
// If logs captured sucessfully send response with data, else send error
|
||||
if (logs) {
|
||||
return cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: logs
|
||||
});
|
||||
} else {
|
||||
return cb(null, {
|
||||
status: status.codes.UnknownError.code
|
||||
, value: "Incompatible logType for this device"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
androidController.getLogTypes = function(cb) {
|
||||
return cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: logTypesSupported
|
||||
});
|
||||
};
|
||||
|
||||
androidController.unpackApp = function(req, cb) {
|
||||
deviceCommon.unpackApp(req, '.apk', cb);
|
||||
|
|
|
@ -10,7 +10,6 @@ var errors = require('../server/errors.js')
|
|||
var UnknownError = errors.UnknownError
|
||||
, ProtocolError = errors.ProtocolError;
|
||||
|
||||
|
||||
exports.respond = function(response, cb) {
|
||||
if (typeof response === 'undefined') {
|
||||
cb(null, '');
|
||||
|
@ -233,28 +232,3 @@ exports.convertElementForAtoms = function(args, cb) {
|
|||
cb(null, args);
|
||||
};
|
||||
|
||||
exports.getLog = function(logType, cb) {
|
||||
// go ahead and respond with 'logcat' type no matter what they send in
|
||||
if (logType !== 'logcat') {
|
||||
logger.warn("Trying to get log type of " + logType + ", giving logcat " +
|
||||
"instead");
|
||||
}
|
||||
var logs;
|
||||
try {
|
||||
logs = this.adb.getLogcatLogs();
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: logs
|
||||
});
|
||||
};
|
||||
|
||||
exports.getLogTypes = function(cb) {
|
||||
return cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: ['logcat']
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ var uuid = require('uuid-js')
|
|||
|
||||
var iOSController = {};
|
||||
|
||||
var logTypesSupported = {
|
||||
'syslog' : 'Logs for iOS applications on real device and simulators'
|
||||
};
|
||||
|
||||
iOSController.findUIElementOrElements = function(strategy, selector, ctx, many, cb) {
|
||||
selector = escapeSpecialChars(selector, "'");
|
||||
if (typeof ctx === "undefined" || !ctx) {
|
||||
|
@ -1427,12 +1431,39 @@ iOSController.getCurrentActivity= function(cb) {
|
|||
cb(new NotYetImplementedError(), null);
|
||||
};
|
||||
|
||||
iOSController.getLogs = function(logType, cb) {
|
||||
cb(new NotYetImplementedError(), null);
|
||||
iOSController.getLogTypes = function(cb) {
|
||||
return cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: logTypesSupported
|
||||
});
|
||||
};
|
||||
|
||||
iOSController.getLogTypes = function(cb) {
|
||||
cb(new NotYetImplementedError(), null);
|
||||
iOSController.getLog = function(logType, cb) {
|
||||
// Check if passed logType is supported
|
||||
if (!_.has(logTypesSupported, logType)) {
|
||||
return cb(null, {
|
||||
status: status.codes.UnknownError.code
|
||||
, value: "Unsupported log type '" + logType + "' for this device, supported types : " + JSON.stringify(logTypesSupported)
|
||||
});
|
||||
}
|
||||
var logs;
|
||||
try {
|
||||
logs = this.logs.getLogs(logType);
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
// If logs captured successfully send response with data, else send error
|
||||
if (logs) {
|
||||
return cb(null, {
|
||||
status: status.codes.Success.code
|
||||
, value: logs
|
||||
});
|
||||
} else {
|
||||
return cb(null, {
|
||||
status: status.codes.UnknownError.code
|
||||
, value: "Unknown error while getting logs"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = iOSController;
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
"use strict";
|
||||
|
||||
var spawn = require('win-spawn')
|
||||
, through = require('through')
|
||||
, _ = require('underscore')
|
||||
, logger = require('../../server/logger.js').get('appium');
|
||||
|
||||
// Date-Utils: Polyfills for the Date object
|
||||
require('date-utils');
|
||||
|
||||
var IosLog = function(opts) {
|
||||
this.udid = opts.udid;
|
||||
this.xcodeVersion = opts.xcodeVersion;
|
||||
this.debugMode = opts.debug;
|
||||
this.debugTrace = opts.debugTrace;
|
||||
this.proc = null;
|
||||
this.onIosLogStart = null;
|
||||
this.iosLogStarted = false;
|
||||
this.iosLogStartTime = null;
|
||||
this.calledBack = false;
|
||||
this.logs = [];
|
||||
this.logRow = "";
|
||||
this.logsSinceLastRequest = [];
|
||||
};
|
||||
|
||||
IosLog.prototype.debug = function(msg) {
|
||||
if (this.debugMode) {
|
||||
logger.debug(("[IOS_SYSLOG_CAPTURE] " + msg).grey);
|
||||
}
|
||||
};
|
||||
|
||||
IosLog.prototype.startCapture = function(cb) {
|
||||
this.onIosLogStart = cb;
|
||||
// Select cmd for log capture
|
||||
if (this.udid) {
|
||||
var spawnEnv = _.clone(process.env);
|
||||
spawnEnv.PATH = process.env.PATH + ":" + process.cwd() + "/build/libimobiledevice-macosx/";
|
||||
spawnEnv.DYLD_LIBRARY_PATH = process.cwd() + "/build/libimobiledevice-macosx/:" + process.env.DYLD_LIBRARY_PATH
|
||||
this.debug("Starting iOS device log capture via idevicesyslog");
|
||||
this.proc = spawn("idevicesyslog", ["-u", this.udid], {env: spawnEnv});
|
||||
}
|
||||
else {
|
||||
if (parseInt(this.xcodeVersion.split(".")[0], 10) >= 5) {
|
||||
this.debug("Starting iOS 7.* simulator log capture");
|
||||
this.proc = spawn("tail", ["-f", "-n0", process.env.HOME + "/Library/Logs/iOS Simulator/7.0/system.log"]);
|
||||
}
|
||||
else {
|
||||
this.debug("Starting iOS 6.* simulator log capture");
|
||||
this.proc = spawn("tail", ["-f", "-n0", "/var/log/system.log"]);
|
||||
}
|
||||
}
|
||||
this.proc.stdout.setEncoding('utf8');
|
||||
this.proc.stderr.setEncoding('utf8');
|
||||
this.proc.on('error', function(err) {
|
||||
logger.error("iOS log capture failed: " + err.message);
|
||||
if (!this.calledBack) {
|
||||
this.calledBack = true;
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this));
|
||||
this.proc.stdout.pipe(through(this.onStdout.bind(this)));
|
||||
this.proc.stderr.pipe(through(this.onStderr.bind(this)));
|
||||
};
|
||||
|
||||
IosLog.prototype.stopCapture = function() {
|
||||
this.debug("Stopping iOS log capture");
|
||||
this.proc.kill();
|
||||
this.proc = null;
|
||||
};
|
||||
|
||||
IosLog.prototype.onStdout = function(data) {
|
||||
this.logRow += data;
|
||||
if (data.substr(-1,1) == "\n") {
|
||||
this.onOutput(data, "");
|
||||
this.logRow = "";
|
||||
}
|
||||
};
|
||||
|
||||
IosLog.prototype.onStderr = function(data) {
|
||||
if (/execvp\(\)/.test(data)) {
|
||||
logger.error("iOS log capture process failed to start");
|
||||
if (!this.calledBack) {
|
||||
this.calledBack = true;
|
||||
this.onIosLogStart(new Error("iOS log capture process failed to start"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.onOutput(data, ' STDERR');
|
||||
};
|
||||
|
||||
IosLog.prototype.onOutput = function(data, prefix) {
|
||||
if (!this.iosLogStarted) {
|
||||
this.iosLogStarted = true;
|
||||
this.iosLogStartTime = new Date();
|
||||
if (!this.calledBack) {
|
||||
this.calledBack = true;
|
||||
this.onIosLogStart();
|
||||
}
|
||||
}
|
||||
var logs = this.logRow.split("\n");
|
||||
_.each(logs, function(log) {
|
||||
log = log.trim();
|
||||
if (log) {
|
||||
// Filter old log rows
|
||||
var logRowParts = log.split(" ");
|
||||
var logRowDate = new Date(
|
||||
Date.parse(this.iosLogStartTime.getFullYear() + " " + logRowParts[0] + " " + logRowParts[2] + " " + logRowParts[3])
|
||||
);
|
||||
if (logRowDate.isAfter(this.iosLogStartTime)) {
|
||||
var logObj = {
|
||||
timestamp: Date.now()
|
||||
, level: 'ALL'
|
||||
, message: log
|
||||
};
|
||||
this.logs.push(logObj);
|
||||
this.logsSinceLastRequest.push(logObj);
|
||||
if (this.debugMode && this.debugTrace) {
|
||||
logger.debug('[IOS_SYSLOG_ROW ' + prefix + '] ' + log);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
IosLog.prototype.getLogs = function() {
|
||||
var ret = this.logsSinceLastRequest;
|
||||
this.logsSinceLastRequest = [];
|
||||
return ret;
|
||||
};
|
||||
|
||||
IosLog.prototype.getAllLogs = function() {
|
||||
return this.logs;
|
||||
};
|
||||
|
||||
module.exports = function(opts) {
|
||||
return new IosLog(opts);
|
||||
};
|
|
@ -14,6 +14,7 @@ var path = require('path')
|
|||
, helpers = require('../../helpers.js')
|
||||
, errors = require('../../server/errors.js')
|
||||
, deviceCommon = require('../common.js')
|
||||
, iOSLog = require('./ios-log.js')
|
||||
, status = require("../../server/status.js")
|
||||
, IDevice = require('node-idevice')
|
||||
, async = require('async')
|
||||
|
@ -38,6 +39,7 @@ var IOS = function(args) {
|
|||
this.automationTraceTemplatePath = args.automationTraceTemplatePath;
|
||||
this.removeTraceDir = args.removeTraceDir;
|
||||
this.deviceType = args.deviceType;
|
||||
this.logs = null;
|
||||
this.startingOrientation = args.startingOrientation || "PORTRAIT";
|
||||
this.curOrientation = this.startingOrientation;
|
||||
this.instruments = null;
|
||||
|
@ -171,6 +173,7 @@ IOS.prototype.start = function(cb, onDie) {
|
|||
this.detectUdid.bind(this),
|
||||
this.parseLocalizableStrings.bind(this),
|
||||
this.setDeviceType.bind(this),
|
||||
this.startLogCapture.bind(this),
|
||||
this.installToRealDevice.bind(this),
|
||||
this.startInstruments.bind(this),
|
||||
this.onInstrumentsLaunch.bind(this),
|
||||
|
@ -301,6 +304,10 @@ IOS.prototype.onInstrumentsExit = function(code, traceDir, launchCb) {
|
|||
}
|
||||
}.bind(this);
|
||||
|
||||
if (this.logs !== null) {
|
||||
this.logs.stopCapture();
|
||||
}
|
||||
|
||||
async.series([removeTraceDir, cleanup], function() {});
|
||||
|
||||
};
|
||||
|
@ -705,6 +712,20 @@ IOS.prototype.unpackApp = function(req, cb) {
|
|||
deviceCommon.unpackApp(req, '.app', cb);
|
||||
};
|
||||
|
||||
IOS.prototype.startLogCapture = function(cb) {
|
||||
if (this.logs !== null) {
|
||||
cb(new Error("Trying to start iOS log capture but it's already started!"));
|
||||
return;
|
||||
}
|
||||
this.logs = new iOSLog({
|
||||
udid: this.udid
|
||||
, xcodeVersion: this.xcodeVersion
|
||||
, debug: false
|
||||
, debugTrace: false
|
||||
});
|
||||
this.logs.startCapture(cb);
|
||||
};
|
||||
|
||||
_.each(_.keys(iOSHybrid), function(method) {
|
||||
IOS.prototype[method] = iOSHybrid[method];
|
||||
});
|
||||
|
|
|
@ -69,7 +69,8 @@
|
|||
"ws": "0.4.31",
|
||||
"socket.io" : "~0.9.16",
|
||||
"MD5" : "~1.1.0",
|
||||
"through": "~2.3.4"
|
||||
"through": "~2.3.4",
|
||||
"date-utils": "~1.2.14"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt travis"
|
||||
|
|
6
reset.sh
6
reset.sh
|
@ -179,7 +179,11 @@ reset_ios() {
|
|||
echo "* Copying SafariLauncher for real devices to build"
|
||||
run_cmd zip -r build/SafariLauncher/SafariLauncher submodules/SafariLauncher/build/Release-iphoneos/SafariLauncher.app
|
||||
fi
|
||||
|
||||
echo "* Cloning/updating libimobiledevice-macosx"
|
||||
run_cmd git submodule update --init submodules/libimobiledevice-macosx
|
||||
echo "* Copying libimobiledevice-macosx to build"
|
||||
run_cmd rm -rf build/libimobiledevice-macosx
|
||||
run_cmd cp -r submodules/libimobiledevice-macosx build/libimobiledevice-macosx
|
||||
}
|
||||
|
||||
get_apidemos() {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 8507dd21f9f3198e988ad68b306613097721523c
|
Загрузка…
Ссылка в новой задаче