Skip to content

Commit 8e1968d

Browse files
authored
Fix MacOS C++ tests compatibility issues (#3677)
* Add portable wrapper around feenableexcept * Add specific linker flags for macos * Remove other calls to feenableexcept * Update enable_fp_exceptions function * Remove macos floating point exception trap after testing * Formatting * Update thunderscope requirements_lock.darwin.txt * Fix and add comment explaining differences in typename test * Use EXPECT_NEAR for floating point calculations * Remove fedisableexcept * Use numeric_limits epsilon instead of 1e-15 * Only use -undefined linker flag for macos
1 parent ed8629c commit 8e1968d

6 files changed

Lines changed: 69 additions & 26 deletions

File tree

src/shared/test_util/tbots_gtest_main.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,37 @@
55

66
std::string runtime_dir = "/tmp/tbots/yellow_test";
77

8+
/**
9+
* Portable wrapper for feenableexcept. Use to specify which floating-point
10+
* exceptions should crash the program when they occur.
11+
*
12+
* @note On MacOS ARM, there are no floating-point exception traps, so tests may
13+
* pass on MacOS that wouldn't pass on other platforms if floating-point
14+
* exceptions occur.
15+
*
16+
* @param excepts A bitmask of floating-point exceptions to be enabled.
17+
* @return True on success, false on failure to set floating-point exceptions.
18+
*/
19+
bool enable_fp_exceptions(unsigned int excepts)
20+
{
21+
#if defined(__linux__) && defined(__GNUC__)
22+
feenableexcept(excepts);
23+
return true;
24+
#else
25+
// Unsupported platform
26+
return false;
27+
#endif
28+
}
29+
830
int main(int argc, char** argv)
931
{
1032
testing::InitGoogleTest(&argc, argv);
11-
feenableexcept(FE_INVALID | FE_OVERFLOW);
33+
34+
// Crash on invalid operations like sqrt of negative and floating-point overflow
35+
if (!enable_fp_exceptions(FE_INVALID | FE_OVERFLOW))
36+
{
37+
std::cerr << "Warning: Could not enable floating-point exceptions." << std::endl;
38+
}
1239

1340
LoggerSingleton::initializeLogger(runtime_dir, nullptr);
1441

src/software/math/math_functions_test.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,71 @@
22

33
#include <gtest/gtest.h>
44

5+
#include <limits>
6+
57
TEST(LinearUtilFunctionTest, testZeroCase)
68
{
79
double out = linear(0, 0, 2);
8-
EXPECT_EQ(out, 0.5);
10+
EXPECT_NEAR(out, 0.5, std::numeric_limits<double>::epsilon());
911
}
1012

1113
TEST(LinearUtilFunctionTest, testOneQuarter)
1214
{
1315
double out = linear(-1, 0, 4);
14-
EXPECT_EQ(out, 0.25);
16+
EXPECT_NEAR(out, 0.25, std::numeric_limits<double>::epsilon());
1517
}
1618

1719
TEST(LinearUtilFunctionTest, testTwoThirds)
1820
{
1921
double out = linear(0.75, 0, 4.5);
20-
EXPECT_EQ(out, 2.0 / 3.0);
22+
EXPECT_NEAR(out, 2.0 / 3.0, std::numeric_limits<double>::epsilon());
2123
}
2224

2325
TEST(LinearUtilFunctionTest, testMinimumNoOffset)
2426
{
2527
double out = linear(-1.5, 0, 3);
26-
EXPECT_EQ(out, 0.0);
28+
EXPECT_NEAR(out, 0.0, std::numeric_limits<double>::epsilon());
2729
}
2830

2931
TEST(LinearUtilFunctionTest, testMaximumNoOffset)
3032
{
3133
double out = linear(2.5, 0, 5.0);
32-
EXPECT_EQ(out, 1.0);
33-
}
34-
35-
TEST(LinearUtilFunctionTest, testClampBelowNoOffset)
36-
{
37-
double out = linear(-2, 0, 1);
38-
EXPECT_EQ(out, 0.0);
39-
}
40-
41-
TEST(LinearUtilFunctionTest, testClampAboveNoOffset)
42-
{
43-
double out = linear(4.2, 0, 6);
44-
EXPECT_EQ(out, 1.0);
34+
EXPECT_NEAR(out, 1.0, std::numeric_limits<double>::epsilon());
4535
}
4636

4737
TEST(LinearUtilFunctionTest, testMinimumNegativeOffset)
4838
{
4939
double out = linear(-4, -2, 4);
50-
EXPECT_EQ(out, 0.0);
40+
EXPECT_NEAR(out, 0.0, std::numeric_limits<double>::epsilon());
5141
}
5242

5343
TEST(LinearUtilFunctionTest, testMaximumNegativeOffset)
5444
{
5545
double out = linear(1.5, -1, 5.0);
56-
EXPECT_EQ(out, 1.0);
46+
EXPECT_NEAR(out, 1.0, std::numeric_limits<double>::epsilon());
5747
}
5848

5949
TEST(LinearUtilFunctionTest, testMinimumPositiveOffset)
6050
{
6151
double out = linear(0, 3, 6);
62-
EXPECT_EQ(out, 0.0);
52+
EXPECT_NEAR(out, 0.0, std::numeric_limits<double>::epsilon());
6353
}
6454

6555
TEST(LinearUtilFunctionTest, testMaximumPositiveOffset)
6656
{
6757
double out = linear(6, 1.5, 9);
58+
EXPECT_NEAR(out, 1.0, std::numeric_limits<double>::epsilon());
59+
}
60+
61+
TEST(LinearUtilFunctionTest, testClampBelowNoOffset)
62+
{
63+
double out = linear(-2, 0, 1);
64+
EXPECT_EQ(out, 0.0);
65+
}
66+
67+
TEST(LinearUtilFunctionTest, testClampAboveNoOffset)
68+
{
69+
double out = linear(4.2, 0, 6);
6870
EXPECT_EQ(out, 1.0);
6971
}
7072

src/software/simulation/er_force_simulator_test.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#include "software/simulation/er_force_simulator.h"
22

33
#include <gtest/gtest.h>
4-
// TODO (#2419): remove this
5-
#include <fenv.h>
64

75
#include "proto/message_translation/er_force_world.h"
86
#include "proto/message_translation/tbots_protobuf.h"
@@ -15,8 +13,6 @@ class ErForceSimulatorTest : public ::testing::Test
1513
protected:
1614
void SetUp() override
1715
{
18-
// TODO (#2419): remove this to re-enable sigfpe checks
19-
fedisableexcept(FE_INVALID | FE_OVERFLOW);
2016
auto realism_config = ErForceSimulator::createDefaultRealismConfig();
2117
simulator = std::make_shared<ErForceSimulator>(TbotsProto::FieldType::DIV_B,
2218
robot_constants, realism_config);

src/software/thunderscope/requirements_lock.darwin.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,13 @@ pyqtgraph==0.13.7 \
120120
--hash=sha256:64f84f1935c6996d0e09b1ee66fe478a7771e3ca6f3aaa05f00f6e068321d9e3 \
121121
--hash=sha256:7754edbefb6c367fa0dfb176e2d0610da3ada20aa7a5318516c74af5fb72bf7a
122122
# via -r software/thunderscope/requirements.in
123+
qtawesome==1.4.0 \
124+
--hash=sha256:783e414d1317f3e978bf67ea8e8a1b1498bad9dbd305dec814027e3b50521be6 \
125+
--hash=sha256:a4d689fa071c595aa6184171ce1f0f847677cb8d2db45382c43129f1d72a3d93
126+
# via -r software/thunderscope/requirements.in
123127
qtpy==2.4.2 \
124128
--hash=sha256:5a696b1dd7a354cb330657da1d17c20c2190c72d4888ba923f8461da67aa1a1c \
125129
--hash=sha256:9d6ec91a587cc1495eaebd23130f7619afa5cdd34a277acb87735b4ad7c65156
126-
# via pyqt-toast-notification
130+
# via
131+
# pyqt-toast-notification
132+
# qtawesome

src/software/util/typename/typename_test.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ class TestTypeA : public TestType
1717
TEST(TypeNameTest, abstract_base_class_concrete_subtype)
1818
{
1919
std::shared_ptr<TestType> test_type = std::make_shared<TestTypeA>();
20+
#ifdef __APPLE__
21+
// With clang / libc++, the standard library uses an inline namespace '__1' for
22+
// ABI versioning, which is reflected in the demangled type name.
23+
EXPECT_EQ("std::__1::shared_ptr<TestType>", objectTypeName(test_type));
24+
#else
2025
EXPECT_EQ("std::shared_ptr<TestType>", objectTypeName(test_type));
26+
#endif
2127
EXPECT_EQ("TestTypeA", objectTypeName(*test_type));
2228
}
2329

src/starlark/nanopb/nanopb.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ def _construct_cc_info(
227227
user_compile_flags = copts,
228228
)
229229

230+
# Flags required for macos linker which allow symbols to be resolved at runtime
231+
link_flags = []
232+
if "darwin" in cc_toolchain.cpu:
233+
link_flags = ["-Wl,-undefined,dynamic_lookup"]
234+
230235
(linking_context, linking_outputs) = \
231236
cc_common.create_linking_context_from_compilation_outputs(
232237
name = "link_nanopb_outputs",
@@ -235,6 +240,7 @@ def _construct_cc_info(
235240
feature_configuration = feature_configuration,
236241
cc_toolchain = cc_toolchain,
237242
linking_contexts = nanopb_linking_contexts,
243+
user_link_flags = link_flags,
238244
)
239245

240246
extra_context = cc_common.create_compilation_context(

0 commit comments

Comments
 (0)