test some number formatting edge cases

This commit is contained in:
Thomas Fussell 2016-07-11 18:10:57 -04:00
parent 8970bfaffb
commit 053508e8b7
2 changed files with 186 additions and 18 deletions

View File

@ -159,8 +159,7 @@ bool format_condition::satisfied_by(long double number) const
return number < value;
case condition_type::not_equal:
return number != value;
case condition_type::none:
default:
default:
return false;
}
}
@ -198,9 +197,6 @@ void number_format_parser::parse()
break;
case number_format_token::token_type::bad:
throw std::runtime_error("bad format");
case number_format_token::token_type::color:
if (section.color != format_color::none
|| section.condition.type != format_condition::condition_type::none
@ -715,7 +711,13 @@ number_format_token number_format_parser::parse_next_token()
token.string.push_back(format_string_[position_++]);
break;
case '*':
token.type = number_format_token::token_type::fill;
token.string.push_back(format_string_[position_++]);
break;
case '0':
case '#':
case '?':
@ -783,10 +785,18 @@ number_format_token number_format_parser::parse_next_token()
while (end != std::string::npos && format_string_[end - 1] == '\\')
{
token.string.append(format_string_.substr(start, end - start - 1));
token.string.push_back('"');
position_ = end + 1;
start = position_;
end = format_string_.find('"', position_);
}
token.string = format_string_.substr(start, end - start);
if (end != start)
{
token.string.append(format_string_.substr(start, end - start));
}
position_ = end + 1;
break;
@ -910,9 +920,9 @@ format_placeholders number_format_parser::parse_placeholders(const std::string &
if (!comma_indices.empty())
{
std::size_t i = placeholders_string.size();
std::size_t i = placeholders_string.size() - 1;
while (i == comma_indices.back())
while (!comma_indices.empty() && i == comma_indices.back())
{
++p.thousands_scale;
--i;
@ -1140,6 +1150,11 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
number *= 100;
}
if (p.thousands_scale > 0)
{
number /= std::pow(1000, p.thousands_scale);
}
auto integer_part = static_cast<int>(number);
if (p.type == format_placeholders::placeholders_type::integer_only
@ -1365,6 +1380,9 @@ std::string number_formatter::format_number(const format_code &format, long doub
}
bool improper_fraction = true;
std::size_t fill_index = 0;
bool fill = false;
std::string fill_character;
for (std::size_t i = 0; i < format.parts.size(); ++i)
{
@ -1378,6 +1396,11 @@ std::string number_formatter::format_number(const format_code &format, long doub
case template_part::template_type::text:
result.append(part.string);
break;
case template_part::template_type::fill:
fill = true;
fill_index = result.size();
fill_character = part.string;
break;
case template_part::template_type::general:
{
if (part.placeholders.type == format_placeholders::placeholders_type::fraction_integer)
@ -1503,6 +1526,28 @@ std::string number_formatter::format_number(const format_code &format, long doub
throw "unhandled";
}
}
const std::size_t width = 11;
if (fill && result.size() < width)
{
auto remaining = width - result.size();
std::string fill_string(remaining, fill_character.front());
// A UTF-8 character could be multiple bytes
if (fill_character.size() > 1)
{
fill_string.clear();
for (std::size_t i = 0; i < remaining; ++i)
{
fill_string.append(fill_character);
}
}
result = result.substr(0, fill_index) + fill_string + result.substr(fill_index);
}
return result;
}

View File

@ -22,6 +22,14 @@ public:
nf.set_format_string("\"any\"General");
formatted = nf.format(-3.14, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "-any3.14");
nf.set_format_string("\"positive\"General;\"negative\"General;\"zero\"General");
formatted = nf.format(3.14, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "positive3.14");
formatted = nf.format(-3.14, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "negative3.14");
formatted = nf.format(0, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "zero0");
}
void test_simple_date()
@ -272,6 +280,14 @@ public:
formatted = nf.format(2, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "third2");
nf.set_format_string("[>=5]\"first\"General");
formatted = nf.format(4, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "###########");
nf.set_format_string("[>=5]\"first\"General;[>=4]\"second\"General");
formatted = nf.format(3, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "###########");
nf.set_format_string("[<1]\"first\"General;[<5]\"second\"General;\"third\"General");
formatted = nf.format(0, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "first0");
@ -307,6 +323,36 @@ public:
TS_ASSERT_EQUALS(formatted, "third3");
formatted = nf.format(0, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "third0");
nf.set_format_string("[<>1]\"first\"General;[<>2]\"second\"General");
formatted = nf.format(2, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "first2");
formatted = nf.format(1, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "second1");
}
void test_space()
{
xlnt::number_format nf;
nf.set_format_string("_(General_)");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, " 6 ");
}
void test_fill()
{
xlnt::number_format nf;
nf.set_format_string("*-General");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "----------6");
nf.set_format_string("General*-");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6----------");
nf.set_format_string("\\a*-\\b");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "a---------b");
}
void test_locale_currency()
@ -352,6 +398,65 @@ public:
nf.set_format_string("[>3][>4]#,##0.00");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
}
void test_escaped_quote_string()
{
xlnt::number_format nf;
nf.set_format_string("\"\\\"\"General");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "\"6");
}
void test_thousands_scale()
{
xlnt::number_format nf;
nf.set_format_string("#,");
auto formatted = nf.format(61234, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "61");
}
void test_colors()
{
xlnt::number_format nf;
nf.set_format_string("[Black]#");
auto formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Black]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Blue]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Green]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Red]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Cyan]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Magenta]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Yellow]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
nf.set_format_string("[Color15]#");
formatted = nf.format(6, xlnt::calendar::windows_1900);
TS_ASSERT_EQUALS(formatted, "6");
}
void test_bad_format()
{
@ -362,6 +467,24 @@ public:
nf.set_format_string("\"first\"General;\"second\"General;\"third\"General;\"fourth\"General;\"fifth\"General");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("[");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("[]");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("[Redd]");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("[$1]#");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("Gee");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
nf.set_format_string("!");
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
}
void format_and_test(const xlnt::number_format &nf, const std::array<std::string, 4> &expect)
@ -404,7 +527,7 @@ public:
// #,##0.00
void test_builtin_format_4()
{
format_and_test(xlnt::number_format::from_builtin_id(4), {{"42,503.12", "-42,503.12", "0.00", "text"}});
format_and_test(xlnt::number_format::number_comma_separated1(), {{"42,503.12", "-42,503.12", "0.00", "text"}});
}
// #,##0;-#,##0
@ -464,49 +587,49 @@ public:
// mm-dd-yy
void test_builtin_format_14()
{
format_and_test(xlnt::number_format::from_builtin_id(14), {{"05-13-16", "###########", "01-00-00", "text"}});
format_and_test(xlnt::number_format::date_xlsx14(), {{"05-13-16", "###########", "01-00-00", "text"}});
}
// d-mmm-yy
void test_builtin_format_15()
{
format_and_test(xlnt::number_format::from_builtin_id(15), {{"13-May-16", "###########", "0-Jan-00", "text"}});
format_and_test(xlnt::number_format::date_xlsx15(), {{"13-May-16", "###########", "0-Jan-00", "text"}});
}
// d-mmm
void test_builtin_format_16()
{
format_and_test(xlnt::number_format::from_builtin_id(16), {{"13-May", "###########", "0-Jan", "text"}});
format_and_test(xlnt::number_format::date_xlsx16(), {{"13-May", "###########", "0-Jan", "text"}});
}
// mmm-yy
void test_builtin_format_17()
{
format_and_test(xlnt::number_format::from_builtin_id(17), {{"May-16", "###########", "Jan-00", "text"}});
format_and_test(xlnt::number_format::date_xlsx17(), {{"May-16", "###########", "Jan-00", "text"}});
}
// h:mm AM/PM
void test_builtin_format_18()
{
format_and_test(xlnt::number_format::from_builtin_id(18), {{"2:57 AM", "###########", "12:00 AM", "text"}});
format_and_test(xlnt::number_format::date_time1(), {{"2:57 AM", "###########", "12:00 AM", "text"}});
}
// h:mm:ss AM/PM
void test_builtin_format_19()
{
format_and_test(xlnt::number_format::from_builtin_id(19), {{"2:57:42 AM", "###########", "12:00:00 AM", "text"}});
format_and_test(xlnt::number_format::date_time2(), {{"2:57:42 AM", "###########", "12:00:00 AM", "text"}});
}
// h:mm
void test_builtin_format_20()
{
format_and_test(xlnt::number_format::from_builtin_id(20), {{"2:57", "###########", "0:00", "text"}});
format_and_test(xlnt::number_format::date_time3(), {{"2:57", "###########", "0:00", "text"}});
}
// h:mm:ss
void test_builtin_format_21()
{
format_and_test(xlnt::number_format::from_builtin_id(21), {{"2:57:42", "###########", "0:00:00", "text"}});
format_and_test(xlnt::number_format::date_time4(), {{"2:57:42", "###########", "0:00:00", "text"}});
}
// m/d/yy h:mm