Skip to content

Commit

Permalink
utils: fix a bug in table_printer and add several unit tests (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
acelyc111 authored and Wu Tao committed Jul 5, 2019
1 parent 36fd568 commit 83959da
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 15 deletions.
14 changes: 7 additions & 7 deletions include/dsn/utility/output_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ class table_printer
enum class data_mode
{
kUninitialized = 0,
KSingleColumn = 1,
KMultiColumns = 2
kSingleColumn = 1,
kMultiColumns = 2
};

public:
Expand All @@ -112,28 +112,28 @@ class table_printer
{
}

// KMultiColumns mode.
// kMultiColumns mode.
void add_title(const std::string &title, alignment align = alignment::kLeft);
void add_column(const std::string &col_name, alignment align = alignment::kLeft);
template <typename T>
void add_row(const T &row_name)
{
check_mode(data_mode::KMultiColumns);
check_mode(data_mode::kMultiColumns);
_matrix_data.emplace_back(std::vector<std::string>());
append_data(row_name);
}
template <typename T>
void append_data(const T &data)
{
check_mode(data_mode::KMultiColumns);
check_mode(data_mode::kMultiColumns);
append_string_data(to_string(data));
}

// KSingleColumn mode.
// kSingleColumn mode.
template <typename T>
void add_row_name_and_data(const std::string &row_name, const T &data)
{
check_mode(data_mode::KSingleColumn);
check_mode(data_mode::kSingleColumn);
add_row_name_and_string_data(row_name, to_string(data));
}

Expand Down
23 changes: 15 additions & 8 deletions src/core/core/output_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ namespace utils {
template <typename Writer>
void json_encode(Writer &writer, const table_printer &tp)
{
if (tp._matrix_data.empty()) {
return;
}

dsn::json::json_encode(writer, tp._name); // table_printer name
if (tp._mode == table_printer::data_mode::KMultiColumns) {
if (tp._mode == table_printer::data_mode::kMultiColumns) {
writer.StartObject();
// The 1st row elements are column names, skip it.
for (size_t row = 1; row < tp._matrix_data.size(); ++row) {
Expand All @@ -48,7 +52,7 @@ void json_encode(Writer &writer, const table_printer &tp)
writer.EndObject();
}
writer.EndObject();
} else if (tp._mode == table_printer::data_mode::KSingleColumn) {
} else if (tp._mode == table_printer::data_mode::kSingleColumn) {
writer.StartObject();
for (size_t row = 0; row < tp._matrix_data.size(); ++row) {
dsn::json::json_encode(writer, tp._matrix_data[row][0]); // row name
Expand All @@ -62,7 +66,7 @@ void json_encode(Writer &writer, const table_printer &tp)

void table_printer::add_title(const std::string &title, alignment align)
{
check_mode(data_mode::KMultiColumns);
check_mode(data_mode::kMultiColumns);
dassert(_matrix_data.empty() && _max_col_width.empty(), "`add_title` must be called only once");
_max_col_width.push_back(title.length());
_align_left.push_back(align == alignment::kLeft);
Expand All @@ -71,9 +75,9 @@ void table_printer::add_title(const std::string &title, alignment align)

void table_printer::add_column(const std::string &col_name, alignment align)
{
check_mode(data_mode::KMultiColumns);
check_mode(data_mode::kMultiColumns);
dassert(_matrix_data.size() == 1, "`add_column` must be called before real data appendding");
_max_col_width.emplace_back(col_name.length());
_max_col_width.push_back(col_name.length());
_align_left.push_back(align == alignment::kLeft);
append_data(col_name);
}
Expand Down Expand Up @@ -118,15 +122,16 @@ void table_printer::output_in_tabular(std::ostream &out) const
}

std::string separator;
if (_mode == data_mode::KSingleColumn) {
if (_mode == data_mode::kSingleColumn) {
separator = ": ";
} else {
dassert(_mode == data_mode::KMultiColumns, "Unknown mode");
dassert(_mode == data_mode::kMultiColumns, "Unknown mode");
}

if (!_name.empty()) {
out << "[" << _name << "]" << std::endl;
}
int i = 0;
for (const auto &row : _matrix_data) {
for (size_t col = 0; col < row.size(); ++col) {
auto data = (col == 0 ? "" : separator) + row[col];
Expand All @@ -140,9 +145,11 @@ void table_printer::output_in_tabular(std::ostream &out) const
void table_printer::append_string_data(const std::string &data)
{
_matrix_data.rbegin()->emplace_back(data);
int last_index = _matrix_data.rbegin()->size() - 1;
dassert(last_index <= _max_col_width.size(), "column data exceed");

// update column max length
int &cur_len = _max_col_width[_matrix_data.rbegin()->size() - 1];
int &cur_len = _max_col_width[last_index];
if (cur_len < data.size()) {
cur_len = data.size();
}
Expand Down
191 changes: 191 additions & 0 deletions src/core/tests/output_utils_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Microsoft Corporation
*
* -=- Robust Distributed System Nucleus (rDSN) -=-
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "dsn/utility/output_utils.h"

#include <gtest/gtest.h>

#include <vector>
#include <string>

using std::vector;
using std::string;
using dsn::utils::table_printer;

namespace dsn {

const vector<string>
single_column_tp_output({"[tp1]\n"
"row1 : 1.23\n"
"row2 : 2345\n"
"row3 : 3456\n",
R"*("tp1":{"row1":"1.23","row2":"2345","row3":"3456"})*",
R"*( "tp1": {)*"
"\n"
R"*( "row1": "1.23",)*"
"\n"
R"*( "row2": "2345",)*"
"\n"
R"*( "row3": "3456")*"
"\n"
" }"});

const vector<string> multi_columns_tp_output(
{"[tp2]\n"
"multi_columns_test col0 col1 col2 \n"
"row0 data00 data01 data02 \n"
"row1 data10 data11 data12 \n"
"row2 data20 data21 data22 \n",
R"*("tp2":{"row0":{"multi_columns_test":"row0","col0":"data00","col1":"data01","col2":"data02"},"row1":{"multi_columns_test":"row1","col0":"data10","col1":"data11","col2":"data12"},"row2":{"multi_columns_test":"row2","col0":"data20","col1":"data21","col2":"data22"}})*",
R"*( "tp2": {)*"
"\n"
R"*( "row0": {)*"
"\n"
R"*( "multi_columns_test": "row0",)*"
"\n"
R"*( "col0": "data00",)*"
"\n"
R"*( "col1": "data01",)*"
"\n"
R"*( "col2": "data02")*"
"\n"
" },\n"
R"*( "row1": {)*"
"\n"
R"*( "multi_columns_test": "row1",)*"
"\n"
R"*( "col0": "data10",)*"
"\n"
R"*( "col1": "data11",)*"
"\n"
R"*( "col2": "data12")*"
"\n"
" },\n"
R"*( "row2": {)*"
"\n"
R"*( "multi_columns_test": "row2",)*"
"\n"
R"*( "col0": "data20",)*"
"\n"
R"*( "col1": "data21",)*"
"\n"
R"*( "col2": "data22")*"
"\n"
" }\n"
" }"});

utils::table_printer generate_single_column_tp()
{
utils::table_printer tp("tp1", 2, 2);
tp.add_row_name_and_data("row1", 1.234);
tp.add_row_name_and_data("row2", 2345);
tp.add_row_name_and_data("row3", "3456");
return tp;
}

utils::table_printer generate_multi_columns_tp()
{
int kColumnCount = 3;
int kRowCount = 3;
utils::table_printer tp("tp2", 2, 2);
tp.add_title("multi_columns_test");
for (int i = 0; i < kColumnCount; i++) {
tp.add_column("col" + std::to_string(i));
}
for (int i = 0; i < kRowCount; i++) {
tp.add_row("row" + std::to_string(i));
for (int j = 0; j < kColumnCount; j++) {
tp.append_data("data" + std::to_string(i) + std::to_string(j));
}
}
return tp;
}

template <typename P>
void check_output(const P &printer, const vector<string> &expect_output)
{
static vector<table_printer::output_format> output_formats(
{table_printer::output_format::kTabular,
table_printer::output_format::kJsonCompact,
table_printer::output_format::kJsonPretty});
ASSERT_EQ(expect_output.size(), output_formats.size());
for (int i = 0; i < output_formats.size(); i++) {
std::ostringstream out;
printer.output(out, output_formats[i]);
ASSERT_EQ(expect_output[i], out.str());
}
}

TEST(table_printer_test, empty_content_test)
{
utils::table_printer tp;
ASSERT_NO_FATAL_FAILURE(check_output(tp, {"", "{}\n", "{}\n"}));
}

TEST(table_printer_test, single_column_test)
{
utils::table_printer tp(generate_single_column_tp());
ASSERT_NO_FATAL_FAILURE(check_output(tp,
{single_column_tp_output[0],
"{" + single_column_tp_output[1] + "}\n",
"{\n" + single_column_tp_output[2] + "\n}\n"}));
}

TEST(table_printer_test, multi_columns_test)
{
utils::table_printer tp(generate_multi_columns_tp());
ASSERT_NO_FATAL_FAILURE(check_output(tp,
{multi_columns_tp_output[0],
"{" + multi_columns_tp_output[1] + "}\n",
"{\n" + multi_columns_tp_output[2] + "\n}\n"}));
}

TEST(multi_table_printer_test, empty_content_test)
{
utils::multi_table_printer mtp;
ASSERT_NO_FATAL_FAILURE(check_output(mtp, {"", "{}\n", "{}\n"}));
}

TEST(multi_table_printer_test, single_empty_sub_test)
{
utils::multi_table_printer mtp;
utils::table_printer tp;
mtp.add(std::move(tp));
ASSERT_NO_FATAL_FAILURE(check_output(mtp, {"\n", "{}\n", "{}\n"}));
}

TEST(multi_table_printer_test, multi_sub_test)
{
utils::multi_table_printer mtp;
mtp.add(generate_single_column_tp());
mtp.add(generate_multi_columns_tp());
ASSERT_NO_FATAL_FAILURE(check_output(
mtp,
{single_column_tp_output[0] + "\n" + multi_columns_tp_output[0] + "\n",
"{" + single_column_tp_output[1] + "," + multi_columns_tp_output[1] + "}\n",
"{\n" + single_column_tp_output[2] + ",\n" + multi_columns_tp_output[2] + "\n}\n"}));
}
} // namespace dsn

0 comments on commit 83959da

Please sign in to comment.