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.
This commit is contained in:
78
tests/engine_config_tests.cpp
Normal file
78
tests/engine_config_tests.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#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";
|
||||
}
|
||||
Reference in New Issue
Block a user