593 lines
21 KiB
C++
593 lines
21 KiB
C++
/*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
* MuseScore-CLA-applies
|
|
*
|
|
* MuseScore
|
|
* Music Composition & Notation
|
|
*
|
|
* Copyright (C) 2021 MuseScore BVBA and others
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 3 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "app.h"
|
|
|
|
#include <QApplication>
|
|
#include <QQmlApplicationEngine>
|
|
#include <QQuickWindow>
|
|
#include <QStyleHints>
|
|
#ifndef Q_OS_WASM
|
|
#include <QThreadPool>
|
|
#endif
|
|
|
|
#include "appshell/view/internal/splashscreen/splashscreen.h"
|
|
#include "appshell/view/dockwindow/docksetup.h"
|
|
|
|
#include "modularity/ioc.h"
|
|
#include "ui/internal/uiengine.h"
|
|
#include "muversion.h"
|
|
|
|
#include "framework/global/globalmodule.h"
|
|
|
|
#include "log.h"
|
|
|
|
using namespace mu::app;
|
|
using namespace mu::appshell;
|
|
|
|
//! NOTE Separately to initialize logger and profiler as early as possible
|
|
static mu::framework::GlobalModule globalModule;
|
|
|
|
App::App()
|
|
{
|
|
}
|
|
|
|
void App::addModule(modularity::IModuleSetup* module)
|
|
{
|
|
m_modules.push_back(module);
|
|
}
|
|
|
|
int App::run(int argc, char** argv)
|
|
{
|
|
// ====================================================
|
|
// Setup global Qt application variables
|
|
// ====================================================
|
|
qputenv("QT_STYLE_OVERRIDE", "Fusion");
|
|
qputenv("QML_DISABLE_DISK_CACHE", "true");
|
|
|
|
#ifdef Q_OS_LINUX
|
|
if (qEnvironmentVariable("QT_QPA_PLATFORM") != "offscreen") {
|
|
qputenv("QT_QPA_PLATFORMTHEME", "gtk3");
|
|
}
|
|
#endif
|
|
|
|
const char* appName;
|
|
if (framework::MUVersion::unstable()) {
|
|
appName = "MuseScore4Development";
|
|
} else {
|
|
appName = "MuseScore4";
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
// NOTE: There are some problems with rendering the application window on some integrated graphics processors
|
|
// see https://github.com/musescore/MuseScore/issues/8270
|
|
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
|
|
|
|
if (!qEnvironmentVariableIsSet("QT_OPENGL_BUGLIST")) {
|
|
qputenv("QT_OPENGL_BUGLIST", ":/resources/win_opengl_buglist.json");
|
|
}
|
|
#endif
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
|
|
|
//! NOTE: For unknown reasons, Linux scaling for 1 is defined as 1.003 in fractional scaling.
|
|
//! Because of this, some elements are drawn with a shift on the score.
|
|
//! Let's make a Linux hack and round values above 0.75(see RoundPreferFloor)
|
|
#ifdef Q_OS_LINUX
|
|
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor);
|
|
#elif defined(Q_OS_WIN)
|
|
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
|
#endif
|
|
|
|
QGuiApplication::styleHints()->setMousePressAndHoldInterval(250);
|
|
|
|
// ====================================================
|
|
// Parse command line options
|
|
// ====================================================
|
|
CommandLineParser commandLineParser;
|
|
commandLineParser.init();
|
|
commandLineParser.parse(argc, argv);
|
|
|
|
framework::IApplication::RunMode runMode = commandLineParser.runMode();
|
|
QCoreApplication* app = nullptr;
|
|
|
|
if (runMode == framework::IApplication::RunMode::AudioPluginRegistration) {
|
|
app = new QCoreApplication(argc, argv);
|
|
} else {
|
|
app = new QApplication(argc, argv);
|
|
}
|
|
|
|
QCoreApplication::setApplicationName(appName);
|
|
QCoreApplication::setOrganizationName("MuseScore");
|
|
QCoreApplication::setOrganizationDomain("musescore.org");
|
|
QCoreApplication::setApplicationVersion(QString::fromStdString(framework::MUVersion::fullVersion().toStdString()));
|
|
|
|
#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) && !defined(Q_OS_WASM)
|
|
// Any OS that uses Freedesktop.org Desktop Entry Specification (e.g. Linux, BSD)
|
|
QGuiApplication::setDesktopFileName("org.musescore.MuseScore" MUSESCORE_INSTALL_SUFFIX ".desktop");
|
|
#endif
|
|
|
|
commandLineParser.processBuiltinArgs(*app);
|
|
|
|
// ====================================================
|
|
// Setup modules: Resources, Exports, Imports, UiTypes
|
|
// ====================================================
|
|
globalModule.registerResources();
|
|
globalModule.registerExports();
|
|
globalModule.registerUiTypes();
|
|
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->registerResources();
|
|
}
|
|
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->registerExports();
|
|
}
|
|
|
|
globalModule.resolveImports();
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->registerUiTypes();
|
|
m->resolveImports();
|
|
}
|
|
|
|
// ====================================================
|
|
// Setup modules: apply the command line options
|
|
// ====================================================
|
|
muapplication()->setRunMode(runMode);
|
|
applyCommandLineOptions(commandLineParser.options(), runMode);
|
|
|
|
// ====================================================
|
|
// Setup modules: onPreInit
|
|
// ====================================================
|
|
globalModule.onPreInit(runMode);
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onPreInit(runMode);
|
|
}
|
|
|
|
#ifdef MUE_BUILD_APPSHELL_MODULE
|
|
SplashScreen* splashScreen = nullptr;
|
|
if (runMode == framework::IApplication::RunMode::GuiApp) {
|
|
if (multiInstancesProvider()->isMainInstance()) {
|
|
splashScreen = new SplashScreen(SplashScreen::Default);
|
|
} else {
|
|
QString fileName = startupScenario()->startupScoreFile().displayName(true /* includingExtension */);
|
|
splashScreen = new SplashScreen(SplashScreen::ForNewInstance, fileName);
|
|
}
|
|
|
|
splashScreen->show();
|
|
}
|
|
#endif
|
|
|
|
// ====================================================
|
|
// Setup modules: onInit
|
|
// ====================================================
|
|
globalModule.onInit(runMode);
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onInit(runMode);
|
|
}
|
|
|
|
// ====================================================
|
|
// Setup modules: onAllInited
|
|
// ====================================================
|
|
globalModule.onAllInited(runMode);
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onAllInited(runMode);
|
|
}
|
|
|
|
// ====================================================
|
|
// Setup modules: onStartApp (on next event loop)
|
|
// ====================================================
|
|
QMetaObject::invokeMethod(qApp, [this]() {
|
|
globalModule.onStartApp();
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onStartApp();
|
|
}
|
|
}, Qt::QueuedConnection);
|
|
|
|
// ====================================================
|
|
// Run
|
|
// ====================================================
|
|
|
|
switch (runMode) {
|
|
case framework::IApplication::RunMode::ConsoleApp: {
|
|
// ====================================================
|
|
// Process Autobot
|
|
// ====================================================
|
|
CommandLineParser::Autobot autobot = commandLineParser.autobot();
|
|
if (!autobot.testCaseNameOrFile.isEmpty()) {
|
|
QMetaObject::invokeMethod(qApp, [this, autobot]() {
|
|
processAutobot(autobot);
|
|
}, Qt::QueuedConnection);
|
|
} else {
|
|
// ====================================================
|
|
// Process Diagnostic
|
|
// ====================================================
|
|
CommandLineParser::Diagnostic diagnostic = commandLineParser.diagnostic();
|
|
if (diagnostic.type != CommandLineParser::DiagnosticType::Undefined) {
|
|
QMetaObject::invokeMethod(qApp, [this, diagnostic]() {
|
|
int code = processDiagnostic(diagnostic);
|
|
qApp->exit(code);
|
|
}, Qt::QueuedConnection);
|
|
} else {
|
|
// ====================================================
|
|
// Process Converter
|
|
// ====================================================
|
|
CommandLineParser::ConverterTask task = commandLineParser.converterTask();
|
|
QMetaObject::invokeMethod(qApp, [this, task]() {
|
|
int code = processConverter(task);
|
|
qApp->exit(code);
|
|
}, Qt::QueuedConnection);
|
|
}
|
|
}
|
|
} break;
|
|
case framework::IApplication::RunMode::GuiApp: {
|
|
#ifdef MUE_BUILD_APPSHELL_MODULE
|
|
// ====================================================
|
|
// Setup Qml Engine
|
|
// ====================================================
|
|
QQmlApplicationEngine* engine = new QQmlApplicationEngine();
|
|
|
|
dock::DockSetup::setup(engine);
|
|
|
|
#if defined(Q_OS_WIN)
|
|
const QString mainQmlFile = "/platform/win/Main.qml";
|
|
#elif defined(Q_OS_MACOS)
|
|
const QString mainQmlFile = "/platform/mac/Main.qml";
|
|
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
|
const QString mainQmlFile = "/platform/linux/Main.qml";
|
|
#elif defined(Q_OS_WASM)
|
|
const QString mainQmlFile = "/Main.wasm.qml";
|
|
#endif
|
|
//! NOTE Move ownership to UiEngine
|
|
ui::UiEngine::instance()->moveQQmlEngine(engine);
|
|
|
|
#ifdef MUE_ENABLE_LOAD_QML_FROM_SOURCE
|
|
const QUrl url(QString(appshell_QML_IMPORT) + mainQmlFile);
|
|
#else
|
|
const QUrl url(QStringLiteral("qrc:/qml") + mainQmlFile);
|
|
#endif
|
|
|
|
QObject::connect(engine, &QQmlApplicationEngine::objectCreated,
|
|
app, [this, url, splashScreen](QObject* obj, const QUrl& objUrl) {
|
|
if (!obj && url == objUrl) {
|
|
LOGE() << "failed Qml load\n";
|
|
QCoreApplication::exit(-1);
|
|
return;
|
|
}
|
|
|
|
if (url == objUrl) {
|
|
// ====================================================
|
|
// Setup modules: onDelayedInit
|
|
// ====================================================
|
|
|
|
globalModule.onDelayedInit();
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onDelayedInit();
|
|
}
|
|
|
|
if (splashScreen) {
|
|
splashScreen->close();
|
|
delete splashScreen;
|
|
}
|
|
|
|
startupScenario()->run();
|
|
}
|
|
}, Qt::QueuedConnection);
|
|
|
|
QObject::connect(engine, &QQmlEngine::warnings, [](const QList<QQmlError>& warnings) {
|
|
for (const QQmlError& e : warnings) {
|
|
LOGE() << "error: " << e.toString().toStdString() << "\n";
|
|
}
|
|
});
|
|
|
|
// ====================================================
|
|
// Load Main qml
|
|
// ====================================================
|
|
|
|
//! Needs to be set because we use transparent windows for PopupView.
|
|
//! Needs to be called before any QQuickWindows are shown.
|
|
QQuickWindow::setDefaultAlphaBuffer(true);
|
|
|
|
engine->load(url);
|
|
#endif // MUE_BUILD_APPSHELL_MODULE
|
|
} break;
|
|
case framework::IApplication::RunMode::AudioPluginRegistration: {
|
|
CommandLineParser::AudioPluginRegistration pluginRegistration = commandLineParser.audioPluginRegistration();
|
|
|
|
QMetaObject::invokeMethod(qApp, [this, pluginRegistration]() {
|
|
int code = processAudioPluginRegistration(pluginRegistration);
|
|
qApp->exit(code);
|
|
}, Qt::QueuedConnection);
|
|
} break;
|
|
}
|
|
|
|
// ====================================================
|
|
// Run main loop
|
|
// ====================================================
|
|
int retCode = app->exec();
|
|
|
|
// ====================================================
|
|
// Quit
|
|
// ====================================================
|
|
|
|
PROFILER_PRINT;
|
|
|
|
// Wait Thread Poll
|
|
#ifndef Q_OS_WASM
|
|
QThreadPool* globalThreadPool = QThreadPool::globalInstance();
|
|
if (globalThreadPool) {
|
|
LOGI() << "activeThreadCount: " << globalThreadPool->activeThreadCount();
|
|
globalThreadPool->waitForDone();
|
|
}
|
|
#endif
|
|
|
|
#ifdef MUE_BUILD_APPSHELL_MODULE
|
|
// Engine quit
|
|
ui::UiEngine::instance()->quit();
|
|
#endif
|
|
|
|
// Deinit
|
|
|
|
globalModule.invokeQueuedCalls();
|
|
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onDeinit();
|
|
}
|
|
|
|
globalModule.onDeinit();
|
|
|
|
for (mu::modularity::IModuleSetup* m : m_modules) {
|
|
m->onDestroy();
|
|
}
|
|
|
|
globalModule.onDestroy();
|
|
|
|
// Delete modules
|
|
qDeleteAll(m_modules);
|
|
m_modules.clear();
|
|
mu::modularity::ioc()->reset();
|
|
|
|
delete app;
|
|
|
|
return retCode;
|
|
}
|
|
|
|
void App::applyCommandLineOptions(const CommandLineParser::Options& options, framework::IApplication::RunMode runMode)
|
|
{
|
|
uiConfiguration()->setPhysicalDotsPerInch(options.ui.physicalDotsPerInch);
|
|
|
|
notationConfiguration()->setTemplateModeEnabled(options.notation.templateModeEnabled);
|
|
notationConfiguration()->setTestModeEnabled(options.notation.testModeEnabled);
|
|
|
|
if (runMode == framework::IApplication::RunMode::ConsoleApp) {
|
|
project::MigrationOptions migration;
|
|
migration.appVersion = mu::engraving::Constants::MSC_VERSION;
|
|
|
|
//! NOTE Don't ask about migration in convert mode
|
|
migration.isAskAgain = false;
|
|
|
|
if (options.project.fullMigration) {
|
|
bool isMigration = options.project.fullMigration.value();
|
|
migration.isApplyMigration = isMigration;
|
|
migration.isApplyEdwin = isMigration;
|
|
migration.isApplyLeland = isMigration;
|
|
}
|
|
|
|
//! NOTE Don't write to settings, just on current session
|
|
for (project::MigrationType type : project::allMigrationTypes()) {
|
|
projectConfiguration()->setMigrationOptions(type, migration, false);
|
|
}
|
|
}
|
|
|
|
#ifdef MUE_BUILD_IMAGESEXPORT_MODULE
|
|
imagesExportConfiguration()->setTrimMarginPixelSize(options.exportImage.trimMarginPixelSize);
|
|
imagesExportConfiguration()->setExportPngDpiResolutionOverride(options.exportImage.pngDpiResolution);
|
|
#endif
|
|
|
|
#ifdef MUE_BUILD_VIDEOEXPORT_MODULE
|
|
videoExportConfiguration()->setResolution(options.exportVideo.resolution);
|
|
videoExportConfiguration()->setFps(options.exportVideo.fps);
|
|
videoExportConfiguration()->setLeadingSec(options.exportVideo.leadingSec);
|
|
videoExportConfiguration()->setTrailingSec(options.exportVideo.trailingSec);
|
|
#endif
|
|
|
|
#ifdef MUE_BUILD_IMPORTEXPORT_MODULE
|
|
audioExportConfiguration()->setExportMp3BitrateOverride(options.exportAudio.mp3Bitrate);
|
|
midiImportExportConfiguration()->setMidiImportOperationsFile(options.importMidi.operationsFile);
|
|
guitarProConfiguration()->setLinkedTabStaffCreated(options.guitarPro.linkedTabStaffCreated);
|
|
guitarProConfiguration()->setExperimental(options.guitarPro.experimental);
|
|
#endif
|
|
|
|
if (options.app.revertToFactorySettings) {
|
|
appshellConfiguration()->revertToFactorySettings(options.app.revertToFactorySettings.value());
|
|
}
|
|
|
|
if (runMode == framework::IApplication::RunMode::GuiApp) {
|
|
startupScenario()->setStartupType(options.startup.type);
|
|
|
|
if (options.startup.scorePath.has_value()) {
|
|
project::ProjectFile file { options.startup.scorePath.value() };
|
|
|
|
if (options.startup.scoreDisplayNameOverride.has_value()) {
|
|
file.displayNameOverride = options.startup.scoreDisplayNameOverride.value();
|
|
}
|
|
|
|
startupScenario()->setStartupScoreFile(file);
|
|
}
|
|
}
|
|
|
|
if (options.app.loggerLevel) {
|
|
globalModule.setLoggerLevel(options.app.loggerLevel.value());
|
|
}
|
|
}
|
|
|
|
int App::processConverter(const CommandLineParser::ConverterTask& task)
|
|
{
|
|
Ret ret = make_ret(Ret::Code::Ok);
|
|
io::path_t stylePath = task.params[CommandLineParser::ParamKey::StylePath].toString();
|
|
bool forceMode = task.params[CommandLineParser::ParamKey::ForceMode].toBool();
|
|
|
|
switch (task.type) {
|
|
case CommandLineParser::ConvertType::Batch:
|
|
ret = converter()->batchConvert(task.inputFile, stylePath, forceMode);
|
|
break;
|
|
case CommandLineParser::ConvertType::ConvertScoreParts:
|
|
ret = converter()->convertScoreParts(task.inputFile, task.outputFile, stylePath);
|
|
break;
|
|
case CommandLineParser::ConvertType::File:
|
|
ret = converter()->fileConvert(task.inputFile, task.outputFile, stylePath, forceMode);
|
|
break;
|
|
case CommandLineParser::ConvertType::ExportScoreMedia: {
|
|
io::path_t highlightConfigPath = task.params[CommandLineParser::ParamKey::HighlightConfigPath].toString();
|
|
ret = converter()->exportScoreMedia(task.inputFile, task.outputFile, highlightConfigPath, stylePath, forceMode);
|
|
} break;
|
|
case CommandLineParser::ConvertType::ExportScoreMeta:
|
|
ret = converter()->exportScoreMeta(task.inputFile, task.outputFile, stylePath, forceMode);
|
|
break;
|
|
case CommandLineParser::ConvertType::ExportScoreParts:
|
|
ret = converter()->exportScoreParts(task.inputFile, task.outputFile, stylePath, forceMode);
|
|
break;
|
|
case CommandLineParser::ConvertType::ExportScorePartsPdf:
|
|
ret = converter()->exportScorePartsPdfs(task.inputFile, task.outputFile, stylePath, forceMode);
|
|
break;
|
|
case CommandLineParser::ConvertType::ExportScoreTranspose: {
|
|
std::string scoreTranspose = task.params[CommandLineParser::ParamKey::ScoreTransposeOptions].toString().toStdString();
|
|
ret = converter()->exportScoreTranspose(task.inputFile, task.outputFile, scoreTranspose, stylePath, forceMode);
|
|
} break;
|
|
case CommandLineParser::ConvertType::ExportScoreVideo: {
|
|
ret = converter()->exportScoreVideo(task.inputFile, task.outputFile);
|
|
} break;
|
|
case CommandLineParser::ConvertType::SourceUpdate: {
|
|
std::string scoreSource = task.params[CommandLineParser::ParamKey::ScoreSource].toString().toStdString();
|
|
ret = converter()->updateSource(task.inputFile, scoreSource, forceMode);
|
|
} break;
|
|
}
|
|
|
|
if (!ret) {
|
|
LOGE() << "failed convert, error: " << ret.toString();
|
|
}
|
|
|
|
return ret.code();
|
|
}
|
|
|
|
int App::processDiagnostic(const CommandLineParser::Diagnostic& task)
|
|
{
|
|
if (!diagnosticDrawProvider()) {
|
|
return make_ret(Ret::Code::NotSupported);
|
|
}
|
|
|
|
Ret ret = make_ret(Ret::Code::Ok);
|
|
|
|
if (task.input.isEmpty()) {
|
|
return make_ret(Ret::Code::UnknownError);
|
|
}
|
|
|
|
io::paths_t input;
|
|
for (const QString& p : task.input) {
|
|
input.push_back(p);
|
|
}
|
|
|
|
io::path_t output = task.output;
|
|
|
|
if (output.empty()) {
|
|
output = "./";
|
|
}
|
|
|
|
switch (task.type) {
|
|
case CommandLineParser::DiagnosticType::GenDrawData:
|
|
ret = diagnosticDrawProvider()->generateDrawData(input.front(), output);
|
|
break;
|
|
case CommandLineParser::DiagnosticType::ComDrawData:
|
|
IF_ASSERT_FAILED(input.size() == 2) {
|
|
return make_ret(Ret::Code::UnknownError);
|
|
}
|
|
ret = diagnosticDrawProvider()->compareDrawData(input.at(0), input.at(1), output);
|
|
break;
|
|
case CommandLineParser::DiagnosticType::DrawDataToPng:
|
|
ret = diagnosticDrawProvider()->drawDataToPng(input.front(), output);
|
|
break;
|
|
case CommandLineParser::DiagnosticType::DrawDiffToPng: {
|
|
io::path_t diffPath = input.at(0);
|
|
io::path_t refPath;
|
|
if (input.size() > 1) {
|
|
refPath = input.at(1);
|
|
}
|
|
ret = diagnosticDrawProvider()->drawDiffToPng(diffPath, refPath, output);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!ret) {
|
|
LOGE() << "diagnostic ret: " << ret.toString();
|
|
}
|
|
|
|
return ret.code();
|
|
}
|
|
|
|
int App::processAudioPluginRegistration(const CommandLineParser::AudioPluginRegistration& task)
|
|
{
|
|
Ret ret = make_ret(Ret::Code::Ok);
|
|
|
|
if (task.failedPlugin) {
|
|
ret = registerAudioPluginsScenario()->registerFailedPlugin(task.pluginPath, task.failCode);
|
|
} else {
|
|
ret = registerAudioPluginsScenario()->registerPlugin(task.pluginPath);
|
|
}
|
|
|
|
if (!ret) {
|
|
LOGE() << ret.toString();
|
|
}
|
|
|
|
return ret.code();
|
|
}
|
|
|
|
void App::processAutobot(const CommandLineParser::Autobot& task)
|
|
{
|
|
using namespace mu::autobot;
|
|
async::Channel<StepInfo, Ret> stepCh = autobot()->stepStatusChanged();
|
|
stepCh.onReceive(nullptr, [](const StepInfo& step, const Ret& ret){
|
|
if (!ret) {
|
|
LOGE() << "failed step: " << step.name << ", ret: " << ret.toString();
|
|
qApp->exit(ret.code());
|
|
} else {
|
|
LOGI() << "success step: " << step.name << ", ret: " << ret.toString();
|
|
}
|
|
});
|
|
|
|
async::Channel<io::path_t, IAutobot::Status> statusCh = autobot()->statusChanged();
|
|
statusCh.onReceive(nullptr, [](const io::path_t& path, IAutobot::Status st){
|
|
if (st == IAutobot::Status::Finished) {
|
|
LOGI() << "success finished, path: " << path;
|
|
qApp->exit(0);
|
|
}
|
|
});
|
|
|
|
IAutobot::Options opt;
|
|
opt.context = task.testCaseContextNameOrFile;
|
|
opt.contextVal = task.testCaseContextValue.toStdString();
|
|
opt.func = task.testCaseFunc.toStdString();
|
|
opt.funcArgs = task.testCaseFuncArgs.toStdString();
|
|
|
|
autobot()->execScript(task.testCaseNameOrFile, opt);
|
|
}
|