mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-10 22:47:22 +02:00
340 lines
12 KiB
C++
340 lines
12 KiB
C++
//
|
|
// TestRunnerMobile.cpp
|
|
//
|
|
// Created by Nissim Hadar on 22 Jan 2019.
|
|
// Copyright 2013 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
#include "TestRunnerMobile.h"
|
|
|
|
#include <QNetworkInterface>
|
|
#include <QThread>
|
|
#include <QtWidgets/QMessageBox>
|
|
#include <QtWidgets/QFileDialog>
|
|
|
|
#include "Nitpick.h"
|
|
extern Nitpick* nitpick;
|
|
|
|
TestRunnerMobile::TestRunnerMobile(
|
|
QLabel* workingFolderLabel,
|
|
QPushButton *connectDeviceButton,
|
|
QPushButton *pullFolderButton,
|
|
QLabel* detectedDeviceLabel,
|
|
QLineEdit *folderLineEdit,
|
|
QPushButton* downloadAPKPushbutton,
|
|
QPushButton* installAPKPushbutton,
|
|
QPushButton* runInterfacePushbutton,
|
|
QCheckBox* usePreviousInstallationOnMobileCheckBox,
|
|
QCheckBox* runLatest,
|
|
QLineEdit* url,
|
|
QCheckBox* runFullSuite,
|
|
QLineEdit* scriptURL,
|
|
QLabel* statusLabel,
|
|
|
|
QObject* parent
|
|
) : QObject(parent), TestRunner(workingFolderLabel, statusLabel, usePreviousInstallationOnMobileCheckBox, runLatest, url, runFullSuite, scriptURL)
|
|
{
|
|
_connectDeviceButton = connectDeviceButton;
|
|
_pullFolderButton = pullFolderButton;
|
|
_detectedDeviceLabel = detectedDeviceLabel;
|
|
_folderLineEdit = folderLineEdit;
|
|
_downloadAPKPushbutton = downloadAPKPushbutton;
|
|
_installAPKPushbutton = installAPKPushbutton;
|
|
_runInterfacePushbutton = runInterfacePushbutton;
|
|
|
|
folderLineEdit->setText("/sdcard/snapshots");
|
|
|
|
modelNames["SM_G955U1"] = "Samsung S8+ unlocked";
|
|
modelNames["SM_N960U1"] = "Samsung Note 9 unlocked";
|
|
modelNames["SM_T380"] = "Samsung Tab A";
|
|
modelNames["Quest"] = "Quest";
|
|
|
|
_adbInterface = NULL;
|
|
}
|
|
|
|
TestRunnerMobile::~TestRunnerMobile() {
|
|
}
|
|
|
|
void TestRunnerMobile::setWorkingFolderAndEnableControls() {
|
|
setWorkingFolder(_workingFolderLabel);
|
|
|
|
_connectDeviceButton->setEnabled(true);
|
|
_downloadAPKPushbutton->setEnabled(true);
|
|
}
|
|
|
|
void TestRunnerMobile::connectDevice() {
|
|
#if defined Q_OS_WIN || defined Q_OS_MAC
|
|
if (!_adbInterface) {
|
|
_adbInterface = new AdbInterface();
|
|
}
|
|
|
|
// Get list of devices
|
|
QString devicesFullFilename{ _workingFolder + "/devices.txt" };
|
|
QString command = _adbInterface->getAdbCommand() + " devices -l > " + devicesFullFilename;
|
|
appendLog(command);
|
|
system(command.toStdString().c_str());
|
|
|
|
if (!QFile::exists(devicesFullFilename)) {
|
|
QMessageBox::critical(0, "Internal error", "devices.txt not found");
|
|
exit (-1);
|
|
}
|
|
|
|
// Get device IP address
|
|
QString ifconfigFullFilename{ _workingFolder + "/ifconfig.txt" };
|
|
command = _adbInterface->getAdbCommand() + " shell ifconfig > " + ifconfigFullFilename;
|
|
appendLog(command);
|
|
system(command.toStdString().c_str());
|
|
|
|
if (!QFile::exists(ifconfigFullFilename)) {
|
|
QMessageBox::critical(0, "Internal error", "ifconfig.txt not found");
|
|
exit (-1);
|
|
}
|
|
|
|
// Device should be in second line
|
|
QFile devicesFile(devicesFullFilename);
|
|
devicesFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
|
QString line1 = devicesFile.readLine();
|
|
QString line2 = devicesFile.readLine();
|
|
|
|
const QString DEVICE{ "device" };
|
|
const QString MODEL{ "model" };
|
|
|
|
if (line2.contains("unauthorized")) {
|
|
QMessageBox::critical(0, "Unauthorized device detected", "Please allow USB debugging on device");
|
|
} else if (line2.contains(DEVICE)) {
|
|
// Make sure only 1 device
|
|
QString line3 = devicesFile.readLine();
|
|
if (line3.contains(DEVICE)) {
|
|
QMessageBox::critical(0, "Too many devices detected", "Tests will run only if a single device is attached");
|
|
} else {
|
|
// Line looks like this: 988a1b47335239434b device product:dream2qlteue model:SM_G955U1 device:dream2qlteue transport_id:2
|
|
QStringList tokens = line2.split(QRegExp("[\r\n\t ]+"));
|
|
QString deviceID = tokens[0];
|
|
|
|
// Find the model entry
|
|
_modelName = "UNKNOWN";
|
|
for (int i = 0; i < tokens.size(); ++i) {
|
|
if (tokens[i].contains(MODEL)) {
|
|
QString modelID = tokens[i].split(':')[1];
|
|
|
|
if (modelNames.count(modelID) == 1) {
|
|
_modelName = modelNames[modelID];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
_detectedDeviceLabel->setText(_modelName + " [" + deviceID + "]");
|
|
_pullFolderButton->setEnabled(true);
|
|
_folderLineEdit->setEnabled(true);
|
|
_downloadAPKPushbutton->setEnabled(true);
|
|
_installAPKPushbutton->setEnabled(true);
|
|
_runInterfacePushbutton->setEnabled(true);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void TestRunnerMobile::downloadAPK() {
|
|
downloadBuildXml((void*)this);
|
|
|
|
downloadComplete();
|
|
}
|
|
|
|
|
|
void TestRunnerMobile::downloadComplete() {
|
|
if (!buildXMLDownloaded) {
|
|
// Download of Build XML has completed
|
|
buildXMLDownloaded = true;
|
|
|
|
// Download the High Fidelity installer
|
|
QStringList urls;
|
|
QStringList filenames;
|
|
if (_runLatest->isChecked()) {
|
|
parseBuildInformation();
|
|
|
|
_installerFilename = INSTALLER_FILENAME_LATEST;
|
|
|
|
|
|
// Replace the `exe` extension with `apk`
|
|
_installerFilename = _installerFilename.replace(_installerFilename.length() - 3, 3, "apk");
|
|
_buildInformation.url = _buildInformation.url.replace(_buildInformation.url.length() - 3, 3, "apk");
|
|
|
|
urls << _buildInformation.url;
|
|
filenames << _installerFilename;
|
|
} else {
|
|
QString urlText = _url->text();
|
|
urls << urlText;
|
|
_installerFilename = getInstallerNameFromURL(urlText);
|
|
filenames << _installerFilename;
|
|
}
|
|
|
|
_statusLabel->setText("Downloading installer");
|
|
|
|
_downloader->downloadFiles(urls, _workingFolder, filenames, (void*)this);
|
|
} else {
|
|
_statusLabel->setText("Installer download complete");
|
|
}
|
|
}
|
|
|
|
void TestRunnerMobile::installAPK() {
|
|
#if defined Q_OS_WIN || defined Q_OS_MAC
|
|
if (!_adbInterface) {
|
|
_adbInterface = new AdbInterface();
|
|
}
|
|
|
|
QString installerPathname = QFileDialog::getOpenFileName(nullptr, "Please select the APK", _workingFolder,
|
|
"Available APKs (*.apk)"
|
|
);
|
|
|
|
if (installerPathname.isNull()) {
|
|
return;
|
|
}
|
|
|
|
_statusLabel->setText("Installing");
|
|
QString command = _adbInterface->getAdbCommand() + " install -r -d " + installerPathname + " >" + _workingFolder + "/installOutput.txt";
|
|
appendLog(command);
|
|
system(command.toStdString().c_str());
|
|
_statusLabel->setText("Installation complete");
|
|
#endif
|
|
}
|
|
|
|
void TestRunnerMobile::runInterface() {
|
|
#if defined Q_OS_WIN || defined Q_OS_MAC
|
|
if (!_adbInterface) {
|
|
_adbInterface = new AdbInterface();
|
|
}
|
|
|
|
_statusLabel->setText("Starting Interface");
|
|
|
|
QString testScript = (_runFullSuite->isChecked())
|
|
? QString("https://raw.githubusercontent.com/") + nitpick->getSelectedUser() + "/hifi_tests/" + nitpick->getSelectedBranch() + "/tests/testRecursive.js"
|
|
: _scriptURL->text();
|
|
|
|
// Quest and Android have different commands to run interface
|
|
QString startCommand;
|
|
if (_modelName == "Quest") {
|
|
startCommand = "io.highfidelity.questInterface/.PermissionsChecker";
|
|
} else {
|
|
startCommand = "io.highfidelity.hifiinterface/.PermissionChecker";
|
|
}
|
|
|
|
QString serverIP { getServerIP() };
|
|
if (serverIP == NETWORK_NOT_FOUND) {
|
|
_runInterfacePushbutton->setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
QString command = _adbInterface->getAdbCommand() +
|
|
" shell am start -n " + startCommand +
|
|
" --es args \\\"" +
|
|
" --url hifi://" + serverIP + "/0,0,0"
|
|
" --no-updater" +
|
|
" --no-login-suggestion" +
|
|
" --testScript " + testScript + " quitWhenFinished" +
|
|
" --testResultsLocation /sdcard/snapshots" +
|
|
"\\\"";
|
|
|
|
appendLog(command);
|
|
system(command.toStdString().c_str());
|
|
_statusLabel->setText("Interface started");
|
|
#endif
|
|
}
|
|
|
|
void TestRunnerMobile::pullFolder() {
|
|
#if defined Q_OS_WIN || defined Q_OS_MAC
|
|
if (!_adbInterface) {
|
|
_adbInterface = new AdbInterface();
|
|
}
|
|
|
|
_statusLabel->setText("Pulling folder");
|
|
QString command = _adbInterface->getAdbCommand() + " pull " + _folderLineEdit->text() + " " + _workingFolder;
|
|
appendLog(command);
|
|
system(command.toStdString().c_str());
|
|
_statusLabel->setText("Pull complete");
|
|
#endif
|
|
}
|
|
|
|
QString TestRunnerMobile::getServerIP() {
|
|
// Get device IP (ifconfig.txt was created when connecting)
|
|
QFile ifconfigFile{ _workingFolder + "/ifconfig.txt" };
|
|
if (!ifconfigFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
|
"Could not open 'ifconfig.txt'");
|
|
exit(-1);
|
|
}
|
|
|
|
QTextStream stream(&ifconfigFile);
|
|
QString line = ifconfigFile.readLine();
|
|
while (!line.isNull()) {
|
|
// The device IP is in the line following the "wlan0" line
|
|
if (line.left(6) == "wlan0 ") {
|
|
break;
|
|
}
|
|
line = ifconfigFile.readLine();
|
|
}
|
|
|
|
// The following line looks like this "inet addr:192.168.0.15 Bcast:192.168.0.255 Mask:255.255.255.0"
|
|
// Extract the address and mask
|
|
line = ifconfigFile.readLine();
|
|
QStringList lineParts = line.split(':');
|
|
if (lineParts.size() < 4) {
|
|
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
|
"IP address line not in expected format: " + line + "(check that device WIFI is on)");
|
|
|
|
return NETWORK_NOT_FOUND;
|
|
}
|
|
|
|
qint64 deviceIP = convertToBinary(lineParts[1].split(' ')[0]);
|
|
qint64 deviceMask = convertToBinary(lineParts[3].split(' ')[0]);
|
|
qint64 deviceSubnet = deviceMask & deviceIP;
|
|
|
|
// The device needs to be on the same subnet as the server
|
|
// To find which of our IPs is the server - choose the 1st that is on the same subnet
|
|
// If more than one found then report an error
|
|
|
|
QString serverIP;
|
|
|
|
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
|
for (int i = 0; i < interfaces.count(); i++) {
|
|
QList<QNetworkAddressEntry> entries = interfaces.at(i).addressEntries();
|
|
for (int j = 0; j < entries.count(); j++) {
|
|
if (entries.at(j).ip().protocol() == QAbstractSocket::IPv4Protocol) {
|
|
qint64 hostIP = convertToBinary(entries.at(j).ip().toString());
|
|
qint64 hostMask = convertToBinary(entries.at(j).netmask().toString());
|
|
qint64 hostSubnet = hostMask & hostIP;
|
|
|
|
if (hostSubnet == deviceSubnet) {
|
|
if (!serverIP.isNull()) {
|
|
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__),
|
|
"Cannot identify server IP (multiple interfaces on device submask)");
|
|
return QString("CANNOT IDENTIFY SERVER IP");
|
|
} else {
|
|
union {
|
|
uint32_t ip;
|
|
uint8_t bytes[4];
|
|
} u;
|
|
u.ip = hostIP;
|
|
|
|
serverIP = QString::number(u.bytes[3]) + '.' + QString::number(u.bytes[2]) + '.' + QString::number(u.bytes[1]) + '.' + QString::number(u.bytes[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ifconfigFile.close();
|
|
|
|
return serverIP;
|
|
}
|
|
|
|
qint64 TestRunnerMobile::convertToBinary(const QString& str) {
|
|
QString binary;
|
|
foreach (const QString& s, str.split(".")) {
|
|
binary += QString::number(s.toInt(), 2).rightJustified(8, '0');
|
|
}
|
|
|
|
return binary.toLongLong(NULL, 2);
|
|
}
|