annotate data/fileio/test/EncodingTest.h @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents 83cb6e9d769b
children
rev   line source
Chris@1345 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1345 2
Chris@1345 3 /*
Chris@1345 4 Sonic Visualiser
Chris@1345 5 An audio file viewer and annotation editor.
Chris@1345 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1345 7
Chris@1345 8 This program is free software; you can redistribute it and/or
Chris@1345 9 modify it under the terms of the GNU General Public License as
Chris@1345 10 published by the Free Software Foundation; either version 2 of the
Chris@1345 11 License, or (at your option) any later version. See the file
Chris@1345 12 COPYING included with this distribution for more information.
Chris@1345 13 */
Chris@1345 14
Chris@1345 15 #ifndef TEST_AUDIO_ENCODINGS_H
Chris@1345 16 #define TEST_AUDIO_ENCODINGS_H
Chris@1345 17
Chris@1345 18 // Quick tests for filename encodings and encoding of ID3 data. Not a
Chris@1345 19 // test of audio codecs.
Chris@1345 20
Chris@1345 21 #include "../AudioFileReaderFactory.h"
Chris@1345 22 #include "../AudioFileReader.h"
Chris@1359 23 #include "../WavFileWriter.h"
Chris@1345 24
Chris@1698 25 #include "UnsupportedFormat.h"
Chris@1698 26
Chris@1345 27 #include <cmath>
Chris@1345 28
Chris@1345 29 #include <QObject>
Chris@1345 30 #include <QtTest>
Chris@1345 31 #include <QDir>
Chris@1345 32
Chris@1345 33 #include <iostream>
Chris@1345 34
Chris@1345 35 using namespace std;
Chris@1345 36
Chris@1346 37 const char utf8_name_cdp_1[] = "Caf\303\251 de Paris";
Chris@1346 38 const char utf8_name_cdp_2[] = "Caf\303\251 de \351\207\215\345\272\206";
Chris@1347 39 const char utf8_name_tsprk[] = "T\303\253mple of Sp\303\266rks";
Chris@1346 40 const char utf8_name_sprkt[] = "\343\202\271\343\203\235\343\203\274\343\202\257\343\201\256\345\257\272\351\231\242";
Chris@1345 41
Chris@1359 42 // Mapping between filename and expected title metadata field
Chris@1345 43 static const char *mapping[][2] = {
Chris@1346 44 { "id3v2-iso-8859-1", utf8_name_cdp_1 },
Chris@1346 45 { "id3v2-ucs-2", utf8_name_cdp_2 },
Chris@1346 46 { utf8_name_tsprk, utf8_name_tsprk },
Chris@1346 47 { utf8_name_sprkt, utf8_name_sprkt },
Chris@1345 48 };
Chris@1345 49 static const int mappingCount = 4;
Chris@1345 50
Chris@1597 51 #ifdef Q_OS_MAC
Chris@1597 52 // see note in addAudioFiles below
Chris@1593 53 static const char *testFiles[][2] = {
Chris@1593 54 { "id3v2-iso-8859-1", "mp3" },
Chris@1593 55 { "id3v2-ucs-2", "mp3" },
Chris@1593 56 { utf8_name_tsprk, "flac" },
Chris@1593 57 { utf8_name_tsprk, "m4a" },
Chris@1593 58 { utf8_name_tsprk, "mp3" },
Chris@1593 59 { utf8_name_tsprk, "ogg" },
Chris@1594 60 { utf8_name_tsprk, "opus" },
Chris@1593 61 { utf8_name_sprkt, "mp3" },
Chris@1593 62 { utf8_name_sprkt, "ogg" },
Chris@1593 63 };
Chris@1593 64 static const int testFileCount = 8;
Chris@1597 65 #endif
Chris@1593 66
Chris@1345 67 class EncodingTest : public QObject
Chris@1345 68 {
Chris@1345 69 Q_OBJECT
Chris@1345 70
Chris@1346 71 private:
Chris@1346 72 QString testDirBase;
Chris@1346 73 QString encodingDir;
Chris@1359 74 QString outDir;
Chris@1346 75
Chris@1346 76 public:
Chris@1346 77 EncodingTest(QString base) {
Chris@1346 78 if (base == "") {
Chris@1346 79 base = "svcore/data/fileio/test";
Chris@1346 80 }
Chris@1346 81 testDirBase = base;
Chris@1346 82 encodingDir = base + "/encodings";
Chris@1359 83 outDir = base + "/outfiles";
Chris@1346 84 }
Chris@1346 85
Chris@1346 86 private:
Chris@1596 87 const char *strOf(QString s) {
Chris@1596 88 return strdup(s.toLocal8Bit().data());
Chris@1596 89 }
Chris@1596 90
Chris@1359 91 void addAudioFiles() {
Chris@1596 92 QTest::addColumn<QString>("audiofile");
Chris@1596 93 #ifndef Q_OS_MAC
Chris@1596 94 // The normal case - populate the file list from the files
Chris@1596 95 // actually present in the encodings directory
Chris@1596 96 QStringList files = QDir(encodingDir).entryList(QDir::Files);
Chris@1596 97 foreach (QString filename, files) {
Chris@1596 98 QTest::newRow(strOf(filename)) << filename;
Chris@1596 99 }
Chris@1596 100 #else
Chris@1596 101 // Deviant case for Mac - populate the file list from the
Chris@1596 102 // hard-coded list of expected files in testFiles. This is
Chris@1596 103 // because QDir::entryList is currently broken on APFS (as of
Chris@1596 104 // Qt 5.12) because of variant Unicode normalisations.
Chris@1596 105 for (int i = 0; i < testFileCount; ++i) {
Chris@1596 106 std::string s = testFiles[i][0];
Chris@1596 107 s += ".";
Chris@1596 108 s += testFiles[i][1];
Chris@1596 109 QTest::newRow(strdup(s.c_str())) << QString::fromStdString(s);
Chris@1596 110 }
Chris@1596 111 #endif
Chris@1359 112 }
Chris@1359 113
Chris@1345 114 private slots:
Chris@1345 115 void init()
Chris@1345 116 {
Chris@1345 117 if (!QDir(encodingDir).exists()) {
Chris@1428 118 SVCERR << "ERROR: Audio encoding file directory \"" << encodingDir << "\" does not exist" << endl;
Chris@1345 119 QVERIFY2(QDir(encodingDir).exists(), "Audio encoding file directory not found");
Chris@1593 120 }
Chris@1359 121 if (!QDir(outDir).exists() && !QDir().mkpath(outDir)) {
Chris@1428 122 SVCERR << "ERROR: Audio out directory \"" << outDir << "\" does not exist and could not be created" << endl;
Chris@1359 123 QVERIFY2(QDir(outDir).exists(), "Audio out directory not found and could not be created");
Chris@1345 124 }
Chris@1345 125 }
Chris@1345 126
Chris@1359 127 void readAudio_data() {
Chris@1359 128 addAudioFiles();
Chris@1359 129 }
Chris@1359 130
Chris@1359 131 void readAudio() {
Chris@1359 132
Chris@1359 133 // Ensure that we can open all the files
Chris@1359 134
Chris@1359 135 QFETCH(QString, audiofile);
Chris@1359 136
Chris@1698 137 QStringList fileAndExt = audiofile.split(".");
Chris@1698 138 QString extension = fileAndExt[1];
Chris@1698 139
Chris@1592 140 if (!AudioFileReaderFactory::isSupported(encodingDir + "/" +
Chris@1592 141 audiofile)) {
Chris@1714 142 if (UnsupportedFormat::isLegitimatelyUnsupported(extension)) {
Chris@1592 143 #if ( QT_VERSION >= 0x050000 )
Chris@1698 144 QSKIP("Known unsupported file, skipping");
Chris@1592 145 #else
Chris@1698 146 QSKIP("Known unsupported file, skipping", SkipSingle);
Chris@1592 147 #endif
Chris@1698 148 }
Chris@1592 149 }
Chris@1592 150
Chris@1359 151 AudioFileReaderFactory::Parameters params;
Chris@1359 152 AudioFileReader *reader =
Chris@1359 153 AudioFileReaderFactory::createReader
Chris@1359 154 (encodingDir + "/" + audiofile, params);
Chris@1359 155
Chris@1359 156 QVERIFY(reader != nullptr);
Chris@1402 157
Chris@1402 158 delete reader;
Chris@1359 159 }
Chris@1359 160
Chris@1359 161 void readMetadata_data() {
Chris@1359 162 addAudioFiles();
Chris@1359 163 }
Chris@1359 164
Chris@1359 165 void readMetadata() {
Chris@1359 166
Chris@1359 167 // All files other than WAVs should have title metadata; check
Chris@1359 168 // that the title matches whatever is in our mapping structure
Chris@1359 169 // defined at the top
Chris@1359 170
Chris@1345 171 QFETCH(QString, audiofile);
Chris@1345 172
Chris@1698 173 QStringList fileAndExt = audiofile.split(".");
Chris@1698 174 QString file = fileAndExt[0];
Chris@1698 175 QString extension = fileAndExt[1];
Chris@1698 176
Chris@1345 177 AudioFileReaderFactory::Parameters params;
Chris@1346 178 AudioFileReader *reader =
Chris@1346 179 AudioFileReaderFactory::createReader
Chris@1346 180 (encodingDir + "/" + audiofile, params);
Chris@1345 181
Chris@1592 182 if (!reader) {
Chris@1714 183 if (UnsupportedFormat::isLegitimatelyUnsupported(extension)) {
Chris@1592 184 #if ( QT_VERSION >= 0x050000 )
Chris@1698 185 QSKIP("Unsupported file, skipping");
Chris@1592 186 #else
Chris@1698 187 QSKIP("Unsupported file, skipping", SkipSingle);
Chris@1592 188 #endif
Chris@1698 189 }
Chris@1592 190 }
Chris@1345 191
Chris@1698 192 QVERIFY(reader != nullptr);
Chris@1345 193
Chris@1402 194 if (extension == "wav") {
Chris@1402 195
Chris@1402 196 // Nothing
Chris@1402 197
Chris@1402 198 delete reader;
Chris@1402 199
Chris@1402 200 } else {
Chris@1345 201
Chris@1359 202 auto blah = reader->getInterleavedFrames(0, 10);
Chris@1359 203
Chris@1345 204 QString title = reader->getTitle();
Chris@1345 205 QVERIFY(title != QString());
Chris@1345 206
Chris@1402 207 delete reader;
Chris@1402 208
Chris@1345 209 bool found = false;
Chris@1345 210 for (int m = 0; m < mappingCount; ++m) {
Chris@1345 211 if (file == QString::fromUtf8(mapping[m][0])) {
Chris@1345 212 found = true;
Chris@1346 213 QString expected = QString::fromUtf8(mapping[m][1]);
Chris@1346 214 if (title != expected) {
Chris@1428 215 SVCERR << "Title does not match expected: codepoints are" << endl;
Chris@1428 216 SVCERR << "Title (" << title.length() << "ch): ";
Chris@1346 217 for (int i = 0; i < title.length(); ++i) {
Chris@1428 218 SVCERR << title[i].unicode() << " ";
Chris@1346 219 }
Chris@1428 220 SVCERR << endl;
Chris@1428 221 SVCERR << "Expected (" << expected.length() << "ch): ";
Chris@1346 222 for (int i = 0; i < expected.length(); ++i) {
Chris@1428 223 SVCERR << expected[i].unicode() << " ";
Chris@1346 224 }
Chris@1428 225 SVCERR << endl;
Chris@1346 226 }
Chris@1346 227 QCOMPARE(title, expected);
Chris@1345 228 break;
Chris@1345 229 }
Chris@1345 230 }
Chris@1345 231
Chris@1345 232 if (!found) {
Chris@1359 233 // Note that this can happen legitimately on Windows,
Chris@1359 234 // where (for annoying VCS-related reasons) the test
Chris@1359 235 // files may have a different filename encoding from
Chris@1359 236 // the expected UTF-16. We check this properly in
Chris@1359 237 // readWriteAudio below, by saving out the file to a
Chris@1359 238 // name matching the metadata
Chris@1428 239 SVCERR << "Couldn't find filename \""
Chris@1345 240 << file << "\" in title mapping array" << endl;
Chris@1346 241 QSKIP("Couldn't find filename in title mapping array");
Chris@1345 242 }
Chris@1345 243 }
Chris@1345 244 }
Chris@1359 245
Chris@1359 246 void readWriteAudio_data() {
Chris@1359 247 addAudioFiles();
Chris@1359 248 }
Chris@1359 249
Chris@1359 250 void readWriteAudio()
Chris@1359 251 {
Chris@1359 252 // For those files that have title metadata (i.e. all of them
Chris@1359 253 // except the WAVs), read the title metadata and write a wav
Chris@1359 254 // file (of arbitrary content) whose name matches that. Then
Chris@1359 255 // check that we can re-read it. This is intended to exercise
Chris@1359 256 // systems on which the original test filename is miscoded (as
Chris@1359 257 // can happen on Windows).
Chris@1359 258
Chris@1359 259 QFETCH(QString, audiofile);
Chris@1359 260
Chris@1359 261 QStringList fileAndExt = audiofile.split(".");
Chris@1359 262 QString file = fileAndExt[0];
Chris@1359 263 QString extension = fileAndExt[1];
Chris@1359 264
Chris@1359 265 if (extension == "wav") {
Chris@1359 266 return;
Chris@1359 267 }
Chris@1359 268
Chris@1359 269 AudioFileReaderFactory::Parameters params;
Chris@1359 270 AudioFileReader *reader =
Chris@1359 271 AudioFileReaderFactory::createReader
Chris@1359 272 (encodingDir + "/" + audiofile, params);
Chris@1592 273
Chris@1592 274 if (!reader) {
Chris@1714 275 if (UnsupportedFormat::isLegitimatelyUnsupported(extension)) {
Chris@1592 276 #if ( QT_VERSION >= 0x050000 )
Chris@1698 277 QSKIP("Unsupported file, skipping");
Chris@1592 278 #else
Chris@1698 279 QSKIP("Unsupported file, skipping", SkipSingle);
Chris@1592 280 #endif
Chris@1698 281 }
Chris@1592 282 }
Chris@1359 283
Chris@1698 284 QVERIFY(reader != nullptr);
Chris@1698 285
Chris@1359 286 QString title = reader->getTitle();
Chris@1359 287 QVERIFY(title != QString());
Chris@1359 288
Chris@1359 289 for (int useTemporary = 0; useTemporary <= 1; ++useTemporary) {
Chris@1359 290
Chris@1359 291 QString outfile = outDir + "/" + file + ".wav";
Chris@1359 292 WavFileWriter writer(outfile,
Chris@1359 293 reader->getSampleRate(),
Chris@1359 294 1,
Chris@1359 295 useTemporary ?
Chris@1359 296 WavFileWriter::WriteToTemporary :
Chris@1359 297 WavFileWriter::WriteToTarget);
Chris@1359 298
Chris@1359 299 QVERIFY(writer.isOK());
Chris@1359 300
Chris@1359 301 floatvec_t data { 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0 };
Chris@1359 302 const float *samples = data.data();
Chris@1359 303 bool ok = writer.writeSamples(&samples, 8);
Chris@1359 304 QVERIFY(ok);
Chris@1359 305
Chris@1359 306 ok = writer.close();
Chris@1359 307 QVERIFY(ok);
Chris@1359 308
Chris@1359 309 AudioFileReader *rereader =
Chris@1359 310 AudioFileReaderFactory::createReader(outfile, params);
Chris@1359 311 QVERIFY(rereader != nullptr);
Chris@1359 312
Chris@1359 313 floatvec_t readFrames = rereader->getInterleavedFrames(0, 8);
Chris@1359 314 QCOMPARE(readFrames, data);
Chris@1359 315
Chris@1359 316 delete rereader;
Chris@1359 317 }
Chris@1359 318
Chris@1359 319 delete reader;
Chris@1359 320 }
Chris@1345 321 };
Chris@1345 322
Chris@1345 323 #endif