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 <QJsonArray>
|
||||
#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
|
||||
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);
|
||||
if (jsonOutput.isNull()) {
|
||||
// parse error
|
||||
return Utils::make_unexpected(
|
||||
return make_unexpected(
|
||||
Tr::tr("Failed to parse devicectl output: %1.").arg(parseError.errorString()));
|
||||
}
|
||||
const QJsonValue errorValue = jsonOutput["error"];
|
||||
@@ -37,12 +42,45 @@ Utils::expected_str<QJsonValue> Ios::Internal::parseDevicectlResult(const QByteA
|
||||
if (!v.isUndefined())
|
||||
error += "\n" + v.toString();
|
||||
}
|
||||
return Utils::make_unexpected(error);
|
||||
return make_unexpected(error);
|
||||
}
|
||||
const QJsonValue resultValue = jsonOutput["result"];
|
||||
if (resultValue.isUndefined()) {
|
||||
return Utils::make_unexpected(
|
||||
Tr::tr("Failed to parse devicectl output: 'result' is missing"));
|
||||
return make_unexpected(Tr::tr("Failed to parse devicectl output: 'result' is missing"));
|
||||
}
|
||||
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 {
|
||||
|
||||
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<QMap<QString, QString>> parseDeviceInfo(const QByteArray &rawOutput,
|
||||
const QString &deviceUsbId);
|
||||
|
||||
} // namespace Ios::Internal
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "iosdevice.h"
|
||||
|
||||
#include "devicectlutils.h"
|
||||
#include "iosconfigurations.h"
|
||||
#include "iosconstants.h"
|
||||
#include "iossimulator.h"
|
||||
@@ -75,16 +76,6 @@ static QString CFStringRef2QString(CFStringRef s)
|
||||
|
||||
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";
|
||||
|
||||
class IosDeviceInfoWidget : public IDeviceWidget
|
||||
@@ -283,34 +274,14 @@ void IosDeviceManager::updateInfo(const QString &devId)
|
||||
{"devicectl", "list", "devices", "--quiet", "--json-output", "-"}});
|
||||
},
|
||||
[this, devId](const Process &process) {
|
||||
auto jsonOutput = QJsonDocument::fromJson(process.rawStdOut());
|
||||
// find device
|
||||
const QJsonArray deviceList = jsonOutput["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('-') == 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;
|
||||
}
|
||||
const expected_str<QMap<QString, QString>> result = parseDeviceInfo(process.rawStdOut(),
|
||||
devId);
|
||||
if (!result) {
|
||||
qCDebug(detectLog) << result.error();
|
||||
return DoneResult::Error;
|
||||
}
|
||||
// device not found, not handled by devicectl
|
||||
return DoneResult::Error;
|
||||
deviceInfo(devId, IosDevice::Handler::DeviceCtl, *result);
|
||||
return DoneResult::Success;
|
||||
},
|
||||
CallDoneIf::Success);
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ class tst_Devicectlutils : public QObject
|
||||
private slots:
|
||||
void parseError_data();
|
||||
void parseError();
|
||||
|
||||
void parseDeviceInfo_data();
|
||||
void parseDeviceInfo();
|
||||
};
|
||||
|
||||
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)
|
||||
|
||||
#include "tst_devicectlutils.moc"
|
||||
|
||||
Reference in New Issue
Block a user