FCF 2.0 development in progress...
> > > > > > > >
[News] [C++ Libraries API] [C++ Downloads] [Donate to the project] [Contacts]

formatData from class fcf::NTest::Logger::OutputTarget

Property: formatData

Type: fcf::NTest::Logger::HandlerDataMap

Class: fcf::NTest::Logger::OutputTarget

Package: fcfTest

File: test.hpp

Available from version: 1.1.13

A map containing persistent data associated with the formatters of this target.

The formatData property is a fcf::NTest::Logger::HandlerDataMap (a map of fcf::NTest::SharedPtrAny). It is used to store and persist data that is specific to the fcf::NTest::Logger::Format handlers of this target. This allows formatters to maintain state (such as an XML document tree or a custom buffer) across multiple log entries for the same target.

Example: Stateful Formatter with formatData

Demonstrating how to use formatData to allow a custom formatter to accumulate data (such as a list of messages to generate JSON) across the entire test run.

#define FCF_TEST_IMPLEMENTATION #include <fcfTest/test.hpp> #include <iostream> #include <vector> struct Message { unsigned int code; std::string message; }; struct Messages { typedef std::vector<Message> Items; bool defaultStream; // flag indicating if this is the default stream Items items; // message history }; // Function to convert message history to json std::string toJson(const Messages& a_messages){ auto escape = [](const std::string& a_source) { std::string result; result.reserve(a_source.size()); for (char c : a_source) { switch (c) { case '"': result += "\\\""; break; case '\\': result += "\\\\"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; case '\x1b': result += "\\u001b"; break; default: result += c; break; } } return result; }; std::string result; result += "["; for(const Message& message : a_messages.items) { if (result.length() > 1){ result += ","; } result += "\n { \"code\": " + std::to_string(message.code) + ", \"message\": \"" + escape(message.message) + "\"}"; } result += "\n]\n"; return result; } FCF_TEST_DEFINE("FormatDataDemo", "Logger", "AccumulatorTest") { fcf::NTest::log() << "First log" << std::endl; fcf::NTest::log() << "Second log" << std::endl; FCF_TEST(true); } int main(int a_argc, char* a_argv[]) { fcf::NTest::Logger& logger = fcf::NTest::logger(); // Setup Format with a dataFactory to initialize our state fcf::NTest::Logger::Format format; format.name = "my-json"; // Set the default factory for user data for our formatter format.dataFactory = []( fcf::NTest::Logger&, fcf::NTest::Logger::OutputTarget&) { return fcf::NTest::SharedPtrAny::make<Messages>( Messages{false, {}} ); }; // Define a custom formatter that will generate JSON output format.handler = []( fcf::NTest::Logger&, fcf::NTest::Logger::MessageContext& a_context) { // Get user data associated with this formatter and output stream Messages* messages = a_context.data->cast<Messages>(); // save the message to our container messages->items.push_back({ a_context.category, a_context.message }); if (a_context.category == fcf::NTest::LMC_ROOT_START){ // If this is the main output stream, output the test start message if (messages->defaultStream) { a_context.message = "Testing has started. Please wait for it to complete.\n\n"; // Reset the system message flag so that data // in a_context.message is sent to the output stream a_context.system = false; } } else if (a_context.category == fcf::NTest::LMC_ROOT_END){ // Process the message about the completion of all tests. // Generate JSON with information about all messages a_context.message = toJson(*messages); // Reset the system message flag so that data // in a_context.message is sent to the output stream a_context.system = false; // Clear history upon completion of testing messages->items.clear(); } else { // For all other messages, we set // the system flag to true so that data is not output a_context.system = true; } }; // Register the format logger.appendFormat(format); // Set the target to use this format fcf::NTest::Logger::OutputTarget target; target.name = "default"; target.stream = &std::cout; target.format = "my-json"; // Set custom data for our formatter in the current output stream. // Since data will be set, the default factory will not be called. target.formatData["my-json"] = fcf::NTest::SharedPtrAny::make<Messages>( Messages{true, {}} ); logger.targets({target}); bool error = false; fcf::NTest::cmdRun(a_argc, a_argv, fcf::NTest::CRM_RUN, &error); return error ? 1 : 0; }

Output:

Testing has started. Please wait for it to complete. [ { "code": 2147549185, "message": ""}, { "code": 2147614721, "message": ""}, { "code": 131074, "message": "Performing the test: \"FormatDataDemo\" -> \"Logger\" -> \"AccumulatorTest\" ...\n"}, { "code": 524288, "message": " > First log\n"}, { "code": 524288, "message": " > Second log\n"}, { "code": 262145, "message": " \u001b[1;32m[SUCCESS]\u001b[0m Test completed successfully (0.000`015`739 sec)\n"}, { "code": 2147614723, "message": ""}, { "code": 65544, "message": "\n"}, { "code": 65539, "message": "\u001b[1;32m[SUCCESS]\u001b[0m All tests were completed.\n"}, { "code": 65541, "message": "Tests: 1 passed, 0 failed, 0 skipped, 1 total\n"}, { "code": 65542, "message": "Duration: 0.000`015`739 sec\n"}, { "code": 2147549186, "message": ""} ]

If you run the application with file output:

app --test-file-my-json "output.json"

The application will create the file output.json

[ { "code": 2147549185, "message": ""}, { "code": 2147614721, "message": ""}, { "code": 131074, "message": "Performing the test: \"FormatDataDemo\" -> \"Logger\" -> \"AccumulatorTest\" ...\n"}, { "code": 524288, "message": " > First log\n"}, { "code": 524288, "message": " > Second log\n"}, { "code": 262145, "message": " \u001b[1;32m[SUCCESS]\u001b[0m Test completed successfully (0.000`015`739 sec)\n"}, { "code": 2147614723, "message": ""}, { "code": 65544, "message": "\n"}, { "code": 65539, "message": "\u001b[1;32m[SUCCESS]\u001b[0m All tests were completed.\n"}, { "code": 65541, "message": "Tests: 1 passed, 0 failed, 0 skipped, 1 total\n"}, { "code": 65542, "message": "Duration: 0.000`015`739 sec\n"}, { "code": 2147549186, "message": ""} ]

Note that the greeting is absent in this file, because the data for the formatter is created by the fcf::NTest::Logger::FormatSettings::dataFactory factory.