integrationTest.cpp (8888B)
1 #include <iostream> 2 3 #include "integrationTest.h" 4 5 #include <fstream> 6 #include <utility> 7 8 #include "virusConsoleLib/consoleApp.h" 9 10 #include "dsp56kEmu/jitunittests.h" 11 12 #include "baseLib/commandline.h" 13 #include "baseLib/filesystem.h" 14 15 #include "synthLib/wavReader.h" 16 17 #include "virusLib/romloader.h" 18 19 namespace synthLib 20 { 21 class WavReader; 22 } 23 24 int main(int _argc, char* _argv[]) 25 { 26 if constexpr (true) 27 { 28 try 29 { 30 puts("Running Unit Tests..."); 31 // dsp56k::InterpreterUnitTests tests; 32 dsp56k::JitUnittests jitTests(false); 33 puts("Unit Tests finished."); 34 } 35 catch (const std::string& _err) 36 { 37 std::cout << "Unit test failed: " << _err << '\n'; 38 return -1; 39 } 40 } 41 42 try 43 { 44 bool forever = true; 45 46 while(forever) 47 { 48 const baseLib::CommandLine cmd(_argc, _argv); 49 50 forever = cmd.contains("forever"); 51 52 std::vector<std::pair<std::string, std::string>> finishedTests; // rom, preset 53 finishedTests.reserve(64); 54 55 if(cmd.contains("rom") && cmd.contains("preset")) 56 { 57 const auto romFile = cmd.get("rom"); 58 const auto preset = cmd.get("preset"); 59 60 IntegrationTest test(cmd, romFile, preset, std::string(), virusLib::DeviceModel::Snow); 61 62 const auto res = test.run(); 63 if(0 == res) 64 std::cout << "test successful, ROM " << baseLib::filesystem::getFilenameWithoutPath(romFile) << ", preset " << preset << '\n'; 65 return res; 66 } 67 if(cmd.contains("folder")) 68 { 69 std::vector<std::string> subfolders; 70 baseLib::filesystem::getDirectoryEntries(subfolders, cmd.get("folder")); 71 72 if(subfolders.empty()) 73 { 74 std::cout << "Nothing found for testing in folder " << cmd.get("folder") << '\n'; 75 return -1; 76 } 77 78 for (auto& subfolder : subfolders) 79 { 80 if(subfolder.find("/.") != std::string::npos) 81 continue; 82 if(subfolder.find('#') != std::string::npos) 83 continue; 84 85 std::vector<std::string> files; 86 baseLib::filesystem::getDirectoryEntries(files, subfolder); 87 88 std::string romFile; 89 std::string presetsFile; 90 91 if(files.empty()) 92 { 93 std::cout << "Directory " << subfolder << " doesn't contain any files" << '\n'; 94 return -1; 95 } 96 97 for (auto& file : files) 98 { 99 if(baseLib::filesystem::hasExtension(file, ".txt")) 100 presetsFile = file; 101 else if(baseLib::filesystem::hasExtension(file, ".bin")) 102 romFile = file; 103 else if(baseLib::filesystem::hasExtension(file, ".mid")) 104 { 105 const auto rom = virusLib::ROMLoader::findROM(file); 106 if(rom.isValid()) 107 romFile = file; 108 } 109 } 110 111 if(romFile.empty()) 112 { 113 std::cout << "Failed to find ROM in folder " << subfolder << '\n'; 114 return -1; 115 } 116 if(presetsFile.empty()) 117 { 118 std::cout << "Failed to find presets file in folder " << subfolder << '\n'; 119 return -1; 120 } 121 122 std::vector<std::string> presets; 123 124 std::ifstream ss; 125 ss.open(presetsFile.c_str(), std::ios::in); 126 127 if(!ss.is_open()) 128 { 129 std::cout << "Failed to open presets file " << presetsFile << '\n'; 130 return -1; 131 } 132 133 std::string line; 134 135 while(std::getline(ss, line)) 136 { 137 while(!line.empty() && line.find_last_of("\r\n") != std::string::npos) 138 line = line.substr(0, line.size()-1); 139 if(!line.empty() && line[0] != '#') 140 presets.push_back(line); 141 } 142 143 ss.close(); 144 145 if(presets.empty()) 146 { 147 std::cout << "Presets file " << presetsFile << " is empty" << '\n'; 148 return -1; 149 } 150 151 for (auto& preset : presets) 152 { 153 IntegrationTest test(cmd, romFile, preset, subfolder + '/', virusLib::DeviceModel::Snow); 154 if(test.run() != 0) 155 return -1; 156 finishedTests.emplace_back(romFile, preset); 157 } 158 } 159 160 if(!forever) 161 { 162 std::cout << "All " << finishedTests.size() << " tests finished successfully:" << '\n'; 163 for (const auto& [rom,preset] : finishedTests) 164 std::cout << "ROM " << baseLib::filesystem::getFilenameWithoutPath(rom) << ", preset " << preset << '\n'; 165 return 0; 166 } 167 } 168 } 169 std::cout << "invalid command line arguments" << '\n'; 170 return -1; 171 } 172 catch(const std::runtime_error& _err) 173 { 174 std::cout << _err.what() << '\n'; 175 return -1; 176 } 177 } 178 179 IntegrationTest::IntegrationTest(const baseLib::CommandLine& _commandLine, std::string _romFile, std::string _presetName, std::string _outputFolder, const virusLib::DeviceModel _tiModel) 180 : m_cmd(_commandLine) 181 , m_romFile(std::move(_romFile)) 182 , m_presetName(std::move(_presetName)) 183 , m_outputFolder(std::move(_outputFolder)) 184 , m_app(m_romFile, _tiModel) 185 { 186 } 187 188 int IntegrationTest::run() 189 { 190 if (!m_app.isValid()) 191 { 192 std::cout << "Failed to load ROM " << m_romFile << ", make sure that the ROM file is valid" << '\n'; 193 return -1; 194 } 195 196 if (!m_app.loadSingle(m_presetName)) 197 { 198 std::cout << "Failed to find preset '" << m_presetName << "', make sure to use a ROM that contains it" << '\n'; 199 return -1; 200 } 201 202 const int lengthSeconds = m_cmd.contains("length") ? m_cmd.getInt("length") : 0; 203 if(lengthSeconds > 0) 204 { 205 // create reference file 206 return runCreate(lengthSeconds); 207 } 208 209 const auto referenceFile = m_app.getSingleNameAsFilename(); 210 211 if (!loadAudioFile(m_referenceFile, m_outputFolder + referenceFile)) 212 return -1; 213 214 return runCompare(); 215 } 216 217 bool IntegrationTest::loadAudioFile(File& _dst, const std::string& _filename) const 218 { 219 const auto hFile = fopen(_filename.c_str(), "rb"); 220 if (!hFile) 221 { 222 std::cout << "Failed to load wav file " << _filename << " for comparison" << '\n'; 223 return false; 224 } 225 fseek(hFile, 0, SEEK_END); 226 const size_t size = ftell(hFile); 227 _dst.file.resize(size); 228 fseek(hFile, 0, SEEK_SET); 229 if (fread(&_dst.file.front(), 1, size, hFile) != size) 230 { 231 std::cout << "Failed to read data from file " << _filename << '\n'; 232 fclose(hFile); 233 return false; 234 } 235 fclose(hFile); 236 237 _dst.data.data = nullptr; 238 239 if (!synthLib::WavReader::load(_dst.data, nullptr, &_dst.file.front(), _dst.file.size())) 240 { 241 std::cout << "Failed to interpret file " << _filename << " as wave data, make sure that the file is a valid 24 bit stereo wav file" << '\n'; 242 return false; 243 } 244 245 if(_dst.data.samplerate != m_app.getRom().getSamplerate()) 246 { 247 std::cout << "Wave file " << _filename << " does not have the correct samplerate, expected " << m_app.getRom().getSamplerate() << " but got " << _dst.data.samplerate << " instead" << '\n'; 248 return false; 249 } 250 251 if (_dst.data.bitsPerSample != 24 || _dst.data.channels != 2 || _dst.data.isFloat) 252 { 253 std::cout << "Wave file " << _filename << " has an invalid format, expected 24 bit / 2 channels but got " << _dst.data.bitsPerSample << " bit / " << _dst.data.channels << " channels" << '\n'; 254 return false; 255 } 256 return true; 257 } 258 259 int IntegrationTest::runCompare() 260 { 261 const auto sampleCount = m_referenceFile.data.dataByteSize * 8 / m_referenceFile.data.bitsPerSample; 262 const auto frameCount = sampleCount >> 1; 263 264 std::vector<uint8_t> temp; 265 266 File compareFile; 267 const auto res = createAudioFile(compareFile, "compare_", static_cast<uint32_t>(frameCount)); 268 if(res) 269 return res; 270 271 auto* ptrA = static_cast<const uint8_t*>(compareFile.data.data); 272 auto* ptrB = static_cast<const uint8_t*>(m_referenceFile.data.data); 273 274 for(uint32_t i=0; i<sampleCount; ++i) 275 { 276 const uint32_t a = (static_cast<uint32_t>(ptrA[0]) << 24) | (static_cast<uint32_t>(ptrA[1]) << 16) | ptrA[2]; 277 const uint32_t b = (static_cast<uint32_t>(ptrB[0]) << 24) | (static_cast<uint32_t>(ptrB[1]) << 16) | ptrB[2]; 278 279 if(b != a) 280 { 281 std::cout << "Test failed, audio output is not identical to reference file, difference starting at frame " << (i>>1) << ", ROM " << m_romFile << ", preset " << m_presetName << '\n'; 282 return -2; 283 } 284 285 ptrA += 3; 286 ptrB += 3; 287 } 288 289 std::cout << "Test succeeded, compared " << sampleCount << " samples, ROM " << m_romFile << ", preset " << m_presetName << '\n'; 290 return 0; 291 } 292 293 int IntegrationTest::runCreate(const int _lengthSeconds) 294 { 295 const auto sampleCount = m_app.getRom().getSamplerate() * _lengthSeconds; 296 297 File file; 298 return createAudioFile(file, "", sampleCount); 299 } 300 301 int IntegrationTest::createAudioFile(File& _dst, const std::string& _prefix, const uint32_t _sampleCount) 302 { 303 const auto filename = m_outputFolder + _prefix + m_app.getSingleNameAsFilename(); 304 305 auto* hFile = fopen(filename.c_str(), "wb"); 306 307 if(!hFile) 308 { 309 std::cout << "Failed to create output file " << filename << '\n'; 310 return -1; 311 } 312 313 fclose(hFile); 314 315 m_app.run(filename, _sampleCount); 316 317 if(!loadAudioFile(_dst, filename)) 318 { 319 std::cout << "Failed to open written file " << filename << " for verification" << '\n'; 320 return -1; 321 } 322 323 const auto sampleCount = _dst.data.dataByteSize * 8 / _dst.data.bitsPerSample / 2; 324 325 if(sampleCount != _sampleCount) 326 { 327 std::cout << "Verification of written file failed, expected " << _sampleCount << " samples but file only has " << sampleCount << " samples" << '\n'; 328 return -1; 329 } 330 return 0; 331 }