Provide mock context to test formatter specializations.
Outline
Purpose
Provide mock context to test formatter specializations
Classes
- MockParseContext: parsing context for use in formatter tests
Description
This component provides a class that holds the format string parsing state and is used for formatter testing.
Usage
This section illustrates intended usage of this component.
Example 1: Testing Formatter's parse Method
Suppose we have a formatter for our custom type representing a month and we want to test it. The following example demonstrates how we can test its parse method using bslfmt::MockParseContext.
First, we define our Month class:
class Month {
private:
int d_index;
public:
Month(int index)
: d_index(index)
{
assert((1 <= index) && (12 >= index));
}
int index() const { return d_index; }
};
Then, we define our custom formatter for this class. In it, two methods are necessary: parse and format. The parse method parses the format string itself to determine the formatting to be used by the format method, which writes the formatted object into user-supplied output iterator.
template <class t_CHAR>
struct MonthFormatter {
The convenience of using the bsl::format function is that the users can come up with the description language themselves. In our case, for simplicity, we will present month in two formats - numeric ("03") and verbal ("March"). Accordingly, to indicate the desired type, we will use one of the two letters in the format description: 'n' ('N') or 'v' ('V'). Additionally, user can specify minimal width of the output either string via digit in the format specification or via additional parameter for bsl::format function.
enum Format {
e_NUMERIC,
e_VERBAL
};
Format d_format;
NumericValue d_rawWidth;
public:
: d_format(e_NUMERIC)
{
}
template <class t_PARSE_CONTEXT>
t_PARSE_CONTEXT& context)
{
#define BSLS_KEYWORD_CONSTEXPR_CPP20
Definition bsls_keyword.h:645
MockParseContext completely repeats the interface and behavior of the bslfmt::basic_format_parse_context, but provides an additional accessor that allows users to get information about the parsing process. Therefore users do not need to declare a separate overload of parse for test purposes as long as their formatter's parse method is templated.
typename t_PARSE_CONTEXT::const_iterator current = context.begin();
typename t_PARSE_CONTEXT::const_iterator end = context.end();
if (current == end || *current == '}') {
return context.begin();
}
d_rawWidth.parse(¤t, end, false);
if (d_rawWidth == NumericValue(NumericValue::e_VALUE, 0)) {
BSLS_THROW(bsl::format_error(
"Field widths must be > 0."));
}
if (d_rawWidth.category() == NumericValue::e_ARG_ID) {
context.check_arg_id(d_rawWidth.value());
}
else if (d_rawWidth.category() == NumericValue::e_NEXT_ARG) {
d_rawWidth = NumericValue(
NumericValue::e_ARG_ID,
static_cast<int>(context.next_arg_id()));
}
if (current == end || *current == '}') {
return context.begin();
}
switch (*current) {
case 'V':
case 'v': {
d_format = e_VERBAL;
} break;
case 'N':
case 'n': {
} break;
default: {
"Unexpected symbol in format specification"));
}
}
++current;
if (current != end && *current != '}') {
"Too many symbols in format specification"));
}
context.advance_to(current);
return context.begin();
}
#define BSLS_THROW(X)
Definition bsls_exceptionutil.h:374
To reduce the size of this example, we will omit the implementation of the format method as it is not essential for our purposes.
Finally, we can test the operation of the parse function for different input specifications:
typedef Context::iterator ContextIterator;
{
MonthFormatter<char> formatter;
Context context("v");
ContextIterator iterator = formatter.parse(context);
assert(context.end() == iterator);
Definition bslfmt_mockparsecontext.h:291
Since width is not presented in the format specification, we don't expect our context to change its indexing mode.
assert(Context::e_UNKNOWN == context.indexingMode());
}
{
MonthFormatter<char> formatter;
Context context("8v");
ContextIterator iterator = formatter.parse(context);
assert(context.end() == iterator);
assert(Context::e_UNKNOWN == context.indexingMode());
}
{
MonthFormatter<char> formatter;
Context context("{}v", 1);
ContextIterator iterator = formatter.parse(context);
assert(context.end() == iterator);
And here it is assumed that the width will be determined by the next parameter of the bsl::format function. The indexing mode of the context changes accordingly.
assert(Context::e_AUTOMATIC == context.indexingMode());
}
{
MonthFormatter<char> formatter;
Context context("{1}v", 2);
ContextIterator iterator = formatter.parse(context);
assert(context.end() == iterator);
Here we explicitly indicate the ordinal number of the bsl::format parameter storing the width value.
assert(Context::e_MANUAL == context.indexingMode());
}