diff --git a/src/shared/test_util/tbots_gtest_main.cpp b/src/shared/test_util/tbots_gtest_main.cpp index 91e0cc254c..0abce8db4d 100644 --- a/src/shared/test_util/tbots_gtest_main.cpp +++ b/src/shared/test_util/tbots_gtest_main.cpp @@ -14,11 +14,37 @@ bool TbotsGtestMain::stop_ai_on_start = false; std::string TbotsGtestMain::runtime_dir = "/tmp/tbots/yellow_test"; double TbotsGtestMain::test_speed = 1.0; +/** + * Portable wrapper for feenableexcept. Use to specify which floating-point + * exceptions should crash the program when they occur. + * + * @note On MacOS ARM, there are no floating-point exception traps, so tests may + * pass on MacOS that wouldn't pass on other platforms if floating-point + * exceptions occur. + * + * @param excepts A bitmask of floating-point exceptions to be enabled. + * @return True on success, false on failure to set floating-point exceptions. + */ +bool enable_fp_exceptions(unsigned int excepts) +{ +#if defined(__linux__) && defined(__GNUC__) + feenableexcept(excepts); + return true; +#else + // Unsupported platform + return false; +#endif +} int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); - feenableexcept(FE_INVALID | FE_OVERFLOW); + + // Crash on invalid operations like sqrt of negative and floating-point overflow + if (!enable_fp_exceptions(FE_INVALID | FE_OVERFLOW)) + { + std::cerr << "Warning: Could not enable floating-point exceptions." << std::endl; + } boost::program_options::options_description desc{"Options"}; @@ -58,13 +84,6 @@ int main(int argc, char **argv) if (!TbotsGtestMain::help) { LoggerSingleton::initializeLogger(TbotsGtestMain::runtime_dir, nullptr); - - if (TbotsGtestMain::enable_visualizer || TbotsGtestMain::run_sim_in_realtime) - { - // disable floating point errors when using visualizer due to potential - // floating point errors in QT - fedisableexcept(FE_INVALID | FE_OVERFLOW); - } return RUN_ALL_TESTS(); } else diff --git a/src/software/math/math_functions_test.cpp b/src/software/math/math_functions_test.cpp index c5a48dd605..033aee4d29 100644 --- a/src/software/math/math_functions_test.cpp +++ b/src/software/math/math_functions_test.cpp @@ -2,69 +2,71 @@ #include +#include + TEST(LinearUtilFunctionTest, testZeroCase) { double out = linear(0, 0, 2); - EXPECT_EQ(out, 0.5); + EXPECT_NEAR(out, 0.5, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testOneQuarter) { double out = linear(-1, 0, 4); - EXPECT_EQ(out, 0.25); + EXPECT_NEAR(out, 0.25, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testTwoThirds) { double out = linear(0.75, 0, 4.5); - EXPECT_EQ(out, 2.0 / 3.0); + EXPECT_NEAR(out, 2.0 / 3.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMinimumNoOffset) { double out = linear(-1.5, 0, 3); - EXPECT_EQ(out, 0.0); + EXPECT_NEAR(out, 0.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMaximumNoOffset) { double out = linear(2.5, 0, 5.0); - EXPECT_EQ(out, 1.0); -} - -TEST(LinearUtilFunctionTest, testClampBelowNoOffset) -{ - double out = linear(-2, 0, 1); - EXPECT_EQ(out, 0.0); -} - -TEST(LinearUtilFunctionTest, testClampAboveNoOffset) -{ - double out = linear(4.2, 0, 6); - EXPECT_EQ(out, 1.0); + EXPECT_NEAR(out, 1.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMinimumNegativeOffset) { double out = linear(-4, -2, 4); - EXPECT_EQ(out, 0.0); + EXPECT_NEAR(out, 0.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMaximumNegativeOffset) { double out = linear(1.5, -1, 5.0); - EXPECT_EQ(out, 1.0); + EXPECT_NEAR(out, 1.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMinimumPositiveOffset) { double out = linear(0, 3, 6); - EXPECT_EQ(out, 0.0); + EXPECT_NEAR(out, 0.0, std::numeric_limits::epsilon()); } TEST(LinearUtilFunctionTest, testMaximumPositiveOffset) { double out = linear(6, 1.5, 9); + EXPECT_NEAR(out, 1.0, std::numeric_limits::epsilon()); +} + +TEST(LinearUtilFunctionTest, testClampBelowNoOffset) +{ + double out = linear(-2, 0, 1); + EXPECT_EQ(out, 0.0); +} + +TEST(LinearUtilFunctionTest, testClampAboveNoOffset) +{ + double out = linear(4.2, 0, 6); EXPECT_EQ(out, 1.0); } diff --git a/src/software/simulated_tests/simulated_er_force_sim_test_fixture.cpp b/src/software/simulated_tests/simulated_er_force_sim_test_fixture.cpp index a855576af0..ed93a705e7 100644 --- a/src/software/simulated_tests/simulated_er_force_sim_test_fixture.cpp +++ b/src/software/simulated_tests/simulated_er_force_sim_test_fixture.cpp @@ -1,10 +1,5 @@ #include "software/simulated_tests/simulated_er_force_sim_test_fixture.h" -#include "proto/message_translation/tbots_protobuf.h" - -// TODO (#2419): remove this -#include - #include #include @@ -107,11 +102,7 @@ bool SimulatedErForceSimTestFixture::validateAndCheckCompletion( void SimulatedErForceSimTestFixture::updateSensorFusion( std::shared_ptr simulator) { - // TODO (#2419): remove this to re-enable sigfpe checks - fedisableexcept(FE_INVALID | FE_OVERFLOW); auto ssl_wrapper_packets = simulator->getSSLWrapperPackets(); - // TODO (#2419): remove this to re-enable sigfpe checks - feenableexcept(FE_INVALID | FE_OVERFLOW); auto blue_robot_statuses = simulator->getBlueRobotStatuses(); auto yellow_robot_statuses = simulator->getYellowRobotStatuses(); @@ -183,15 +174,11 @@ void SimulatedErForceSimTestFixture::runTest( std::shared_ptr simulator(std::make_shared( field_type, create2021RobotConstants(), realism_config, ramping)); - // TODO (#2419): remove this to re-enable sigfpe checks - fedisableexcept(FE_INVALID | FE_OVERFLOW); simulator->setBallState(ball); // step the simulator to make sure the robots and the ball are in position simulator->stepSimulation(simulation_time_step); simulator->setYellowRobots(friendly_robots); simulator->setBlueRobots(enemy_robots); - // TODO (#2419): remove this to re-enable sigfpe checks - feenableexcept(FE_INVALID | FE_OVERFLOW); updateSensorFusion(simulator); std::shared_ptr friendly_world; @@ -521,11 +508,7 @@ bool SimulatedErForceSimTestFixture::tickTest( auto wall_start_time = std::chrono::steady_clock::now(); bool validation_functions_done = false; - // TODO (#2419): remove this to re-enable sigfpe checks - fedisableexcept(FE_INVALID | FE_OVERFLOW); simulator->stepSimulation(simulation_time_step); - // TODO (#2419): remove this to re-enable sigfpe checks - feenableexcept(FE_INVALID | FE_OVERFLOW); updateSensorFusion(simulator); if (friendly_sensor_fusion.getWorld().has_value() && diff --git a/src/software/simulation/er_force_simulator_test.cpp b/src/software/simulation/er_force_simulator_test.cpp index bda9a6d7c2..ec72d7dbcc 100644 --- a/src/software/simulation/er_force_simulator_test.cpp +++ b/src/software/simulation/er_force_simulator_test.cpp @@ -1,8 +1,6 @@ #include "software/simulation/er_force_simulator.h" #include -// TODO (#2419): remove this -#include #include "proto/message_translation/er_force_world.h" #include "proto/message_translation/tbots_protobuf.h" @@ -15,8 +13,6 @@ class ErForceSimulatorTest : public ::testing::Test protected: void SetUp() override { - // TODO (#2419): remove this to re-enable sigfpe checks - fedisableexcept(FE_INVALID | FE_OVERFLOW); auto realism_config = ErForceSimulator::createDefaultRealismConfig(); simulator = std::make_shared(TbotsProto::FieldType::DIV_B, robot_constants, realism_config); diff --git a/src/software/thunderscope/requirements_lock.darwin.txt b/src/software/thunderscope/requirements_lock.darwin.txt index 87532ad2ba..55045c79c5 100644 --- a/src/software/thunderscope/requirements_lock.darwin.txt +++ b/src/software/thunderscope/requirements_lock.darwin.txt @@ -120,7 +120,13 @@ pyqtgraph==0.13.7 \ --hash=sha256:64f84f1935c6996d0e09b1ee66fe478a7771e3ca6f3aaa05f00f6e068321d9e3 \ --hash=sha256:7754edbefb6c367fa0dfb176e2d0610da3ada20aa7a5318516c74af5fb72bf7a # via -r software/thunderscope/requirements.in +qtawesome==1.4.0 \ + --hash=sha256:783e414d1317f3e978bf67ea8e8a1b1498bad9dbd305dec814027e3b50521be6 \ + --hash=sha256:a4d689fa071c595aa6184171ce1f0f847677cb8d2db45382c43129f1d72a3d93 + # via -r software/thunderscope/requirements.in qtpy==2.4.2 \ --hash=sha256:5a696b1dd7a354cb330657da1d17c20c2190c72d4888ba923f8461da67aa1a1c \ --hash=sha256:9d6ec91a587cc1495eaebd23130f7619afa5cdd34a277acb87735b4ad7c65156 - # via pyqt-toast-notification + # via + # pyqt-toast-notification + # qtawesome diff --git a/src/software/util/typename/typename_test.cpp b/src/software/util/typename/typename_test.cpp index f28a4b25cd..4acd815c25 100644 --- a/src/software/util/typename/typename_test.cpp +++ b/src/software/util/typename/typename_test.cpp @@ -17,7 +17,13 @@ class TestTypeA : public TestType TEST(TypeNameTest, abstract_base_class_concrete_subtype) { std::shared_ptr test_type = std::make_shared(); +#ifdef __APPLE__ + // With clang / libc++, the standard library uses an inline namespace '__1' for + // ABI versioning, which is reflected in the demangled type name. + EXPECT_EQ("std::__1::shared_ptr", objectTypeName(test_type)); +#else EXPECT_EQ("std::shared_ptr", objectTypeName(test_type)); +#endif EXPECT_EQ("TestTypeA", objectTypeName(*test_type)); } diff --git a/src/starlark/nanopb/nanopb.bzl b/src/starlark/nanopb/nanopb.bzl index 83fc6bf648..cdec9947de 100644 --- a/src/starlark/nanopb/nanopb.bzl +++ b/src/starlark/nanopb/nanopb.bzl @@ -227,6 +227,11 @@ def _construct_cc_info( user_compile_flags = copts, ) + # Flags required for macos linker which allow symbols to be resolved at runtime + link_flags = [] + if "darwin" in cc_toolchain.cpu: + link_flags = ["-Wl,-undefined,dynamic_lookup"] + (linking_context, linking_outputs) = \ cc_common.create_linking_context_from_compilation_outputs( name = "link_nanopb_outputs", @@ -235,6 +240,7 @@ def _construct_cc_info( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, linking_contexts = nanopb_linking_contexts, + user_link_flags = link_flags, ) extra_context = cc_common.create_compilation_context(