test(libSceNgs2.native): run the waveform parse tests in their own executable

On Linux ngs2_atrac9 aborted without any output once TestParseFile was part of
its main(). The exception that TestCorruptSuperframeAtPageEnd throws in the
Ngs2 module and catches in main() has to resume through a cleanup landing pad
inside main(). That resume goes through the unwinder statically linked into the
test executable, which then fails with _URC_FATAL_PHASE2_ERROR and aborts
silently. Whether main() gets such a pad depends on what the compiler inlines
into it, so adding any function with non-trivial cleanups to that main() is
enough to trigger it.

Move the PCM and file parse tests into a new ngs2_parse executable and restore
Ngs2Atrac9.cpp to its upstream content, so the test that catches exceptions is
unchanged. The new executable has no catch and never relies on that path.
This commit is contained in:
Gjon Gojcevic 2026-10-07 09:49:31 -04:00
parent 1bdc37bb86
commit 02166268ea
3 changed files with 95 additions and 71 deletions

View File

@ -8,7 +8,7 @@ if(MINGW)
endif()
if(BUILD_TESTING)
foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom)
foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom Parse)
string(TOLOWER ${ngs2Test} testName)
add_test_executable(ngs2_${testName}_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Ngs2${ngs2Test}.cpp)
target_include_directories(ngs2_${testName}_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/3rdparty/LibAtrac9/C/src)

View File

