1// Copyright 2012 Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "utils/text/table.hpp"
30
31#include <algorithm>
32
33#include <atf-c++.hpp>
34
35#include "utils/text/operations.ipp"
36
37namespace text = utils::text;
38
39
40/// Performs a check on text::table_formatter.
41///
42/// This is provided for test simplicity's sake.  Having to match the result of
43/// the formatting on a line by line basis would result in too verbose tests
44/// (maybe not with C++11, but not using this yet).
45///
46/// Because of the flattening of the formatted table into a string, we risk
47/// misdetecting problems when the algorithm bundles newlines into the lines of
48/// a table.  This should not happen, and not accounting for this little detail
49/// makes testing so much easier.
50///
51/// \param expected Textual representation of the table, as a collection of
52///     lines separated by newline characters.
53/// \param formatter The formatter to use.
54/// \param table The table to format.
55static void
56table_formatter_check(const std::string& expected,
57                      const text::table_formatter& formatter,
58                      const text::table& table)
59{
60    ATF_REQUIRE_EQ(expected, text::join(formatter.format(table), "\n") + "\n");
61}
62
63
64
65ATF_TEST_CASE_WITHOUT_HEAD(table__ncolumns);
66ATF_TEST_CASE_BODY(table__ncolumns)
67{
68    ATF_REQUIRE_EQ(5, text::table(5).ncolumns());
69    ATF_REQUIRE_EQ(10, text::table(10).ncolumns());
70}
71
72
73ATF_TEST_CASE_WITHOUT_HEAD(table__column_width);
74ATF_TEST_CASE_BODY(table__column_width)
75{
76    text::table_row row1;
77    row1.push_back("1234");
78    row1.push_back("123456");
79    text::table_row row2;
80    row2.push_back("12");
81    row2.push_back("12345678");
82
83    text::table table(2);
84    table.add_row(row1);
85    table.add_row(row2);
86
87    ATF_REQUIRE_EQ(4, table.column_width(0));
88    ATF_REQUIRE_EQ(8, table.column_width(1));
89}
90
91
92ATF_TEST_CASE_WITHOUT_HEAD(table__column_widths);
93ATF_TEST_CASE_BODY(table__column_widths)
94{
95    text::table_row row1;
96    row1.push_back("1234");
97    row1.push_back("123456");
98    text::table_row row2;
99    row2.push_back("12");
100    row2.push_back("12345678");
101
102    text::table table(2);
103    table.add_row(row1);
104    table.add_row(row2);
105
106    ATF_REQUIRE_EQ(4, table.column_widths()[0]);
107    ATF_REQUIRE_EQ(8, table.column_widths()[1]);
108}
109
110
111ATF_TEST_CASE_WITHOUT_HEAD(table__empty);
112ATF_TEST_CASE_BODY(table__empty)
113{
114    text::table table(2);
115    ATF_REQUIRE(table.empty());
116    table.add_row(text::table_row(2));
117    ATF_REQUIRE(!table.empty());
118}
119
120
121ATF_TEST_CASE_WITHOUT_HEAD(table__iterate);
122ATF_TEST_CASE_BODY(table__iterate)
123{
124    text::table_row row1;
125    row1.push_back("foo");
126    text::table_row row2;
127    row2.push_back("bar");
128
129    text::table table(1);
130    table.add_row(row1);
131    table.add_row(row2);
132
133    text::table::const_iterator iter = table.begin();
134    ATF_REQUIRE(iter != table.end());
135    ATF_REQUIRE(row1 == *iter);
136    ++iter;
137    ATF_REQUIRE(iter != table.end());
138    ATF_REQUIRE(row2 == *iter);
139    ++iter;
140    ATF_REQUIRE(iter == table.end());
141}
142
143
144ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__empty);
145ATF_TEST_CASE_BODY(table_formatter__empty)
146{
147    ATF_REQUIRE(text::table_formatter().set_separator(" ")
148                .format(text::table(1)).empty());
149    ATF_REQUIRE(text::table_formatter().set_separator(" ")
150                .format(text::table(10)).empty());
151}
152
153
154ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__defaults);
155ATF_TEST_CASE_BODY(table_formatter__defaults)
156{
157    text::table table(3);
158    {
159        text::table_row row;
160        row.push_back("First");
161        row.push_back("Second");
162        row.push_back("Third");
163        table.add_row(row);
164    }
165    {
166        text::table_row row;
167        row.push_back("Fourth with some text");
168        row.push_back("Fifth with some more text");
169        row.push_back("Sixth foo");
170        table.add_row(row);
171    }
172
173    table_formatter_check(
174        "First                Second                   Third\n"
175        "Fourth with some textFifth with some more textSixth foo\n",
176        text::table_formatter(), table);
177}
178
179
180ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__one_column__no_max_width);
181ATF_TEST_CASE_BODY(table_formatter__one_column__no_max_width)
182{
183    text::table table(1);
184    {
185        text::table_row row;
186        row.push_back("First row with some words");
187        table.add_row(row);
188    }
189    {
190        text::table_row row;
191        row.push_back("Second row with some words");
192        table.add_row(row);
193    }
194
195    table_formatter_check(
196        "First row with some words\n"
197        "Second row with some words\n",
198        text::table_formatter().set_separator(" | "), table);
199}
200
201
202ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__one_column__explicit_width);
203ATF_TEST_CASE_BODY(table_formatter__one_column__explicit_width)
204{
205    text::table table(1);
206    {
207        text::table_row row;
208        row.push_back("First row with some words");
209        table.add_row(row);
210    }
211    {
212        text::table_row row;
213        row.push_back("Second row with some words");
214        table.add_row(row);
215    }
216
217    table_formatter_check(
218        "First row with some words\n"
219        "Second row with some words\n",
220        text::table_formatter().set_separator(" | ").set_column_width(0, 1024),
221        table);
222}
223
224
225ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__one_column__max_width);
226ATF_TEST_CASE_BODY(table_formatter__one_column__max_width)
227{
228    text::table table(1);
229    {
230        text::table_row row;
231        row.push_back("First row with some words");
232        table.add_row(row);
233    }
234    {
235        text::table_row row;
236        row.push_back("Second row with some words");
237        table.add_row(row);
238    }
239
240    table_formatter_check(
241        "First row\nwith some\nwords\n"
242        "Second row\nwith some\nwords\n",
243        text::table_formatter().set_separator(" | ").set_table_width(11),
244        table);
245}
246
247
248ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__many_columns__no_max_width);
249ATF_TEST_CASE_BODY(table_formatter__many_columns__no_max_width)
250{
251    text::table table(3);
252    {
253        text::table_row row;
254        row.push_back("First");
255        row.push_back("Second");
256        row.push_back("Third");
257        table.add_row(row);
258    }
259    {
260        text::table_row row;
261        row.push_back("Fourth with some text");
262        row.push_back("Fifth with some more text");
263        row.push_back("Sixth foo");
264        table.add_row(row);
265    }
266
267    table_formatter_check(
268        "First                 | Second                    | Third\n"
269        "Fourth with some text | Fifth with some more text | Sixth foo\n",
270        text::table_formatter().set_separator(" | "), table);
271}
272
273
274ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__many_columns__explicit_width);
275ATF_TEST_CASE_BODY(table_formatter__many_columns__explicit_width)
276{
277    text::table table(3);
278    {
279        text::table_row row;
280        row.push_back("First");
281        row.push_back("Second");
282        row.push_back("Third");
283        table.add_row(row);
284    }
285    {
286        text::table_row row;
287        row.push_back("Fourth with some text");
288        row.push_back("Fifth with some more text");
289        row.push_back("Sixth foo");
290        table.add_row(row);
291    }
292
293    table_formatter_check(
294        "First                   | Second                       | Third\n"
295        "Fourth with some text   | Fifth with some more text    | Sixth foo\n",
296        text::table_formatter().set_separator(" | ").set_column_width(0, 23)
297        .set_column_width(1, 28), table);
298}
299
300
301ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__many_columns__max_width);
302ATF_TEST_CASE_BODY(table_formatter__many_columns__max_width)
303{
304    text::table table(3);
305    {
306        text::table_row row;
307        row.push_back("First");
308        row.push_back("Second");
309        row.push_back("Third");
310        table.add_row(row);
311    }
312    {
313        text::table_row row;
314        row.push_back("Fourth with some text");
315        row.push_back("Fifth with some more text");
316        row.push_back("Sixth foo");
317        table.add_row(row);
318    }
319
320    table_formatter_check(
321        "First                 | Second     | Third\n"
322        "Fourth with some text | Fifth with | Sixth foo\n"
323        "                      | some more  | \n"
324        "                      | text       | \n",
325        text::table_formatter().set_separator(" | ").set_table_width(46)
326        .set_column_width(1, text::table_formatter::width_refill)
327        .set_column_width(0, text::table_formatter::width_auto), table);
328
329    table_formatter_check(
330        "First                   | Second     | Third\n"
331        "Fourth with some text   | Fifth with | Sixth foo\n"
332        "                        | some more  | \n"
333        "                        | text       | \n",
334        text::table_formatter().set_separator(" | ").set_table_width(48)
335        .set_column_width(1, text::table_formatter::width_refill)
336        .set_column_width(0, 23), table);
337}
338
339
340ATF_TEST_CASE_WITHOUT_HEAD(table_formatter__use_case__cli_help);
341ATF_TEST_CASE_BODY(table_formatter__use_case__cli_help)
342{
343    text::table options_table(2);
344    {
345        text::table_row row;
346        row.push_back("-a a_value");
347        row.push_back("This is the description of the first flag");
348        options_table.add_row(row);
349    }
350    {
351        text::table_row row;
352        row.push_back("-b");
353        row.push_back("And this is the text for the second flag");
354        options_table.add_row(row);
355    }
356
357    text::table commands_table(2);
358    {
359        text::table_row row;
360        row.push_back("first");
361        row.push_back("This is the first command");
362        commands_table.add_row(row);
363    }
364    {
365        text::table_row row;
366        row.push_back("second");
367        row.push_back("And this is the second command");
368        commands_table.add_row(row);
369    }
370
371    const text::widths_vector::value_type first_width =
372        std::max(options_table.column_width(0), commands_table.column_width(0));
373
374    table_formatter_check(
375        "-a a_value  This is the description\n"
376        "            of the first flag\n"
377        "-b          And this is the text for\n"
378        "            the second flag\n",
379        text::table_formatter().set_separator("  ").set_table_width(36)
380        .set_column_width(0, first_width)
381        .set_column_width(1, text::table_formatter::width_refill),
382        options_table);
383
384    table_formatter_check(
385        "first       This is the first\n"
386        "            command\n"
387        "second      And this is the second\n"
388        "            command\n",
389        text::table_formatter().set_separator("  ").set_table_width(36)
390        .set_column_width(0, first_width)
391        .set_column_width(1, text::table_formatter::width_refill),
392        commands_table);
393}
394
395
396ATF_INIT_TEST_CASES(tcs)
397{
398    ATF_ADD_TEST_CASE(tcs, table__ncolumns);
399    ATF_ADD_TEST_CASE(tcs, table__column_width);
400    ATF_ADD_TEST_CASE(tcs, table__column_widths);
401    ATF_ADD_TEST_CASE(tcs, table__empty);
402    ATF_ADD_TEST_CASE(tcs, table__iterate);
403
404    ATF_ADD_TEST_CASE(tcs, table_formatter__empty);
405    ATF_ADD_TEST_CASE(tcs, table_formatter__defaults);
406    ATF_ADD_TEST_CASE(tcs, table_formatter__one_column__no_max_width);
407    ATF_ADD_TEST_CASE(tcs, table_formatter__one_column__explicit_width);
408    ATF_ADD_TEST_CASE(tcs, table_formatter__one_column__max_width);
409    ATF_ADD_TEST_CASE(tcs, table_formatter__many_columns__no_max_width);
410    ATF_ADD_TEST_CASE(tcs, table_formatter__many_columns__explicit_width);
411    ATF_ADD_TEST_CASE(tcs, table_formatter__many_columns__max_width);
412    ATF_ADD_TEST_CASE(tcs, table_formatter__use_case__cli_help);
413}
414