2121#include " iox/std_string_support.hpp"
2222#include " test.hpp"
2323
24-
24+ # include < cmath >
2525#include < cstdint>
2626namespace
2727{
@@ -38,6 +38,15 @@ class convert_test : public Test
3838 void TearDown () override
3939 {
4040 }
41+ template <typename T>
42+ std::string fp_to_string (T value)
43+ {
44+ static_assert (std::is_floating_point<T>::value, " fp_to_string requires floating point type" );
45+
46+ std::ostringstream oss;
47+ oss << std::scientific << std::setprecision (std::numeric_limits<T>::max_digits10) << value;
48+ return oss.str ();
49+ }
4150};
4251
4352
@@ -549,6 +558,178 @@ TEST_F(convert_test, fromString_EdgeCase_UnSignedLongLong)
549558
550559// / UNSINGED INTEGRAL EDGE CASES END
551560
561+ // / NORMAL FLOATING POINT TYPE EDGE CASES START
562+
563+ TEST_F (convert_test, fromString_EdgeCase_Float)
564+ {
565+ ::testing::Test::RecordProperty (" TEST_ID" , " 68d4f096-a93c-406b-b081-fe50e4b1a2c9" );
566+
567+ std::string source = fp_to_string (std::numeric_limits<float >::min ());
568+ auto float_min = iox::convert::from_string<float >(source.c_str ());
569+ ASSERT_THAT (float_min.has_value (), Eq (true ));
570+ EXPECT_THAT (float_min.value (), FloatEq (std::numeric_limits<float >::min ()));
571+
572+ // strtof will trigger ERANGE if the input is a subnormal float, resulting in a nullopt return value.
573+ auto normal_float_min_eps = std::nextafter (std::numeric_limits<float >::min (), 0 .0F );
574+ source = fp_to_string (std::numeric_limits<float >::min () - normal_float_min_eps);
575+ auto float_min_dec_eps = iox::convert::from_string<float >(source.c_str ());
576+ ASSERT_THAT (float_min_dec_eps.has_value (), Eq (false ));
577+
578+ source = fp_to_string (std::numeric_limits<float >::lowest ());
579+ auto float_lowest = iox::convert::from_string<float >(source.c_str ());
580+ ASSERT_THAT (float_lowest.has_value (), Eq (true ));
581+ EXPECT_THAT (float_lowest.value (), FloatEq (std::numeric_limits<float >::lowest ()));
582+
583+ source = fp_to_string (std::numeric_limits<float >::max ());
584+ auto float_max = iox::convert::from_string<float >(source.c_str ());
585+ ASSERT_THAT (float_max.has_value (), Eq (true ));
586+ EXPECT_THAT (float_max.value (), FloatEq (std::numeric_limits<float >::max ()));
587+ }
588+
589+ TEST_F (convert_test, fromString_EdgeCase_Double)
590+ {
591+ ::testing::Test::RecordProperty (" TEST_ID" , " af7ca2e6-ba7e-41f7-a321-5f68617d3566" );
592+
593+ std::string source = fp_to_string (std::numeric_limits<double >::min ());
594+ auto double_min = iox::convert::from_string<double >(source.c_str ());
595+ ASSERT_THAT (double_min.has_value (), Eq (true ));
596+ EXPECT_THAT (double_min.value (), DoubleEq (std::numeric_limits<double >::min ()));
597+
598+ auto normal_double_min_eps = std::nextafter (std::numeric_limits<double >::min (), 0.0 );
599+ source = fp_to_string (std::numeric_limits<double >::min () - normal_double_min_eps);
600+ auto double_min_dec_eps = iox::convert::from_string<double >(source.c_str ());
601+ ASSERT_THAT (double_min_dec_eps.has_value (), Eq (false ));
602+
603+ source = fp_to_string (std::numeric_limits<double >::lowest ());
604+ auto double_lowest = iox::convert::from_string<double >(source.c_str ());
605+ ASSERT_THAT (double_lowest.has_value (), Eq (true ));
606+ EXPECT_THAT (double_lowest.value (), DoubleEq (std::numeric_limits<double >::lowest ()));
607+
608+ source = fp_to_string (std::numeric_limits<double >::max ());
609+ auto double_max = iox::convert::from_string<double >(source.c_str ());
610+ ASSERT_THAT (double_max.has_value (), Eq (true ));
611+ EXPECT_THAT (double_max.value (), DoubleEq (std::numeric_limits<double >::max ()));
612+ }
613+
614+ TEST_F (convert_test, fromString_EdgeCase_LongDouble)
615+ {
616+ ::testing::Test::RecordProperty (" TEST_ID" , " fb96e526-8fb6-4af9-87f0-dfd4193237a5" );
617+
618+ std::string source = fp_to_string (std::numeric_limits<long double >::min ());
619+ auto long_double_min = iox::convert::from_string<long double >(source.c_str ());
620+ ASSERT_THAT (long_double_min.has_value (), Eq (true ));
621+ // There's no LongDoubleEq
622+ EXPECT_THAT (long_double_min.value (), Eq (std::numeric_limits<long double >::min ()));
623+
624+ auto normal_long_double_min_eps = std::nextafter (std::numeric_limits<long double >::min (), 0 .0L );
625+ source = fp_to_string (std::numeric_limits<long double >::min () - normal_long_double_min_eps);
626+ auto long_double_min_dec_eps = iox::convert::from_string<long double >(source.c_str ());
627+ ASSERT_THAT (long_double_min_dec_eps.has_value (), Eq (false ));
628+
629+ source = fp_to_string (std::numeric_limits<long double >::lowest ());
630+ auto long_double_lowest = iox::convert::from_string<long double >(source.c_str ());
631+ ASSERT_THAT (long_double_lowest.has_value (), Eq (true ));
632+ EXPECT_THAT (long_double_lowest.value (), Eq (std::numeric_limits<long double >::lowest ()));
633+
634+ source = fp_to_string (std::numeric_limits<long double >::max ());
635+ auto long_double_max = iox::convert::from_string<long double >(source.c_str ());
636+ ASSERT_THAT (long_double_max.has_value (), Eq (true ));
637+ EXPECT_THAT (long_double_max.value (), Eq (std::numeric_limits<long double >::max ()));
638+ }
639+
640+ // / NORMAL FLOATING POINT TYPE EDGE CASES END
641+
642+ // / SPECIAL FLOATING POINT TYPE EDGE CASES START
643+
644+ TEST_F (convert_test, fromString_EdgeCase_Float_NaN)
645+ {
646+ ::testing::Test::RecordProperty (" TEST_ID" , " 772bcbc3-d55b-464f-873f-82754ad543f3" );
647+
648+ std::vector<std::string> nan_vec = {" NaN" , " nan" };
649+
650+ for (const auto & v : nan_vec)
651+ {
652+ auto nan_ret = iox::convert::from_string<float >(v.c_str ());
653+ ASSERT_THAT (nan_ret.has_value (), Eq (true ));
654+ ASSERT_THAT (std::isnan (nan_ret.value ()), Eq (true ));
655+ }
656+ }
657+
658+ TEST_F (convert_test, fromString_EdgeCase_Double_NaN)
659+ {
660+ ::testing::Test::RecordProperty (" TEST_ID" , " a27c8575-658c-465d-a1a2-4f2f6b9a723a" );
661+
662+ std::vector<std::string> nan_vec = {" NaN" , " nan" };
663+
664+ for (const auto & v : nan_vec)
665+ {
666+ auto nan_ret = iox::convert::from_string<double >(v.c_str ());
667+ ASSERT_THAT (nan_ret.has_value (), Eq (true ));
668+ ASSERT_THAT (std::isnan (nan_ret.value ()), Eq (true ));
669+ }
670+ }
671+
672+ TEST_F (convert_test, fromString_EdgeCase_LongDouble_NaN)
673+ {
674+ ::testing::Test::RecordProperty (" TEST_ID" , " 486f4e78-6000-4401-bb66-62d26b1d0cce" );
675+
676+ std::vector<std::string> nan_vec = {" NaN" , " nan" };
677+
678+ for (const auto & v : nan_vec)
679+ {
680+ auto nan_ret = iox::convert::from_string<long double >(v.c_str ());
681+ ASSERT_THAT (nan_ret.has_value (), Eq (true ));
682+ ASSERT_THAT (std::isnan (nan_ret.value ()), Eq (true ));
683+ }
684+ }
685+
686+ TEST_F (convert_test, fromString_EdgeCase_Float_Inf)
687+ {
688+ ::testing::Test::RecordProperty (" TEST_ID" , " 82dba3ae-5802-4fbc-aa91-15f4a2953573" );
689+
690+ std::vector<std::string> inf_vec = {
691+ " INF" , " Inf" , " inf" , " INFINITY" , " Infinity" , " -INF" , " -Inf" , " -inf" , " -INFINITY" , " -Infinity" };
692+
693+ for (const auto & v : inf_vec)
694+ {
695+ auto inf_ret = iox::convert::from_string<float >(v.c_str ());
696+ ASSERT_THAT (inf_ret.has_value (), Eq (true ));
697+ ASSERT_THAT (std::isinf (inf_ret.value ()), Eq (true ));
698+ }
699+ }
700+
701+ TEST_F (convert_test, fromString_EdgeCase_Double_Inf)
702+ {
703+ ::testing::Test::RecordProperty (" TEST_ID" , " e4ccd01d-b1d1-433e-ba04-548dcc479bb1" );
704+
705+ std::vector<std::string> inf_vec = {
706+ " INF" , " Inf" , " inf" , " INFINITY" , " Infinity" , " -INF" , " -Inf" , " -inf" , " -INFINITY" , " -Infinity" };
707+
708+ for (const auto & v : inf_vec)
709+ {
710+ auto inf_ret = iox::convert::from_string<double >(v.c_str ());
711+ ASSERT_THAT (inf_ret.has_value (), Eq (true ));
712+ ASSERT_THAT (std::isinf (inf_ret.value ()), Eq (true ));
713+ }
714+ }
715+
716+ TEST_F (convert_test, fromString_EdgeCase_LongDouble_Inf)
717+ {
718+ ::testing::Test::RecordProperty (" TEST_ID" , " 6b8a3284-5f20-4cd6-9958-a2abb348ebe2" );
719+
720+ std::vector<std::string> inf_vec = {
721+ " INF" , " Inf" , " inf" , " INFINITY" , " Infinity" , " -INF" , " -Inf" , " -inf" , " -INFINITY" , " -Infinity" };
722+
723+ for (const auto & v : inf_vec)
724+ {
725+ auto inf_ret = iox::convert::from_string<long double >(v.c_str ());
726+ ASSERT_THAT (inf_ret.has_value (), Eq (true ));
727+ ASSERT_THAT (std::isinf (inf_ret.value ()), Eq (true ));
728+ }
729+ }
730+
731+ // / SPECIAL FLOATING POINT TYPE EDGE CASES END
732+
552733TEST_F (convert_test, fromString_cxxString)
553734{
554735 ::testing::Test::RecordProperty (" TEST_ID" , " dbf015bb-5f51-47e1-9d0e-0525f65e7803" );
0 commit comments