#include "doctest/doctest.h" #include "fgc/HardwareChecks.h" using namespace fgc; TEST_CASE("usbfs_memory_mb is checked against the value frames actually need") { CHECK(checkUsbfsMemory("1000", 1000).pass); CHECK(checkUsbfsMemory("2048\n", 1000).pass); // The reboot default. This is the exact state that was found on the // LattePanda and stalled frame delivery. auto low = checkUsbfsMemory("16", 1000); CHECK_FALSE(low.pass); CHECK(low.detail.find("kernel cmdline") != std::string::npos); } TEST_CASE("an unreadable usbfs node fails rather than passing by default") { // A check that did not happen must not report PASS - that is how a silent // precondition regression survives a green test run. CHECK_FALSE(checkUsbfsMemory("", 1000).pass); CHECK_FALSE(checkUsbfsMemory("not-a-number", 1000).pass); } TEST_CASE("USB3 LPM must be off, and anything unreadable is a failure") { CHECK(checkUsb3Lpm("0").pass); CHECK(checkUsb3Lpm(" 0\n").pass); auto on = checkUsb3Lpm("1"); CHECK_FALSE(on.pass); CHECK(on.detail.find("re-enumerate") != std::string::npos); CHECK_FALSE(checkUsb3Lpm("").pass); } TEST_CASE("an SMBus-only adapter is caught before it looks like a dead sensor") { CHECK(checkI2cFunctionality(kI2cFuncI2c).pass); CHECK(checkI2cFunctionality(kI2cFuncI2c | 0xF0).pass); auto smbus = checkI2cFunctionality(0xF0); // SMBus bits only CHECK_FALSE(smbus.pass); CHECK(smbus.detail.find("i2cdetect -F") != std::string::npos); } TEST_CASE("an undeclared axis has no drivetrain skew to find") { DrivetrainExpectation want; // declared == false DrivetrainEcho got; // all -1 auto r = checkDrivetrainAdopted("yaw", want, got); CHECK_FALSE(r.ran); CHECK(r.pass); } TEST_CASE("a firmware that cannot echo the current is a version skew, not zero current") { DrivetrainExpectation want{true, 80, 31, 7}; DrivetrainEcho got; // all absent auto r = checkDrivetrainAdopted("yaw", want, got); CHECK(r.ran); CHECK_FALSE(r.pass); CHECK(r.detail.find("predates") != std::string::npos); CHECK(r.detail.find("Re-flash") != std::string::npos); } TEST_CASE("the IOTA tower's declared current, adopted") { DrivetrainExpectation want{true, 80, 31, 7}; // 756 mA run, 189 mA homing at 75 mOhm DrivetrainEcho got{80, 31, 7}; auto r = checkDrivetrainAdopted("yaw", want, got); CHECK(r.ran); CHECK(r.pass); } TEST_CASE("a homing current that did not land is named as the damaging case") { // The exact failure that sheared the endstop: run current correct, homing // current still at the firmware default. DrivetrainExpectation want{true, 80, 31, 7}; DrivetrainEcho got{80, 31, 31}; auto r = checkDrivetrainAdopted("yaw", want, got); CHECK_FALSE(r.pass); CHECK(r.detail.find("homing IRUN wanted 7, firmware has 31") != std::string::npos); CHECK(r.detail.find("endstop") != std::string::npos); } TEST_CASE("every mismatched field is reported, not just the first") { DrivetrainExpectation want{true, 80, 31, 7}; DrivetrainEcho got{255, 16, 31}; auto r = checkDrivetrainAdopted("yaw", want, got); CHECK_FALSE(r.pass); CHECK(r.detail.find("GLOBALSCALER") != std::string::npos); CHECK(r.detail.find("IRUN wanted 31") != std::string::npos); CHECK(r.detail.find("homing IRUN") != std::string::npos); } // --- the generalised adoption check (config-plan A.6) ----------------------- TEST_CASE("an axis with nothing declared beyond the current is not checked") { const DeclaredValue none[] = {{"steps_per_rev", false, 0, -1}}; auto r = checkDeclaredValues("yaw", none, 1); CHECK_FALSE(r.ran); CHECK(r.pass); } TEST_CASE("every declared value matching is a pass that says how many") { const DeclaredValue v[] = { {"steps_per_rev", true, 1210778, 1210778}, {"enc_deviation", true, 1500, 1500}, {"normal_speed", true, 39738, 39738}, }; auto r = checkDeclaredValues("yaw", v, 3); CHECK(r.ran); CHECK(r.pass); CHECK(r.detail.find("all 3") != std::string::npos); } TEST_CASE("a firmware that cannot echo a declared value fails, and is named a skew") { // Not a mismatch: the value cannot be confirmed either way, and a check that // did not happen must never read as a pass. const DeclaredValue v[] = { {"steps_per_rev", true, 1210778, -1}, {"enc_deviation", true, 1500, -1}, }; auto r = checkDeclaredValues("yaw", v, 2); CHECK(r.ran); CHECK_FALSE(r.pass); CHECK(r.detail.find("does not echo 2") != std::string::npos); CHECK(r.detail.find("Re-flash") != std::string::npos); } TEST_CASE("a lost SET is caught, and every wrong field is named") { // The failure mode the handshake introduces: most SETs land, one does not, // and the axis runs on a value nobody declared. const DeclaredValue v[] = { {"steps_per_rev", true, 1210778, 1210778}, {"enc_deviation", true, 1500, 177000}, // stale Delta value {"homing_speed", true, 9958, 12000}, // firmware default }; auto r = checkDeclaredValues("yaw", v, 3); CHECK_FALSE(r.pass); CHECK(r.detail.find("enc_deviation wanted 1500, firmware has 177000") != std::string::npos); CHECK(r.detail.find("homing_speed wanted 9958, firmware has 12000") != std::string::npos); CHECK(r.detail.find("NEEDCFG") != std::string::npos); } TEST_CASE("an undeclared value is skipped even when the firmware echoes one") { // hardware.ini not declaring it means the firmware default IS the intent. const DeclaredValue v[] = { {"steps_per_rev", true, 1210778, 1210778}, {"sw_mode", false, 0, 3}, }; auto r = checkDeclaredValues("yaw", v, 2); CHECK(r.pass); CHECK(r.detail.find("all 1") != std::string::npos); } TEST_CASE("a partly echoed current is a version skew, not a wrong value") { // The bug this caught: comparing an absent field against -1 reported // "firmware has -1", which reads as a wrong value and sends someone to // re-send a CURRENT that was never the problem. DrivetrainExpectation want{true, 80, 31, 7}; DrivetrainEcho got; got.global_scaler = 80; // echoed got.run_irun = -1; // not echoed got.homing_irun = -1; auto r = checkDrivetrainAdopted("yaw", want, got); CHECK_FALSE(r.pass); CHECK(r.detail.find("2 of 3 fields absent") != std::string::npos); CHECK(r.detail.find("firmware has -1") == std::string::npos); } TEST_CASE("a real mismatch alongside an absent field still names the mismatch") { DrivetrainExpectation want{true, 80, 31, 7}; DrivetrainEcho got; got.global_scaler = 255; // wrong got.run_irun = 31; // right got.homing_irun = -1; // absent auto r = checkDrivetrainAdopted("yaw", want, got); CHECK_FALSE(r.pass); CHECK(r.detail.find("GLOBALSCALER wanted 80, firmware has 255") != std::string::npos); }