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