NissimHadarCR comments.d230fc87 years agoHistory
340 lines · 11.7 KB · C++
RawDownload
////  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);}