mirror of https://github.com/OpenRCT2/OpenRCT2.git
Create new templated format string
This commit is contained in:
parent
79c6b22600
commit
14377be487
|
@ -154,6 +154,32 @@ namespace String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase)
|
||||||
|
{
|
||||||
|
if (ignoreCase)
|
||||||
|
{
|
||||||
|
if (a.size() == b.size())
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < a.size(); i++)
|
||||||
|
{
|
||||||
|
if (tolower(a[i]) != tolower(b[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Equals(const std::string& a, const std::string& b, bool ignoreCase)
|
bool Equals(const std::string& a, const std::string& b, bool ignoreCase)
|
||||||
{
|
{
|
||||||
return Equals(a.c_str(), b.c_str(), ignoreCase);
|
return Equals(a.c_str(), b.c_str(), ignoreCase);
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace String
|
||||||
bool IsNullOrEmpty(const utf8* str);
|
bool IsNullOrEmpty(const utf8* str);
|
||||||
int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false);
|
int32_t Compare(const std::string& a, const std::string& b, bool ignoreCase = false);
|
||||||
int32_t Compare(const utf8* a, const utf8* b, bool ignoreCase = false);
|
int32_t Compare(const utf8* a, const utf8* b, bool ignoreCase = false);
|
||||||
|
bool Equals(const std::string_view& a, const std::string_view& b, bool ignoreCase);
|
||||||
bool Equals(const std::string& a, const std::string& b, bool ignoreCase = false);
|
bool Equals(const std::string& a, const std::string& b, bool ignoreCase = false);
|
||||||
bool Equals(const utf8* a, const utf8* b, bool ignoreCase = false);
|
bool Equals(const utf8* a, const utf8* b, bool ignoreCase = false);
|
||||||
bool StartsWith(const utf8* str, const utf8* match, bool ignoreCase = false);
|
bool StartsWith(const utf8* str, const utf8* match, bool ignoreCase = false);
|
||||||
|
|
|
@ -220,6 +220,7 @@
|
||||||
<ClInclude Include="localisation\Date.h" />
|
<ClInclude Include="localisation\Date.h" />
|
||||||
<ClInclude Include="localisation\FormatCodes.h" />
|
<ClInclude Include="localisation\FormatCodes.h" />
|
||||||
<ClInclude Include="localisation\Formatter.h" />
|
<ClInclude Include="localisation\Formatter.h" />
|
||||||
|
<ClInclude Include="localisation\Formatting.h" />
|
||||||
<ClInclude Include="localisation\Language.h" />
|
<ClInclude Include="localisation\Language.h" />
|
||||||
<ClInclude Include="localisation\LanguagePack.h" />
|
<ClInclude Include="localisation\LanguagePack.h" />
|
||||||
<ClInclude Include="localisation\Localisation.h" />
|
<ClInclude Include="localisation\Localisation.h" />
|
||||||
|
@ -554,6 +555,7 @@
|
||||||
<ClCompile Include="localisation\Currency.cpp" />
|
<ClCompile Include="localisation\Currency.cpp" />
|
||||||
<ClCompile Include="localisation\FormatCodes.cpp" />
|
<ClCompile Include="localisation\FormatCodes.cpp" />
|
||||||
<ClCompile Include="localisation\Formatter.cpp" />
|
<ClCompile Include="localisation\Formatter.cpp" />
|
||||||
|
<ClCompile Include="localisation\Formatting.cpp" />
|
||||||
<ClCompile Include="localisation\Language.cpp" />
|
<ClCompile Include="localisation\Language.cpp" />
|
||||||
<ClCompile Include="localisation\LanguagePack.cpp" />
|
<ClCompile Include="localisation\LanguagePack.cpp" />
|
||||||
<ClCompile Include="localisation\Localisation.cpp" />
|
<ClCompile Include="localisation\Localisation.cpp" />
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "FormatCodes.h"
|
#include "FormatCodes.h"
|
||||||
|
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
|
#include "../core/String.hpp"
|
||||||
#include "Localisation.h"
|
#include "Localisation.h"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
@ -76,14 +77,12 @@ static constexpr const format_code_token format_code_tokens[] = {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
uint32_t format_get_code(const char* token)
|
uint32_t format_get_code(std::string_view token)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < std::size(format_code_tokens); i++)
|
auto result = std::find_if(std::begin(format_code_tokens), std::end(format_code_tokens), [token](auto& fct) {
|
||||||
{
|
return String::Equals(token, fct.token, true);
|
||||||
if (_strcmpi(token, format_code_tokens[i].token) == 0)
|
});
|
||||||
return format_code_tokens[i].code;
|
return result != std::end(format_code_tokens) ? result->code : 0;
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* format_get_token(uint32_t code)
|
const char* format_get_token(uint32_t code)
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
|
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
|
|
||||||
uint32_t format_get_code(const char* token);
|
#include <string_view>
|
||||||
|
|
||||||
|
uint32_t format_get_code(std::string_view token);
|
||||||
const char* format_get_token(uint32_t code);
|
const char* format_get_token(uint32_t code);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||||
|
*
|
||||||
|
* For a complete list of all authors, please refer to contributors.md
|
||||||
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||||
|
*
|
||||||
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "Formatting.h"
|
||||||
|
|
||||||
|
#include "../config/Config.h"
|
||||||
|
#include "../util/Util.h"
|
||||||
|
#include "FormatCodes.h"
|
||||||
|
#include "Language.h"
|
||||||
|
#include "StringIds.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace OpenRCT2
|
||||||
|
{
|
||||||
|
char GetDigitSeperator()
|
||||||
|
{
|
||||||
|
return ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
char GetDecimalSeperator()
|
||||||
|
{
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t TDecimalPlace, bool TDigitSep, typename T> void FormatNumber(std::stringstream& ss, T value)
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
int32_t i = 0;
|
||||||
|
|
||||||
|
size_t num;
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
// TODO handle edge case: std::numeric_limits<int64_t>::min();
|
||||||
|
num = -value;
|
||||||
|
ss << '-';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
num = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal digits
|
||||||
|
if constexpr (TDecimalPlace > 0)
|
||||||
|
{
|
||||||
|
while (num != 0 && i < sizeof(buffer) && i < TDecimalPlace)
|
||||||
|
{
|
||||||
|
buffer[i++] = (char)('0' + (num % 10));
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
buffer[i++] = GetDecimalSeperator();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole digits
|
||||||
|
size_t groupLen = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if constexpr (TDigitSep)
|
||||||
|
{
|
||||||
|
if (groupLen >= 3)
|
||||||
|
{
|
||||||
|
groupLen = 0;
|
||||||
|
buffer[i++] = GetDigitSeperator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer[i++] = (char)('0' + (num % 10));
|
||||||
|
num /= 10;
|
||||||
|
if constexpr (TDigitSep)
|
||||||
|
{
|
||||||
|
groupLen++;
|
||||||
|
}
|
||||||
|
} while (num != 0 && i < sizeof(buffer));
|
||||||
|
|
||||||
|
// Finally reverse append the string
|
||||||
|
for (int32_t j = i - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
ss << buffer[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg)
|
||||||
|
{
|
||||||
|
switch (token)
|
||||||
|
{
|
||||||
|
case FORMAT_UINT16:
|
||||||
|
case FORMAT_INT32:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<0, false>(ss, arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_COMMA16:
|
||||||
|
case FORMAT_COMMA32:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<0, true>(ss, arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_COMMA1DP16:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<1, true>(ss, arg);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_floating_point<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<1, true>(ss, std::round(arg * 10));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_COMMA2DP32:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<2, true>(ss, arg);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_floating_point<T>())
|
||||||
|
{
|
||||||
|
FormatNumber<2, true>(ss, std::round(arg * 100));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_VELOCITY:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
switch (gConfigGeneral.measurement_format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case MeasurementFormat::Imperial:
|
||||||
|
FormatStringId(ss, STR_UNIT_SUFFIX_MILES_PER_HOUR, arg);
|
||||||
|
break;
|
||||||
|
case MeasurementFormat::Metric:
|
||||||
|
FormatStringId(ss, STR_UNIT_SUFFIX_KILOMETRES_PER_HOUR, mph_to_kmph(arg));
|
||||||
|
break;
|
||||||
|
case MeasurementFormat::SI:
|
||||||
|
FormatStringId(ss, STR_UNIT_SUFFIX_METRES_PER_SECOND, mph_to_dmps(arg));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_STRING:
|
||||||
|
if constexpr (std::is_same<T, const char*>())
|
||||||
|
{
|
||||||
|
ss << arg;
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same<T, const std::string&>())
|
||||||
|
{
|
||||||
|
ss << arg.c_str();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMAT_STRINGID:
|
||||||
|
case FORMAT_STRINGID2:
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
{
|
||||||
|
ss << language_get_string(arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt)
|
||||||
|
{
|
||||||
|
if (fmt.size() > 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < fmt.size() - 1; i++)
|
||||||
|
{
|
||||||
|
if (fmt[i] == '{' && fmt[i + 1] != '}')
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
// Find end brace
|
||||||
|
for (size_t j = i + 1; j < fmt.size(); j++)
|
||||||
|
{
|
||||||
|
if (fmt[j] == '}')
|
||||||
|
{
|
||||||
|
auto result = fmt.substr(0, j + 1);
|
||||||
|
fmt = fmt.substr(j + 1);
|
||||||
|
return { result, format_get_code(result.substr(1, result.size() - 2)) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = fmt.substr(0, i);
|
||||||
|
fmt = fmt.substr(i);
|
||||||
|
return { result, 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto result = fmt;
|
||||||
|
fmt = {};
|
||||||
|
return { result, 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanFormatToken(FormatToken t)
|
||||||
|
{
|
||||||
|
return t == FORMAT_COMMA1DP16 || (t >= FORMAT_COMMA32 && t <= FORMAT_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
template void FormatArgument(std::stringstream&, uint32_t, int32_t);
|
||||||
|
template void FormatArgument(std::stringstream&, uint32_t, const char*);
|
||||||
|
} // namespace OpenRCT2
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||||
|
*
|
||||||
|
* For a complete list of all authors, please refer to contributors.md
|
||||||
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||||
|
*
|
||||||
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
#include "Language.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace OpenRCT2
|
||||||
|
{
|
||||||
|
using FormatToken = uint32_t;
|
||||||
|
|
||||||
|
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
||||||
|
|
||||||
|
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt);
|
||||||
|
bool CanFormatToken(FormatToken t);
|
||||||
|
|
||||||
|
inline void FormatString(std::stringstream& ss, std::string_view& fmtc)
|
||||||
|
{
|
||||||
|
while (!fmtc.empty())
|
||||||
|
{
|
||||||
|
auto [part, token] = FormatNextPart(fmtc);
|
||||||
|
if (!CanFormatToken(token))
|
||||||
|
{
|
||||||
|
ss << part;
|
||||||
|
FormatString(ss, fmtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TArg0> static void FormatString(std::stringstream& ss, std::string_view& fmtc, TArg0 arg0)
|
||||||
|
{
|
||||||
|
if (!fmtc.empty())
|
||||||
|
{
|
||||||
|
auto [part, token] = FormatNextPart(fmtc);
|
||||||
|
if (CanFormatToken(token))
|
||||||
|
{
|
||||||
|
FormatArgument(ss, token, arg0);
|
||||||
|
FormatString(ss, fmtc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << part;
|
||||||
|
FormatString(ss, fmtc, arg0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TArg0, typename... TArgs>
|
||||||
|
static void FormatString(std::stringstream& ss, std::string_view& fmtc, TArg0 arg0, TArgs&&... argN)
|
||||||
|
{
|
||||||
|
if (!fmtc.empty())
|
||||||
|
{
|
||||||
|
auto [part, token] = FormatNextPart(fmtc);
|
||||||
|
if (CanFormatToken(token))
|
||||||
|
{
|
||||||
|
FormatArgument(ss, token, arg0);
|
||||||
|
return FormatString(ss, fmtc, argN...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << part;
|
||||||
|
return FormatString(ss, fmtc, arg0, argN...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... TArgs> std::string FormatString(std::string_view fmt, TArgs&&... argN)
|
||||||
|
{
|
||||||
|
thread_local std::stringstream ss;
|
||||||
|
// Reset the buffer (reported as most efficient way)
|
||||||
|
std::stringstream().swap(ss);
|
||||||
|
FormatString(ss, fmt, argN...);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... TArgs> static void FormatStringId(std::stringstream& ss, rct_string_id fmt, TArgs&&... argN)
|
||||||
|
{
|
||||||
|
auto lang = language_get_string(fmt);
|
||||||
|
auto fmtsz = language_convert_string_to_tokens(lang);
|
||||||
|
auto fmtc = std::string_view(fmtsz);
|
||||||
|
FormatString(ss, fmtc, argN...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... TArgs> std::string FormatStringId(rct_string_id fmt, TArgs&&... argN)
|
||||||
|
{
|
||||||
|
auto lang = language_get_string(fmt);
|
||||||
|
auto fmtc = language_convert_string_to_tokens(lang);
|
||||||
|
return FormatString(fmtc, argN...);
|
||||||
|
}
|
||||||
|
} // namespace OpenRCT2
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||||
|
*
|
||||||
|
* For a complete list of all authors, please refer to contributors.md
|
||||||
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||||
|
*
|
||||||
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "openrct2/localisation/Formatting.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <openrct2/Context.h>
|
||||||
|
#include <openrct2/OpenRCT2.h>
|
||||||
|
#include <openrct2/config/Config.h>
|
||||||
|
|
||||||
|
using namespace OpenRCT2;
|
||||||
|
|
||||||
|
class FormattingTests : public testing::Test
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static std::shared_ptr<IContext> _context;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase()
|
||||||
|
{
|
||||||
|
gOpenRCT2Headless = true;
|
||||||
|
gOpenRCT2NoGraphics = true;
|
||||||
|
_context = CreateContext();
|
||||||
|
bool initialised = _context->Initialise();
|
||||||
|
ASSERT_TRUE(initialised);
|
||||||
|
|
||||||
|
// load_from_sv6(parkPath.c_str());
|
||||||
|
// game_load_init();
|
||||||
|
|
||||||
|
// Changed in some tests. Store to restore its value
|
||||||
|
// _gScreenFlags = gScreenFlags;
|
||||||
|
SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TearDownTestCase()
|
||||||
|
{
|
||||||
|
if (_context)
|
||||||
|
_context.reset();
|
||||||
|
|
||||||
|
// gScreenFlags = _gScreenFlags;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<IContext> FormattingTests::_context;
|
||||||
|
TEST_F(FormattingTests, no_args)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("test string");
|
||||||
|
ASSERT_EQ("test string", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, missing_arg)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("test {STRING} arg");
|
||||||
|
ASSERT_EQ("test arg", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, integer)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("Guests: {INT32}", 32);
|
||||||
|
ASSERT_EQ("Guests: 32", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, integer_integer)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("Guests: {INT32}, Staff: {INT32}", 32, 10);
|
||||||
|
ASSERT_EQ("Guests: 32, Staff: 10", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, comma)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("Guests: {COMMA16}", 12534);
|
||||||
|
ASSERT_EQ("Guests: 12,534", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, comma_0)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("Guests: {COMMA16}", 0);
|
||||||
|
ASSERT_EQ("Guests: 0", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, string)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("{RED}{STRING} has broken down.", "Woodchip");
|
||||||
|
ASSERT_EQ("{RED}Woodchip has broken down.", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, escaped_braces)
|
||||||
|
{
|
||||||
|
auto actual = FormatString("--{{ESCAPED}}--", 0);
|
||||||
|
ASSERT_EQ("--{{ESCAPED}}--", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, velocity_mph)
|
||||||
|
{
|
||||||
|
gConfigGeneral.measurement_format = MeasurementFormat::Imperial;
|
||||||
|
auto actual = FormatString("Train is going at {VELOCITY}.", 1024);
|
||||||
|
ASSERT_EQ("Train is going at 1,024 mph.", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, velocity_kph)
|
||||||
|
{
|
||||||
|
gConfigGeneral.measurement_format = MeasurementFormat::Metric;
|
||||||
|
auto actual = FormatString("Train is going at {VELOCITY}.", 1024);
|
||||||
|
ASSERT_EQ("Train is going at 1,648 km/h.", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FormattingTests, velocity_mps)
|
||||||
|
{
|
||||||
|
gConfigGeneral.measurement_format = MeasurementFormat::SI;
|
||||||
|
auto actual = FormatString("Train is going at {VELOCITY}.", 1024);
|
||||||
|
ASSERT_EQ("Train is going at 457.7 m/s.", actual);
|
||||||
|
}
|
|
@ -59,6 +59,7 @@
|
||||||
<ClCompile Include="CircularBuffer.cpp" />
|
<ClCompile Include="CircularBuffer.cpp" />
|
||||||
<ClCompile Include="CryptTests.cpp" />
|
<ClCompile Include="CryptTests.cpp" />
|
||||||
<ClCompile Include="Endianness.cpp" />
|
<ClCompile Include="Endianness.cpp" />
|
||||||
|
<ClCompile Include="FormattingTests.cpp" />
|
||||||
<ClCompile Include="LanguagePackTest.cpp" />
|
<ClCompile Include="LanguagePackTest.cpp" />
|
||||||
<ClCompile Include="ImageImporterTests.cpp" />
|
<ClCompile Include="ImageImporterTests.cpp" />
|
||||||
<ClCompile Include="IniReaderTest.cpp" />
|
<ClCompile Include="IniReaderTest.cpp" />
|
||||||
|
|
Loading…
Reference in New Issue