1# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3#
4# This file is part of logilab-common.
5#
6# logilab-common is free software: you can redistribute it and/or modify it under
7# the terms of the GNU Lesser General Public License as published by the Free
8# Software Foundation, either version 2.1 of the License, or (at your option) any
9# later version.
10#
11# logilab-common is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
14# details.
15#
16# You should have received a copy of the GNU Lesser General Public License along
17# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
18"""
19Unittests for table management
20"""
21
22
23import sys
24
25from logilab.common.compat import StringIO
26from logilab.common.testlib import TestCase, unittest_main
27from logilab.common.table import (
28    Table,
29    TableStyleSheet,
30    DocbookTableWriter,
31    DocbookRenderer,
32    TableStyle,
33    TableWriter,
34    TableCellRenderer,
35)
36
37
38class TableTC(TestCase):
39    """Table TestCase class"""
40
41    def setUp(self):
42        """Creates a default table"""
43        # from logilab.common import table
44        # reload(table)
45        self.table = Table()
46        self.table.create_rows(["row1", "row2", "row3"])
47        self.table.create_columns(["col1", "col2"])
48
49    def test_valeur_scalaire(self):
50        tab = Table()
51        tab.create_columns(["col1"])
52        tab.append_row([1])
53        self.assertEqual(tab, [[1]])
54        tab.append_row([2])
55        self.assertEqual(tab[0, 0], 1)
56        self.assertEqual(tab[1, 0], 2)
57
58    def test_valeur_ligne(self):
59        tab = Table()
60        tab.create_columns(["col1", "col2"])
61        tab.append_row([1, 2])
62        self.assertEqual(tab, [[1, 2]])
63
64    def test_valeur_colonne(self):
65        tab = Table()
66        tab.create_columns(["col1"])
67        tab.append_row([1])
68        tab.append_row([2])
69        self.assertEqual(tab, [[1], [2]])
70        self.assertEqual(tab[:, 0], [1, 2])
71
72    def test_indexation(self):
73        """we should be able to use [] to access rows"""
74        self.assertEqual(self.table[0], self.table.data[0])
75        self.assertEqual(self.table[1], self.table.data[1])
76
77    def test_iterable(self):
78        """test iter(table)"""
79        it = iter(self.table)
80        self.assertEqual(next(it), self.table.data[0])
81        self.assertEqual(next(it), self.table.data[1])
82
83    def test_get_rows(self):
84        """tests Table.get_rows()"""
85        self.assertEqual(self.table, [[0, 0], [0, 0], [0, 0]])
86        self.assertEqual(self.table[:], [[0, 0], [0, 0], [0, 0]])
87        self.table.insert_column(1, range(3), "supp")
88        self.assertEqual(self.table, [[0, 0, 0], [0, 1, 0], [0, 2, 0]])
89        self.assertEqual(self.table[:], [[0, 0, 0], [0, 1, 0], [0, 2, 0]])
90
91    def test_get_cells(self):
92        self.table.insert_column(1, range(3), "supp")
93        self.assertEqual(self.table[0, 1], 0)
94        self.assertEqual(self.table[1, 1], 1)
95        self.assertEqual(self.table[2, 1], 2)
96        self.assertEqual(self.table["row1", "supp"], 0)
97        self.assertEqual(self.table["row2", "supp"], 1)
98        self.assertEqual(self.table["row3", "supp"], 2)
99        self.assertRaises(KeyError, self.table.__getitem__, ("row1", "foo"))
100        self.assertRaises(KeyError, self.table.__getitem__, ("foo", "bar"))
101
102    def test_shape(self):
103        """tests table shape"""
104        self.assertEqual(self.table.shape, (3, 2))
105        self.table.insert_column(1, range(3), "supp")
106        self.assertEqual(self.table.shape, (3, 3))
107
108    def test_set_column(self):
109        """Tests that table.set_column() works fine."""
110        self.table.set_column(0, range(3))
111        self.assertEqual(self.table[0, 0], 0)
112        self.assertEqual(self.table[1, 0], 1)
113        self.assertEqual(self.table[2, 0], 2)
114
115    def test_set_column_by_id(self):
116        """Tests that table.set_column_by_id() works fine."""
117        self.table.set_column_by_id("col1", range(3))
118        self.assertEqual(self.table[0, 0], 0)
119        self.assertEqual(self.table[1, 0], 1)
120        self.assertEqual(self.table[2, 0], 2)
121        self.assertRaises(KeyError, self.table.set_column_by_id, "col123", range(3))
122
123    def test_cells_ids(self):
124        """tests that we can access cells by giving row/col ids"""
125        self.assertRaises(KeyError, self.table.set_cell_by_ids, "row12", "col1", 12)
126        self.assertRaises(KeyError, self.table.set_cell_by_ids, "row1", "col12", 12)
127        self.assertEqual(self.table[0, 0], 0)
128        self.table.set_cell_by_ids("row1", "col1", "DATA")
129        self.assertEqual(self.table[0, 0], "DATA")
130        self.assertRaises(KeyError, self.table.set_row_by_id, "row12", [])
131        self.table.set_row_by_id("row1", ["1.0", "1.1"])
132        self.assertEqual(self.table[0, 0], "1.0")
133
134    def test_insert_row(self):
135        """tests a row insertion"""
136        tmp_data = ["tmp1", "tmp2"]
137        self.table.insert_row(1, tmp_data, "tmprow")
138        self.assertEqual(self.table[1], tmp_data)
139        self.assertEqual(self.table["tmprow"], tmp_data)
140        self.table.delete_row_by_id("tmprow")
141        self.assertRaises(KeyError, self.table.delete_row_by_id, "tmprow")
142        self.assertEqual(self.table[1], [0, 0])
143        self.assertRaises(KeyError, self.table.__getitem__, "tmprow")
144
145    def test_get_column(self):
146        """Tests that table.get_column() works fine."""
147        self.table.set_cell(0, 1, 12)
148        self.table.set_cell(2, 1, 13)
149        self.assertEqual(self.table[:, 1], [12, 0, 13])
150        self.assertEqual(self.table[:, "col2"], [12, 0, 13])
151
152    def test_get_columns(self):
153        """Tests if table.get_columns() works fine."""
154        self.table.set_cell(0, 1, 12)
155        self.table.set_cell(2, 1, 13)
156        self.assertEqual(self.table.get_columns(), [[0, 0, 0], [12, 0, 13]])
157
158    def test_insert_column(self):
159        """Tests that table.insert_column() works fine."""
160        self.table.insert_column(1, range(3), "inserted_column")
161        self.assertEqual(self.table[:, 1], [0, 1, 2])
162        self.assertEqual(self.table.col_names, ["col1", "inserted_column", "col2"])
163
164    def test_delete_column(self):
165        """Tests that table.delete_column() works fine."""
166        self.table.delete_column(1)
167        self.assertEqual(self.table.col_names, ["col1"])
168        self.assertEqual(self.table[:, 0], [0, 0, 0])
169        self.assertRaises(KeyError, self.table.delete_column_by_id, "col2")
170        self.table.delete_column_by_id("col1")
171        self.assertEqual(self.table.col_names, [])
172
173    def test_transpose(self):
174        """Tests that table.transpose() works fine."""
175        self.table.append_column(range(5, 8), "col3")
176        ttable = self.table.transpose()
177        self.assertEqual(ttable.row_names, ["col1", "col2", "col3"])
178        self.assertEqual(ttable.col_names, ["row1", "row2", "row3"])
179        self.assertEqual(ttable.data, [[0, 0, 0], [0, 0, 0], [5, 6, 7]])
180
181    def test_sort_table(self):
182        """Tests the table sort by column"""
183        self.table.set_column(0, [3, 1, 2])
184        self.table.set_column(1, [1, 2, 3])
185        self.table.sort_by_column_index(0)
186        self.assertEqual(self.table.row_names, ["row2", "row3", "row1"])
187        self.assertEqual(self.table.data, [[1, 2], [2, 3], [3, 1]])
188        self.table.sort_by_column_index(1, "desc")
189        self.assertEqual(self.table.row_names, ["row3", "row2", "row1"])
190        self.assertEqual(self.table.data, [[2, 3], [1, 2], [3, 1]])
191
192    def test_sort_by_id(self):
193        """tests sort_by_column_id()"""
194        self.table.set_column_by_id("col1", [3, 1, 2])
195        self.table.set_column_by_id("col2", [1, 2, 3])
196        self.table.sort_by_column_id("col1")
197        self.assertRaises(KeyError, self.table.sort_by_column_id, "col123")
198        self.assertEqual(self.table.row_names, ["row2", "row3", "row1"])
199        self.assertEqual(self.table.data, [[1, 2], [2, 3], [3, 1]])
200        self.table.sort_by_column_id("col2", "desc")
201        self.assertEqual(self.table.row_names, ["row3", "row2", "row1"])
202        self.assertEqual(self.table.data, [[2, 3], [1, 2], [3, 1]])
203
204    def test_pprint(self):
205        """only tests pprint doesn't raise an exception"""
206        self.table.pprint()
207        str(self.table)
208
209
210class GroupByTC(TestCase):
211    """specific test suite for groupby()"""
212
213    def setUp(self):
214        t = Table()
215        t.create_columns(["date", "res", "task", "usage"])
216        t.append_row(["date1", "ing1", "task1", 0.3])
217        t.append_row(["date1", "ing2", "task2", 0.3])
218        t.append_row(["date2", "ing3", "task3", 0.3])
219        t.append_row(["date3", "ing4", "task2", 0.3])
220        t.append_row(["date1", "ing1", "task3", 0.3])
221        t.append_row(["date3", "ing1", "task3", 0.3])
222        self.table = t
223
224    def test_single_groupby(self):
225        """tests groupby() on several columns"""
226        grouped = self.table.groupby("date")
227        self.assertEqual(len(grouped), 3)
228        self.assertEqual(len(grouped["date1"]), 3)
229        self.assertEqual(len(grouped["date2"]), 1)
230        self.assertEqual(len(grouped["date3"]), 2)
231        self.assertEqual(
232            grouped["date1"],
233            [
234                ("date1", "ing1", "task1", 0.3),
235                ("date1", "ing2", "task2", 0.3),
236                ("date1", "ing1", "task3", 0.3),
237            ],
238        )
239        self.assertEqual(grouped["date2"], [("date2", "ing3", "task3", 0.3)])
240        self.assertEqual(
241            grouped["date3"],
242            [
243                ("date3", "ing4", "task2", 0.3),
244                ("date3", "ing1", "task3", 0.3),
245            ],
246        )
247
248    def test_multiple_groupby(self):
249        """tests groupby() on several columns"""
250        grouped = self.table.groupby("date", "task")
251        self.assertEqual(len(grouped), 3)
252        self.assertEqual(len(grouped["date1"]), 3)
253        self.assertEqual(len(grouped["date2"]), 1)
254        self.assertEqual(len(grouped["date3"]), 2)
255        self.assertEqual(grouped["date1"]["task1"], [("date1", "ing1", "task1", 0.3)])
256        self.assertEqual(grouped["date2"]["task3"], [("date2", "ing3", "task3", 0.3)])
257        self.assertEqual(grouped["date3"]["task2"], [("date3", "ing4", "task2", 0.3)])
258        date3 = grouped["date3"]
259        self.assertRaises(KeyError, date3.__getitem__, "task1")
260
261    def test_select(self):
262        """tests Table.select() method"""
263        rows = self.table.select("date", "date1")
264        self.assertEqual(
265            rows,
266            [
267                ("date1", "ing1", "task1", 0.3),
268                ("date1", "ing2", "task2", 0.3),
269                ("date1", "ing1", "task3", 0.3),
270            ],
271        )
272
273
274class TableStyleSheetTC(TestCase):
275    """The Stylesheet test case"""
276
277    def setUp(self):
278        """Builds a simple table to test the stylesheet"""
279        self.table = Table()
280        self.table.create_row("row1")
281        self.table.create_columns(["a", "b", "c"])
282        self.stylesheet = TableStyleSheet()
283        # We don't want anything to be printed
284        self.stdout_backup = sys.stdout
285        sys.stdout = StringIO()
286
287    def tearDown(self):
288        sys.stdout = self.stdout_backup
289
290    def test_add_rule(self):
291        """Tests that the regex pattern works as expected."""
292        rule = "0_2 = sqrt(0_0**2 + 0_1**2)"
293        self.stylesheet.add_rule(rule)
294        self.table.set_row(0, [3, 4, 0])
295        self.table.apply_stylesheet(self.stylesheet)
296        self.assertEqual(self.table[0], [3, 4, 5])
297        self.assertEqual(len(self.stylesheet.rules), 1)
298        self.stylesheet.add_rule("some bad rule with bad syntax")
299        self.assertEqual(len(self.stylesheet.rules), 1, "Ill-formed rule mustn't be added")
300        self.assertEqual(len(self.stylesheet.instructions), 1, "Ill-formed rule mustn't be added")
301
302    def test_stylesheet_init(self):
303        """tests Stylesheet.__init__"""
304        rule = "0_2 = 1"
305        sheet = TableStyleSheet([rule, "bad rule"])
306        self.assertEqual(len(sheet.rules), 1, "Ill-formed rule mustn't be added")
307        self.assertEqual(len(sheet.instructions), 1, "Ill-formed rule mustn't be added")
308
309    def test_rowavg_rule(self):
310        """Tests that add_rowavg_rule works as expected"""
311        self.table.set_row(0, [10, 20, 0])
312        self.stylesheet.add_rowavg_rule((0, 2), 0, 0, 1)
313        self.table.apply_stylesheet(self.stylesheet)
314        val = self.table[0, 2]
315        self.assertEqual(int(val), 15)
316
317    def test_rowsum_rule(self):
318        """Tests that add_rowsum_rule works as expected"""
319        self.table.set_row(0, [10, 20, 0])
320        self.stylesheet.add_rowsum_rule((0, 2), 0, 0, 1)
321        self.table.apply_stylesheet(self.stylesheet)
322        val = self.table[0, 2]
323        self.assertEqual(val, 30)
324
325    def test_colavg_rule(self):
326        """Tests that add_colavg_rule works as expected"""
327        self.table.set_row(0, [10, 20, 0])
328        self.table.append_row([12, 8, 3], "row2")
329        self.table.create_row("row3")
330        self.stylesheet.add_colavg_rule((2, 0), 0, 0, 1)
331        self.table.apply_stylesheet(self.stylesheet)
332        val = self.table[2, 0]
333        self.assertEqual(int(val), 11)
334
335    def test_colsum_rule(self):
336        """Tests that add_colsum_rule works as expected"""
337        self.table.set_row(0, [10, 20, 0])
338        self.table.append_row([12, 8, 3], "row2")
339        self.table.create_row("row3")
340        self.stylesheet.add_colsum_rule((2, 0), 0, 0, 1)
341        self.table.apply_stylesheet(self.stylesheet)
342        val = self.table[2, 0]
343        self.assertEqual(val, 22)
344
345
346class TableStyleTC(TestCase):
347    """Test suite for TableSuite"""
348
349    def setUp(self):
350        self.table = Table()
351        self.table.create_rows(["row1", "row2", "row3"])
352        self.table.create_columns(["col1", "col2"])
353        self.style = TableStyle(self.table)
354        self._tested_attrs = (("size", "1*"), ("alignment", "right"), ("unit", ""))
355
356    def test_getset(self):
357        """tests style's get and set methods"""
358        for attrname, default_value in self._tested_attrs:
359            getter = getattr(self.style, "get_%s" % attrname)
360            setter = getattr(self.style, "set_%s" % attrname)
361            self.assertRaises(KeyError, getter, "badcol")
362            self.assertEqual(getter("col1"), default_value)
363            setter("FOO", "col1")
364            self.assertEqual(getter("col1"), "FOO")
365
366    def test_getset_index(self):
367        """tests style's get and set by index methods"""
368        for attrname, default_value in self._tested_attrs:
369            getter = getattr(self.style, "get_%s" % attrname)
370            getattr(self.style, "set_%s" % attrname)
371            igetter = getattr(self.style, "get_%s_by_index" % attrname)
372            isetter = getattr(self.style, "set_%s_by_index" % attrname)
373            self.assertEqual(getter("__row_column__"), default_value)
374            isetter("FOO", 0)
375            self.assertEqual(getter("__row_column__"), "FOO")
376            self.assertEqual(igetter(0), "FOO")
377            self.assertEqual(getter("col1"), default_value)
378            isetter("FOO", 1)
379            self.assertEqual(getter("col1"), "FOO")
380            self.assertEqual(igetter(1), "FOO")
381
382
383class RendererTC(TestCase):
384    """Test suite for DocbookRenderer"""
385
386    def setUp(self):
387        self.renderer = DocbookRenderer(alignment=True)
388        self.table = Table()
389        self.table.create_rows(["row1", "row2", "row3"])
390        self.table.create_columns(["col1", "col2"])
391        self.style = TableStyle(self.table)
392        self.base_renderer = TableCellRenderer()
393
394    def test_cell_content(self):
395        """test how alignment is rendered"""
396        entry_xml = self.renderer._render_cell_content("data", self.style, 1)
397        self.assertEqual(entry_xml, "<entry align='right'>data</entry>\n")
398        self.style.set_alignment_by_index("left", 1)
399        entry_xml = self.renderer._render_cell_content("data", self.style, 1)
400        self.assertEqual(entry_xml, "<entry align='left'>data</entry>\n")
401
402    def test_default_content_rendering(self):
403        """tests that default rendering just prints the cell's content"""
404        rendered_cell = self.base_renderer._render_cell_content("data", self.style, 1)
405        self.assertEqual(rendered_cell, "data")
406
407    def test_replacement_char(self):
408        """tests that 0 is replaced when asked for"""
409        cell_content = self.base_renderer._make_cell_content(0, self.style, 1)
410        self.assertEqual(cell_content, 0)
411        self.base_renderer.properties["skip_zero"] = "---"
412        cell_content = self.base_renderer._make_cell_content(0, self.style, 1)
413        self.assertEqual(cell_content, "---")
414
415    def test_unit(self):
416        """tests if units are added"""
417        self.base_renderer.properties["units"] = True
418        self.style.set_unit_by_index("EUR", 1)
419        cell_content = self.base_renderer._make_cell_content(12, self.style, 1)
420        self.assertEqual(cell_content, "12 EUR")
421
422
423class DocbookTableWriterTC(TestCase):
424    """TestCase for table's writer"""
425
426    def setUp(self):
427        self.stream = StringIO()
428        self.table = Table()
429        self.table.create_rows(["row1", "row2", "row3"])
430        self.table.create_columns(["col1", "col2"])
431        self.writer = DocbookTableWriter(self.stream, self.table, None)
432        self.writer.set_renderer(DocbookRenderer())
433
434    def test_write_table(self):
435        """make sure write_table() doesn't raise any exception"""
436        self.writer.write_table()
437
438    def test_abstract_writer(self):
439        """tests that Abstract Writers can't be used !"""
440        writer = TableWriter(self.stream, self.table, None)
441        self.assertRaises(NotImplementedError, writer.write_table)
442
443
444if __name__ == "__main__":
445    unittest_main()
446