diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp index 27b7ee5e3d6..399a3781dfc 100644 --- a/src/plugins/ios/simulatorcontrol.cpp +++ b/src/plugins/ios/simulatorcontrol.cpp @@ -54,7 +54,19 @@ namespace Ios { namespace Internal { 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) { @@ -81,6 +93,56 @@ static bool runSimCtlCommand(QStringList args, QByteArray *output) return runCommand(QStringLiteral("xcrun"), args, output); } +static QList getAvailableDeviceTypes() +{ + QList 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 getAvailableRuntimes() +{ + QList 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 { private: SimulatorControlPrivate(); @@ -97,8 +159,21 @@ private: const QString &bundleIdentifier, bool waitForDebugger, const QStringList &extraArgs, const QString &stdoutPath, const QString &stderrPath); + void deleteSimulator(QFutureInterface &fi, + const QString &simUdid); + void resetSimulator(QFutureInterface &fi, + const QString &simUdid); + void renameSimulator(QFutureInterface &fi, + const QString &simUdid, const QString &newName); + void createSimulator(QFutureInterface &fi, + const QString &name, const DeviceTypeInfo &deviceType, + const RuntimeInfo &runtime); + void takeSceenshot(QFutureInterface &fi, const QString &simUdid, + const QString &filePath); static QList availableDevices; + static QList availableDeviceTypes; + static QList availableRuntimes; friend class SimulatorControl; }; @@ -122,21 +197,21 @@ static QList getAllSimulatorDevices() { QList simulatorDevices; QByteArray output; - runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), QLatin1String("devices")}, &output); + runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), DevicesTag}, &output); QJsonDocument doc = QJsonDocument::fromJson(output); 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()) { const QJsonArray devices = runtimeObject.value(runtime).toArray(); foreach (const QJsonValue deviceValue, devices) { QJsonObject deviceObject = deviceValue.toObject(); SimulatorInfo device; - device.identifier = deviceObject.value(QStringLiteral("udid")).toString(); - device.name = deviceObject.value(QStringLiteral("name")).toString(); + device.identifier = deviceObject.value(UdidTag).toString(); + device.name = deviceObject.value(NameTag).toString(); device.runtimeName = runtime; - const QString availableStr = deviceObject.value(QStringLiteral("availability")).toString(); - device.available = !availableStr.contains("unavailable"); - device.state = deviceObject.value(QStringLiteral("state")).toString(); + const QString availableStr = deviceObject.value(AvailabilityTag).toString(); + device.available = !availableStr.contains(UnavailabilityToken); + device.state = deviceObject.value(StateTag).toString(); simulatorDevices.append(device); } } @@ -154,12 +229,36 @@ static QList getAvailableSimulators() return availableDevices; } -void SimulatorControl::updateAvailableSimulators() +QFuture > SimulatorControl::updateDeviceTypes() +{ + QFuture< QList > future = Utils::runAsync(getAvailableDeviceTypes); + Utils::onResultReady(future, [](const QList &deviceTypes) { + SimulatorControlPrivate::availableDeviceTypes = deviceTypes; + }); + return future; +} + +QList SimulatorControl::availableRuntimes() +{ + return SimulatorControlPrivate::availableRuntimes; +} + +QFuture > SimulatorControl::updateRuntimes() +{ + QFuture< QList > future = Utils::runAsync(getAvailableRuntimes); + Utils::onResultReady(future, [](const QList &runtimes) { + SimulatorControlPrivate::availableRuntimes = runtimes; + }); + return future; +} + +QFuture< QList > SimulatorControl::updateAvailableSimulators() { QFuture< QList > future = Utils::runAsync(getAvailableSimulators); Utils::onResultReady(future, [](const QList &devices) { SimulatorControlPrivate::availableDevices = devices; }); + return future; } bool SimulatorControl::isSimulatorRunning(const QString &simUdid) @@ -199,7 +298,40 @@ SimulatorControl::launchApp(const QString &simUdid, const QString &bundleIdentif waitForDebugger, extraArgs, stdoutPath, stderrPath); } +QFuture SimulatorControl::deleteSimulator(const QString &simUdid) const +{ + return Utils::runAsync(&SimulatorControlPrivate::deleteSimulator, d, simUdid); +} + +QFuture SimulatorControl::resetSimulator(const QString &simUdid) const +{ + return Utils::runAsync(&SimulatorControlPrivate::resetSimulator, d, simUdid); +} + +QFuture SimulatorControl::renameSimulator(const QString &simUdid, + const QString &newName) const +{ + return Utils::runAsync(&SimulatorControlPrivate::renameSimulator, d, simUdid, newName); +} + +QFuture +SimulatorControl::createSimulator(const QString &name, + const DeviceTypeInfo &deviceType, + const RuntimeInfo &runtime) +{ + return Utils::runAsync(&SimulatorControlPrivate::createSimulator, d, name, deviceType, runtime); +} + +QFuture SimulatorControl::takeSceenshot(const QString &simUdid, + const QString &filePath) +{ + return Utils::runAsync(&SimulatorControlPrivate::takeSceenshot, d, simUdid, filePath); +} + +// Static members QList SimulatorControlPrivate::availableDevices; +QList SimulatorControlPrivate::availableDeviceTypes; +QList SimulatorControlPrivate::availableRuntimes; SimulatorControlPrivate::SimulatorControlPrivate() { @@ -348,5 +480,66 @@ void SimulatorControlPrivate::launchApp(QFutureInterface &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 &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 &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 &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 &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 Ios diff --git a/src/plugins/ios/simulatorcontrol.h b/src/plugins/ios/simulatorcontrol.h index c983890820b..bce0406401b 100644 --- a/src/plugins/ios/simulatorcontrol.h +++ b/src/plugins/ios/simulatorcontrol.h @@ -39,20 +39,33 @@ namespace Internal { 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: bool isBooted() const { return state.compare(QStringLiteral("Booted")) == 0; } bool available; QString state; 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 { Q_OBJECT @@ -72,8 +85,12 @@ public: ~SimulatorControl(); public: + static QList availableDeviceTypes(); + static QFuture > updateDeviceTypes(); + static QList availableRuntimes(); + static QFuture > updateRuntimes(); static QList availableSimulators(); - static void updateAvailableSimulators(); + static QFuture > updateAvailableSimulators(); static bool isSimulatorRunning(const QString &simUdid); static QString bundleIdentifier(const Utils::FileName &bundlePath); static QString bundleExecutable(const Utils::FileName &bundlePath); @@ -85,6 +102,13 @@ public: bool waitForDebugger, const QStringList &extraArgs, const QString& stdoutPath = QString(), const QString& stderrPath = QString()) const; + QFuture deleteSimulator(const QString &simUdid) const; + QFuture resetSimulator(const QString &simUdid) const; + QFuture renameSimulator(const QString &simUdid, const QString &newName) const; + QFuture createSimulator(const QString &name, + const DeviceTypeInfo &deviceType, + const RuntimeInfo &runtime); + QFuture takeSceenshot(const QString &simUdid, const QString &filePath); private: SimulatorControlPrivate *d; @@ -92,4 +116,6 @@ private: } // namespace Internal } // namespace Ios +Q_DECLARE_METATYPE(Ios::Internal::DeviceTypeInfo) +Q_DECLARE_METATYPE(Ios::Internal::RuntimeInfo) Q_DECLARE_METATYPE(Ios::Internal::SimulatorInfo)