Add initial code
[ta/build-tools.git] / tools / convert_test.py
diff --git a/tools/convert_test.py b/tools/convert_test.py
new file mode 100755 (executable)
index 0000000..cdfe7dc
--- /dev/null
@@ -0,0 +1,165 @@
+# Copyright 2019 Nokia
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# pylint: disable=invalid-name
+from collections import OrderedDict
+
+import pytest
+
+from tools.convert import to_json, CsvConverter
+
+
+class TestJson(object):
+    @staticmethod
+    def test_json():
+        _input = dict(b=1, a=2)
+        expected = '\n'.join(['{',
+                              '    "a": 2, ',
+                              '    "b": 1',
+                              '}'])
+        assert to_json(_input) == expected
+
+
+class TestCsv(object):
+    @staticmethod
+    @pytest.mark.parametrize('_input, expected', [
+        ([], ''),
+        ([{}], '\r\n'),
+        ([dict(a=1)], '\r\n'.join(['"a"',
+                                   '"1"'])),
+        ([dict(a=1), dict(a=2)], '\r\n'.join(['"a"',
+                                              '"1"',
+                                              '"2"'])),
+        ([dict(a=1, b=2), dict(a=3, b=4)], '\r\n'.join(['"a","b"',
+                                                        '"1","2"',
+                                                        '"3","4"'])),
+        ([dict(x=OrderedDict({'b': 1, 'a': 2})),
+          dict(x={'a': 2, 'b': 1})], '\r\n'.join(['"x"',
+                                                  '"{""a"": 2, ""b"": 1}"',
+                                                  '"{""a"": 2, ""b"": 1}"']))
+
+    ])
+    def test_csv(_input, expected):
+        assert str(CsvConverter(_input)) == expected
+
+    @staticmethod
+    def test_str():
+        _input = [dict(a=1)]
+        assert str(CsvConverter(_input)) == CsvConverter(_input).convert()
+
+    @staticmethod
+    @pytest.mark.parametrize('_input, exception_re', [
+        ('boom', r'NOT a list'),
+        ([['boom']], r'NOT a dict')
+    ])
+    def test_to_csv_fail(_input, exception_re):
+        with pytest.raises(Exception, match=exception_re):
+            str(CsvConverter(_input))
+
+    @staticmethod
+    def test_newlines_are_escaped():
+        """
+        ...in order to get properly formatted file when written
+        """
+        _input = [dict(a='1"2')]
+        expected = '\r\n'.join(['"a"',
+                                '"1""2"'])
+        assert CsvConverter(_input).convert() == expected
+
+    @staticmethod
+    def test_missing_fields_are_filled_with_none():
+        _input = [dict(a='1"2')]
+        expected = '\r\n'.join(['"a"',
+                                '"1""2"'])
+        assert CsvConverter(_input).convert() == expected
+
+    @staticmethod
+    def test_double_quote_is_escaped_with_double_quote():
+        """
+        RFC 4180
+        """
+        _input = [dict(a='1"2')]
+        expected = '\r\n'.join(['"a"',
+                                '"1""2"'])
+        assert CsvConverter(_input).convert() == expected
+
+    @staticmethod
+    @pytest.mark.parametrize('_input, expected', [
+        ([dict(a=1, b=2)], '\r\n'.join(['"b","a"', '"2","1"'])),
+        ([dict(a=1, c=2)], '\r\n'.join(['"a","c"', '"1","2"']))
+    ])
+    def test_csv_preferred_order(_input, expected):
+        assert CsvConverter(_input, preferred_field_order=['b', 'a']).convert() == expected
+
+    @staticmethod
+    @pytest.mark.parametrize('_input, expected', [
+        ([dict(a='Copyright \xc2\xa9 2014')], '\r\n'.join(['"a"',
+                                                           '"Copyright \xc2\xa9 2014"'])),
+        ([dict(a=u'Copyright \xa9 2014')], '\r\n'.join(['"a"',
+                                                        '"Copyright \xc2\xa9 2014"'])),
+    ])
+    def test_unicode_input(_input, expected):
+        assert str(CsvConverter(_input)) == expected
+
+
+class TestCsvMSExcel(object):
+    @staticmethod
+    def test_ms_excel_format():
+        """
+        MS Excel treats CSV files with 'sep=,' as the first line to get automatically columnized
+        """
+        _input = [dict(a=1, b=2)]
+        expected = '\r\n'.join(['sep=,',
+                                '"a","b"',
+                                '"1","2"'])
+        assert CsvConverter(_input).convert_to_ms_excel() == expected
+
+    @staticmethod
+    def test_text_fields():
+        """
+        MS Excel CSV fields prefixed with '=' will be treated as equations to string.
+        This makes it possible to e.g. to have all RPM version strings treated equally instead
+        of making some of them treated as generic and some of them as integers.
+        """
+        _input = [dict(a=1, b=2)]
+        expected = '\r\n'.join(['sep=,',
+                                '"a",="b"',
+                                '"1",="2"'])
+        assert CsvConverter(_input).convert_to_ms_excel(text_fields=['b']) == expected
+
+    @staticmethod
+    def test_field_with_beginning_minus_is_prefixed():
+        """
+        MS Excel CSV fields beginning with '-' are treated as an equation even they would be
+        essentially just strings. Make sure to escape the beginning signs with something in order
+        not to get field braking equation.
+        """
+        _input = [dict(a=-1), dict(a="-2a")]
+        expected = '\r\n'.join(['sep=,',
+                                '"a"',
+                                '"-1"',
+                                r'"\-2a"'])
+        assert CsvConverter(_input).convert_to_ms_excel() == expected
+
+    @staticmethod
+    def test_too_big_cell_is_truncated():
+        """
+        MS Excel has ~32k character limit per cell. BOM information can easily exceed this.
+        """
+        _input = [dict(a='1' * 32000), dict(a='2' * 32001)]
+        expected = '\r\n'.join(['sep=,',
+                                '"a"',
+                                '"{}"'.format('1' * 32000),
+                                '"{}...{}"'.format('2' * 16000, '2' * 16000)])
+        assert CsvConverter(_input).convert_to_ms_excel() == expected