forked from qt-creator/qt-creator
iOS: Extend SimulatorControl
Add new API's to extend iOS simulator support in Qt Creator Task-number: QTCREATORBUG-17602 Change-Id: I7e949762a7740f2ffb927f25f327b3e82cd8e180 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -54,7 +54,19 @@ namespace Ios {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
static int SIMULATOR_START_TIMEOUT = 60000;
|
static int SIMULATOR_START_TIMEOUT = 60000;
|
||||||
static QString SIM_UDID_TAG = QStringLiteral("SimUdid");
|
|
||||||
|
// simctl Json Tags and tokens.
|
||||||
|
static QString DeviceTypeTag = QStringLiteral("devicetypes");
|
||||||
|
static QString DevicesTag = QStringLiteral("devices");
|
||||||
|
static QString AvailabilityTag = QStringLiteral("availability");
|
||||||
|
static QString UnavailabilityToken = QStringLiteral("unavailable");
|
||||||
|
static QString IdentifierTag = QStringLiteral("identifier");
|
||||||
|
static QString RuntimesTag = QStringLiteral("runtimes");
|
||||||
|
static QString NameTag = QStringLiteral("name");
|
||||||
|
static QString StateTag = QStringLiteral("state");
|
||||||
|
static QString UdidTag = QStringLiteral("udid");
|
||||||
|
static QString RuntimeVersionTag = QStringLiteral("version");
|
||||||
|
static QString BuildVersionTag = QStringLiteral("buildversion");
|
||||||
|
|
||||||
static bool checkForTimeout(const chrono::high_resolution_clock::time_point &start, int msecs = 10000)
|
static bool checkForTimeout(const chrono::high_resolution_clock::time_point &start, int msecs = 10000)
|
||||||
{
|
{
|
||||||
@@ -81,6 +93,56 @@ static bool runSimCtlCommand(QStringList args, QByteArray *output)
|
|||||||
return runCommand(QStringLiteral("xcrun"), args, output);
|
return runCommand(QStringLiteral("xcrun"), args, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QList<DeviceTypeInfo> getAvailableDeviceTypes()
|
||||||
|
{
|
||||||
|
QList<DeviceTypeInfo> deviceTypes;
|
||||||
|
QByteArray output;
|
||||||
|
runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), DeviceTypeTag}, &output);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(output);
|
||||||
|
if (!doc.isNull()) {
|
||||||
|
const QJsonArray runtimesArray = doc.object().value(DeviceTypeTag).toArray();
|
||||||
|
foreach (const QJsonValue deviceTypeValue, runtimesArray) {
|
||||||
|
QJsonObject deviceTypeObject = deviceTypeValue.toObject();
|
||||||
|
if (!deviceTypeObject.value(AvailabilityTag).toString().contains(UnavailabilityToken)) {
|
||||||
|
DeviceTypeInfo deviceType;
|
||||||
|
deviceType.name = deviceTypeObject.value(NameTag).toString("unknown");
|
||||||
|
deviceType.identifier = deviceTypeObject.value(IdentifierTag).toString("unknown");
|
||||||
|
deviceTypes.append(deviceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stable_sort(deviceTypes.begin(), deviceTypes.end());
|
||||||
|
} else {
|
||||||
|
qCDebug(simulatorLog) << "Error parsing json output from simctl. Output:" << output;
|
||||||
|
}
|
||||||
|
return deviceTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<RuntimeInfo> getAvailableRuntimes()
|
||||||
|
{
|
||||||
|
QList<RuntimeInfo> runtimes;
|
||||||
|
QByteArray output;
|
||||||
|
runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), RuntimesTag}, &output);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(output);
|
||||||
|
if (!doc.isNull()) {
|
||||||
|
const QJsonArray runtimesArray = doc.object().value(RuntimesTag).toArray();
|
||||||
|
foreach (const QJsonValue runtimeValue, runtimesArray) {
|
||||||
|
QJsonObject runtimeObject = runtimeValue.toObject();
|
||||||
|
if (!runtimeObject.value(AvailabilityTag).toString().contains(UnavailabilityToken)) {
|
||||||
|
RuntimeInfo runtime;
|
||||||
|
runtime.name = runtimeObject.value(NameTag).toString("unknown");
|
||||||
|
runtime.build = runtimeObject.value(BuildVersionTag).toString("unknown");
|
||||||
|
runtime.identifier = runtimeObject.value(IdentifierTag).toString("unknown");
|
||||||
|
runtime.version = runtimeObject.value(RuntimeVersionTag).toString("unknown");
|
||||||
|
runtimes.append(runtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stable_sort(runtimes.begin(), runtimes.end());
|
||||||
|
} else {
|
||||||
|
qCDebug(simulatorLog) << "Error parsing json output from simctl. Output:" << output;
|
||||||
|
}
|
||||||
|
return runtimes;
|
||||||
|
}
|
||||||
|
|
||||||
class SimulatorControlPrivate {
|
class SimulatorControlPrivate {
|
||||||
private:
|
private:
|
||||||
SimulatorControlPrivate();
|
SimulatorControlPrivate();
|
||||||
@@ -97,8 +159,21 @@ private:
|
|||||||
const QString &bundleIdentifier, bool waitForDebugger,
|
const QString &bundleIdentifier, bool waitForDebugger,
|
||||||
const QStringList &extraArgs, const QString &stdoutPath,
|
const QStringList &extraArgs, const QString &stdoutPath,
|
||||||
const QString &stderrPath);
|
const QString &stderrPath);
|
||||||
|
void deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid);
|
||||||
|
void resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid);
|
||||||
|
void renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid, const QString &newName);
|
||||||
|
void createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &name, const DeviceTypeInfo &deviceType,
|
||||||
|
const RuntimeInfo &runtime);
|
||||||
|
void takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi, const QString &simUdid,
|
||||||
|
const QString &filePath);
|
||||||
|
|
||||||
static QList<SimulatorInfo> availableDevices;
|
static QList<SimulatorInfo> availableDevices;
|
||||||
|
static QList<DeviceTypeInfo> availableDeviceTypes;
|
||||||
|
static QList<RuntimeInfo> availableRuntimes;
|
||||||
friend class SimulatorControl;
|
friend class SimulatorControl;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -122,21 +197,21 @@ static QList<SimulatorInfo> getAllSimulatorDevices()
|
|||||||
{
|
{
|
||||||
QList<SimulatorInfo> simulatorDevices;
|
QList<SimulatorInfo> simulatorDevices;
|
||||||
QByteArray output;
|
QByteArray output;
|
||||||
runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), QLatin1String("devices")}, &output);
|
runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), DevicesTag}, &output);
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(output);
|
QJsonDocument doc = QJsonDocument::fromJson(output);
|
||||||
if (!doc.isNull()) {
|
if (!doc.isNull()) {
|
||||||
const QJsonObject runtimeObject = doc.object().value(QStringLiteral("devices")).toObject();
|
const QJsonObject runtimeObject = doc.object().value(DevicesTag).toObject();
|
||||||
foreach (const QString &runtime, runtimeObject.keys()) {
|
foreach (const QString &runtime, runtimeObject.keys()) {
|
||||||
const QJsonArray devices = runtimeObject.value(runtime).toArray();
|
const QJsonArray devices = runtimeObject.value(runtime).toArray();
|
||||||
foreach (const QJsonValue deviceValue, devices) {
|
foreach (const QJsonValue deviceValue, devices) {
|
||||||
QJsonObject deviceObject = deviceValue.toObject();
|
QJsonObject deviceObject = deviceValue.toObject();
|
||||||
SimulatorInfo device;
|
SimulatorInfo device;
|
||||||
device.identifier = deviceObject.value(QStringLiteral("udid")).toString();
|
device.identifier = deviceObject.value(UdidTag).toString();
|
||||||
device.name = deviceObject.value(QStringLiteral("name")).toString();
|
device.name = deviceObject.value(NameTag).toString();
|
||||||
device.runtimeName = runtime;
|
device.runtimeName = runtime;
|
||||||
const QString availableStr = deviceObject.value(QStringLiteral("availability")).toString();
|
const QString availableStr = deviceObject.value(AvailabilityTag).toString();
|
||||||
device.available = !availableStr.contains("unavailable");
|
device.available = !availableStr.contains(UnavailabilityToken);
|
||||||
device.state = deviceObject.value(QStringLiteral("state")).toString();
|
device.state = deviceObject.value(StateTag).toString();
|
||||||
simulatorDevices.append(device);
|
simulatorDevices.append(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,12 +229,36 @@ static QList<SimulatorInfo> getAvailableSimulators()
|
|||||||
return availableDevices;
|
return availableDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulatorControl::updateAvailableSimulators()
|
QFuture<QList<DeviceTypeInfo> > SimulatorControl::updateDeviceTypes()
|
||||||
|
{
|
||||||
|
QFuture< QList<DeviceTypeInfo> > future = Utils::runAsync(getAvailableDeviceTypes);
|
||||||
|
Utils::onResultReady(future, [](const QList<DeviceTypeInfo> &deviceTypes) {
|
||||||
|
SimulatorControlPrivate::availableDeviceTypes = deviceTypes;
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<RuntimeInfo> SimulatorControl::availableRuntimes()
|
||||||
|
{
|
||||||
|
return SimulatorControlPrivate::availableRuntimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QList<RuntimeInfo> > SimulatorControl::updateRuntimes()
|
||||||
|
{
|
||||||
|
QFuture< QList<RuntimeInfo> > future = Utils::runAsync(getAvailableRuntimes);
|
||||||
|
Utils::onResultReady(future, [](const QList<RuntimeInfo> &runtimes) {
|
||||||
|
SimulatorControlPrivate::availableRuntimes = runtimes;
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture< QList<SimulatorInfo> > SimulatorControl::updateAvailableSimulators()
|
||||||
{
|
{
|
||||||
QFuture< QList<SimulatorInfo> > future = Utils::runAsync(getAvailableSimulators);
|
QFuture< QList<SimulatorInfo> > future = Utils::runAsync(getAvailableSimulators);
|
||||||
Utils::onResultReady(future, [](const QList<SimulatorInfo> &devices) {
|
Utils::onResultReady(future, [](const QList<SimulatorInfo> &devices) {
|
||||||
SimulatorControlPrivate::availableDevices = devices;
|
SimulatorControlPrivate::availableDevices = devices;
|
||||||
});
|
});
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimulatorControl::isSimulatorRunning(const QString &simUdid)
|
bool SimulatorControl::isSimulatorRunning(const QString &simUdid)
|
||||||
@@ -199,7 +298,40 @@ SimulatorControl::launchApp(const QString &simUdid, const QString &bundleIdentif
|
|||||||
waitForDebugger, extraArgs, stdoutPath, stderrPath);
|
waitForDebugger, extraArgs, stdoutPath, stderrPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QFuture<SimulatorControl::ResponseData> SimulatorControl::deleteSimulator(const QString &simUdid) const
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&SimulatorControlPrivate::deleteSimulator, d, simUdid);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<SimulatorControl::ResponseData> SimulatorControl::resetSimulator(const QString &simUdid) const
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&SimulatorControlPrivate::resetSimulator, d, simUdid);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<SimulatorControl::ResponseData> SimulatorControl::renameSimulator(const QString &simUdid,
|
||||||
|
const QString &newName) const
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&SimulatorControlPrivate::renameSimulator, d, simUdid, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<SimulatorControl::ResponseData>
|
||||||
|
SimulatorControl::createSimulator(const QString &name,
|
||||||
|
const DeviceTypeInfo &deviceType,
|
||||||
|
const RuntimeInfo &runtime)
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&SimulatorControlPrivate::createSimulator, d, name, deviceType, runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<SimulatorControl::ResponseData> SimulatorControl::takeSceenshot(const QString &simUdid,
|
||||||
|
const QString &filePath)
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&SimulatorControlPrivate::takeSceenshot, d, simUdid, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static members
|
||||||
QList<SimulatorInfo> SimulatorControlPrivate::availableDevices;
|
QList<SimulatorInfo> SimulatorControlPrivate::availableDevices;
|
||||||
|
QList<DeviceTypeInfo> SimulatorControlPrivate::availableDeviceTypes;
|
||||||
|
QList<RuntimeInfo> SimulatorControlPrivate::availableRuntimes;
|
||||||
|
|
||||||
SimulatorControlPrivate::SimulatorControlPrivate()
|
SimulatorControlPrivate::SimulatorControlPrivate()
|
||||||
{
|
{
|
||||||
@@ -348,5 +480,66 @@ void SimulatorControlPrivate::launchApp(QFutureInterface<SimulatorControl::Respo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SimulatorControlPrivate::deleteSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid)
|
||||||
|
{
|
||||||
|
SimulatorControl::ResponseData response(simUdid);
|
||||||
|
response.success = runSimCtlCommand({QStringLiteral("delete"), simUdid}, &response.commandOutput);
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorControlPrivate::resetSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid)
|
||||||
|
{
|
||||||
|
SimulatorControl::ResponseData response(simUdid);
|
||||||
|
response.success = runSimCtlCommand({QStringLiteral("erase"), simUdid}, &response.commandOutput);
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorControlPrivate::renameSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid, const QString &newName)
|
||||||
|
{
|
||||||
|
SimulatorControl::ResponseData response(simUdid);
|
||||||
|
response.success = runSimCtlCommand({QStringLiteral("rename"), simUdid, newName},
|
||||||
|
&response.commandOutput);
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorControlPrivate::createSimulator(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &name,
|
||||||
|
const DeviceTypeInfo &deviceType,
|
||||||
|
const RuntimeInfo &runtime)
|
||||||
|
{
|
||||||
|
SimulatorControl::ResponseData response("Invalid");
|
||||||
|
if (!name.isEmpty()) {
|
||||||
|
response.success = runSimCtlCommand({ QStringLiteral("create"), name,
|
||||||
|
deviceType.identifier,
|
||||||
|
runtime.identifier },
|
||||||
|
&response.commandOutput);
|
||||||
|
response.simUdid = response.success ? QString::fromLatin1(response.commandOutput.trimmed())
|
||||||
|
: QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulatorControlPrivate::takeSceenshot(QFutureInterface<SimulatorControl::ResponseData> &fi,
|
||||||
|
const QString &simUdid, const QString &filePath)
|
||||||
|
{
|
||||||
|
SimulatorControl::ResponseData response(simUdid);
|
||||||
|
response.success = runSimCtlCommand({ QStringLiteral("io"), simUdid, QStringLiteral("screenshot"),
|
||||||
|
filePath },
|
||||||
|
&response.commandOutput);
|
||||||
|
if (!fi.isCanceled())
|
||||||
|
fi.reportResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Ios
|
} // namespace Ios
|
||||||
|
@@ -39,20 +39,33 @@ namespace Internal {
|
|||||||
|
|
||||||
class SimulatorControlPrivate;
|
class SimulatorControlPrivate;
|
||||||
|
|
||||||
class SimulatorInfo {
|
|
||||||
|
class SimulatorEntity {
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString identifier;
|
||||||
|
bool operator <(const SimulatorEntity &o) const
|
||||||
|
{
|
||||||
|
return name < o.name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimulatorInfo : public SimulatorEntity {
|
||||||
public:
|
public:
|
||||||
bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; }
|
bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; }
|
||||||
bool available;
|
bool available;
|
||||||
QString state;
|
QString state;
|
||||||
QString runtimeName;
|
QString runtimeName;
|
||||||
QString name;
|
|
||||||
QString identifier;
|
|
||||||
bool operator <(const SimulatorInfo &o) const
|
|
||||||
{
|
|
||||||
return name < o.name;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RuntimeInfo : public SimulatorEntity{
|
||||||
|
public:
|
||||||
|
QString version;
|
||||||
|
QString build;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceTypeInfo : public SimulatorEntity {};
|
||||||
|
|
||||||
class SimulatorControl : public QObject
|
class SimulatorControl : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -72,8 +85,12 @@ public:
|
|||||||
~SimulatorControl();
|
~SimulatorControl();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static QList<DeviceTypeInfo> availableDeviceTypes();
|
||||||
|
static QFuture<QList<DeviceTypeInfo> > updateDeviceTypes();
|
||||||
|
static QList<RuntimeInfo> availableRuntimes();
|
||||||
|
static QFuture<QList<RuntimeInfo> > updateRuntimes();
|
||||||
static QList<SimulatorInfo> availableSimulators();
|
static QList<SimulatorInfo> availableSimulators();
|
||||||
static void updateAvailableSimulators();
|
static QFuture<QList<SimulatorInfo> > updateAvailableSimulators();
|
||||||
static bool isSimulatorRunning(const QString &simUdid);
|
static bool isSimulatorRunning(const QString &simUdid);
|
||||||
static QString bundleIdentifier(const Utils::FileName &bundlePath);
|
static QString bundleIdentifier(const Utils::FileName &bundlePath);
|
||||||
static QString bundleExecutable(const Utils::FileName &bundlePath);
|
static QString bundleExecutable(const Utils::FileName &bundlePath);
|
||||||
@@ -85,6 +102,13 @@ public:
|
|||||||
bool waitForDebugger, const QStringList &extraArgs,
|
bool waitForDebugger, const QStringList &extraArgs,
|
||||||
const QString& stdoutPath = QString(),
|
const QString& stdoutPath = QString(),
|
||||||
const QString& stderrPath = QString()) const;
|
const QString& stderrPath = QString()) const;
|
||||||
|
QFuture<ResponseData> deleteSimulator(const QString &simUdid) const;
|
||||||
|
QFuture<ResponseData> resetSimulator(const QString &simUdid) const;
|
||||||
|
QFuture<ResponseData> renameSimulator(const QString &simUdid, const QString &newName) const;
|
||||||
|
QFuture<ResponseData> createSimulator(const QString &name,
|
||||||
|
const DeviceTypeInfo &deviceType,
|
||||||
|
const RuntimeInfo &runtime);
|
||||||
|
QFuture<ResponseData> takeSceenshot(const QString &simUdid, const QString &filePath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SimulatorControlPrivate *d;
|
SimulatorControlPrivate *d;
|
||||||
@@ -92,4 +116,6 @@ private:
|
|||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Ios
|
} // namespace Ios
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Ios::Internal::DeviceTypeInfo)
|
||||||
|
Q_DECLARE_METATYPE(Ios::Internal::RuntimeInfo)
|
||||||
Q_DECLARE_METATYPE(Ios::Internal::SimulatorInfo)
|
Q_DECLARE_METATYPE(Ios::Internal::SimulatorInfo)
|
||||||
|
Reference in New Issue
Block a user