Skip to content

Commit

Permalink
Use QCommandLineParser
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey committed Apr 14, 2024
1 parent de2bac7 commit c7d8b3a
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 122 deletions.
120 changes: 80 additions & 40 deletions src/CmdLineOptParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,85 @@

#include "CmdLineOptParser.h"

#include <QString>

/// @brief Implements a simple command line parser which sets booleans to true if the option is found.
void ParseCmdLineOptions(int& argc, ///< count of arguments in argv
char* argv[], ///< command line arguments
CmdLineOpt_t* prgOpts, ///< command line options
size_t cOpts, ///< count of command line options
bool removeParsedOptions) ///< true: remove parsed option from argc/argv
CommandLineParseResult parseCommandLine(QCommandLineParser &parser)
{
// Start with all options off
for (size_t iOption=0; iOption<cOpts; iOption++) {
*prgOpts[iOption].optionFound = false;
}

for (int iArg=1; iArg<argc; iArg++) {
for (size_t iOption=0; iOption<cOpts; iOption++) {
bool found = false;

QString arg(argv[iArg]);
QString optionStr(prgOpts[iOption].optionStr);

if (arg.startsWith(QString("%1:").arg(optionStr), Qt::CaseInsensitive)) {
found = true;
if (prgOpts[iOption].optionArg) {
*prgOpts[iOption].optionArg = arg.right(arg.length() - (optionStr.length() + 1));
}
} else if (arg.compare(optionStr, Qt::CaseInsensitive) == 0) {
found = true;
}
if (found) {
*prgOpts[iOption].optionFound = true;
if (removeParsedOptions) {
for (int iShift=iArg; iShift<argc-1; iShift++) {
argv[iShift] = argv[iShift+1];
}
argc--;
iArg--;
}
}
}
}
using Status = CommandLineParseResult::Status;

parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);

const QCommandLineOption runUnitTestsOption("unittest", "Run unit tests.");
parser.addOption(runUnitTestsOption);
const QCommandLineOption stressUnitTestsOption("unittest-stress", "Stress test unit tests.");
parser.addOption(stressUnitTestsOption);
const QCommandLineOption clearSettingsOption("clear-settings", "Clear stored settings.");
parser.addOption(clearSettingsOption);
const QCommandLineOption clearCacheOption("clear-cache", "Clear parameter/airframe caches.");
parser.addOption(clearCacheOption);
const QCommandLineOption loggingOption("logging", "Turn on logging.", "options");
parser.addOption(loggingOption);
const QCommandLineOption fakeMobileOption("fake-mobile", "Fake Mobile.");
parser.addOption(fakeMobileOption);
const QCommandLineOption logOutputOption("log-output", "Log Output.");
parser.addOption(logOutputOption);
#ifdef Q_OS_WIN
const QCommandLineOption quietWindowsAssertsOption("no-windows-assert-ui", "Don't let asserts pop dialog boxes.");
parser.addOption(quietWindowsAssertsOption);
#endif
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption();

if (!parser.parse(QCoreApplication::arguments()))
return { Status::Error, parser.errorText() };

if (parser.isSet(versionOption))
return { Status::VersionRequested };

if (parser.isSet(helpOption))
return { Status::HelpRequested };

runningUnitTests = parser.isSet(runUnitTestsOption);
// unitTests = "";
stressUnitTests = parser.isSet(stressUnitTestsOption);
fClearSettingsOptions = parser.isSet(clearSettingsOption);
fClearCache = parser.isSet(clearCacheOption);
logging = parser.isSet(loggingOption);
loggingOptions = parser.value(loggingOption);
fakeMobile = parser.isSet(fakeMobileOption);
logOutput = parser.isSet(logOutputOption);
#ifdef Q_OS_WIN
quietWindowsAsserts = parser.isSet(quietWindowsAssertsOption);
#endif







// if (parser.isSet(nameServerOption)) {
// const QString nameserver = parser.value(nameServerOption);
// query->nameServer = QHostAddress(nameserver);
// if (query->nameServer.isNull()
// || query->nameServer.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
// return { Status::Error,
// u"Bad nameserver address: %1"_s.arg(nameserver) };
// }
// }

