diff options
| author | Jannis Leidel <jannis@leidel.info> | 2019-10-18 15:57:13 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-10-18 15:57:13 +0200 |
| commit | f6bf14afd22d8e5b706670590cc95f29d4483434 (patch) | |
| tree | a65fbc8b6fd1112222beb6f66c31082ffe231460 /tests | |
| parent | f3d02aa3b088e2f9044fe5e4869e8c8cb91d2cdc (diff) | |
| download | tablib-f6bf14afd22d8e5b706670590cc95f29d4483434.tar.gz | |
Add project release config and cleanup project setup. (#398)
* Add project release config and use Travis build stages.
Refs #378.
* Restructure project to use src/ and tests/ directories.
* Fix testing.
* Remove eggs.
* More fixes.
- isort and flake8 config
- manifest template update
- tox ini extension
- docs build fixes
- docs content fixes
* Docs and license cleanup.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/requirements.txt | 10 | ||||
| -rwxr-xr-x | tests/test_tablib.py | 1105 |
2 files changed, 1115 insertions, 0 deletions
diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..cd43514 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,10 @@ +pytest +pytest-cov +backports.csv; python_version < '3.0' +MarkupPy +odfpy +openpyxl>=2.4.0 +pandas +pyyaml +xlrd +xlwt diff --git a/tests/test_tablib.py b/tests/test_tablib.py new file mode 100755 index 0000000..c872e81 --- /dev/null +++ b/tests/test_tablib.py @@ -0,0 +1,1105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Tests for Tablib.""" +from __future__ import unicode_literals + +import datetime +import doctest +import json +import sys +import unittest +from uuid import uuid4 + +from MarkupPy import markup +import tablib +from tablib.compat import unicode, is_py3 +from tablib.core import Row, detect_format +from tablib.formats import _csv as csv_module + + +class BaseTestCase(unittest.TestCase): + def setUp(self): + """Create simple data set with headers.""" + + global data, book + + data = tablib.Dataset() + book = tablib.Databook() + + self.headers = ('first_name', 'last_name', 'gpa') + self.john = ('John', 'Adams', 90) + self.george = ('George', 'Washington', 67) + self.tom = ('Thomas', 'Jefferson', 50) + + self.founders = tablib.Dataset(headers=self.headers, title='Founders') + self.founders.append(self.john) + self.founders.append(self.george) + self.founders.append(self.tom) + + +class TablibTestCase(BaseTestCase): + """Tablib test cases.""" + + def _test_export_data_in_all_formats(self, dataset, exclude=()): + all_formats = [ + 'json', 'yaml', 'csv', 'tsv', 'xls', 'xlsx', 'ods', 'html', 'jira', + 'latex', 'df', 'rst', + ] + for format_ in all_formats: + if format_ in exclude: + continue + dataset.export(format_) + + def test_empty_append(self): + """Verify append() correctly adds tuple with no headers.""" + new_row = (1, 2, 3) + data.append(new_row) + + # Verify width/data + self.assertTrue(data.width == len(new_row)) + self.assertTrue(data[0] == new_row) + + def test_empty_append_with_headers(self): + """Verify append() correctly detects mismatch of number of + headers and data. + """ + data.headers = ['first', 'second'] + new_row = (1, 2, 3, 4) + + self.assertRaises(tablib.InvalidDimensions, data.append, new_row) + + def test_set_headers_with_incorrect_dimension(self): + """Verify headers correctly detects mismatch of number of + headers and data. + """ + + data.append(self.john) + + def set_header_callable(): + data.headers = ['first_name'] + + self.assertRaises(tablib.InvalidDimensions, set_header_callable) + + def test_add_column(self): + """Verify adding column works with/without headers.""" + + data.append(['kenneth']) + data.append(['bessie']) + + new_col = ['reitz', 'monke'] + + data.append_col(new_col) + + self.assertEqual(data[0], ('kenneth', 'reitz')) + self.assertEqual(data.width, 2) + + # With Headers + data.headers = ('fname', 'lname') + new_col = [21, 22] + data.append_col(new_col, header='age') + + self.assertEqual(data['age'], new_col) + + def test_add_column_no_data_no_headers(self): + """Verify adding new column with no headers.""" + + new_col = ('reitz', 'monke') + + data.append_col(new_col) + + self.assertEqual(data[0], tuple([new_col[0]])) + self.assertEqual(data.width, 1) + self.assertEqual(data.height, len(new_col)) + + def test_add_column_with_header_ignored(self): + """Verify append_col() ignores the header if data.headers has + not previously been set + """ + + new_col = ('reitz', 'monke') + + data.append_col(new_col, header='first_name') + + self.assertEqual(data[0], tuple([new_col[0]])) + self.assertEqual(data.width, 1) + self.assertEqual(data.height, len(new_col)) + self.assertEqual(data.headers, None) + + def test_add_column_with_header_and_headers_only_exist(self): + """Verify append_col() with header correctly detects mismatch when + headers exist but there is no existing row data + """ + + data.headers = ['first_name'] + # no data + + new_col = ('allen') + + def append_col_callable(): + data.append_col(new_col, header='middle_name') + + self.assertRaises(tablib.InvalidDimensions, append_col_callable) + + def test_add_column_with_header_and_data_exists(self): + """Verify append_col() works when headers and rows exists""" + + data.headers = self.headers + data.append(self.john) + + new_col = [10] + + data.append_col(new_col, header='age') + + self.assertEqual(data.height, 1) + self.assertEqual(data.width, len(self.john) + 1) + self.assertEqual(data['age'], new_col) + self.assertEqual(len(data.headers), len(self.headers) + 1) + + def test_add_callable_column(self): + """Verify adding column with values specified as callable.""" + + new_col = lambda x: x[0] + + self.founders.append_col(new_col, header='first_again') + + def test_header_slicing(self): + """Verify slicing by headers.""" + + self.assertEqual(self.founders['first_name'], + [self.john[0], self.george[0], self.tom[0]]) + + self.assertEqual(self.founders['last_name'], + [self.john[1], self.george[1], self.tom[1]]) + + self.assertEqual(self.founders['gpa'], + [self.john[2], self.george[2], self.tom[2]]) + + def test_get_col(self): + """Verify getting columns by index""" + + self.assertEqual( + self.founders.get_col(list(self.headers).index('first_name')), + [self.john[0], self.george[0], self.tom[0]]) + + self.assertEqual( + self.founders.get_col(list(self.headers).index('last_name')), + [self.john[1], self.george[1], self.tom[1]]) + + self.assertEqual( + self.founders.get_col(list(self.headers).index('gpa')), + [self.john[2], self.george[2], self.tom[2]]) + + def test_data_slicing(self): + """Verify slicing by data.""" + + # Slice individual rows + self.assertEqual(self.founders[0], self.john) + self.assertEqual(self.founders[:1], [self.john]) + self.assertEqual(self.founders[1:2], [self.george]) + self.assertEqual(self.founders[-1], self.tom) + self.assertEqual(self.founders[3:], []) + + # Slice multiple rows + self.assertEqual(self.founders[:], [self.john, self.george, self.tom]) + self.assertEqual(self.founders[0:2], [self.john, self.george]) + self.assertEqual(self.founders[1:3], [self.george, self.tom]) + self.assertEqual(self.founders[2:], [self.tom]) + + def test_row_slicing(self): + """Verify Row's __getslice__ method. Issue #184.""" + + john = Row(self.john) + + self.assertEqual(john[:], list(self.john[:])) + self.assertEqual(john[0:], list(self.john[0:])) + self.assertEqual(john[:2], list(self.john[:2])) + self.assertEqual(john[0:2], list(self.john[0:2])) + self.assertEqual(john[0:-1], list(self.john[0:-1])) + + def test_delete(self): + """Verify deleting from dataset works.""" + + # Delete from front of object + del self.founders[0] + self.assertEqual(self.founders[:], [self.george, self.tom]) + + # Verify dimensions, width should NOT change + self.assertEqual(self.founders.height, 2) + self.assertEqual(self.founders.width, 3) + + # Delete from back of object + del self.founders[1] + self.assertEqual(self.founders[:], [self.george]) + + # Verify dimensions, width should NOT change + self.assertEqual(self.founders.height, 1) + self.assertEqual(self.founders.width, 3) + + # Delete from invalid index + self.assertRaises(IndexError, self.founders.__delitem__, 3) + + def test_str_no_columns(self): + d = tablib.Dataset(['a', 1], ['b', 2], ['c', 3]) + output = '%s' % d + + self.assertEqual(output.splitlines(), [ + 'a|1', + 'b|2', + 'c|3' + ]) + + def test_unicode_append(self): + """Passes in a single unicode character and exports.""" + + if is_py3: + new_row = ('å', 'é') + else: + exec ("new_row = (u'å', u'é')") + + data.append(new_row) + self._test_export_data_in_all_formats(data) + + def test_datetime_append(self): + """Passes in a single datetime and a single date and exports.""" + + new_row = ( + datetime.datetime.now(), + datetime.datetime.today(), + ) + + data.append(new_row) + self._test_export_data_in_all_formats(data) + + def test_separator_append(self): + for a in range(3): + data.append_separator('foobar') + for a in range(5): + data.append(['asdf', 'asdf', 'asdf']) + self._test_export_data_in_all_formats(data) + + def test_book_export_no_exceptions(self): + """Test that various exports don't error out.""" + + book = tablib.Databook() + book.add_sheet(data) + # These formats don't implement the book abstraction. + unsupported = ['csv', 'tsv', 'jira', 'latex', 'df'] + self._test_export_data_in_all_formats(book, exclude=unsupported) + + def test_auto_format_detect(self): + """Test auto format detection.""" + # html, jira, latex, rst are export only. + + _xls = self.founders.export('xls') + self.assertEqual(tablib.detect_format(_xls), 'xls') + + _xlsx = self.founders.export('xlsx') + self.assertEqual(tablib.detect_format(_xlsx), 'xlsx') + + _ods = self.founders.export('ods') + self.assertEqual(tablib.detect_format(_ods), 'ods') + + _df = self.founders.export('df') + self.assertEqual(tablib.detect_format(_df), 'df') + + _yaml = '- {age: 90, first_name: John, last_name: Adams}' + self.assertEqual(tablib.detect_format(_yaml), 'yaml') + + _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]' + self.assertEqual(tablib.detect_format(_json), 'json') + + _csv = '1,2,3\n4,5,6\n7,8,9\n' + self.assertEqual(tablib.detect_format(_csv), 'csv') + + _tsv = '1\t2\t3\n4\t5\t6\n7\t8\t9\n' + self.assertEqual(tablib.detect_format(_tsv), 'tsv') + + _bunk = '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + self.assertEqual(tablib.detect_format(_bunk), None) + + def test_transpose(self): + """Transpose a dataset.""" + + transposed_founders = self.founders.transpose() + first_row = transposed_founders[0] + second_row = transposed_founders[1] + + self.assertEqual(transposed_founders.headers, + ["first_name", "John", "George", "Thomas"]) + self.assertEqual(first_row, + ("last_name", "Adams", "Washington", "Jefferson")) + self.assertEqual(second_row, + ("gpa", 90, 67, 50)) + + def test_transpose_multiple_headers(self): + + data = tablib.Dataset() + data.headers = ("first_name", "last_name", "age") + data.append(('John', 'Adams', 90)) + data.append(('George', 'Washington', 67)) + data.append(('John', 'Tyler', 71)) + self.assertEqual(data.transpose().transpose().dict, data.dict) + + def test_row_stacking(self): + """Row stacking.""" + + to_join = tablib.Dataset(headers=self.founders.headers) + + for row in self.founders: + to_join.append(row=row) + + row_stacked = self.founders.stack(to_join) + + for column in row_stacked.headers: + original_data = self.founders[column] + expected_data = original_data + original_data + self.assertEqual(row_stacked[column], expected_data) + + def test_column_stacking(self): + """Column stacking""" + + to_join = tablib.Dataset(headers=self.founders.headers) + + for row in self.founders: + to_join.append(row=row) + + column_stacked = self.founders.stack_cols(to_join) + + for index, row in enumerate(column_stacked): + original_data = self.founders[index] + expected_data = original_data + original_data + self.assertEqual(row, expected_data) + + self.assertEqual(column_stacked[0], + ("John", "Adams", 90, "John", "Adams", 90)) + + def test_sorting(self): + """Sort columns.""" + + sorted_data = self.founders.sort(col="first_name") + self.assertEqual(sorted_data.title, 'Founders') + + first_row = sorted_data[0] + second_row = sorted_data[2] + third_row = sorted_data[1] + expected_first = self.founders[1] + expected_second = self.founders[2] + expected_third = self.founders[0] + + self.assertEqual(first_row, expected_first) + self.assertEqual(second_row, expected_second) + self.assertEqual(third_row, expected_third) + + def test_remove_duplicates(self): + """Unique Rows.""" + + self.founders.append(self.john) + self.founders.append(self.george) + self.founders.append(self.tom) + self.assertEqual(self.founders[0], self.founders[3]) + self.assertEqual(self.founders[1], self.founders[4]) + self.assertEqual(self.founders[2], self.founders[5]) + self.assertEqual(self.founders.height, 6) + + self.founders.remove_duplicates() + + self.assertEqual(self.founders[0], self.john) + self.assertEqual(self.founders[1], self.george) + self.assertEqual(self.founders[2], self.tom) + self.assertEqual(self.founders.height, 3) + + def test_wipe(self): + """Purge a dataset.""" + + new_row = (1, 2, 3) + data.append(new_row) + + # Verify width/data + self.assertTrue(data.width == len(new_row)) + self.assertTrue(data[0] == new_row) + + data.wipe() + new_row = (1, 2, 3, 4) + data.append(new_row) + self.assertTrue(data.width == len(new_row)) + self.assertTrue(data[0] == new_row) + + def test_subset(self): + """Create a subset of a dataset""" + + rows = (0, 2) + columns = ('first_name', 'gpa') + + data.headers = self.headers + + data.append(self.john) + data.append(self.george) + data.append(self.tom) + + # Verify data is truncated + subset = data.subset(rows=rows, cols=columns) + self.assertEqual(type(subset), tablib.Dataset) + self.assertEqual(subset.headers, list(columns)) + self.assertEqual(subset._data[0].list, ['John', 90]) + self.assertEqual(subset._data[1].list, ['Thomas', 50]) + + def test_formatters(self): + """Confirm formatters are being triggered.""" + + def _formatter(cell_value): + return str(cell_value).upper() + + self.founders.add_formatter('last_name', _formatter) + + for name in [r['last_name'] for r in self.founders.dict]: + self.assertTrue(name.isupper()) + + def test_unicode_renders_markdown_table(self): + # add another entry to test right field width for + # integer + self.founders.append(('Old', 'Man', 100500)) + self.assertEqual('first_name|last_name |gpa ', unicode(self.founders).split('\n')[0]) + + def test_databook_add_sheet_accepts_only_dataset_instances(self): + class NotDataset(object): + def append(self, item): + pass + + dataset = NotDataset() + dataset.append(self.john) + + self.assertRaises(tablib.InvalidDatasetType, book.add_sheet, dataset) + + def test_databook_add_sheet_accepts_dataset_subclasses(self): + class DatasetSubclass(tablib.Dataset): + pass + + # just checking if subclass of tablib.Dataset can be added to Databook + dataset = DatasetSubclass() + dataset.append(self.john) + dataset.append(self.tom) + + try: + book.add_sheet(dataset) + except tablib.InvalidDatasetType: + self.fail("Subclass of tablib.Dataset should be accepted by Databook.add_sheet") + + def test_databook_formatter_support_kwargs(self): + """Test XLSX export with formatter configuration.""" + self.founders.export('xlsx', freeze_panes=False) + + def test_databook_formatter_with_new_lines(self): + """Test XLSX export with new line in content.""" + self.founders.append(('First\nSecond', 'Name', 42)) + self.founders.export('xlsx') + + def test_rst_force_grid(self): + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + simple = tablib.formats._rst.export_set(data) + grid = tablib.formats._rst.export_set(data, force_grid=True) + self.assertNotEqual(simple, grid) + self.assertNotIn('+', simple) + self.assertIn('+', grid) + + +class HTMLTests(BaseTestCase): + def test_html_export(self): + """HTML export""" + + html = markup.page() + html.table.open() + html.thead.open() + + html.tr(markup.oneliner.th(self.founders.headers)) + html.thead.close() + + for founder in self.founders: + html.tr(markup.oneliner.td(founder)) + + html.table.close() + html = str(html) + + self.assertEqual(html, self.founders.html) + + def test_html_export_none_value(self): + """HTML export""" + + html = markup.page() + html.table.open() + html.thead.open() + + html.tr(markup.oneliner.th(['foo', '', 'bar'])) + html.thead.close() + + html.tr(markup.oneliner.td(['foo', '', 'bar'])) + + html.table.close() + html = str(html) + + headers = ['foo', None, 'bar']; + d = tablib.Dataset(['foo', None, 'bar'], headers=headers) + + self.assertEqual(html, d.html) + + +class CSVTests(BaseTestCase): + def test_csv_format_detect(self): + """Test CSV format detection.""" + + _csv = ( + '1,2,3\n' + '4,5,6\n' + '7,8,9\n' + ) + _bunk = ( + '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + ) + + self.assertTrue(tablib.formats.csv.detect(_csv)) + self.assertFalse(tablib.formats.csv.detect(_bunk)) + + def test_csv_import_set(self): + """Generate and import CSV set serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _csv = data.csv + + data.csv = _csv + + self.assertEqual(_csv, data.csv) + + def test_csv_import_set_semicolons(self): + """Test for proper output with semicolon separated CSV.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _csv = data.get_csv(delimiter=';') + + data.set_csv(_csv, delimiter=';') + + self.assertEqual(_csv, data.get_csv(delimiter=';')) + + def test_csv_import_set_with_spaces(self): + """Generate and import CSV set serialization when row values have + spaces.""" + data.append(('Bill Gates', 'Microsoft')) + data.append(('Steve Jobs', 'Apple')) + data.headers = ('Name', 'Company') + + _csv = data.csv + + data.csv = _csv + + self.assertEqual(_csv, data.csv) + + def test_csv_import_set_semicolon_with_spaces(self): + """Generate and import semicolon separated CSV set serialization when row values have + spaces.""" + data.append(('Bill Gates', 'Microsoft')) + data.append(('Steve Jobs', 'Apple')) + data.headers = ('Name', 'Company') + + _csv = data.get_csv(delimiter=';') + + data.set_csv(_csv, delimiter=';') + + self.assertEqual(_csv, data.get_csv(delimiter=';')) + + def test_csv_import_set_with_newlines(self): + """Generate and import CSV set serialization when row values have + newlines.""" + data.append(('Markdown\n=======', + 'A cool language\n\nwith paragraphs')) + data.append(('reStructedText\n==============', + 'Another cool language\n\nwith paragraphs')) + data.headers = ('title', 'body') + + _csv = data.csv + data.csv = _csv + + self.assertEqual(_csv, data.csv) + + def test_csv_import_set_with_unicode_str(self): + """Import CSV set with non-ascii characters in unicode literal""" + csv_text = ( + "id,givenname,surname,loginname,email,pref_firstname,pref_lastname\n" + "13765,Ævar,Arnfjörð,testing,test@example.com,Ævar,Arnfjörð" + ) + data.csv = csv_text + self.assertEqual(data.width, 7) + + def test_csv_export(self): + """Verify exporting dataset object as CSV.""" + + # Build up the csv string with headers first, followed by each row + csv = '' + for col in self.headers: + csv += col + ',' + + csv = csv.strip(',') + '\r\n' + + for founder in self.founders: + for col in founder: + csv += str(col) + ',' + csv = csv.strip(',') + '\r\n' + + self.assertEqual(csv, self.founders.csv) + + def test_csv_stream_export(self): + """Verify exporting dataset object as CSV from file object.""" + + # Build up the csv string with headers first, followed by each row + csv = '' + for col in self.headers: + csv += col + ',' + + csv = csv.strip(',') + '\r\n' + + for founder in self.founders: + for col in founder: + csv += str(col) + ',' + csv = csv.strip(',') + '\r\n' + + csv_stream = csv_module.export_stream_set(self.founders) + self.assertEqual(csv, csv_stream.getvalue()) + + def test_unicode_csv(self): + """Check if unicode in csv export doesn't raise.""" + + data = tablib.Dataset() + + if sys.version_info[0] > 2: + data.append(['\xfc', '\xfd']) + else: + exec ("data.append([u'\xfc', u'\xfd'])") + + data.csv + + def test_csv_column_select(self): + """Build up a CSV and test selecting a column""" + + data = tablib.Dataset() + data.csv = self.founders.csv + + headers = data.headers + self.assertTrue(isinstance(headers[0], unicode)) + + orig_first_name = self.founders[self.headers[0]] + csv_first_name = data[headers[0]] + self.assertEqual(orig_first_name, csv_first_name) + + def test_csv_column_delete(self): + """Build up a CSV and test deleting a column""" + + data = tablib.Dataset() + data.csv = self.founders.csv + + target_header = data.headers[0] + self.assertTrue(isinstance(target_header, unicode)) + + del data[target_header] + + self.assertTrue(target_header not in data.headers) + + def test_csv_column_sort(self): + """Build up a CSV and test sorting a column by name""" + + data = tablib.Dataset() + data.csv = self.founders.csv + + orig_target_header = self.founders.headers[1] + target_header = data.headers[1] + + self.founders.sort(orig_target_header) + data.sort(target_header) + + self.assertEqual(self.founders[orig_target_header], data[target_header]) + + def test_csv_formatter_support_kwargs(self): + """Test CSV import and export with formatter configuration.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + expected = 'first_name;last_name;gpa\nJohn;Adams;90\nGeorge;Washington;67\n' + + kwargs = dict(delimiter=';', lineterminator='\n') + _csv = data.export('csv', **kwargs) + self.assertEqual(expected, _csv) + + # the import works but consider default delimiter=',' + d1 = tablib.import_set(_csv, format="csv") + self.assertEqual(1, len(d1.headers)) + + d2 = tablib.import_set(_csv, format="csv", **kwargs) + self.assertEqual(3, len(d2.headers)) + + +class TSVTests(BaseTestCase): + def test_tsv_import_set(self): + """Generate and import TSV set serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _tsv = data.tsv + + data.tsv = _tsv + + self.assertEqual(_tsv, data.tsv) + + def test_tsv_format_detect(self): + """Test TSV format detection.""" + + _tsv = ( + '1\t2\t3\n' + '4\t5\t6\n' + '7\t8\t9\n' + ) + _bunk = ( + '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + ) + + self.assertTrue(tablib.formats.tsv.detect(_tsv)) + self.assertFalse(tablib.formats.tsv.detect(_bunk)) + + def test_tsv_export(self): + """Verify exporting dataset object as TSV.""" + + # Build up the tsv string with headers first, followed by each row + tsv = '' + for col in self.headers: + tsv += col + '\t' + + tsv = tsv.strip('\t') + '\r\n' + + for founder in self.founders: + for col in founder: + tsv += str(col) + '\t' + tsv = tsv.strip('\t') + '\r\n' + + self.assertEqual(tsv, self.founders.tsv) + + +class XLSXTests(BaseTestCase): + def test_xlsx_format_detect(self): + """Test the XLSX format detection.""" + in_stream = self.founders.xlsx + self.assertEqual(detect_format(in_stream), 'xlsx') + + def test_xls_format_detect(self): + """Test the XLS format detection.""" + in_stream = self.founders.xls + self.assertEqual(detect_format(in_stream), 'xls') + + def test_xlsx_import_set(self): + date_time = datetime.datetime(2019, 10, 4, 12, 30, 8) + data.append(('string', '004', 42, 21.55, date_time)) + data.headers = ('string', 'start0', 'integer', 'float', 'date/time') + _xlsx = data.xlsx + data.xlsx = _xlsx + self.assertEqual(data.dict[0]['string'], 'string') + self.assertEqual(data.dict[0]['start0'], '004') + self.assertEqual(data.dict[0]['integer'], 42) + self.assertEqual(data.dict[0]['float'], 21.55) + self.assertEqual(data.dict[0]['date/time'], date_time) + + def test_xlsx_wrong_char(self): + """Bad characters are not silently ignored. We let the exception bubble up.""" + from openpyxl.utils.exceptions import IllegalCharacterError + + with self.assertRaises(IllegalCharacterError): + data.append(('string', b'\x0cf')) + data.xlsx + + +class JSONTests(BaseTestCase): + def test_json_format_detect(self): + """Test JSON format detection.""" + + _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]' + _bunk = ( + '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + ) + + self.assertTrue(tablib.formats.json.detect(_json)) + self.assertFalse(tablib.formats.json.detect(_bunk)) + + def test_json_import_book(self): + """Generate and import JSON book serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + book.add_sheet(data) + _json = book.json + + book.json = _json + + self.assertEqual(json.loads(_json), json.loads(book.json)) + # Same with the load interface + book2 = tablib.Databook().load(_json, None) + self.assertEqual(json.loads(book.json), json.loads(book2.json)) + + def test_json_import_set(self): + """Generate and import JSON set serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _json = data.json + + data.json = _json + + self.assertEqual(json.loads(_json), json.loads(data.json)) + + def test_json_export(self): + """Verify exporting dataset object as JSON""" + + address_id = uuid4() + headers = self.headers + ('address_id',) + founders = tablib.Dataset(headers=headers, title='Founders') + founders.append(('John', 'Adams', 90, address_id)) + founders_json = founders.export('json') + + expected_json = ( + '[{"first_name": "John", "last_name": "Adams", "gpa": 90, ' + '"address_id": "%s"}]' % str(address_id) + ) + + self.assertEqual(founders_json, expected_json) + + +class YAMLTests(BaseTestCase): + def test_yaml_format_detect(self): + """Test YAML format detection.""" + + _yaml = '- {age: 90, first_name: John, last_name: Adams}' + _tsv = 'foo\tbar' + _bunk = ( + '¡¡¡¡¡¡---///\n\n\n¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + ) + + self.assertTrue(tablib.formats.yaml.detect(_yaml)) + self.assertFalse(tablib.formats.yaml.detect(_bunk)) + self.assertFalse(tablib.formats.yaml.detect(_tsv)) + + def test_yaml_import_book(self): + """Generate and import YAML book serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + book.add_sheet(data) + _yaml = book.yaml + + book.yaml = _yaml + + self.assertEqual(_yaml, book.yaml) + # Same with the load interface + book2 = tablib.Databook().load(_yaml, None) + self.assertEqual(_yaml, book2.yaml) + + def test_yaml_import_set(self): + """Generate and import YAML set serialization.""" + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _yaml = data.yaml + + data.yaml = _yaml + + self.assertEqual(_yaml, data.yaml) + + +class LatexTests(BaseTestCase): + def test_latex_export(self): + """LaTeX export""" + + expected = """\ +% Note: add \\usepackage{booktabs} to your preamble +% +\\begin{table}[!htbp] + \\centering + \\caption{Founders} + \\begin{tabular}{lrr} + \\toprule + first\\_name & last\\_name & gpa \\\\ + \\cmidrule(r){1-1} \\cmidrule(lr){2-2} \\cmidrule(l){3-3} + John & Adams & 90 \\\\ + George & Washington & 67 \\\\ + Thomas & Jefferson & 50 \\\\ + \\bottomrule + \\end{tabular} +\\end{table} +""" + output = self.founders.latex + self.assertEqual(output, expected) + + def test_latex_export_empty_dataset(self): + self.assertTrue(tablib.Dataset().latex is not None) + + def test_latex_export_no_headers(self): + d = tablib.Dataset() + d.append(('one', 'two', 'three')) + self.assertTrue('one' in d.latex) + + def test_latex_export_caption(self): + d = tablib.Dataset() + d.append(('one', 'two', 'three')) + self.assertFalse('caption' in d.latex) + + d.title = 'Title' + self.assertTrue('\\caption{Title}' in d.latex) + + def test_latex_export_none_values(self): + headers = ['foo', None, 'bar'] + d = tablib.Dataset(['foo', None, 'bar'], headers=headers) + output = d.latex + self.assertTrue('foo' in output) + self.assertFalse('None' in output) + + def test_latex_escaping(self): + d = tablib.Dataset(['~', '^']) + output = d.latex + + self.assertFalse('~' in output) + self.assertTrue('textasciitilde' in output) + self.assertFalse('^' in output) + self.assertTrue('textasciicircum' in output) + + +class DBFTests(BaseTestCase): + def test_dbf_import_set(self): + data.append(self.john) + data.append(self.george) + data.headers = self.headers + + _dbf = data.dbf + data.dbf = _dbf + + # self.assertEqual(_dbf, data.dbf) + try: + self.assertEqual(_dbf, data.dbf) + except AssertionError: + index = 0 + so_far = '' + for reg_char, data_char in zip(_dbf, data.dbf): + so_far += chr(data_char) + if reg_char != data_char and index not in [1, 2, 3]: + raise AssertionError('Failing at char %s: %s vs %s %s' % ( + index, reg_char, data_char, so_far)) + index += 1 + + def test_dbf_export_set(self): + """Test DBF import.""" + data.append(self.john) + data.append(self.george) + data.append(self.tom) + data.headers = self.headers + + _regression_dbf = (b'\x03r\x06\x06\x03\x00\x00\x00\x81\x00\xab\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00' + b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n' + b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r' + ) + _regression_dbf += b' John' + (b' ' * 75) + _regression_dbf += b' Adams' + (b' ' * 74) + _regression_dbf += b' 90.0000000' + _regression_dbf += b' George' + (b' ' * 73) + _regression_dbf += b' Washington' + (b' ' * 69) + _regression_dbf += b' 67.0000000' + _regression_dbf += b' Thomas' + (b' ' * 73) + _regression_dbf += b' Jefferson' + (b' ' * 70) + _regression_dbf += b' 50.0000000' + _regression_dbf += b'\x1a' + + if is_py3: + # If in python3, decode regression string to binary. + # _regression_dbf = bytes(_regression_dbf, 'utf-8') + # _regression_dbf = _regression_dbf.replace(b'\n', b'\r') + pass + + try: + self.assertEqual(_regression_dbf, data.dbf) + except AssertionError: + index = 0 + found_so_far = '' + for reg_char, data_char in zip(_regression_dbf, data.dbf): + # found_so_far += chr(data_char) + if reg_char != data_char and index not in [1, 2, 3]: + raise AssertionError( + 'Failing at char %s: %s vs %s (found %s)' % ( + index, reg_char, data_char, found_so_far)) + index += 1 + + def test_dbf_format_detect(self): + """Test the DBF format detection.""" + _dbf = (b'\x03r\x06\x03\x03\x00\x00\x00\x81\x00\xab\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00FIRST_NAME\x00C\x00\x00\x00\x00P\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LAST_NAME\x00\x00C\x00' + b'\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00GPA\x00\x00\x00\x00\x00\x00\x00\x00N\x00\x00\x00\x00\n' + b'\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r' + ) + _dbf += b' John' + (b' ' * 75) + _dbf += b' Adams' + (b' ' * 74) + _dbf += b' 90.0000000' + _dbf += b' George' + (b' ' * 73) + _dbf += b' Washington' + (b' ' * 69) + _dbf += b' 67.0000000' + _dbf += b' Thomas' + (b' ' * 73) + _dbf += b' Jefferson' + (b' ' * 70) + _dbf += b' 50.0000000' + _dbf += b'\x1a' + + _yaml = '- {age: 90, first_name: John, last_name: Adams}' + _tsv = 'foo\tbar' + _csv = '1,2,3\n4,5,6\n7,8,9\n' + _json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]' + + _bunk = ( + '¡¡¡¡¡¡¡¡£™∞¢£§∞§¶•¶ª∞¶•ªº••ª–º§•†•§º¶•†¥ª–º•§ƒø¥¨©πƒø†ˆ¥ç©¨√øˆ¥≈†ƒ¥ç©ø¨çˆ¥ƒçø¶' + ) + self.assertTrue(tablib.formats.dbf.detect(_dbf)) + self.assertFalse(tablib.formats.dbf.detect(_yaml)) + self.assertFalse(tablib.formats.dbf.detect(_tsv)) + self.assertFalse(tablib.formats.dbf.detect(_csv)) + self.assertFalse(tablib.formats.dbf.detect(_json)) + self.assertFalse(tablib.formats.dbf.detect(_bunk)) + + +class JiraTests(BaseTestCase): + def test_jira_export(self): + expected = """||first_name||last_name||gpa|| +|John|Adams|90| +|George|Washington|67| +|Thomas|Jefferson|50|""" + self.assertEqual(expected, self.founders.jira) + + def test_jira_export_no_headers(self): + self.assertEqual('|a|b|c|', tablib.Dataset(['a', 'b', 'c']).jira) + + def test_jira_export_none_and_empty_values(self): + self.assertEqual('| | |c|', tablib.Dataset(['', None, 'c']).jira) + + def test_jira_export_empty_dataset(self): + self.assertTrue(tablib.Dataset().jira is not None) + + +class DocTests(unittest.TestCase): + + def test_rst_formatter_doctests(self): + results = doctest.testmod(tablib.formats._rst) + self.assertEqual(results.failed, 0) |
