1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Sonic Visualiser 5 An audio file viewer and annotation editor. 6 Centre for Digital Music, Queen Mary, University of London. 7 This file copyright 2017 Queen Mary, University of London. 8 9 This program is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License as 11 published by the Free Software Foundation; either version 2 of the 12 License, or (at your option) any later version. See the file 13 COPYING included with this distribution for more information. 14 */ 15 16 #ifndef TEST_CSV_STREAM_H 17 #define TEST_CSV_STREAM_H 18 19 #include <QtTest> 20 #include <QObject> 21 #include <sstream> 22 #include <functional> 23 24 #include "base/ProgressReporter.h" 25 #include "base/DataExportOptions.h" 26 #include "base/Selection.h" 27 #include "data/model/NoteModel.h" 28 #include "../CSVStreamWriter.h" 29 #include "../../model/test/MockWaveModel.h" 30 31 class StubReporter : public ProgressReporter 32 { 33 public: 34 StubReporter( std::function<bool()> isCancelled ) 35 : m_isCancelled(isCancelled) {} 36 bool isDefinite() const override { return true; } 37 void setDefinite(bool) override {} 38 bool wasCancelled() const override { return m_isCancelled(); } 39 void setMessage(QString) override {} 40 void setProgress(int p) override 41 { 42 ++m_calls; 43 m_percentageLog.push_back(p); 44 } 45 46 size_t getCallCount() const { return m_calls; } 47 std::vector<int> getPercentageLog() const { return m_percentageLog; } 48 void reset() { m_calls = 0; } 49 private: 50 size_t m_calls = 0; 51 std::function<bool()> m_isCancelled; 52 std::vector<int> m_percentageLog; 53 }; 54 55 class CSVStreamWriterTest : public QObject 56 { 57 Q_OBJECT 58 public: 59 std::string getExpectedString() 60 { 61 return 62 { 63 "0,0,0\n" 64 "1,0,0\n" 65 "2,0,0\n" 66 "3,0,0\n" 67 "4,1,1\n" 68 "5,1,1\n" 69 "6,1,1\n" 70 "7,1,1\n" 71 "8,1,1\n" 72 "9,1,1\n" 73 "10,1,1\n" 74 "11,1,1\n" 75 "12,1,1\n" 76 "13,1,1\n" 77 "14,1,1\n" 78 "15,1,1\n" 79 "16,1,1\n" 80 "17,1,1\n" 81 "18,1,1\n" 82 "19,1,1\n" 83 "20,0,0\n" 84 "21,0,0\n" 85 "22,0,0\n" 86 "23,0,0" 87 }; 88 } 89 90 private slots: 91 void simpleValidOutput() 92 { 93 MockWaveModel mwm({ DC, DC }, 16, 4); 94 95 std::ostringstream oss; 96 const auto result = CSVStreamWriter::writeInChunks(oss, mwm); 97 QVERIFY( oss.str() == getExpectedString() ); 98 QVERIFY( result ); 99 } 100 101 void callsReporterCorrectTimes() 102 { 103 MockWaveModel mwm({ DC, DC }, 16, 4); 104 StubReporter reporter { []() -> bool { return false; } }; 105 const auto expected = getExpectedString(); 106 107 std::ostringstream oss; 108 const auto writeStreamWithBlockSize = [&](int blockSize) { 109 return CSVStreamWriter::writeInChunks( 110 oss, 111 mwm, 112 &reporter, 113 ",", 114 DataExportDefaults, 115 blockSize 116 ); 117 }; 118 119 const auto reset = [&]() { 120 oss.str({}); 121 reporter.reset(); 122 }; 123 124 const auto nonIntegerMultipleResult = writeStreamWithBlockSize(5); 125 QVERIFY( nonIntegerMultipleResult ); 126 QVERIFY( reporter.getCallCount() == 5 /* 4.8 rounded up */ ); 127 QVERIFY( oss.str() == expected ); 128 reset(); 129 130 const auto integerMultiple = writeStreamWithBlockSize(2); 131 QVERIFY( integerMultiple ); 132 QVERIFY( reporter.getCallCount() == 12 ); 133 QVERIFY( oss.str() == expected ); 134 reset(); 135 136 const auto largerThanNumberOfSamples = writeStreamWithBlockSize(100); 137 QVERIFY( largerThanNumberOfSamples ); 138 QVERIFY( reporter.getCallCount() == 1 ); 139 QVERIFY( oss.str() == expected ); 140 reset(); 141 142 const auto zero = writeStreamWithBlockSize(0); 143 QVERIFY( zero == false ); 144 QVERIFY( reporter.getCallCount() == 0 ); 145 } 146 147 void isCancellable() 148 { 149 MockWaveModel mwm({ DC, DC }, 16, 4); 150 StubReporter reporter { []() -> bool { return true; } }; 151 152 std::ostringstream oss; 153 const auto cancelImmediately = CSVStreamWriter::writeInChunks( 154 oss, 155 mwm, 156 &reporter, 157 ",", 158 DataExportDefaults, 159 4 160 ); 161 QVERIFY( cancelImmediately == false ); 162 QVERIFY( reporter.getCallCount() == 0 ); 163 164 StubReporter cancelMidway { 165 [&]() { return cancelMidway.getCallCount() == 3; } 166 }; 167 const auto cancelledMidway = CSVStreamWriter::writeInChunks( 168 oss, 169 mwm, 170 &cancelMidway, 171 ",", 172 DataExportDefaults, 173 4 174 ); 175 QVERIFY( cancelMidway.getCallCount() == 3 ); 176 QVERIFY( cancelledMidway == false ); 177 } 178 179 void zeroStartTimeReportsPercentageCorrectly() 180 { 181 MockWaveModel mwm({ DC, DC }, 16, 4); 182 StubReporter reporter { []() -> bool { return false; } }; 183 std::ostringstream oss; 184 const auto succeeded = CSVStreamWriter::writeInChunks( 185 oss, 186 mwm, 187 &reporter, 188 ",", 189 DataExportDefaults, 190 4 191 ); 192 QVERIFY( succeeded == true ); 193 QVERIFY( reporter.getCallCount() == 6 ); 194 const std::vector<int> expectedCallLog { 195 16, 196 33, 197 50, 198 66, 199 83, 200 100 201 }; 202 QVERIFY( reporter.getPercentageLog() == expectedCallLog ); 203 QVERIFY( oss.str() == getExpectedString() ); 204 } 205 206 void nonZeroStartTimeReportsPercentageCorrectly() 207 { 208 MockWaveModel mwm({ DC, DC }, 16, 4); 209 StubReporter reporter { []() -> bool { return false; } }; 210 std::ostringstream oss; 211 const auto writeSubSection = CSVStreamWriter::writeInChunks( 212 oss, 213 mwm, 214 {4, 20}, 215 &reporter, 216 ",", 217 DataExportDefaults, 218 4 219 ); 220 QVERIFY( reporter.getCallCount() == 4 ); 221 const std::vector<int> expectedCallLog { 222 25, 223 50, 224 75, 225 100 226 }; 227 QVERIFY( reporter.getPercentageLog() == expectedCallLog ); 228 QVERIFY( writeSubSection == true ); 229 const std::string expectedOutput { 230 "4,1,1\n" 231 "5,1,1\n" 232 "6,1,1\n" 233 "7,1,1\n" 234 "8,1,1\n" 235 "9,1,1\n" 236 "10,1,1\n" 237 "11,1,1\n" 238 "12,1,1\n" 239 "13,1,1\n" 240 "14,1,1\n" 241 "15,1,1\n" 242 "16,1,1\n" 243 "17,1,1\n" 244 "18,1,1\n" 245 "19,1,1" 246 }; 247 QVERIFY( oss.str() == expectedOutput ); 248 } 249 250 void multipleSelectionOutput() 251 { 252 MockWaveModel mwm({ DC, DC }, 16, 4); 253 StubReporter reporter { []() -> bool { return false; } }; 254 std::ostringstream oss; 255 MultiSelection regions; 256 regions.addSelection({0, 2}); 257 regions.addSelection({4, 6}); 258 regions.addSelection({16, 18}); 259 // qDebug("End frame: %lld", (long long int)mwm.getEndFrame()); 260 const std::string expectedOutput { 261 "0,0,0\n" 262 "1,0,0\n" 263 "4,1,1\n" 264 "5,1,1\n" 265 "16,1,1\n" 266 "17,1,1" 267 }; 268 const auto wroteMultiSection = CSVStreamWriter::writeInChunks( 269 oss, 270 mwm, 271 regions, 272 &reporter, 273 ",", 274 DataExportDefaults, 275 2 276 ); 277 QVERIFY( wroteMultiSection == true ); 278 QVERIFY( reporter.getCallCount() == 3 ); 279 const std::vector<int> expectedCallLog { 33, 66, 100 }; 280 QVERIFY( reporter.getPercentageLog() == expectedCallLog ); 281 // qDebug("%s", oss.str().c_str()); 282 QVERIFY( oss.str() == expectedOutput ); 283 } 284 285 void writeSparseModel() 286 { 287 const auto pentatonicFromRoot = [](float midiPitch) { 288 return std::vector<float> { 289 0 + midiPitch, 290 2 + midiPitch, 291 4 + midiPitch, 292 7 + midiPitch, 293 9 + midiPitch 294 }; 295 }; 296 const auto cMajorPentatonic = pentatonicFromRoot(60.0); 297 NoteModel notes(8 /* sampleRate */, 4 /* resolution */); 298 sv_frame_t startFrame = 0; 299 for (const auto& note : cMajorPentatonic) { 300 notes.add({startFrame, note, 4, 1.f, ""}); 301 startFrame += 8; 302 } 303 // qDebug("Create Expected Output\n"); 304 305 // NB. removed end line break 306 const auto expectedOutput = 307 notes.toDelimitedDataString(",", {}, 0, notes.getEndFrame()) 308 .trimmed(); 309 310 StubReporter reporter { []() -> bool { return false; } }; 311 std::ostringstream oss; 312 // qDebug("End frame: %lld", (long long int)notes.getEndFrame()); 313 // qDebug("Write streaming\n"); 314 const auto wroteSparseModel = CSVStreamWriter::writeInChunks( 315 oss, 316 notes, 317 &reporter, 318 ",", 319 DataExportDefaults, 320 2 321 ); 322 323 // qDebug("\n->>%s<<-\n", expectedOutput.toLocal8Bit().data()); 324 // qDebug("\n->>%s<<-\n", oss.str().c_str()); 325 QVERIFY( wroteSparseModel == true ); 326 QVERIFY( oss.str() != std::string() ); 327 QVERIFY( oss.str() == expectedOutput.toStdString() ); 328 } 329 }; 330 331 #endif 332