// if (parser.isSet(typeOption)) {
// const QString typeParameter = parser.value(typeOption);
// if (std::optional<QDnsLookup::Type> type = typeFromParameter(typeParameter))
// query->type = *type;
// else
// return { Status::Error, u"Bad record type: %1"_s.arg(typeParameter) };
// }

// const QStringList positionalArguments = parser.positionalArguments();
// if (positionalArguments.isEmpty())
// return { Status::Error, u"Argument 'name' missing."_s };
// if (positionalArguments.size() > 1)
// return { Status::Error, u"Several 'name' arguments specified."_s };
// query->name = positionalArguments.first();

return { Status::Ok };
}
37 changes: 24 additions & 13 deletions src/CmdLineOptParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,30 @@

#pragma once

#include <QString>
#include <cstring>
#include <QCommandLineParser>

/// @brief Structure used to pass command line options to the ParseCmdLineOptions function.
typedef struct {
const char* optionStr; ///< Command line option, for example "--foo"
bool* optionFound; ///< If option is found this variable will be set to true
QString* optionArg; ///< Option has additional argument, form is option:arg
} CmdLineOpt_t;
struct CommandLineParseResult
{
enum class Status {
Ok,
Error,
VersionRequested,
HelpRequested
};
Status statusCode = Status::Ok;
std::optional<QString> errorString = std::nullopt;

void ParseCmdLineOptions(int& argc,
char* argv[],
CmdLineOpt_t* prgOpts,
size_t cOpts,
bool removeParsedOptions);
bool runningUnitTests = false;
QStringList unitTests = {};
bool stressUnitTests = false;
int stressUnitTestsCount = 0;
bool fClearSettingsOptions = false;
bool fClearCache = false;
bool logging = false;
QString loggingOptions = "";
bool fakeMobile = false;
bool logOutput = false;
bool quietWindowsAsserts = false;
};

CommandLineParseResult parseCommandLine(QCommandLineParser &parser);
67 changes: 44 additions & 23 deletions src/QGCApplication.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,18 @@ static QObject* shapeFileHelperSingletonFactory(QQmlEngine*, QJSEngine*)
return new ShapeFileHelper;
}

QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
: QApplication (argc, argv)
, _runningUnitTests (unitTesting)
QGCApplication::QGCApplication(int &argc, char* argv[])
: QApplication(argc, argv)
{
_app = this;
_msecsElapsedTime.start();

(void) _parseCommandLine();
// TODO: put in main and rearrange following code into init
// if(app->parseCommands()) {
// app->exec();
// }

#ifdef Q_OS_LINUX
#ifndef __mobile__
if (!_runningUnitTests) {
Expand All @@ -211,24 +216,6 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
// Setup for network proxy support
QNetworkProxyFactory::setUseSystemConfiguration(true);

// Parse command line options

bool fClearSettingsOptions = false; // Clear stored settings
bool fClearCache = false; // Clear parameter/airframe caches
bool logging = false; // Turn on logging
QString loggingOptions;

CmdLineOpt_t rgCmdLineOptions[] = {
{ "--clear-settings", &fClearSettingsOptions, nullptr },
{ "--clear-cache", &fClearCache, nullptr },
{ "--logging", &logging, &loggingOptions },
{ "--fake-mobile", &_fakeMobile, nullptr },
{ "--log-output", &_logOutput, nullptr },
// Add additional command line option flags here
};

ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);

// Set up timer for delayed missing fact display
_missingParamsDelayedDisplayTimer.setSingleShot(true);
_missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
Expand Down Expand Up @@ -302,8 +289,10 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
parameter.remove();
}

// Set up our logging filters
QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);
if(logging) {
// Set up our logging filters
QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);
}

// Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
Expand Down Expand Up @@ -336,6 +325,38 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
_checkForNewVersion();
}

bool QGCApplication::_parseCommandLine()
{
QCommandLineParser parser;
// const CommandLineParseResult cmdLineResult = parseCommandLine(&parser);
// _cmdLineResult = parseCommandLine(&parser);

switch (parseResult.statusCode) {
case Status::Ok:
_runningUnitTests = cmdLineResult.runningUnitTests;
_fakeMobile = cmdLineResult.fakeMobile;
_logOutput = cmdLineResult.logOutput;
break;

case Status::Error:
std::fputs(qPrintable(parseResult.errorString.value_or(u"Unknown error occurred"_s)),
stderr);
std::fputs("\n\n", stderr);
std::fputs(qPrintable(parser.helpText()), stderr);
return false;

case Status::VersionRequested:
parser.showVersion();
break;

case Status::HelpRequested:
parser.showHelp();
break;
}

return true;
}