@ -1,12 +1,9 @@
#include "Ngs2Test.hpp"
#include "libatrac9.h"
#include "prx/libc/include/General.hpp"
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <random>
#include <stdexcept>
#include <vector>
@ -179,71 +176,6 @@ static void TestParse() {
Require(sceNgs2ParseWaveformData(wrongRate.data(), wrongRate.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}
static std::vector<std::uint8_t> PcmFile(std::uint32_t channels, std::uint32_t sampleRate, std::uint32_t bits, std::uint32_t frames) {
const std::uint32_t frameBytes = channels * bits / 8;
std::vector<std::uint8_t> file;
PutTag(file, "RIFF");
Put32(file, 36 + frames * frameBytes);
PutTag(file, "WAVE");
PutTag(file, "fmt ");
Put32(file, 16);
Put16(file, 1);
Put16(file, channels);
Put32(file, sampleRate);
Put32(file, sampleRate * frameBytes);
Put16(file, frameBytes);
Put16(file, bits);
PutTag(file, "data");
Put32(file, frames * frameBytes);
file.resize(file.size() + frames * frameBytes, 0x11);
return file;
}
static void TestParsePcm() {
const auto file = PcmFile(2, 44100, 16, 100);
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.format.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L && info.format.num_channels == 2 && info.format.sample_rate == 44100);
Require(info.data_offset == 44 && info.data_size == 400 && info.num_samples == 100);
Require(info.num_blocks == 1 && info.block[0].data_offset == 44 && info.block[0].data_size == 400 && info.block[0].num_samples == 100);
auto truncated = file;
truncated.resize(44 + 202);
Require(sceNgs2ParseWaveformData(truncated.data(), truncated.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
auto partialFrame = PcmFile(2, 44100, 16, 100);
partialFrame.resize(44 + 402, 0x11);
const std::uint32_t partialSize = 402;
std::memcpy(partialFrame.data() + 40, &partialSize, sizeof(partialSize));
Require(sceNgs2ParseWaveformData(partialFrame.data(), partialFrame.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
const auto eightBit = PcmFile(1, 44100, 8, 10);
Require(sceNgs2ParseWaveformData(eightBit.data(), eightBit.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
const auto noChannels = PcmFile(0, 44100, 16, 10);
Require(sceNgs2ParseWaveformData(noChannels.data(), noChannels.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}
static void TestParseFile() {
const auto wave = PcmFile(1, 22050, 16, 64);
const char* const guestPath = "/aps5_ngs2_parse_file.wav";
const auto path = ResolvePath_nid_no_patch(guestPath);
{
std::ofstream out(path, std::ios::binary);
const char pad[3]{};
out.write(pad, sizeof(pad));
out.write(reinterpret_cast<const char*>(wave.data()), static_cast<std::streamsize>(wave.size()));
}
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformFile(guestPath, 3, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2ParseWaveformFile(nullptr, 0, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Require(sceNgs2ParseWaveformFile(guestPath, 3, &info) == SCE_NGS2_OK);
Require(info.format.num_channels == 1 && info.format.sample_rate == 22050 && info.num_samples == 64);
Require(info.data_offset == 3 + 44 && info.block[0].data_offset == 3 + 44 && info.data_size == 128);
Require(sceNgs2ParseWaveformFile(guestPath, static_cast<std::uint32_t>(wave.size()) + 4, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
std::filesystem::remove(path);
}
static void TestCalcBlock() {
const Ngs2WaveformFormat format{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, Config, 0, 0};
Ngs2WaveformBlock block{};
@ -303,8 +235,6 @@ int main() {
TestSkipAndBlockEnd(reference);
TestRepeatAndState(reference);
TestParse();
TestParsePcm();
TestParseFile();
TestCalcBlock();
TestCorruptSuperframeAtPageEnd();
return 0;

View File

@ -0,0 +1,94 @@
#include "Ngs2Test.hpp"
#include "prx/libc/include/General.hpp"
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <vector>
static void Put16(std::vector<std::uint8_t>& out, std::uint32_t value) {
out.push_back(static_cast<std::uint8_t>(value));
out.push_back(static_cast<std::uint8_t>(value >> 8));
}
static void Put32(std::vector<std::uint8_t>& out, std::uint32_t value) {
Put16(out, value & 0xffff);
Put16(out, value >> 16);
}
static void PutTag(std::vector<std::uint8_t>& out, const char* tag) {
out.insert(out.end(), tag, tag + 4);
}
static std::vector<std::uint8_t> PcmFile(std::uint32_t channels, std::uint32_t sampleRate, std::uint32_t bits, std::uint32_t frames) {
const std::uint32_t frameBytes = channels * bits / 8;
std::vector<std::uint8_t> file;
PutTag(file, "RIFF");
Put32(file, 36 + frames * frameBytes);
PutTag(file, "WAVE");
PutTag(file, "fmt ");
Put32(file, 16);
Put16(file, 1);
Put16(file, channels);
Put32(file, sampleRate);
Put32(file, sampleRate * frameBytes);
Put16(file, frameBytes);
Put16(file, bits);
PutTag(file, "data");
Put32(file, frames * frameBytes);
file.resize(file.size() + frames * frameBytes, 0x11);
return file;
}
static void TestParsePcm() {
const auto file = PcmFile(2, 44100, 16, 100);
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.format.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L && info.format.num_channels == 2 && info.format.sample_rate == 44100);
Require(info.data_offset == 44 && info.data_size == 400 && info.num_samples == 100);
Require(info.num_blocks == 1 && info.block[0].data_offset == 44 && info.block[0].data_size == 400 && info.block[0].num_samples == 100);
auto truncated = file;
truncated.resize(44 + 202);
Require(sceNgs2ParseWaveformData(truncated.data(), truncated.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
auto partialFrame = PcmFile(2, 44100, 16, 100);
partialFrame.resize(44 + 402, 0x11);
const std::uint32_t partialSize = 402;
std::memcpy(partialFrame.data() + 40, &partialSize, sizeof(partialSize));
Require(sceNgs2ParseWaveformData(partialFrame.data(), partialFrame.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
const auto eightBit = PcmFile(1, 44100, 8, 10);
Require(sceNgs2ParseWaveformData(eightBit.data(), eightBit.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
const auto noChannels = PcmFile(0, 44100, 16, 10);
Require(sceNgs2ParseWaveformData(noChannels.data(), noChannels.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}
static void TestParseFile() {
const auto wave = PcmFile(1, 22050, 16, 64);
const char* const guestPath = "/aps5_ngs2_parse_file.wav";
const auto path = ResolvePath_nid_no_patch(guestPath);
{
std::ofstream out(path, std::ios::binary);
const char pad[3]{};
out.write(pad, sizeof(pad));
out.write(reinterpret_cast<const char*>(wave.data()), static_cast<std::streamsize>(wave.size()));
}
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformFile(guestPath, 3, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2ParseWaveformFile(nullptr, 0, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Require(sceNgs2ParseWaveformFile(guestPath, 3, &info) == SCE_NGS2_OK);
Require(info.format.num_channels == 1 && info.format.sample_rate == 22050 && info.num_samples == 64);
Require(info.data_offset == 3 + 44 && info.block[0].data_offset == 3 + 44 && info.data_size == 128);
Require(sceNgs2ParseWaveformFile(guestPath, static_cast<std::uint32_t>(wave.size()) + 4, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
std::filesystem::remove(path);
}
int main() {
TestParsePcm();
TestParseFile();
return 0;
}