Migrating to Python v3

 - support for Python v3.8.x
 - support for Python v3.5.x
 - new tag, 2019.2.8
 - updates class generation and iterators
 - unittests updated with coverage >75%
 - new coverage routines
 - unittests profiling
 - full fake data for unittests
 - unittest testrun is ~1.5 seconds long

Bugfixes
 - 34834, proper use of 'sudo' option
 - multiple proper iterator use
 - 37919, show warning when installed and candidate versions
   are newer comparing to release version

Change-Id: Idd6b889f7ce94ae0c832e2f0a0346e4fdc3264a3
Related-PROD: PROD-34834 PROD-34664 PROD-34919
diff --git a/tests/test_common.py b/tests/test_common.py
new file mode 100644
index 0000000..65159a1
--- /dev/null
+++ b/tests/test_common.py
@@ -0,0 +1,208 @@
+import inspect
+import os
+import sys
+from unittest import mock
+
+
+from tests.test_base import CfgCheckerTestBase
+from tests.test_base import tests_dir
+
+gzip_filename = "textfile.txt.gz"
+fake_gzip_file_path = os.path.join(tests_dir, 'res', gzip_filename)
+_patch_buf = []
+with open(fake_gzip_file_path, 'rb') as _f:
+    _patch_buf = _f.read()
+
+
+def mocked_requests_get(*args, **kwargs):
+    class MockResponse:
+        def __init__(self, content, status_code):
+            self.content = content
+            self.status_code = status_code
+
+        def content(self):
+            return self.content
+
+    if args[0] == fake_gzip_file_path:
+        return MockResponse(_patch_buf, 200)
+
+    return MockResponse(None, 404)
+
+
+class TestCommonModules(CfgCheckerTestBase):
+    def test_exceptions(self):
+        _m = self._try_import("cfg_checker.common.exception")
+        # Get all classes from the exceptions module
+        _classes = inspect.getmembers(
+            sys.modules[_m.common.exception.__name__],
+            inspect.isclass
+        )
+        # Create instance for all detected classes except for the Base one
+        _errors = []
+        for _name, _class in _classes:
+            if _name.startswith("CheckerBase"):
+                continue
+            _, _msg = self._safe_run(_class, "Fake exception message")
+            if _msg:
+                _errors.append(_msg)
+
+        self.assertEqual(
+            len(_errors),
+            0,
+            "Invalid Exception classes detected: \n{}".format(
+                "\n".join(_errors)
+            )
+        )
+
+    def test_file_utils(self):
+        # File operations itself is not to be tested
+        # Only classes that provide api methods
+        # I.e. no exceptions - no errors,
+        # file contents is not to be checked, only return types
+        _m = self._try_import("cfg_checker.common.file_utils")
+        _futils = _m.common.file_utils
+        _filename = "/tmp/fakefile.txt"
+        _fakestr = "Fake String in the file"
+        _errors = []
+
+        # write_str_to_file
+        _, _msg = self._safe_run(
+            _futils.write_str_to_file,
+            _filename,
+            _fakestr
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        # append_str_to_file
+        _, _msg = self._safe_run(
+            _futils.append_str_to_file,
+            _filename,
+            _fakestr
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        # remove_file
+        _, _msg = self._safe_run(_futils.remove_file, _filename)
+        if _msg:
+            _errors.append(_msg)
+
+        # write_lines_to_file
+        _, _msg = self._safe_run(
+            _futils.write_lines_to_file,
+            _filename,
+            [_fakestr]
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        # append_lines_to_file
+        _, _msg = self._safe_run(
+            _futils.append_lines_to_file,
+            _filename,
+            [_fakestr]
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        # append_line_to_file
+        _, _msg = self._safe_run(
+            _futils.append_line_to_file,
+            _filename,
+            _fakestr
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        # read_file
+        _r, _msg = self._safe_run(_futils.read_file, _filename)
+        if _msg:
+            _errors.append(_msg)
+        self.assertNotEqual(
+            len(_r),
+            0,
+            "Empty buffer returned by 'read_file'"
+        )
+
+        # read_file_as_lines
+        _r, _msg = self._safe_run(_futils.read_file_as_lines, _filename)
+        if _msg:
+            _errors.append(_msg)
+        self.assertNotEqual(
+            len(_r),
+            0,
+            "Empty buffer returned by 'read_file_as_lines'"
+        )
+        self.assertIsInstance(
+            _r,
+            list,
+            "Non-list type returned by 'read_file_as_lines'"
+        )
+        # get_file_info_fd
+        with open(_filename) as _fd:
+            _r, _msg = self._safe_run(_futils.get_file_info_fd, _fd)
+            if _msg:
+                _errors.append(_msg)
+            self.assertIsInstance(
+                _r,
+                dict,
+                "Non-dict type returned by get_file_info_fd"
+            )
+            _, _msg = self._safe_run(_futils.remove_file, _filename)
+
+        # get_gzipped_file
+
+        _folder = "/tmp/cfgcheckertmpfolder"
+        # ensure_folder_exists
+        _, _msg = self._safe_run(_futils.ensure_folder_exists, _folder)
+        if _msg:
+            _errors.append(_msg)
+        _, _msg = self._safe_run(_futils.ensure_folder_exists, _folder)
+        if _msg:
+            _errors.append(_msg)
+
+        # ensure_folder_removed
+        _, _msg = self._safe_run(_futils.ensure_folder_removed, _folder)
+        if _msg:
+            _errors.append(_msg)
+        _, _msg = self._safe_run(_futils.ensure_folder_removed, _folder)
+        if _msg:
+            _errors.append(_msg)
+
+        self.assertEqual(
+            len(_errors),
+            0,
+            "Invalid file operations: \n{}".format(
+                "\n".join(_errors)
+            )
+        )
+
+    @mock.patch(
+        'requests.get',
+        side_effect=mocked_requests_get
+    )
+    def test_get_gzip_file(self, mock_get):
+        _m = self._try_import("cfg_checker.common.file_utils")
+        _futils = _m.common.file_utils
+        _fakecontent = b"fakecontent\n"
+        _errors = []
+
+        # Call the method with patched data
+        _buf, _msg = self._safe_run(
+            _futils.get_gzipped_file,
+            fake_gzip_file_path
+        )
+        if _msg:
+            _errors.append(_msg)
+
+        self.assertNotEqual(
+            len(_buf),
+            0,
+            "Empty buffer returned by 'get_gzipped_file'"
+        )
+        self.assertEqual(
+            _buf,
+            _fakecontent,
+            "Incorrect content returned by 'get_gzipped_file'"
+        )