Files
FluidSimCpp/tests/engine_config_tests.cpp
max bf8621ad39 Load engines from TOML and improve scenario audio and carb tuning
Migrate 32 engine definitions into validated startup-loaded TOML files and separate configuration and snapshots from scenario assembly. Add camera-based stereo propagation from shared pipe geometry, preserve exhaust character, tune carburetor mixtures and idle, use ignition cuts for carb engines, and support returning to the scenario menu.

Validation: full Windows build and all 14 CTest checks passed.
2026-10-24 03:24:35 +02:00

79 lines
4.0 KiB
C++

#include "FluidSim/ScenarioCatalog.hpp"
#include "FluidSim/DesktopScenario.hpp"
#include <chrono>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
using namespace fluidsim::port;
namespace {
void check(bool condition,const std::string& message) {if(!condition)throw std::runtime_error(message);}
std::string read(const std::filesystem::path& path) {
std::ifstream file(path);std::ostringstream text;text<<file.rdbuf();return text.str();
}
void replace(std::string& text,const std::string& from,const std::string& to) {
const auto pos=text.find(from);check(pos!=std::string::npos,"test fixture missing "+from);text.replace(pos,from.size(),to);
}
struct TemporaryDirectory {
std::filesystem::path path=std::filesystem::temp_directory_path()/
("fluidsim-engine-config-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
TemporaryDirectory() {std::filesystem::create_directory(path);}
~TemporaryDirectory() {std::error_code error;std::filesystem::remove_all(path,error);}
};
}
int main() {
const auto& catalog=scenario_examples();
check(catalog.size()>=32,"bundled engines missing");
const auto& thumper=scenario_example("single-450");
check(thumper.engine.carbureted && std::abs(thumper.engine.cylinder.bore-.096f)<1e-7f,
"four-stroke units or fuel type changed");
check(std::abs(scenario_example("yz-racing").two.idle_slide_opening-.03f)<1e-7f,
"125 idle tune lost");
for(const auto& e:catalog) {
DesktopScenario s(e);s.set_controls(.35f,0,1);
for(int i=0;i<256;++i) {
const auto audio=s.process();
check(std::isfinite(audio.left) && std::isfinite(audio.right),e.id+": invalid audio after TOML load");
}
const auto state=s.snapshot();
check(state.rpm>0 && state.pipe_positivity_repairs==0,e.id+": startup changed");
}
TemporaryDirectory temp;
const auto file=temp.path/"test.toml";
const auto original=read(engine_directory()/"twin.toml");
const auto reject=[&](std::string text,const std::string& expected) {
{std::ofstream output(file);output<<text;}
bool rejected=false;
try {load_engine_file(file);}
catch(const std::exception& error) {
const std::string message=error.what();
check(message.find("test.toml")!=std::string::npos,"diagnostic lost the file path");
check(message.find(expected)!=std::string::npos,"unexpected diagnostic: "+message);
rejected=true;
}
check(rejected,"invalid config accepted: "+expected);
};
auto changed=original;replace(changed,"schema_version = 1","schema_version = 99");reject(changed,"schema_version");
changed=original;replace(changed,"bore_mm = 75.0","bore_mn = 75.0");reject(changed,"bore_mm");
changed=original;replace(changed,"bore_mm = 75.0","bore_mm = -75.0");reject(changed,"cylinder");
changed=original;replace(changed,"cylinders = 2","cylinders = 2.5");reject(changed,"integer");
changed=original;replace(changed,"layout = \"inline\"","layout = \"flatbread\"");reject(changed,"layout");
changed=original;replace(changed,"topology = \"collector\"","topology = \"american_v8\"");reject(changed,"V8");
changed=original;replace(changed,"cells = 10","cells = 1000000");reject(changed,"cell");
changed=original;replace(changed,"phase_degrees = [0.0, 180.0]","phase_degrees = [0.0]");reject(changed,"phase_degrees");
reject(original+"\n[unknown_section]\nvalue = 1\n","unknown field");
reject("schema_version = [","Error");
{std::ofstream output(file);output<<original;}
std::filesystem::copy_file(file,temp.path/"duplicate.toml");
bool duplicate=false;
try {load_engine_directory(temp.path);}catch(const std::exception& error) {
duplicate=std::string(error.what()).find("duplicate engine id")!=std::string::npos;
}
check(duplicate,"duplicate engine IDs accepted");
std::cout<<catalog.size()<<" engine imports, startup smoke checks and invalid-config diagnostics passed\n";
}