forked from qt-creator/qt-creator
iOS: Move info parsing from devicectl to a function
And add a test to document what we expect from devicectl. Change-Id: I395171bb5316c21b461a01dce5c9ec87d81fb0c4 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -5,9 +5,14 @@
|
|||||||
|
|
||||||
#include "iostr.h"
|
#include "iostr.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
Utils::expected_str<QJsonValue> Ios::Internal::parseDevicectlResult(const QByteArray &rawOutput)
|
using namespace Utils;
|
||||||
|
|
||||||
|
namespace Ios::Internal {
|
||||||
|
|
||||||
|
expected_str<QJsonValue> parseDevicectlResult(const QByteArray &rawOutput)
|
||||||
{
|
{
|
||||||
// there can be crap (progress info) at front and/or end
|
// there can be crap (progress info) at front and/or end
|
||||||
const int firstCurly = rawOutput.indexOf('{');
|
const int firstCurly = rawOutput.indexOf('{');
|
||||||
@@ -18,7 +23,7 @@ Utils::expected_str<QJsonValue> Ios::Internal::parseDevicectlResult(const QByteA
|
|||||||
auto jsonOutput = QJsonDocument::fromJson(rawOutput.sliced(start, end - start + 1), &parseError);
|
auto jsonOutput = QJsonDocument::fromJson(rawOutput.sliced(start, end - start + 1), &parseError);
|
||||||
if (jsonOutput.isNull()) {
|
if (jsonOutput.isNull()) {
|
||||||
// parse error
|
// parse error
|
||||||
return Utils::make_unexpected(
|
return make_unexpected(
|
||||||
Tr::tr("Failed to parse devicectl output: %1.").arg(parseError.errorString()));
|
Tr::tr("Failed to parse devicectl output: %1.").arg(parseError.errorString()));
|
||||||
}
|
}
|
||||||
const QJsonValue errorValue = jsonOutput["error"];
|
const QJsonValue errorValue = jsonOutput["error"];
|
||||||
@@ -37,12 +42,45 @@ Utils::expected_str<QJsonValue> Ios::Internal::parseDevicectlResult(const QByteA
|
|||||||
if (!v.isUndefined())
|
if (!v.isUndefined())
|
||||||
error += "\n" + v.toString();
|
error += "\n" + v.toString();
|
||||||
}
|
}
|
||||||
return Utils::make_unexpected(error);
|
return make_unexpected(error);
|
||||||
}
|
}
|
||||||
const QJsonValue resultValue = jsonOutput["result"];
|
const QJsonValue resultValue = jsonOutput["result"];
|
||||||
if (resultValue.isUndefined()) {
|
if (resultValue.isUndefined()) {
|
||||||
return Utils::make_unexpected(
|
return make_unexpected(Tr::tr("Failed to parse devicectl output: 'result' is missing"));
|
||||||
Tr::tr("Failed to parse devicectl output: 'result' is missing"));
|
|
||||||
}
|
}
|
||||||
return resultValue;
|
return resultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected_str<QMap<QString, QString>> parseDeviceInfo(const QByteArray &rawOutput,
|
||||||
|
const QString &deviceUsbId)
|
||||||
|
{
|
||||||
|
const expected_str<QJsonValue> result = parseDevicectlResult(rawOutput);
|
||||||
|
if (!result)
|
||||||
|
return make_unexpected(result.error());
|
||||||
|
// find device
|
||||||
|
const QJsonArray deviceList = (*result)["devices"].toArray();
|
||||||
|
for (const QJsonValue &device : deviceList) {
|
||||||
|
const QString udid = device["hardwareProperties"]["udid"].toString();
|
||||||
|
// USB identifiers don't have dashes, but iOS device udids can. Remove.
|
||||||
|
if (QString(udid).remove('-') == deviceUsbId) {
|
||||||
|
// fill in the map that we use for the iostool data
|
||||||
|
QMap<QString, QString> info;
|
||||||
|
info[kDeviceName] = device["deviceProperties"]["name"].toString();
|
||||||
|
info[kDeveloperStatus] = QLatin1String(
|
||||||
|
device["deviceProperties"]["developerModeStatus"] == "enabled" ? vDevelopment
|
||||||
|
: vOff);
|
||||||
|
info[kDeviceConnected] = vYes; // that's the assumption
|
||||||
|
info[kOsVersion] = QLatin1String("%1 (%2)")
|
||||||
|
.arg(device["deviceProperties"]["osVersionNumber"].toString(),
|
||||||
|
device["deviceProperties"]["osBuildUpdate"].toString());
|
||||||
|
info[kCpuArchitecture] = device["hardwareProperties"]["cpuType"]["name"].toString();
|
||||||
|
info[kUniqueDeviceId] = udid;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// device not found, not handled by devicectl
|
||||||
|
// not translated, only internal logging
|
||||||
|
return make_unexpected(QLatin1String("Device is not handled by devicectl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ios::Internal
|
||||||
|
|||||||
@@ -9,6 +9,18 @@
|
|||||||
|
|
||||||
namespace Ios::Internal {
|
namespace Ios::Internal {
|
||||||
|
|
||||||
|
const char kDeviceName[] = "deviceName";
|
||||||
|
const char kDeveloperStatus[] = "developerStatus";
|
||||||
|
const char kDeviceConnected[] = "deviceConnected";
|
||||||
|
const char kOsVersion[] = "osVersion";
|
||||||
|
const char kCpuArchitecture[] = "cpuArchitecture";
|
||||||
|
const char kUniqueDeviceId[] = "uniqueDeviceId";
|
||||||
|
const char vOff[] = "*off*";
|
||||||
|
const char vDevelopment[] = "Development";
|
||||||
|
const char vYes[] = "YES";
|
||||||
|
|
||||||
Utils::expected_str<QJsonValue> parseDevicectlResult(const QByteArray &rawOutput);
|
Utils::expected_str<QJsonValue> parseDevicectlResult(const QByteArray &rawOutput);
|
||||||
|
Utils::expected_str<QMap<QString, QString>> parseDeviceInfo(const QByteArray &rawOutput,
|
||||||
|
const QString &deviceUsbId);
|
||||||
|
|
||||||
} // namespace Ios::Internal
|
} // namespace Ios::Internal
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "iosdevice.h"
|
#include "iosdevice.h"
|
||||||
|
|
||||||
|
#include "devicectlutils.h"
|
||||||
#include "iosconfigurations.h"
|
#include "iosconfigurations.h"
|
||||||
#include "iosconstants.h"
|
#include "iosconstants.h"
|
||||||
#include "iossimulator.h"
|
#include "iossimulator.h"
|
||||||
@@ -75,16 +76,6 @@ static QString CFStringRef2QString(CFStringRef s)
|
|||||||
|
|
||||||
namespace Ios::Internal {
|
namespace Ios::Internal {
|
||||||
|
|
||||||
const char kDeviceName[] = "deviceName";
|
|
||||||
const char kDeveloperStatus[] = "developerStatus";
|
|
||||||
const char kDeviceConnected[] = "deviceConnected";
|
|
||||||
const char kOsVersion[] = "osVersion";
|
|
||||||
const char kCpuArchitecture[] = "cpuArchitecture";
|
|
||||||
const char kUniqueDeviceId[] = "uniqueDeviceId";
|
|
||||||
const char vOff[] = "*off*";
|
|
||||||
const char vDevelopment[] = "Development";
|
|
||||||
const char vYes[] = "YES";
|
|
||||||
|
|
||||||
const char kHandler[] = "Handler";
|
const char kHandler[] = "Handler";
|
||||||
|
|
||||||
class IosDeviceInfoWidget : public IDeviceWidget
|
class IosDeviceInfoWidget : public IDeviceWidget
|
||||||
@@ -283,34 +274,14 @@ void IosDeviceManager::updateInfo(const QString &devId)
|
|||||||
{"devicectl", "list", "devices", "--quiet", "--json-output", "-"}});
|
{"devicectl", "list", "devices", "--quiet", "--json-output", "-"}});
|
||||||
},
|
},
|
||||||
[this, devId](const Process &process) {
|
[this, devId](const Process &process) {
|
||||||
auto jsonOutput = QJsonDocument::fromJson(process.rawStdOut());
|
const expected_str<QMap<QString, QString>> result = parseDeviceInfo(process.rawStdOut(),
|
||||||
// find device
|
devId);
|
||||||
const QJsonArray deviceList = jsonOutput["result"]["devices"].toArray();
|
if (!result) {
|
||||||
for (const QJsonValue &device : deviceList) {
|
qCDebug(detectLog) << result.error();
|
||||||
const QString udid = device["hardwareProperties"]["udid"].toString();
|
return DoneResult::Error;
|
||||||
// USB identifiers don't have dashes, but iOS device udids can. Remove.
|
|
||||||
if (QString(udid).remove('-') == devId) {
|
|
||||||
// fill in the map that we use for the iostool data
|
|
||||||
QMap<QString, QString> info;
|
|
||||||
info[kDeviceName] = device["deviceProperties"]["name"].toString();
|
|
||||||
info[kDeveloperStatus] = QLatin1String(
|
|
||||||
device["deviceProperties"]["developerModeStatus"] == "enabled"
|
|
||||||
? vDevelopment
|
|
||||||
: vOff);
|
|
||||||
info[kDeviceConnected] = vYes; // that's the assumption
|
|
||||||
info[kOsVersion]
|
|
||||||
= QLatin1String("%1 (%2)")
|
|
||||||
.arg(device["deviceProperties"]["osVersionNumber"].toString(),
|
|
||||||
device["deviceProperties"]["osBuildUpdate"].toString());
|
|
||||||
info[kCpuArchitecture]
|
|
||||||
= device["hardwareProperties"]["cpuType"]["name"].toString();
|
|
||||||
info[kUniqueDeviceId] = udid;
|
|
||||||
deviceInfo(devId, IosDevice::Handler::DeviceCtl, info);
|
|
||||||
return DoneResult::Success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// device not found, not handled by devicectl
|
deviceInfo(devId, IosDevice::Handler::DeviceCtl, *result);
|
||||||
return DoneResult::Error;
|
return DoneResult::Success;
|
||||||
},
|
},
|
||||||
CallDoneIf::Success);
|
CallDoneIf::Success);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ class tst_Devicectlutils : public QObject
|
|||||||
private slots:
|
private slots:
|
||||||
void parseError_data();
|
void parseError_data();
|
||||||
void parseError();
|
void parseError();
|
||||||
|
|
||||||
|
void parseDeviceInfo_data();
|
||||||
|
void parseDeviceInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_Devicectlutils::parseError_data()
|
void tst_Devicectlutils::parseError_data()
|
||||||
@@ -152,6 +155,156 @@ void tst_Devicectlutils::parseError()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_Devicectlutils::parseDeviceInfo_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("data");
|
||||||
|
QTest::addColumn<QString>("usbId");
|
||||||
|
QTest::addColumn<QString>("error");
|
||||||
|
QTest::addColumn<QMap<QString, QString>>("info");
|
||||||
|
|
||||||
|
const QByteArray data(R"raw(
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"arguments" : [
|
||||||
|
"devicectl",
|
||||||
|
"list",
|
||||||
|
"devices",
|
||||||
|
"--quiet",
|
||||||
|
"--json-output",
|
||||||
|
"-"
|
||||||
|
],
|
||||||
|
"commandType" : "devicectl.list.devices",
|
||||||
|
"environment" : {
|
||||||
|
"TERM" : "xterm-256color"
|
||||||
|
},
|
||||||
|
"jsonVersion" : 2,
|
||||||
|
"outcome" : "success",
|
||||||
|
"version" : "355.7.7"
|
||||||
|
},
|
||||||
|
"result" : {
|
||||||
|
"devices" : [
|
||||||
|
{
|
||||||
|
"capabilities" : [
|
||||||
|
{
|
||||||
|
"featureIdentifier" : "com.apple.coredevice.feature.connectdevice",
|
||||||
|
"name" : "Connect to Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"featureIdentifier" : "com.apple.coredevice.feature.acquireusageassertion",
|
||||||
|
"name" : "Acquire Usage Assertion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"featureIdentifier" : "com.apple.coredevice.feature.unpairdevice",
|
||||||
|
"name" : "Unpair Device"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connectionProperties" : {
|
||||||
|
"authenticationType" : "manualPairing",
|
||||||
|
"isMobileDeviceOnly" : false,
|
||||||
|
"lastConnectionDate" : "2024-01-29T08:49:25.179Z",
|
||||||
|
"pairingState" : "paired",
|
||||||
|
"potentialHostnames" : [
|
||||||
|
"00000000-0000000000000000.coredevice.local",
|
||||||
|
"00000000-0000-0000-0000-000000000000.coredevice.local"
|
||||||
|
],
|
||||||
|
"transportType" : "wired",
|
||||||
|
"tunnelState" : "disconnected",
|
||||||
|
"tunnelTransportProtocol" : "tcp"
|
||||||
|
},
|
||||||
|
"deviceProperties" : {
|
||||||
|
"bootedFromSnapshot" : true,
|
||||||
|
"bootedSnapshotName" : "com.apple.os.update-0000",
|
||||||
|
"ddiServicesAvailable" : false,
|
||||||
|
"developerModeStatus" : "enabled",
|
||||||
|
"hasInternalOSBuild" : false,
|
||||||
|
"name" : "Some iOS device",
|
||||||
|
"osBuildUpdate" : "21D50",
|
||||||
|
"osVersionNumber" : "17.3",
|
||||||
|
"rootFileSystemIsWritable" : false
|
||||||
|
},
|
||||||
|
"hardwareProperties" : {
|
||||||
|
"cpuType" : {
|
||||||
|
"name" : "arm64e",
|
||||||
|
"subType" : 2,
|
||||||
|
"type" : 16777228
|
||||||
|
},
|
||||||
|
"deviceType" : "iPad",
|
||||||
|
"ecid" : 0,
|
||||||
|
"hardwareModel" : "J211AP",
|
||||||
|
"internalStorageCapacity" : 64000000000,
|
||||||
|
"isProductionFused" : true,
|
||||||
|
"marketingName" : "iPad mini (5th generation)",
|
||||||
|
"platform" : "iOS",
|
||||||
|
"productType" : "iPad11,2",
|
||||||
|
"reality" : "physical",
|
||||||
|
"serialNumber" : "000000000000",
|
||||||
|
"supportedCPUTypes" : [
|
||||||
|
{
|
||||||
|
"name" : "arm64e",
|
||||||
|
"subType" : 2,
|
||||||
|
"type" : 16777228
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "arm64",
|
||||||
|
"subType" : 0,
|
||||||
|
"type" : 16777228
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "arm64",
|
||||||
|
"subType" : 1,
|
||||||
|
"type" : 16777228
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "arm64_32",
|
||||||
|
"subType" : 1,
|
||||||
|
"type" : 33554444
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supportedDeviceFamilies" : [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"thinningProductType" : "iPad11,2",
|
||||||
|
"udid" : "00000000-0000000000000000"
|
||||||
|
},
|
||||||
|
"identifier" : "00000000-0000-0000-0000-000000000000",
|
||||||
|
"visibilityClass" : "default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})raw");
|
||||||
|
|
||||||
|
QTest::addRow("handled device")
|
||||||
|
<< data << QString("000000000000000000000000") << QString()
|
||||||
|
<< QMap<QString, QString>({{"cpuArchitecture", "arm64e"},
|
||||||
|
{"developerStatus", "Development"},
|
||||||
|
{"deviceConnected", "YES"},
|
||||||
|
{"deviceName", "Some iOS device"},
|
||||||
|
{"osVersion", "17.3 (21D50)"},
|
||||||
|
{"uniqueDeviceId", "00000000-0000000000000000"}});
|
||||||
|
QTest::addRow("unhandled device")
|
||||||
|
<< data << QString("000000000000000000000001")
|
||||||
|
<< QString("Device is not handled by devicectl") << QMap<QString, QString>({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_Devicectlutils::parseDeviceInfo()
|
||||||
|
{
|
||||||
|
using InfoMap = QMap<QString, QString>;
|
||||||
|
QFETCH(QByteArray, data);
|
||||||
|
QFETCH(QString, usbId);
|
||||||
|
QFETCH(QString, error);
|
||||||
|
QFETCH(InfoMap, info);
|
||||||
|
|
||||||
|
const Utils::expected_str<InfoMap> result = Ios::Internal::parseDeviceInfo(data, usbId);
|
||||||
|
if (error.isEmpty()) {
|
||||||
|
QVERIFY(result);
|
||||||
|
QCOMPARE(*result, info);
|
||||||
|
} else {
|
||||||
|
QVERIFY(!result);
|
||||||
|
QCOMPARE(result.error(), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(tst_Devicectlutils)
|
QTEST_GUILESS_MAIN(tst_Devicectlutils)
|
||||||
|
|
||||||
#include "tst_devicectlutils.moc"
|
#include "tst_devicectlutils.moc"
|
||||||
|
|||||||
Reference in New Issue
Block a user