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