94 lines
4.9 KiB
C++
94 lines
4.9 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 = 2","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_per_meter = 50.0","cells_per_meter = 1000000");reject(changed,"cells_per_meter");
|
|
changed=original;replace(changed,"phase_degrees = [0.0, 180.0]","phase_degrees = [0.0]");reject(changed,"phase_degrees");
|
|
reject(original+"\n[junctions]\nmodel = \"mystery\"\n","model must be");
|
|
reject(original+"\n[junctions]\nturning_loss = -1\n","turning_loss");
|
|
reject(original+"\n[[junctions.branches]]\npipe=3\nleft=false\ndirection=[0,0,0]\n","nonzero");
|
|
{
|
|
std::ofstream output(file);
|
|
output<<original<<"\n[junctions]\nmodel=\"characteristic\"\nturning_loss=0.5\n"
|
|
<<"[[junctions.branches]]\npipe=3\nleft=false\ndirection=[-1,0,0]\n";
|
|
}
|
|
const auto directed=load_engine_file(file);
|
|
check(directed.junctions.model=="characteristic" && directed.junctions.branches.size()==1,"junction configuration was not loaded");
|
|
{
|
|
DesktopScenario scenario(directed);
|
|
for(int i=0;i<2048;++i) scenario.process();
|
|
check(scenario.snapshot().pipe_positivity_repairs==0,"configured characteristic engine needs positivity repairs");
|
|
}
|
|
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";
|
|
}
|