void QGCApplication::_exitWithError(QString errorMessage)
{
_error = true;
Expand Down
2 changes: 1 addition & 1 deletion src/QGCApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class QGCApplication : public QApplication
{
Q_OBJECT
public:
QGCApplication(int &argc, char* argv[], bool unitTesting);
QGCApplication(int &argc, char* argv[]);
~QGCApplication();

/// @brief Sets the persistent flag to delete all settings the next time QGroundControl is started.
Expand Down
59 changes: 16 additions & 43 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
#endif

#ifdef QT_DEBUG
#include "CmdLineOptParser.h"
#ifdef Q_OS_WIN
#include <crtdbg.h>
#endif
Expand Down Expand Up @@ -201,51 +200,13 @@ int main(int argc, char *argv[])

Q_IMPORT_PLUGIN(QGeoServiceProviderFactoryQGC)

bool runUnitTests = false; // Run unit tests

#ifdef QT_DEBUG
// We parse a small set of command line options here prior to QGCApplication in order to handle the ones
// which need to be handled before a QApplication object is started.

bool stressUnitTests = false; // Stress test unit tests
bool quietWindowsAsserts = false; // Don't let asserts pop dialog boxes

QString unitTestOptions;
CmdLineOpt_t rgCmdLineOptions[] = {
{ "--unittest", &runUnitTests, &unitTestOptions },
{ "--unittest-stress", &stressUnitTests, &unitTestOptions },
{ "--no-windows-assert-ui", &quietWindowsAsserts, nullptr },
// Add additional command line option flags here
};

ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
if (stressUnitTests) {
runUnitTests = true;
}

if (quietWindowsAsserts) {
#ifdef Q_OS_WIN
_CrtSetReportHook(WindowsCrtReportHook);
#endif
}

#ifdef Q_OS_WIN
if (runUnitTests) {
// Don't pop up Windows Error Reporting dialog when app crashes. This prevents TeamCity from
// hanging.
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
#endif
#endif // QT_DEBUG

#ifdef Q_OS_DARWIN
// Gstreamer video playback requires OpenGL
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
#endif

QQuickStyle::setStyle("Basic");
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
QGCApplication* app = new QGCApplication(argc, argv);
Q_CHECK_PTR(app);
if(app->isErrorState()) {
app->exec();
Expand All @@ -269,15 +230,27 @@ int main(int argc, char *argv[])

int exitCode = 0;

#ifdef UNITTEST_BUILD
if (runUnitTests) {
#if defined(QT_DEBUG) && defined(UNITTEST_BUILD)
#ifdef Q_OS_WIN
if (quietWindowsAsserts) {
_CrtSetReportHook(WindowsCrtReportHook);
}
if (app->runningUnitTests()) {
// Don't pop up Windows Error Reporting dialog when app crashes. This prevents TeamCity from
// hanging.
const DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
(void) SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
#endif

if (app->runningUnitTests()) {
for (int i=0; i < (stressUnitTests ? 20 : 1); i++) {
if (!app->_initForUnitTests()) {
return -1;
}

// Run the test
int failures = UnitTest::run(unitTestOptions);
int failures = UnitTest::run(app->unitTests());
if (failures == 0) {
qDebug() << "ALL TESTS PASSED";
exitCode = 0;
Expand Down
2 changes: 1 addition & 1 deletion test/qgcunittest/UnitTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ QList<UnitTest*>& UnitTest::_testList(void)
return tests;
}

int UnitTest::run(QString& singleTest)
int UnitTest::run(QStringView singleTest)
{
int ret = 0;

Expand Down
2 changes: 1 addition & 1 deletion test/qgcunittest/UnitTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class UnitTest : public QObject

/// @brief Called to run all the registered unit tests
/// @param singleTest Name of test to just run a single test
static int run(QString& singleTest);
static int run(QStringView singleTest);

/// @brief Sets up for an expected QGCMessageBox
/// @param response Response to take on message box
Expand Down

0 comments on commit c7d8b3a

Please sign in to comment.