1 #include "base/NotationTypes.h" 2 #include "base/Segment.h" 3 #include "base/Selection.h" 4 #include <QTest> 5 #include "gui/seqmanager/SequenceManager.h" 6 #include "document/RosegardenDocument.h" 7 #include "gui/editors/notation/NotationView.h" 8 9 int init() 10 { 11 qputenv("QT_FATAL_WARNINGS", "1"); 12 return 0; 13 } 14 Q_CONSTRUCTOR_FUNCTION(init) 15 16 using namespace Rosegarden; 17 18 // This test opens data/examples/test_selection.rg and simulates using the keyboard to navigate and select in the notation view 19 class TestNotationViewSelection : public QObject 20 { 21 Q_OBJECT 22 23 public: 24 TestNotationViewSelection() 25 : m_doc(nullptr, {}, true /*skip autoload*/, true, false /*no sound*/), 26 m_view(nullptr) {} 27 28 private Q_SLOTS: 29 void initTestCase(); 30 void cleanupTestCase(); 31 void init(); 32 void testNavigate(); 33 void testKeyboardSelection_data(); 34 void testKeyboardSelection(); 35 void testSelectForwardAndBackward(); 36 37 private: 38 QString selectedNotes() const; 39 40 RosegardenDocument m_doc; 41 Segment *m_segment; 42 NotationView *m_view; 43 SequenceManager m_seqManager; 44 }; 45 46 void TestNotationViewSelection::initTestCase() 47 { 48 // Loading from a file 49 const QString input = QFINDTESTDATA("../data/examples/test_selection.rg"); 50 QVERIFY(!input.isEmpty()); // file not found 51 m_doc.openDocument(input, false /*not permanent, i.e. don't create midi devices*/, true /*no progress dlg*/); 52 53 const SegmentMultiSet segments = m_doc.getComposition().getSegments(); 54 QVERIFY(!segments.empty()); 55 m_segment = *segments.begin(); 56 std::vector<Segment *> segmentsVec; 57 segmentsVec.push_back(m_segment); 58 59 m_view = new NotationView(&m_doc, segmentsVec); 60 61 QVERIFY(!m_view->getSelection()); 62 QCOMPARE(m_view->getCurrentSegment(), m_segment); 63 QCOMPARE(m_segment->getStartTime(), timeT(0)); 64 65 m_seqManager.setDocument(&m_doc); 66 67 // The mainwindow connects fast-forward and rewind (to intercept them when recording), so we need to do it ourselves here. 68 connect(m_view, &NotationView::fastForwardPlayback, 69 &m_seqManager, &SequenceManager::fastforward); 70 connect(m_view, &NotationView::rewindPlayback, 71 &m_seqManager, &SequenceManager::rewind); 72 connect(m_view, &NotationView::fastForwardPlaybackToEnd, 73 &m_seqManager, &SequenceManager::fastForwardToEnd); 74 connect(m_view, &NotationView::rewindPlaybackToBeginning, 75 &m_seqManager, &SequenceManager::rewindToBeginning); 76 77 } 78 79 void TestNotationViewSelection::cleanupTestCase() 80 { 81 delete m_view; 82 } 83 84 // Returns the notes in the selection, as a string of note names. Ex: "ABBDG". 85 QString TestNotationViewSelection::selectedNotes() const 86 { 87 EventSelection *selection = m_view->getSelection(); 88 if (!selection) { 89 return QString(); 90 } 91 const EventContainer &eventContainer = selection->getSegmentEvents(); 92 QString ret; 93 Key defaultKey; 94 for (EventContainer::const_iterator it = eventContainer.begin(); it != eventContainer.end(); ++it) { 95 Event *ev = *it; 96 if (ev->isa(Note::EventType)) { 97 ret += Pitch(*ev).getNoteName(defaultKey); 98 } else if (ev->isa(Note::EventRestType)) { 99 ret += 'R'; 100 } 101 } 102 return ret; 103 } 104 105 void TestNotationViewSelection::init() 106 { 107 // Before each test, unselect all and go back to position 0 108 m_view->setSelection(nullptr, false); 109 m_doc.slotSetPointerPosition(0); 110 } 111 112 void TestNotationViewSelection::testNavigate() 113 { 114 // Go right one note 115 m_view->slotStepForward(); 116 QCOMPARE(m_doc.getComposition().getPosition(), timeT(960)); // one quarter 117 118 // Go to next bar 119 m_seqManager.fastforward(); 120 QCOMPARE(m_doc.getComposition().getPosition(), timeT(3840)); // one quarter 121 122 QVector<timeT> expectedPositions; 123 expectedPositions << 960 // one quarter 124 << 960 * 2 // one rest 125 << 960 * 2.5 // one eighth 126 << 960 * 3 // another 127 << 960 * 3.5 // another 128 << 3840 // second bar 129 << 3840 + 480 130 << 3840 + 960 131 << 3840 + 960 * 2 132 << 7680 133 << 7680 + 3840 134 << 15360 135 << 15360 + 480 136 << 15360 + 480 * 2 137 << 15360 + 480 * 3 138 << 15360 + 480 * 4 139 ; 140 for (int i = 0 ; i < expectedPositions.size(); ++i) { 141 m_view->slotStepForward(); 142 QCOMPARE(m_doc.getComposition().getPosition(), timeT(3840 + expectedPositions.at(i))); 143 } 144 } 145 146 void TestNotationViewSelection::testKeyboardSelection_data() 147 { 148 QTest::addColumn<QString>("keysPressed"); 149 QTest::addColumn<QStringList>("expectedSelections"); 150 151 // Syntax for keyPressed: 152 // l = left, r = right, L = shift-left, R = shift right 153 // n = next bar (ctrl+right), N = select until next bar (ctrl+shift+right) 154 // p = prev bar (ctrl+left), P = select until previous bar (ctrl+shift+left) 155 156 // To understand expectedSelections, note that the beginning of the file says: ABCDG[rest]... 157 QTest::newRow("1-3-5") << "RrRrR" << (QStringList() << "A" << "A" << "AC" << "AC" << "ACG"); 158 QTest::newRow("shift_change_direction_1") << "RRLR" << (QStringList() << "A" << "AB" << "A" << "AB"); 159 QTest::newRow("shift_change_direction_2") << "nLLRL" << (QStringList() << "" << "D" << "CD" << "D" << "CD"); 160 QTest::newRow("bug_1519_testcase_2") << "RrL" << (QStringList() << "A" << "A" << "AB"); 161 QTest::newRow("shift_right_again_same_note") << "RRlR" << (QStringList() << "A" << "AB" << "AB" << "A"); 162 QTest::newRow("shift_left_again_same_note") << "nLLrL" << (QStringList() << "" << "D" << "CD" << "CD" << "D"); 163 QTest::newRow("select_unselect_bar") << "NprNP" << (QStringList() << "ABCD" << "ABCD" << "ABCD" << "A" << "ABCD"); 164 } 165 166 void TestNotationViewSelection::testKeyboardSelection() 167 { 168 QFETCH(QString, keysPressed); 169 QFETCH(QStringList, expectedSelections); 170 for (int i = 0 ; i < keysPressed.size(); ++i) { 171 const QChar key = keysPressed.at(i); 172 switch (key.toLatin1()) { 173 case 'l': 174 m_view->slotStepBackward(); 175 break; 176 case 'r': 177 m_view->slotStepForward(); 178 break; 179 case 'L': 180 m_view->slotExtendSelectionBackward(); 181 break; 182 case 'R': 183 m_view->slotExtendSelectionForward(); 184 break; 185 case 'n': 186 m_seqManager.fastforward(); 187 break; 188 case 'N': 189 m_view->slotExtendSelectionForwardBar(); 190 break; 191 case 'p': 192 m_seqManager.rewind(); 193 break; 194 case 'P': 195 m_view->slotExtendSelectionBackwardBar(); 196 break; 197 } 198 const QString prefix = QString("step %1, key %2: ").arg(i).arg(key); // more info in case of failure 199 QCOMPARE(prefix + selectedNotes(), prefix + expectedSelections.at(i)); 200 } 201 } 202 203 void TestNotationViewSelection::testSelectForwardAndBackward() 204 { 205 m_seqManager.fastforward(); 206 207 QStringList expectedSelections; 208 expectedSelections << "G" 209 << "G" // the rest doesn't get selected, currently 210 << "GB" 211 << "GBCC" // tied notes get selected together 212 << "GBCCBB" 213 << "GBCCBBGGG" 214 << "GBCCBBGGGCC" 215 << "GBCCBBGGGCCG" 216 << "GBCCBBGGGCCGBDBD" 217 << "GBCCBBGGGCCGBDBDG" 218 << "GBCCBBGGGCCGBDBDGC" 219 ; 220 221 // select forward 222 for (int i = 0 ; i < expectedSelections.size(); ++i) { 223 m_view->slotExtendSelectionForward(); 224 QCOMPARE(selectedNotes(), expectedSelections.at(i)); 225 } 226 227 const int pos = m_doc.getComposition().getPosition(); 228 229 // unselect backward 230 QStringList expectedSelectionsBack = expectedSelections; 231 std::reverse(expectedSelectionsBack.begin(), expectedSelectionsBack.end()); 232 expectedSelectionsBack.append(QString()); 233 234 for (int i = 0 ; i < expectedSelectionsBack.size(); ++i) { 235 if (i > 0) 236 m_view->slotExtendSelectionBackward(); 237 QCOMPARE(selectedNotes(), expectedSelectionsBack.at(i)); 238 } 239 QCOMPARE(selectedNotes(), QString()); 240 241 // select everything backward, check at end 242 m_doc.slotSetPointerPosition(pos); 243 for (int i = 0 ; i < expectedSelections.size(); ++i) { 244 m_view->slotExtendSelectionBackward(); 245 } 246 QCOMPARE(m_doc.getComposition().getPosition(), timeT(3840)); // one quarter 247 QCOMPARE(selectedNotes(), QString("GBCCBBGGGCCGDBDBGC")); // order of notes in the chords is reversed, doesn't matter 248 } 249 250 QTEST_MAIN(TestNotationViewSelection) 251 252 #include "test_notationview_selection.moc" 253