Add script tests and script recorder
This commit is contained in:
parent
d2471e0c35
commit
ee78931e10
22 changed files with 1889 additions and 2 deletions
|
@ -1129,6 +1129,19 @@ void ScoreDiff::editPropertyDiffs()
|
|||
deleteDiffs(_diffs, abandonedDiffs);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScoreDiff::equal
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool ScoreDiff::equal() const
|
||||
{
|
||||
for (const TextDiff& td : _textDiffs) {
|
||||
if (td.type != DiffType::EQUAL)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScoreDiff::rawDiff
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -162,6 +162,8 @@ class ScoreDiff {
|
|||
const Score* score1() const { return _s1; }
|
||||
const Score* score2() const { return _s2; }
|
||||
|
||||
bool equal() const;
|
||||
|
||||
QString rawDiff(bool skipEqual = true) const;
|
||||
QString userDiff() const;
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@ QT5_WRAP_UI (ui_headers
|
|||
importmidi/importmidi_panel.ui
|
||||
|
||||
scorecmp/scorecmp_tool.ui
|
||||
script/script_recorder.ui
|
||||
|
||||
debugger/barline.ui
|
||||
debugger/harmony.ui
|
||||
|
@ -426,6 +427,7 @@ add_executable ( ${ExecutableName}
|
|||
preferenceslistwidget.cpp preferenceslistwidget.h
|
||||
extension.cpp extension.h
|
||||
tourhandler.cpp
|
||||
script/script.cpp script/scriptentry.cpp script/testscript.cpp script/recorderwidget.cpp
|
||||
|
||||
${COCOABRIDGE}
|
||||
${OMR_FILES}
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#include "importmidi/importmidi_panel.h"
|
||||
#include "importmidi/importmidi_operations.h"
|
||||
#include "scorecmp/scorecmp.h"
|
||||
#include "script/recorderwidget.h"
|
||||
#include "libmscore/scorediff.h"
|
||||
#include "libmscore/chord.h"
|
||||
#include "libmscore/segment.h"
|
||||
|
@ -156,6 +157,7 @@ QString iconPath;
|
|||
bool converterMode = false;
|
||||
static bool rawDiffMode = false;
|
||||
static bool diffMode = false;
|
||||
static bool scriptTestMode = false;
|
||||
bool processJob = false;
|
||||
bool externalIcons = false;
|
||||
bool pluginMode = false;
|
||||
|
@ -1107,6 +1109,17 @@ MuseScore::MuseScore()
|
|||
}
|
||||
addDockWidget(Qt::BottomDockWidgetArea, scoreCmpTool);
|
||||
|
||||
if (MuseScore::unstable()) {
|
||||
scriptRecorder = new ScriptRecorderWidget(this, this);
|
||||
scriptRecorder->setVisible(false);
|
||||
QAction* a = getAction("toggle-script-recorder");
|
||||
connect(
|
||||
scriptRecorder, &ScriptRecorderWidget::visibilityChanged,
|
||||
a, &QAction::setChecked
|
||||
);
|
||||
addDockWidget(Qt::RightDockWidgetArea, scriptRecorder);
|
||||
}
|
||||
|
||||
mainWindow->setStretchFactor(0, 1);
|
||||
mainWindow->setStretchFactor(1, 0);
|
||||
mainWindow->setSizes(QList<int>({500, 50}));
|
||||
|
@ -1637,6 +1650,13 @@ MuseScore::MuseScore()
|
|||
|
||||
menuTools->addAction(getAction("del-empty-measures"));
|
||||
|
||||
if (MuseScore::unstable()) {
|
||||
menuTools->addSeparator();
|
||||
a = getAction("toggle-script-recorder");
|
||||
a->setCheckable(true);
|
||||
menuTools->addAction(a);
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// Menu Plugins
|
||||
//---------------------
|
||||
|
@ -2915,7 +2935,7 @@ void MuseScore::removeTab(int i)
|
|||
|
||||
QString tmpName = score->tmpName();
|
||||
|
||||
if (checkDirty(score))
|
||||
if (!scriptTestMode && checkDirty(score))
|
||||
return;
|
||||
if (seq && seq->score() == score) {
|
||||
seq->stopWait();
|
||||
|
@ -2950,6 +2970,35 @@ void MuseScore::removeTab(int i)
|
|||
update();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// runTestScripts
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MuseScore::runTestScripts(const QStringList& scriptFiles)
|
||||
{
|
||||
ScriptContext ctx(this);
|
||||
bool allPassed = true;
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
for (const QString& scriptFile : scriptFiles) {
|
||||
// ensure no scores are open not to interfere with
|
||||
// the next script initialization process.
|
||||
while (!scores().empty()) {
|
||||
closeScore(scores().back());
|
||||
}
|
||||
std::unique_ptr<Script> script = Script::fromFile(scriptFile);
|
||||
const bool pass = script->execute(ctx);
|
||||
QTextStream(stdout) << "Test " << scriptFile << (pass ? " PASS" : " FAIL") << endl;
|
||||
++total;
|
||||
if (pass)
|
||||
++passed;
|
||||
else
|
||||
allPassed = false;
|
||||
}
|
||||
QTextStream(stdout) << "Test scripts: total: " << total << ", passed: " << passed << endl;
|
||||
return allPassed;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// loadTranslation
|
||||
//---------------------------------------------------------
|
||||
|
@ -3418,6 +3467,9 @@ static bool processNonGui(const QStringList& argv)
|
|||
delete s2;
|
||||
}
|
||||
|
||||
if (scriptTestMode)
|
||||
return mscore->runTestScripts(argv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5519,6 +5571,11 @@ ScoreTab* MuseScore::createScoreTab()
|
|||
|
||||
void MuseScore::cmd(QAction* a, const QString& cmd)
|
||||
{
|
||||
#ifdef MSCORE_UNSTABLE
|
||||
if (scriptRecorder)
|
||||
scriptRecorder->recordCommand(cmd);
|
||||
#endif
|
||||
|
||||
if (cmd == "instruments") {
|
||||
editInstrList();
|
||||
if (mixer)
|
||||
|
@ -5692,6 +5749,10 @@ void MuseScore::cmd(QAction* a, const QString& cmd)
|
|||
showPianoKeyboard(a->isChecked());
|
||||
else if (cmd == "toggle-scorecmp-tool")
|
||||
scoreCmpTool->setVisible(a->isChecked());
|
||||
#ifdef MSCORE_UNSTABLE
|
||||
else if (cmd == "toggle-script-recorder")
|
||||
scriptRecorder->setVisible(a->isChecked());
|
||||
#endif
|
||||
else if (cmd == "plugin-creator")
|
||||
showPluginCreator(a);
|
||||
else if (cmd == "plugin-manager")
|
||||
|
@ -6692,6 +6753,7 @@ int main(int argc, char* av[])
|
|||
parser.addOption(QCommandLineOption({"e", "experimental"}, "Enable experimental features"));
|
||||
parser.addOption(QCommandLineOption({"c", "config-folder"}, "Override configuration and settings folder", "dir"));
|
||||
parser.addOption(QCommandLineOption({"t", "test-mode"}, "Set test mode flag for all files")); // this includes --template-mode
|
||||
parser.addOption(QCommandLineOption("run-test-script", "Run script tests listed in the command line arguments"));
|
||||
parser.addOption(QCommandLineOption({"M", "midi-operations"}, "Specify MIDI import operations file", "file"));
|
||||
parser.addOption(QCommandLineOption({"w", "no-webview"}, "No web view in start center"));
|
||||
parser.addOption(QCommandLineOption({"P", "export-score-parts"}, "Used with '-o <file>.pdf', export score and parts"));
|
||||
|
@ -6846,6 +6908,12 @@ int main(int argc, char* av[])
|
|||
MScore::noGui = true;
|
||||
diffMode = true;
|
||||
}
|
||||
if (parser.isSet("run-test-script")) {
|
||||
if (rawDiffMode || diffMode)
|
||||
qFatal("incompatible options");
|
||||
MScore::noGui = true;
|
||||
scriptTestMode = true;
|
||||
}
|
||||
|
||||
QStringList argv = parser.positionalArguments();
|
||||
|
||||
|
@ -6874,6 +6942,8 @@ int main(int argc, char* av[])
|
|||
if (argv.size() != 2)
|
||||
qFatal("Only two scores are needed for performing a comparison");
|
||||
}
|
||||
if (scriptTestMode && argv.empty())
|
||||
qFatal("Please specify scripts to execute");
|
||||
|
||||
if (dataPath.isEmpty())
|
||||
dataPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
|
@ -6972,8 +7042,10 @@ int main(int argc, char* av[])
|
|||
|
||||
if (!MScore::noGui)
|
||||
MuseScore::updateUiStyleAndTheme();
|
||||
else
|
||||
else {
|
||||
genIcons(); // in GUI mode generated in updateUiStyleAndTheme()
|
||||
noSeq = true;
|
||||
}
|
||||
|
||||
// Do not create sequencer and audio drivers if run with '-s'
|
||||
if (!noSeq) {
|
||||
|
|
|
@ -97,6 +97,7 @@ class Driver;
|
|||
class Seq;
|
||||
class ImportMidiPanel;
|
||||
class ScoreComparisonTool;
|
||||
class ScriptRecorderWidget;
|
||||
class Startcenter;
|
||||
class HelpBrowser;
|
||||
class ToolbarEditor;
|
||||
|
@ -246,6 +247,9 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
QSplitter* mainWindow;
|
||||
|
||||
ScoreComparisonTool* scoreCmpTool { 0 };
|
||||
#ifdef MSCORE_UNSTABLE
|
||||
ScriptRecorderWidget* scriptRecorder { nullptr };
|
||||
#endif
|
||||
|
||||
MagBox* mag;
|
||||
QComboBox* viewModeCombo;
|
||||
|
@ -816,6 +820,9 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
bool importExtension(QString path);
|
||||
bool uninstallExtension(QString extensionId);
|
||||
Q_INVOKABLE bool isInstalledExtension(QString extensionId);
|
||||
|
||||
bool runTestScripts(const QStringList& scripts);
|
||||
friend class Script;
|
||||
};
|
||||
|
||||
extern MuseScore* mscore;
|
||||
|
|
155
mscore/script/recorderwidget.cpp
Normal file
155
mscore/script/recorderwidget.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "recorderwidget.h"
|
||||
|
||||
#include "ui_script_recorder.h"
|
||||
#include "musescore.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget
|
||||
//---------------------------------------------------------
|
||||
|
||||
ScriptRecorderWidget::ScriptRecorderWidget(MuseScore* mscore, QWidget* parent)
|
||||
: QDockWidget(parent), _ui(new Ui::ScriptRecorderWidget), _mscore(mscore),
|
||||
_recorder(mscore), _scriptDir(QDir::homePath())
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
updateDirLabel();
|
||||
updateActions();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ~ScriptRecorderWidget
|
||||
//---------------------------------------------------------
|
||||
|
||||
ScriptRecorderWidget::~ScriptRecorderWidget()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::changeEvent
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::changeEvent(QEvent* e)
|
||||
{
|
||||
QDockWidget::changeEvent(e);
|
||||
switch(e->type()) {
|
||||
case QEvent::LanguageChange:
|
||||
_ui->retranslateUi(this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::scriptFileName
|
||||
//---------------------------------------------------------
|
||||
|
||||
QString ScriptRecorderWidget::scriptFileName() const
|
||||
{
|
||||
QDir dir(scriptDir());
|
||||
return dir.filePath(_ui->scriptNameEdit->text() + ".script");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::updateDirLabel
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::updateDirLabel()
|
||||
{
|
||||
QFontMetrics metrics(_ui->folderLabel->font());
|
||||
_ui->folderLabel->setText(metrics.elidedText(_scriptDir, Qt::ElideLeft, _ui->folderLabel->width()));
|
||||
_ui->folderLabel->setToolTip(_scriptDir);
|
||||
}
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::updateActions
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::updateActions()
|
||||
{
|
||||
QDir dir(scriptDir());
|
||||
QFileInfo script(scriptFileName());
|
||||
_ui->recordButton->setEnabled(dir.exists() && !script.baseName().isEmpty() && !script.exists());
|
||||
_ui->replayButton->setEnabled(script.isFile());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::on_changeFolderButton_clicked
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::on_changeFolderButton_clicked()
|
||||
{
|
||||
QString newDir = QFileDialog::getExistingDirectory(this, "Open scripts directory");
|
||||
if (!newDir.isEmpty())
|
||||
_scriptDir = newDir;
|
||||
updateDirLabel();
|
||||
updateActions();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::on_scriptNameEdit_textEdited
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::on_scriptNameEdit_textEdited()
|
||||
{
|
||||
updateActions();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::on_recordButton_clicked
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::on_recordButton_clicked()
|
||||
{
|
||||
const bool startRecord = !_recorder.isRecording();
|
||||
_ui->changeFolderButton->setEnabled(!startRecord);
|
||||
_ui->scriptNameEdit->setEnabled(!startRecord);
|
||||
_ui->replayButton->setEnabled(false);
|
||||
_ui->recordButton->setText(startRecord ? "Stop recording" : "Start recording");
|
||||
|
||||
if (startRecord) {
|
||||
_recorder.setFile(scriptFileName());
|
||||
_recorder.context().setCwd(scriptDir());
|
||||
_recorder.setRecording(true);
|
||||
_recorder.recordInitState();
|
||||
}
|
||||
else {
|
||||
QFileInfo scriptInfo(scriptFileName());
|
||||
QString scoreName(scriptInfo.baseName() + ".mscx");
|
||||
_recorder.recordScoreTest(scoreName);
|
||||
_recorder.close();
|
||||
}
|
||||
|
||||
if (!_recorder.isRecording())
|
||||
updateActions();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget::on_replayButton_clicked
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorderWidget::on_replayButton_clicked()
|
||||
{
|
||||
auto script = Script::fromFile(scriptFileName());
|
||||
if (!script)
|
||||
return;
|
||||
ScriptContext ctx(_mscore);
|
||||
ctx.setCwd(scriptDir());
|
||||
ctx.setStopOnError(false);
|
||||
script->execute(ctx);
|
||||
}
|
||||
}
|
61
mscore/script/recorderwidget.h
Normal file
61
mscore/script/recorderwidget.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __SCRIPT_RECORDER_WIDGET_H__
|
||||
#define __SCRIPT_RECORDER_WIDGET_H__
|
||||
|
||||
#include "script.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScriptRecorderWidget;
|
||||
}
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class MuseScore;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorderWidget
|
||||
//---------------------------------------------------------
|
||||
|
||||
class ScriptRecorderWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Ui::ScriptRecorderWidget* _ui;
|
||||
MuseScore* _mscore;
|
||||
ScriptRecorder _recorder;
|
||||
QString _scriptDir;
|
||||
|
||||
const QString& scriptDir() const { return _scriptDir; }
|
||||
QString scriptFileName() const;
|
||||
void updateDirLabel();
|
||||
void updateActions();
|
||||
|
||||
private slots:
|
||||
void on_changeFolderButton_clicked();
|
||||
void on_scriptNameEdit_textEdited();
|
||||
void on_recordButton_clicked();
|
||||
void on_replayButton_clicked();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent* e) override;
|
||||
|
||||
public:
|
||||
explicit ScriptRecorderWidget(MuseScore* mscore, QWidget* parent = nullptr);
|
||||
~ScriptRecorderWidget();
|
||||
|
||||
void recordCommand(const QString& cmd) { _recorder.recordCommand(cmd); }
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
#endif
|
172
mscore/script/script.cpp
Normal file
172
mscore/script/script.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "script.h"
|
||||
|
||||
#include "musescore.h"
|
||||
#include "scoreview.h"
|
||||
#include "scriptentry.h"
|
||||
#include "testscript.h"
|
||||
|
||||
#include "libmscore/score.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptContext
|
||||
//---------------------------------------------------------
|
||||
|
||||
ScriptContext::ScriptContext(MuseScore* context)
|
||||
: _mscore(context), _cwd(QDir::current())
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptContext::execLog
|
||||
// Returns logging stream to be used by script entries
|
||||
// at execution time.
|
||||
// Current implementation returns a stream writing to
|
||||
// stdout performing its initialization if necessary.
|
||||
//---------------------------------------------------------
|
||||
|
||||
QTextStream& ScriptContext::execLog()
|
||||
{
|
||||
if (!_execLog)
|
||||
_execLog.reset(new QTextStream(stdout));
|
||||
return *_execLog;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Script::execute
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool Script::execute(ScriptContext& ctx) const
|
||||
{
|
||||
bool success = true;
|
||||
for (const auto& e : _entries) {
|
||||
if (e) {
|
||||
if (!e->execute(ctx)) {
|
||||
if (ctx.stopOnError())
|
||||
return false;
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Script::execCmd
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Script::execCmd(MuseScore* mscore, QAction* a, const QString& cmd)
|
||||
{
|
||||
mscore->cmd(a, cmd);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Script::fromFile
|
||||
//---------------------------------------------------------
|
||||
|
||||
std::unique_ptr<Script> Script::fromFile(QString fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return nullptr;
|
||||
QTextStream stream(&file);
|
||||
|
||||
QString line;
|
||||
auto script = std::unique_ptr<Script>(new Script());
|
||||
while (stream.readLineInto(&line))
|
||||
script->addFromLine(line);
|
||||
return script;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Script::writeToFile
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Script::writeToFile(QString fileName) const
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
return;
|
||||
QTextStream stream(&file);
|
||||
|
||||
for (const auto& e : _entries) {
|
||||
if (e)
|
||||
stream << e->serialize() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder::ensureFileOpen
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool ScriptRecorder::ensureFileOpen()
|
||||
{
|
||||
if (_file.isOpen())
|
||||
return true;
|
||||
if (_file.open(QIODevice::WriteOnly)) {
|
||||
_stream.setDevice(&_file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder::syncRecord
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorder::syncRecord()
|
||||
{
|
||||
if (!_recording)
|
||||
return;
|
||||
if (!ensureFileOpen())
|
||||
return;
|
||||
for (; _recorded < _script.nentries(); ++_recorded)
|
||||
_stream << _script.entry(_recorded).serialize() << endl;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder::recordInitState
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorder::recordInitState()
|
||||
{
|
||||
if (_recording)
|
||||
_script.addEntry(new InitScriptEntry(_ctx));
|
||||
syncRecord();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder::recordCommand
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorder::recordCommand(const QString& name)
|
||||
{
|
||||
if (_recording)
|
||||
_script.addCommand(name);
|
||||
syncRecord();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder::recordScoreTest
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ScriptRecorder::recordScoreTest(QString scoreName)
|
||||
{
|
||||
if (_recording)
|
||||
_script.addEntry(ScoreTestScriptEntry::fromContext(_ctx, scoreName));
|
||||
syncRecord();
|
||||
}
|
||||
}
|
121
mscore/script/script.h
Normal file
121
mscore/script/script.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __SCRIPT_H__
|
||||
#define __SCRIPT_H__
|
||||
|
||||
#include "scriptentry.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class MasterScore;
|
||||
class MuseScore;
|
||||
class ScoreView;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptContext
|
||||
//---------------------------------------------------------
|
||||
|
||||
class ScriptContext {
|
||||
MuseScore* _mscore;
|
||||
QDir _cwd; // current working directory
|
||||
bool _relativePaths = true;
|
||||
bool _stopOnError = true;
|
||||
std::unique_ptr<QTextStream> _execLog;
|
||||
|
||||
public:
|
||||
ScriptContext(MuseScore* mscore);
|
||||
|
||||
MuseScore* mscore() { return _mscore; }
|
||||
const MuseScore* mscore() const { return _mscore; }
|
||||
|
||||
const QDir& cwd() const { return _cwd; }
|
||||
void setCwd(QDir dir) { _cwd = dir; }
|
||||
bool relativePaths() const { return _relativePaths; }
|
||||
void setRelativePaths(bool rel) { _relativePaths = rel; }
|
||||
bool stopOnError() const { return _stopOnError; }
|
||||
void setStopOnError(bool stop) { _stopOnError = stop; }
|
||||
|
||||
QString absoluteFilePath(const QString& filePath) const { return _cwd.absoluteFilePath(filePath); }
|
||||
QString relativeFilePath(const QString& filePath) const { return _cwd.relativeFilePath(filePath); }
|
||||
QTextStream& execLog(); // logging stream to be used while executing scripts
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Script
|
||||
//---------------------------------------------------------
|
||||
|
||||
class Script final {
|
||||
std::vector<std::unique_ptr<ScriptEntry>> _entries;
|
||||
|
||||
// FIXME: do something better?
|
||||
friend class CommandScriptEntry;
|
||||
static void execCmd(MuseScore* mscore, QAction* a, const QString& cmd);
|
||||
|
||||
public:
|
||||
const ScriptEntry& entry(int n) const { return *_entries[n]; }
|
||||
const ScriptEntry& lastEntry() const { return *_entries.back(); }
|
||||
int nentries() const { return _entries.size(); }
|
||||
bool empty() const { return _entries.empty(); }
|
||||
|
||||
bool execute(ScriptContext& ctx) const;
|
||||
|
||||
void clear() { _entries.clear(); }
|
||||
void addEntry(std::unique_ptr<ScriptEntry>&& e) { _entries.push_back(std::move(e)); }
|
||||
void addEntry(ScriptEntry* e) { _entries.emplace_back(e); }
|
||||
void addCommand(const QByteArray& cmd) { _entries.emplace_back(new CommandScriptEntry(cmd)); }
|
||||
void addCommand(const QString& cmd) { addCommand(cmd.toLatin1()); }
|
||||
|
||||
void addFromLine(const QString& line) { _entries.push_back(ScriptEntry::deserialize(line)); }
|
||||
|
||||
static std::unique_ptr<Script> fromFile(QString fileName);
|
||||
void writeToFile(QString fileName) const;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptRecorder
|
||||
// Records script writing it to a file just on the fly
|
||||
// so that the script is preserved in case actions
|
||||
// series wasn't finished properly (e.g. in case of
|
||||
// MuseScore crash).
|
||||
//---------------------------------------------------------
|
||||
|
||||
class ScriptRecorder {
|
||||
QFile _file;
|
||||
QTextStream _stream;
|
||||
Script _script;
|
||||
ScriptContext _ctx;
|
||||
|
||||
bool _recording = false;
|
||||
int _recorded = 0;
|
||||
|
||||
bool ensureFileOpen();
|
||||
void syncRecord();
|
||||
|
||||
public:
|
||||
ScriptRecorder(MuseScore* context) : _ctx(context) {}
|
||||
|
||||
bool isRecording() const { return _recording; }
|
||||
void setRecording(bool r) { _recording = r; }
|
||||
|
||||
void close() { setRecording(false); _file.close(); _script.clear(); _recorded = 0; }
|
||||
void setFile(QString fileName) { close(); _file.setFileName(fileName); }
|
||||
ScriptContext& context() { return _ctx; }
|
||||
const ScriptContext& context() const { return _ctx; }
|
||||
|
||||
void recordInitState();
|
||||
void recordCommand(const QString& name);
|
||||
void recordScoreTest(QString scoreName = QString());
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
#endif
|
86
mscore/script/script_recorder.ui
Normal file
86
mscore/script/script_recorder.ui
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptRecorderWidget</class>
|
||||
<widget class="QDockWidget" name="ScriptRecorderWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>858</width>
|
||||
<height>245</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Script Recorder</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="changeFolderButton">
|
||||
<property name="text">
|
||||
<string>Change folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="folderLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Folder:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>changeFolderButton</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Script name:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>scriptNameEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="recordButton">
|
||||
<property name="text">
|
||||
<string>Record</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="replayButton">
|
||||
<property name="text">
|
||||
<string>Replay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="scriptNameEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>changeFolderButton</tabstop>
|
||||
<tabstop>scriptNameEdit</tabstop>
|
||||
<tabstop>recordButton</tabstop>
|
||||
<tabstop>replayButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
106
mscore/script/scriptentry.cpp
Normal file
106
mscore/script/scriptentry.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "scriptentry.h"
|
||||
|
||||
#include "musescore.h"
|
||||
#include "script.h"
|
||||
#include "testscript.h"
|
||||
|
||||
#include "libmscore/score.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptEntry::deserialize
|
||||
//---------------------------------------------------------
|
||||
|
||||
std::unique_ptr<ScriptEntry> ScriptEntry::deserialize(const QString& line)
|
||||
{
|
||||
QStringList tokens = line.split(' ');
|
||||
const QString& type = tokens[0];
|
||||
if (tokens.empty()) {
|
||||
qWarning("Script line is empty");
|
||||
return nullptr;
|
||||
}
|
||||
if (type == SCRIPT_CMD) {
|
||||
if (tokens.size() != 2) {
|
||||
qWarning("Unexpected number of tokens in command: %d", tokens.size());
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<ScriptEntry>(new CommandScriptEntry(tokens[1]));
|
||||
}
|
||||
if (type == SCRIPT_INIT)
|
||||
return InitScriptEntry::deserialize(tokens);
|
||||
if (type == SCRIPT_TEST)
|
||||
return TestScriptEntry::deserialize(tokens);
|
||||
qWarning() << "Unsupported action type:" << type;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InitScriptEntry::InitScriptEntry
|
||||
//---------------------------------------------------------
|
||||
|
||||
InitScriptEntry::InitScriptEntry(const ScriptContext& ctx)
|
||||
{
|
||||
MasterScore* score = ctx.mscore()->currentScore()->masterScore();
|
||||
_filePath = score->importedFilePath();
|
||||
if (ctx.relativePaths())
|
||||
_filePath = ctx.relativeFilePath(_filePath);
|
||||
// TODO: handle excerpts
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InitScriptEntry::execute
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool InitScriptEntry::execute(ScriptContext& ctx) const
|
||||
{
|
||||
return ctx.mscore()->openScore(ctx.absoluteFilePath(_filePath));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InitScriptEntry::execute
|
||||
//---------------------------------------------------------
|
||||
|
||||
QString InitScriptEntry::serialize() const
|
||||
{
|
||||
// TODO: handle spaces in file path!
|
||||
return entryTemplate(SCRIPT_INIT).arg(_filePath);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InitScriptEntry::deserialize
|
||||
//---------------------------------------------------------
|
||||
|
||||
std::unique_ptr<ScriptEntry> InitScriptEntry::deserialize(const QStringList& tokens)
|
||||
{
|
||||
if (tokens.size() != 2) {
|
||||
qWarning("init: unexpected number of tokens: %d", tokens.size());
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<ScriptEntry>(new InitScriptEntry(tokens[1]));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// CommandScriptEntry::execute
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool CommandScriptEntry::execute(ScriptContext& ctx) const
|
||||
{
|
||||
// ctx.mscore()->cmd(getAction(_command.constData()), _command);
|
||||
Script::execCmd(ctx.mscore(), getAction(_command.constData()), _command);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
69
mscore/script/scriptentry.h
Normal file
69
mscore/script/scriptentry.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __SCRIPTENTRY_H__
|
||||
#define __SCRIPTENTRY_H__
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class ScriptContext;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScriptEntry
|
||||
//---------------------------------------------------------
|
||||
|
||||
class ScriptEntry {
|
||||
protected:
|
||||
static constexpr const char* SCRIPT_INIT = "init";
|
||||
static constexpr const char* SCRIPT_CMD = "cmd";
|
||||
static constexpr const char* SCRIPT_TEST = "test";
|
||||
|
||||
static QString entryTemplate(const char* entryType) { return QString("%1 %2").arg(entryType); }
|
||||
|
||||
public:
|
||||
virtual ~ScriptEntry() = default;
|
||||
virtual bool execute(ScriptContext& ctx) const = 0;
|
||||
virtual QString serialize() const = 0;
|
||||
static std::unique_ptr<ScriptEntry> deserialize(const QString& line);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InitScriptEntry
|
||||
// Initializes the environment for the script.
|
||||
//---------------------------------------------------------
|
||||
|
||||
class InitScriptEntry : public ScriptEntry {
|
||||
QString _filePath;
|
||||
public:
|
||||
explicit InitScriptEntry(const ScriptContext& ctx);
|
||||
explicit InitScriptEntry(const QString& filePath) : _filePath(filePath) {}
|
||||
bool execute(ScriptContext& ctx) const override;
|
||||
QString serialize() const override;
|
||||
static std::unique_ptr<ScriptEntry> deserialize(const QStringList& tokens);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// CommandScriptEntry
|
||||
//---------------------------------------------------------
|
||||
|
||||
class CommandScriptEntry : public ScriptEntry {
|
||||
QByteArray _command;
|
||||
public:
|
||||
explicit CommandScriptEntry(const QByteArray& cmd) : _command(cmd) {}
|
||||
explicit CommandScriptEntry(const QString& cmd) : _command(cmd.toLatin1()) {}
|
||||
explicit CommandScriptEntry(const char* cmd) : _command(cmd) {}
|
||||
bool execute(ScriptContext& ctx) const override;
|
||||
QString serialize() const override { return entryTemplate(SCRIPT_CMD).arg(_command.constData()); };
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
#endif
|
100
mscore/script/testscript.cpp
Normal file
100
mscore/script/testscript.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include "testscript.h"
|
||||
|
||||
#include "musescore.h"
|
||||
#include "script.h"
|
||||
|
||||
#include "libmscore/scorediff.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// TestScriptEntry::deserialize
|
||||
//---------------------------------------------------------
|
||||
|
||||
std::unique_ptr<ScriptEntry> TestScriptEntry::deserialize(const QStringList& tokens)
|
||||
{
|
||||
// assume that 0th token is just a "test" statement
|
||||
if (tokens.size() < 2) {
|
||||
qDebug("test: unexpected tokens size: %d", tokens.size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const QString& type = tokens[1];
|
||||
if (type == TEST_SCORE) {
|
||||
if (tokens.size() != 3) {
|
||||
qDebug("test: unexpected tokens size: %d", tokens.size());
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<ScriptEntry>(new ScoreTestScriptEntry(tokens[2]));
|
||||
}
|
||||
|
||||
qDebug() << "test: unsupported type:" << tokens[1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScoreTestScriptEntry::fromContext
|
||||
//---------------------------------------------------------
|
||||
|
||||
std::unique_ptr<ScriptEntry> ScoreTestScriptEntry::fromContext(const ScriptContext& ctx, QString fileName)
|
||||
{
|
||||
MasterScore* score = ctx.mscore()->currentScore()->masterScore();
|
||||
// TODO: handle excerpts
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
int scoreNum = 1;
|
||||
const QString templ("%1.mscx");
|
||||
fileName = templ.arg(QString::number(scoreNum));
|
||||
while (QFileInfo(fileName).exists())
|
||||
fileName = templ.arg(QString::number(++scoreNum));
|
||||
}
|
||||
|
||||
QString filePath = ctx.absoluteFilePath(fileName);
|
||||
QFileInfo fi(filePath);
|
||||
score->Score::saveFile(fi);
|
||||
|
||||
if (ctx.relativePaths())
|
||||
filePath = fileName;
|
||||
|
||||
return std::unique_ptr<ScriptEntry>(new ScoreTestScriptEntry(filePath));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScoreTestScriptEntry::execute
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool ScoreTestScriptEntry::execute(ScriptContext& ctx) const
|
||||
{
|
||||
MasterScore* curScore = ctx.mscore()->currentScore()->masterScore();
|
||||
if (!curScore) {
|
||||
ctx.execLog() << "ScoreTestScriptEntry: no current score" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString refFilePath = ctx.absoluteFilePath(_refPath);
|
||||
std::unique_ptr<MasterScore> refScore(ctx.mscore()->readScore(refFilePath));
|
||||
if (!refScore) {
|
||||
ctx.execLog() << "reference score loaded with errors: " << refFilePath << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ScoreDiff diff(curScore, refScore.get(), /* textDiffOnly */ true);
|
||||
if (!diff.equal()) {
|
||||
ctx.execLog() << "ScoreTestScriptEntry: fail\n" << diff.rawDiff() << endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
49
mscore/script/testscript.h
Normal file
49
mscore/script/testscript.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __TESTSCRIPT_H__
|
||||
#define __TESTSCRIPT_H__
|
||||
|
||||
#include "scriptentry.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
class ScriptContext;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// TestScriptEntry
|
||||
//---------------------------------------------------------
|
||||
|
||||
class TestScriptEntry : public ScriptEntry {
|
||||
protected:
|
||||
static QString entryTemplate(const char* testType) { return ScriptEntry::entryTemplate(SCRIPT_TEST).arg(testType) + " %1"; }
|
||||
public:
|
||||
static constexpr const char* TEST_SCORE = "score";
|
||||
|
||||
static std::unique_ptr<ScriptEntry> deserialize(const QStringList& tokens);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ScoreTestScriptEntry
|
||||
//---------------------------------------------------------
|
||||
|
||||
class ScoreTestScriptEntry : public TestScriptEntry {
|
||||
QString _refPath;
|
||||
public:
|
||||
ScoreTestScriptEntry(QString refPath) : _refPath(refPath) {}
|
||||
bool execute(ScriptContext& ctx) const override;
|
||||
QString serialize() const override { return entryTemplate(TEST_SCORE).arg(_refPath); }
|
||||
static std::unique_ptr<ScriptEntry> fromContext(const ScriptContext& ctx, QString fileName = QString());
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
#endif
|
|
@ -3583,6 +3583,18 @@ Shortcut Shortcut::_sc[] = {
|
|||
Icons::mail_ICON,
|
||||
Qt::ApplicationShortcut
|
||||
},
|
||||
#ifdef MSCORE_UNSTABLE
|
||||
{
|
||||
MsWidget::MAIN_WINDOW,
|
||||
STATE_NORMAL,
|
||||
"toggle-script-recorder",
|
||||
"Script Recorder",
|
||||
"Script recorder",
|
||||
0,
|
||||
Icons::Invalid_ICON,
|
||||
Qt::ApplicationShortcut
|
||||
},
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
MsWidget::MAIN_WINDOW,
|
||||
|
|
|
@ -232,6 +232,7 @@ subdirs (
|
|||
zerberus/opcodeparse
|
||||
zerberus/inputControls
|
||||
zerberus/loop
|
||||
testscript
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ const char* tests[] = {
|
|||
"importmidi/tst_importmidi",
|
||||
"libmscore/selectionrangedelete/tst_selectionrangedelete",
|
||||
"libmscore/parts/tst_parts",
|
||||
"testscript/tst_runscripts",
|
||||
#endif
|
||||
#if 0
|
||||
"libmscore/spanners/tst_spanners", // FAIL
|
||||
|
|
18
mtest/testscript/CMakeLists.txt
Normal file
18
mtest/testscript/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
#=============================================================================
|
||||
# MuseScore
|
||||
# Music Composition & Notation
|
||||
#
|
||||
# Copyright (C) 2018 Werner Schweer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2
|
||||
# as published by the Free Software Foundation and appearing in
|
||||
# the file LICENSE.GPL
|
||||
#=============================================================================
|
||||
|
||||
set(TARGET tst_runscripts)
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/mtest/cmake.inc)
|
||||
|
||||
add_dependencies(tst_runscripts mscore)
|
||||
add_definitions(-DMSCORE_EXECUTABLE="$<TARGET_FILE:mscore>")
|
403
mtest/testscript/scripts/basic_note_input.mscx
Normal file
403
mtest/testscript/scripts/basic_note_input.mscx
Normal file
|
@ -0,0 +1,403 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<museScore version="3.01">
|
||||
<programVersion>3.0.0</programVersion>
|
||||
<programRevision>d4d1d69</programRevision>
|
||||
<Score>
|
||||
<LayerTag id="0" tag="default"></LayerTag>
|
||||
<currentLayer>0</currentLayer>
|
||||
<Synthesizer>
|
||||
</Synthesizer>
|
||||
<Division>480</Division>
|
||||
<Style>
|
||||
<pageWidth>8.27</pageWidth>
|
||||
<pageHeight>11.69</pageHeight>
|
||||
<pagePrintableWidth>7.4826</pagePrintableWidth>
|
||||
<lastSystemFillLimit>0</lastSystemFillLimit>
|
||||
<Spatium>1.76389</Spatium>
|
||||
</Style>
|
||||
<showInvisible>1</showInvisible>
|
||||
<showUnprintable>1</showUnprintable>
|
||||
<showFrames>1</showFrames>
|
||||
<showMargins>0</showMargins>
|
||||
<metaTag name="arranger"></metaTag>
|
||||
<metaTag name="composer"></metaTag>
|
||||
<metaTag name="copyright"></metaTag>
|
||||
<metaTag name="creationDate">2018-11-12</metaTag>
|
||||
<metaTag name="lyricist"></metaTag>
|
||||
<metaTag name="movementNumber"></metaTag>
|
||||
<metaTag name="movementTitle"></metaTag>
|
||||
<metaTag name="platform">Linux</metaTag>
|
||||
<metaTag name="poet"></metaTag>
|
||||
<metaTag name="source"></metaTag>
|
||||
<metaTag name="translator"></metaTag>
|
||||
<metaTag name="workNumber"></metaTag>
|
||||
<metaTag name="workTitle">Treble</metaTag>
|
||||
<Part>
|
||||
<Staff id="1">
|
||||
<StaffType group="pitched">
|
||||
<name>stdNormal</name>
|
||||
</StaffType>
|
||||
</Staff>
|
||||
<trackName>Piano</trackName>
|
||||
<Instrument>
|
||||
<longName>Piano</longName>
|
||||
<shortName>Pno.</shortName>
|
||||
<trackName>Piano</trackName>
|
||||
<minPitchP>21</minPitchP>
|
||||
<maxPitchP>108</maxPitchP>
|
||||
<minPitchA>21</minPitchA>
|
||||
<maxPitchA>108</maxPitchA>
|
||||
<instrumentId>keyboard.piano</instrumentId>
|
||||
<Articulation>
|
||||
<velocity>100</velocity>
|
||||
<gateTime>95</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="staccatissimo">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>33</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="staccato">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>50</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="portato">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>67</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="tenuto">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>100</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="marcato">
|
||||
<velocity>120</velocity>
|
||||
<gateTime>67</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="sforzato">
|
||||
<velocity>120</velocity>
|
||||
<gateTime>100</gateTime>
|
||||
</Articulation>
|
||||
<Channel>
|
||||
<program value="0"/>
|
||||
<synti>Fluid</synti>
|
||||
</Channel>
|
||||
</Instrument>
|
||||
</Part>
|
||||
<Staff id="1">
|
||||
<VBox>
|
||||
<height>10</height>
|
||||
<Text>
|
||||
<style>Title</style>
|
||||
<text>Treble</text>
|
||||
</Text>
|
||||
</VBox>
|
||||
<Measure>
|
||||
<voice>
|
||||
<TimeSig>
|
||||
<sigN>4</sigN>
|
||||
<sigD>4</sigD>
|
||||
</TimeSig>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>69</pitch>
|
||||
<tpc>17</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>71</pitch>
|
||||
<tpc>19</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>72</pitch>
|
||||
<tpc>14</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>74</pitch>
|
||||
<tpc>16</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>76</pitch>
|
||||
<tpc>18</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>77</pitch>
|
||||
<tpc>13</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>79</pitch>
|
||||
<tpc>15</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
<Chord>
|
||||
<durationType>quarter</durationType>
|
||||
<Note>
|
||||
<pitch>81</pitch>
|
||||
<tpc>17</tpc>
|
||||
</Note>
|
||||
</Chord>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
</Staff>
|
||||
</Score>
|
||||
</museScore>
|
12
mtest/testscript/scripts/basic_note_input.script
Normal file
12
mtest/testscript/scripts/basic_note_input.script
Normal file
|
@ -0,0 +1,12 @@
|
|||
init init/Treble.mscx
|
||||
cmd note-input
|
||||
cmd note-a
|
||||
cmd note-b
|
||||
cmd note-c
|
||||
cmd note-d
|
||||
cmd note-e
|
||||
cmd note-f
|
||||
cmd note-g
|
||||
cmd note-a
|
||||
cmd escape
|
||||
test score basic_note_input.mscx
|
355
mtest/testscript/scripts/init/Treble.mscx
Normal file
355
mtest/testscript/scripts/init/Treble.mscx
Normal file
|
@ -0,0 +1,355 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<museScore version="3.01">
|
||||
<programVersion>3.0.0</programVersion>
|
||||
<programRevision>d4d1d69</programRevision>
|
||||
<Score>
|
||||
<LayerTag id="0" tag="default"></LayerTag>
|
||||
<currentLayer>0</currentLayer>
|
||||
<Synthesizer>
|
||||
</Synthesizer>
|
||||
<Division>480</Division>
|
||||
<Style>
|
||||
<pageWidth>8.27</pageWidth>
|
||||
<pageHeight>11.69</pageHeight>
|
||||
<pagePrintableWidth>7.4826</pagePrintableWidth>
|
||||
<lastSystemFillLimit>0</lastSystemFillLimit>
|
||||
<Spatium>1.76389</Spatium>
|
||||
</Style>
|
||||
<showInvisible>1</showInvisible>
|
||||
<showUnprintable>1</showUnprintable>
|
||||
<showFrames>1</showFrames>
|
||||
<showMargins>0</showMargins>
|
||||
<metaTag name="arranger"></metaTag>
|
||||
<metaTag name="composer"></metaTag>
|
||||
<metaTag name="copyright"></metaTag>
|
||||
<metaTag name="creationDate">2018-11-12</metaTag>
|
||||
<metaTag name="lyricist"></metaTag>
|
||||
<metaTag name="movementNumber"></metaTag>
|
||||
<metaTag name="movementTitle"></metaTag>
|
||||
<metaTag name="platform">Linux</metaTag>
|
||||
<metaTag name="poet"></metaTag>
|
||||
<metaTag name="source"></metaTag>
|
||||
<metaTag name="translator"></metaTag>
|
||||
<metaTag name="workNumber"></metaTag>
|
||||
<metaTag name="workTitle">Treble</metaTag>
|
||||
<Part>
|
||||
<Staff id="1">
|
||||
<StaffType group="pitched">
|
||||
<name>stdNormal</name>
|
||||
</StaffType>
|
||||
</Staff>
|
||||
<trackName>Piano</trackName>
|
||||
<Instrument>
|
||||
<longName>Piano</longName>
|
||||
<shortName>Pno.</shortName>
|
||||
<trackName>Piano</trackName>
|
||||
<minPitchP>21</minPitchP>
|
||||
<maxPitchP>108</maxPitchP>
|
||||
<minPitchA>21</minPitchA>
|
||||
<maxPitchA>108</maxPitchA>
|
||||
<instrumentId>keyboard.piano</instrumentId>
|
||||
<Articulation>
|
||||
<velocity>100</velocity>
|
||||
<gateTime>95</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="staccatissimo">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>33</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="staccato">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>50</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="portato">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>67</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="tenuto">
|
||||
<velocity>100</velocity>
|
||||
<gateTime>100</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="marcato">
|
||||
<velocity>120</velocity>
|
||||
<gateTime>67</gateTime>
|
||||
</Articulation>
|
||||
<Articulation name="sforzato">
|
||||
<velocity>120</velocity>
|
||||
<gateTime>100</gateTime>
|
||||
</Articulation>
|
||||
<Channel>
|
||||
<program value="0"/>
|
||||
<synti>Fluid</synti>
|
||||
</Channel>
|
||||
</Instrument>
|
||||
</Part>
|
||||
<Staff id="1">
|
||||
<VBox>
|
||||
<height>10</height>
|
||||
<Text>
|
||||
<style>Title</style>
|
||||
<text>Treble</text>
|
||||
</Text>
|
||||
</VBox>
|
||||
<Measure>
|
||||
<voice>
|
||||
<TimeSig>
|
||||
<sigN>4</sigN>
|
||||
<sigD>4</sigD>
|
||||
</TimeSig>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
</Staff>
|
||||
</Score>
|
||||
</museScore>
|
70
mtest/testscript/tst_runscripts.cpp
Normal file
70
mtest/testscript/tst_runscripts.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "mtest/testutils.h"
|
||||
#include "mscore/script/script.h"
|
||||
|
||||
#define DIR QString("testscript/")
|
||||
|
||||
using namespace Ms;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// TestScripts
|
||||
//---------------------------------------------------------
|
||||
|
||||
class TestScripts : public QObject, public MTest
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString scriptsPath;
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void runTestScripts();
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// initTestCase
|
||||
//---------------------------------------------------------
|
||||
|
||||
void TestScripts::initTestCase()
|
||||
{
|
||||
initMTest();
|
||||
scriptsPath = root + '/' + DIR + "scripts";
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// runTestScripts
|
||||
//---------------------------------------------------------
|
||||
|
||||
void TestScripts::runTestScripts()
|
||||
{
|
||||
Q_ASSERT(QDir::setCurrent(scriptsPath));
|
||||
|
||||
QDir cwd = QDir::current();
|
||||
QStringList nameFilters({ "*.script" });
|
||||
cwd.setNameFilters(nameFilters);
|
||||
cwd.setFilter(QDir::Files);
|
||||
cwd.setSorting(QDir::Name);
|
||||
QStringList scripts = cwd.entryList();
|
||||
|
||||
QStringList args({ "--run-test-script" });
|
||||
args << scripts;
|
||||
|
||||
if (!QFileInfo(MSCORE_EXECUTABLE).exists())
|
||||
qFatal("Cannot find executable: %s", MSCORE_EXECUTABLE);
|
||||
QVERIFY(QProcess::execute(MSCORE_EXECUTABLE, args) == 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestScripts)
|
||||
#include "tst_runscripts.moc"
|
Loading…
Reference in a new issue