Make JSONWriter's number handling locale-independent

Closes #3176
This commit is contained in:
Martin Hořeňovský
2026-07-23 00:10:28 +02:00
parent 8494e2dce4
commit 0cc833ea95
19 changed files with 211 additions and 19 deletions
@@ -12,6 +12,7 @@
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <locale>
#include <sstream>
namespace {
@@ -20,6 +21,27 @@ namespace {
return os << "custom";
}
// Obviously wrong numpunct if it is actually used by the JSON writer.
class TestNumpunct : public std::numpunct<char> {
char do_decimal_point() const override { return '?'; }
char do_thousands_sep() const override { return '!'; }
std::string do_grouping() const override { return "\1"; }
std::string do_truename() const override { return "real"; }
std::string do_falsename() const override { return "fake"; }
};
class LocaleGuard {
std::locale m_previous_locale;
public:
explicit LocaleGuard( std::locale const& locale ):
m_previous_locale{ std::locale() } {
std::locale::global( locale );
}
~LocaleGuard() { std::locale::global( m_previous_locale ); }
};
TEST_CASE( "JsonWriter", "[JSON][JsonWriter]" ) {
std::stringstream stream;
@@ -152,6 +174,31 @@ TEST_CASE( "JsonWriter escapes characters in strings properly", "[JsonWriter]" )
}
}
TEST_CASE( "JsonWriter serializes numbers independently of the global locale",
"[JsonWriter]" ) {
using Catch::Matchers::ContainsSubstring;
LocaleGuard locale_guard{ std::locale{ std::locale(), new TestNumpunct } };
std::stringstream sstream;
{
auto writer = Catch::JsonValueWriter{ sstream }.writeObject();
writer.write( "double" ).write( 1.5 );
writer.write( "int" ).write( 1234567 );
writer.write( "bool-1" ).write( true );
writer.write( "bool-2" ).write( false );
writer.write( "array" ).writeArray().write( 2.5 ).write( 1234567 );
}
REQUIRE_THAT( sstream.str(),
ContainsSubstring( "\"double\": 1.5," ) &&
ContainsSubstring( "\"int\": 1234567," ) &&
ContainsSubstring( "\"bool-1\": true," ) &&
ContainsSubstring( "\"bool-2\": false," ) &&
ContainsSubstring(
"\"array\": [\n 2.5,\n 1234567\n ]\n}" ) );
}
TEST_CASE( "JsonWriter benchmarks", "[JsonWriter][!benchmark]" ) {
const auto input_length = GENERATE( as<size_t>{}, 10, 100, 10'000 );
std::string test_input( input_length, 'a' );