#
# Collective Knowledge
#
# See CK LICENSE.txt for licensing details
# See CK COPYRIGHT.txt for copyright details
#

import unittest
import sys
import os

ck=None           # Will be updated by CK (initialized CK kernel)
test_util=None    # Will be updated by CK (initialized CK test utils)

# Contains new kernel tests. Add new tests here!
class TestKernel(unittest.TestCase):

    def test_out(self):
        with test_util.tmp_sys():
            ck.out('test')
            self.assertEqual('test', sys.stdout.getvalue().strip())

    def test_eout(self):
        with test_util.tmp_sys():
            ck.eout('test')
            self.assertEqual('test', sys.stderr.getvalue().strip())

    def test_err(self):
        with test_util.tmp_sys():
            ck.err({'return': 2, 'error': 'test.'})
            self.assertEqual('Error: test.\nExit code: 2', sys.stdout.getvalue().strip())

    def test_jerr(self):
        with test_util.tmp_sys():
            with self.assertRaises(KeyboardInterrupt):
                ck.jerr({'return': 2, 'error': 'test.'})
            self.assertEqual('Error: test.', sys.stdout.getvalue().strip())

    def test_safe_float(self):
        import math

        self.assertEqual(ck.safe_float(1, 0), 1.0)
        self.assertEqual(ck.safe_float('a', 0), 0)
        self.assertEqual(ck.safe_float('-5.35', 0), -5.35)
        self.assertEqual(ck.safe_float('Infinity', 0), float('inf'))
        self.assertTrue(math.isnan(ck.safe_float('nan', 0)))

    def test_safe_int(self):
        self.assertEqual(ck.safe_int(1, 0), 1)
        self.assertEqual(ck.safe_int('a', 0), 0)
        self.assertEqual(ck.safe_int('-5', 0), -5)

    def test_safe_get_val_from_list(self):
        self.assertEqual(ck.safe_get_val_from_list([1, 2], 0, 0), 1)
        self.assertEqual(ck.safe_get_val_from_list([1, 2], 1, 0), 2)
        self.assertEqual(ck.safe_get_val_from_list([], 1, 0), 0)

    def test_system_with_timeout(self):
        r = ck.system_with_timeout({'cmd': 'exit 2'})
        self.assertEqual(0, r['return'])
        self.assertEqual(2, r['return_code'])

        r = ck.system_with_timeout({'cmd': 'sleep 5', 'timeout': 0.05})
        self.assertEqual(8, r['return'])

    def test_run_and_get_stdout(self):
        r = ck.run_and_get_stdout({'cmd': ['sh', '-c', 'echo abcd && >&2 echo err && exit 2']})
        self.assertEqual(0, r['return'])
        self.assertEqual(2, r['return_code'])
        self.assertEqual('abcd\n', r['stdout'])
        self.assertEqual('err\n', r['stderr'])

        r = ck.run_and_get_stdout({'cmd': 'sh -c "echo abcd && >&2 echo err && exit 2"'})
        self.assertEqual(0, r['return'])
        self.assertEqual(2, r['return_code'])
        self.assertEqual('abcd\n', r['stdout'])
        self.assertEqual('err\n', r['stderr'])

    def test_get_from_dicts(self):
        d1 = {'a': 1, 'b': 2}
        d2 = {'c': 3}
        r = ck.get_from_dicts(d1, 'a', '', d2)
        self.assertEqual(d1, {'b': 2})
        self.assertEqual(d2, {'a': 1, 'c': 3})
        self.assertEqual(1, r)

        ck.get_from_dicts(d1, 'a', 4, d2)
        self.assertEqual(d1, {'b': 2})
        self.assertEqual(d2, {'a': 1, 'c': 3})
        self.assertEqual(1, r)

    def test_convert_iso_time(self):
        r = ck.convert_iso_time({'iso_datetime': 'invalid'})
        self.assertEqual(1, r['return'])

        r = ck.convert_iso_time({'iso_datetime': '2016-09-07T13:47:34.5'})
        self.assertEqual(0, r['return'])
        self.assertEqual(2016, r['datetime_obj'].year)

    def test_convert_str_key_to_int(self):
        self.assertEqual(5, ck.convert_str_key_to_int('5'))
        self.assertEqual(0, ck.convert_str_key_to_int('a'))

    def test_inp(self):
        with test_util.tmp_sys('test input'):
            r = ck.inp({'text': 'test output'})
            self.assertEqual('test output', sys.stdout.getvalue().strip())
            self.assertEqual('test input', r['string'])
            self.assertEqual(0, r['return'])

    def test_select(self):
        d = {
            'key0': { 'name': 'n0', 'sort': 1 },
            'key1': { 'name': 'n1', 'sort': 0 }
        }
        with test_util.tmp_sys('1\n'):
            r = ck.select({'dict': d, 'title': 'Select:'})

            self.assertEqual('Select:\n\n0) n1\n1) n0\n\nMake your selection (or press Enter for 0):', sys.stdout.getvalue().strip())
            self.assertEqual(0, r['return'])
            self.assertEqual('key0', r['string'])

        with test_util.tmp_sys('\n'):
            r = ck.select({'dict': d, 'title': 'Select:', 'error_if_empty': 'yes'})
            self.assertEqual(1, r['return'])
            self.assertEqual('selection is empty', r['error'])

        with test_util.tmp_sys('\n'):
            r = ck.select({'dict': d, 'title': 'Select:'})
            self.assertEqual(0, r['return'])
            self.assertEqual('key1', r['string'])

        with test_util.tmp_sys('2\n'):
            r = ck.select({'dict': d, 'title': 'Select:'})
            self.assertEqual(1, r['return'])
            self.assertEqual('selection is not recognized', r['error'])

    def test_select_uoa(self):
        lst = [
            {'data_uid': 'uid1', 'data_uoa': 'b'},
            {'data_uid': 'uid2', 'data_uoa': 'a'}
        ]
        with test_util.tmp_sys('1\n'):
            r = ck.select_uoa({'choices': lst})

            self.assertEqual('0) a (uid2)\n1) b (uid1)\n\nSelect UOA (or press Enter for 0):', sys.stdout.getvalue().strip())
            self.assertEqual(0, r['return'])
            self.assertEqual('uid1', r['choice'])

        with test_util.tmp_sys('2\n'):
            r = ck.select_uoa({'choices': lst})
            self.assertEqual(1, r['return'])
            self.assertEqual('number is not recognized', r['error'])

    def test_convert_str_tags_to_list(self):
        self.assertEqual([1, 2], ck.convert_str_tags_to_list([1, 2]))
        self.assertEqual(['1', '2'], ck.convert_str_tags_to_list('  1 , 2  '))

    def test_check_writing(self):
        with test_util.tmp_cfg('forbid_global_delete'):
            r = ck.check_writing({'delete': 'yes'})
            self.assertEqual(1, r['return'])

        with test_util.tmp_cfg('forbid_global_writing'):
            r = ck.check_writing({})
            self.assertEqual(1, r['return'])

        with test_util.tmp_cfg('forbid_writing_modules'):
            r = ck.check_writing({'module_uoa': 'module'})
            self.assertEqual(1, r['return'])

        with test_util.tmp_cfg('forbid_writing_to_default_repo'):
            r = ck.check_writing({'repo_uoa': 'default'})
            self.assertEqual(1, r['return'])

        with test_util.tmp_cfg('forbid_writing_to_local_repo'):
            r = ck.check_writing({'repo_uoa': 'local'})
            self.assertEqual(1, r['return'])

        with test_util.tmp_cfg('allow_writing_only_to_allowed'):
            r = ck.check_writing({'repo_uoa': 'default'})
            self.assertEqual(1, r['return'])

        r = ck.check_writing({'repo_uoa': 'default', 'delete': 'yes', 'repo_dict': {'forbid_deleting': 'yes'}})
        self.assertEqual(1, r['return'])

    def test_gen_tmp_file(self):
        with test_util.tmp_file(suffix='.txt') as fname:
            # check we can write to the file
            with open(fname, 'w') as f:
                f.write('test')

        fname = ck.gen_tmp_file({})['file_name']
        self.assertIn(os.sep, fname)

        fname = ck.gen_tmp_file({'remove_dir': 'yes'})['file_name']
        self.assertNotIn(os.sep, fname)

    def test_gen_uid(self):
        r = ck.gen_uid({})
        self.assertEqual(0, r['return'])

        uid = r['data_uid']
        self.assertEqual(16, len(uid))

        chars = '0123456789abcdef'
        for c in uid:
            self.assertIn(c, chars)
        
    def test_is_uid(self):
        self.assertTrue(ck.is_uid('5eac2a24f4a98e90'))

        self.assertFalse(ck.is_uid('a'))
        self.assertFalse(ck.is_uid('5evc2a24f4a98e90'))

    def test_is_uoa(self):
        self.assertTrue(ck.is_uoa('5evac2a24f4a98e90'))
        self.assertTrue(ck.is_uoa(''))

        self.assertFalse(ck.is_uoa('ab#12'))
        self.assertFalse(ck.is_uoa('ab^12'))
        self.assertFalse(ck.is_uoa('5evc2*a24f4a98e90'))
        self.assertFalse(ck.is_uoa('5evc2?a24f4a98e90'))


    def test_prepare_special_info_about_entry(self):
        r = ck.prepare_special_info_about_entry({})
        self.assertEqual(0, r['return'])
#        keys = sorted(['engine', 'version', 'author', 'author_email', 'author_webpage', 'license', 'copyright', 'iso_datetime'])
        keys = ['engine', 'version', 'iso_datetime'] # min set of keys to check, since other keys can differ depending on user configuration
        xkeys = []

        xdict=r['dict'].keys()
        for k in keys:
            if k in xdict:
               xkeys.append(k)
        self.assertEqual(keys, xkeys)

    def test_convert_json_str_to_dict(self):
        r = ck.convert_json_str_to_dict({'str': "{'a': 1, 'b': [2, 3]}"})
        self.assertEqual(0, r['return'])
        self.assertEqual({'a': 1, 'b': [2, 3]}, r['dict'])

    def test_load_json_file(self):
        with test_util.tmp_file(suffix='.json', content='{"a": 1, "b": [2, 3]}') as fname:
            r = ck.load_json_file({'json_file': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual({'a': 1, 'b': [2, 3]}, r['dict'])

    def test_load_yaml_file(self):
        with test_util.tmp_file(suffix='.yml', content='a: 1\nb:\n  - 2\n  - 3\n') as fname:
            r = ck.load_yaml_file({'yaml_file': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual({'a': 1, 'b': [2, 3]}, r['dict'])

    def test_load_text_file(self):
        content = 'a\nb\nc'
        with test_util.tmp_file(suffix='.txt', content=content) as fname:
            r = ck.load_text_file({'text_file': fname, 'split_to_list': 'yes'})
            self.assertEqual(0, r['return'])
            self.assertEqual(str.encode(content.replace('\n', os.linesep)), r['bin'])
            self.assertEqual(content, r['string'])
            self.assertEqual(content.strip().split('\n'), r['lst'])

        content = 'a:1\nb:"z"\nc:2'
        with test_util.tmp_file(suffix='.txt', content=content) as fname:
            r = ck.load_text_file({'text_file': fname, 'convert_to_dict': 'yes', 'remove_quotes': 'yes'})
            self.assertEqual(0, r['return'])
            self.assertEqual({'a': '1', 'b': 'z', 'c': '2'}, r['dict'])

    def test_substitute_str_in_file(self):
        content = 'a\nb\nc'
        with test_util.tmp_file(suffix='.txt', content=content) as fname:
            r = ck.substitute_str_in_file({'filename': fname, 'string1': 'b', 'string2': 'd'})
            self.assertEqual(0, r['return'])
            self.assertEqual('a\nd\nc', ck.load_text_file({'text_file': fname})['string'])

    def test_dumps_json(self):
        d = {'a': 1, 'b': [2, 3]}
        json_str = ck.dumps_json({'dict': d, 'skip_indent': 'yes'})['string']
        parsed_dict = ck.convert_json_str_to_dict({'str': json_str, 'skip_quote_replacement': 'yes'})['dict']
        self.assertEqual(d, parsed_dict)

    def test_merge_dicts(self):
        d1 = {'a': 1, 'b': 2}
        d2 = {'c': 3, 'b': {}}
        ck.merge_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual({'a': 1, 'b': {}, 'c': 3}, d1)

    def test_save_yaml_to_file(self):
        d = {'a': 1, 'b': [2, 3]}
        with test_util.tmp_file(suffix='.yml') as fname:
            r = ck.save_yaml_to_file({'yaml_file': fname, 'dict': d})
            self.assertEqual(0, r['return'])

            r = ck.load_yaml_file({'yaml_file': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual(d, r['dict'])

    def test_convert_file_to_upload_string(self):
        with test_util.tmp_file(suffix='.txt', content='http://ctuning.org/') as fname:
            r = ck.convert_file_to_upload_string({'filename': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual('aHR0cDovL2N0dW5pbmcub3JnLw==', r['file_content_base64'])

        with test_util.tmp_file() as fname:
            r = ck.convert_file_to_upload_string({'filename': fname})
            self.assertEqual(1, r['return'])

    def test_convert_upload_string_to_file(self):
        with test_util.tmp_file(suffix='.txt') as fname:
            r = ck.convert_upload_string_to_file({'file_content_base64': 'aHR0cDovL2N0dW5pbmcub3JnLw==', 'filename': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual(fname, r['filename'])

            r = ck.load_text_file({'text_file': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual('http://ctuning.org/', r['string'])

        r = ck.convert_upload_string_to_file({'file_content_base64': 'aHR0cDovL2N0dW5pbmcub3JnLw=='})
        fname = r['filename']
        try:
            self.assertEqual(0, r['return'])

            r = ck.load_text_file({'text_file': fname})
            self.assertEqual(0, r['return'])
            self.assertEqual('http://ctuning.org/', r['string'])
        finally:
            os.remove(fname)

        with test_util.tmp_file(suffix='.txt', content='test') as fname:
            r = ck.convert_upload_string_to_file({'file_content_base64': 'aHR0cDovL2N0dW5pbmcub3JnLw==', 'filename': fname})
            self.assertEqual(1, r['return'])

    def test_convert_ck_list_to_dict(self):
        r = ck.convert_ck_list_to_dict(['a', '--', 1, '2'])
        self.assertEqual(0, r['return'])
        self.assertEqual({'action': 'a', 'cids': [], 'unparsed': [1, '2']}, r['ck_dict'])

        r = ck.convert_ck_list_to_dict(['a', '-k=1'])
        self.assertEqual(0, r['return'])
        self.assertEqual({'action': 'a', 'cids': [], 'k': '1'}, r['ck_dict'])

        r = ck.convert_ck_list_to_dict(['a', '@@@2'])
        self.assertEqual(1, r['return'])

        r = ck.convert_ck_list_to_dict(['a', '@@@{\'k\': 1}'])
        self.assertEqual(0, r['return'])
        self.assertEqual({'action': 'a', 'cids': [], 'k': 1}, r['ck_dict'])

        with test_util.tmp_sys('{"k": 1}\n\n'):
            r = ck.convert_ck_list_to_dict(['a', '@@'])
            self.assertEqual(0, r['return'])
            self.assertEqual({'action': 'a', 'cids': [], 'k': 1}, r['ck_dict'])

        with test_util.tmp_sys('{"k": 1}\n\n'):
            r = ck.convert_ck_list_to_dict(['a', '@@q'])
            self.assertEqual(0, r['return'])
            self.assertEqual({'action': 'a', 'cids': [], 'q':{'k': 1}}, r['ck_dict'])

        r = ck.convert_ck_list_to_dict(['a', '@2'])
        self.assertEqual(1, r['return'])

        with test_util.tmp_file(suffix='.tmp', content='{"k": 1}') as fname:
            r = ck.convert_ck_list_to_dict(['a', '@' + fname])
            self.assertEqual(0, r['return'])
            self.assertEqual({'action': 'a', 'cids': [], 'k': 1}, r['ck_dict'])
            self.assertFalse(os.path.isfile(fname))

    def test_input_json(self):
        content = '{"a": 1, "b": [2, 3]}\n\n'
        with test_util.tmp_sys(content):
            r = ck.input_json({'text': 'input json'})
            self.assertEqual(0, r['return'])
            self.assertEqual('input json', sys.stdout.getvalue().strip())
            self.assertEqual(content.strip(), r['string'])
            self.assertEqual({'a': 1, 'b': [2, 3]}, r['dict'])

    def test_list_all_files(self):
        with test_util.tmp_dir() as fname:
            os.makedirs(fname + '/a')
            os.makedirs(fname + '/b')

            with open(fname + '/a/test.txt', 'a') as f:
                f.write('abc')

            with open(fname + '/b/test.log', 'a') as f:
                f.write('12')

            s = os.sep

            r = ck.list_all_files({'path': fname, 'limit': 10})
            self.assertEqual(0, r['return'])
            t = r['list']
            self.assertEqual({'size': 3}, t['a' + s + 'test.txt'])
            self.assertEqual({'size': 2}, t['b' + s + 'test.log'])

            r = ck.list_all_files({'path': fname, 'pattern': '*.txt'})
            self.assertEqual(0, r['return'])
            t = r['list']
            self.assertEqual({'size': 3}, t['a' + s + 'test.txt'])
            self.assertEqual(None, t.get('b' + s + 'test.log'))

            r = ck.list_all_files({'path': fname, 'file_name': 'test.txt'})
            self.assertEqual(0, r['return'])
            t = r['list']
            self.assertEqual({'size': 3}, t['a' + s + 'test.txt'])

        with test_util.tmp_dir() as fname:
            with open(fname + '/test.txt', 'a') as f:
                f.write('abc')
            with open(fname + '/test.log', 'a') as f:
                f.write('12')
            with open(fname + '/test.abc', 'a') as f:
                f.write('12')

            r = ck.list_all_files({'path': fname, 'limit': 1})
            self.assertEqual(0, r['return'])
            self.assertEqual(1, len(r['list']))

            os.makedirs(fname + '/a')
            os.makedirs(fname + '/b')

            with open(fname + '/a/1.txt', 'a') as f:
                f.write('abc')

            with open(fname + '/b/1.log', 'a') as f:
                f.write('12')

            r = ck.list_all_files({'path': fname, 'limit': 1})
            self.assertEqual(0, r['return'])
            self.assertEqual(1, len(r['list']))

    def test_save_repo_cache(self):
        r = ck.save_repo_cache({})
        self.assertEqual(0, r['return'])

        r = ck.load_json_file({'json_file': ck.work['dir_cache_repo_uoa']})
        self.assertEqual(ck.cache_repo_uoa, r['dict'])

        r = ck.load_json_file({'json_file': ck.work['dir_cache_repo_info']})
        self.assertEqual(ck.cache_repo_info, r['dict'])

    def test_load_repo_info_from_cache(self):
        r = ck.load_repo_info_from_cache({'repo_uoa': 'default'})
        self.assertEqual(0, r['return'])
        self.assertEqual('default', r['data_alias'])
        self.assertEqual('default', r['data_uoa'])

        missing_repo = ck.gen_uid({})['data_uid']
        r = ck.load_repo_info_from_cache({'repo_uoa': missing_repo})
        self.assertEqual(1, r['return'])

        r = ck.load_repo_info_from_cache({'repo_uoa': missing_repo + '='})
        self.assertEqual(1, r['return'])

    def test_find_repo_by_path(self):
        r = ck.find_path_to_repo({'repo_uoa': 'default'})
        r = ck.find_repo_by_path({'path': r['path']})
        self.assertEqual(0, r['return'])
        self.assertEqual('default', r['repo_uoa'])
        self.assertEqual('default', r['repo_alias'])

        r = ck.find_path_to_repo({'repo_uoa': 'local'})
        r = ck.find_repo_by_path({'path': r['path']})
        self.assertEqual(0, r['return'])
        self.assertEqual('local', r['repo_uoa'])
        self.assertEqual('local', r['repo_alias'])

        with test_util.tmp_file() as missing_repo_path:
            r = ck.find_repo_by_path({'path': missing_repo_path})
            self.assertEqual(16, r['return'])

        with test_util.tmp_repo() as r:
            path = r['dict']['path']
            name = r['data_name']
            r = ck.find_repo_by_path({'path': path})
            self.assertEqual(0, r['return'])
            self.assertEqual(name, r['repo_uoa'])
            self.assertEqual(name, r['repo_alias'])

    def test_find_path_to_repo(self):
        with test_util.tmp_repo() as r:
            path = r['dict']['path']
            name = r['data_name']
            r = ck.find_path_to_repo({'repo_uoa': name})
            self.assertEqual(0, r['return'])
            self.assertEqual(path, r['path'])

        missing_repo = ck.gen_uid({})['data_uid']
        r = ck.find_path_to_repo({'repo_uoa': missing_repo + '='})
        self.assertEqual(1, r['return'])

    def test_find_path_to_data(self):
        missing_data = ck.gen_uid({})['data_uid']
        r = ck.find_path_to_data({'module_uoa': 'default', 'data_uoa': missing_data})
        self.assertEqual(16, r['return'])

    def test_load_module_from_path(self):
        repo_path = ck.find_path_to_repo({'repo_uoa': 'local'})['path']
        missing_module = ck.gen_uid({})['data_uid']
        r = ck.load_module_from_path({'path': repo_path, 'module_code_name': missing_module})
        self.assertEqual(1, r['return'])

        with test_util.tmp_dir() as dname:
            module_name = 'test'
            module_file = os.path.join(dname, module_name + '.py')
            with open(module_file, 'w') as f:
                f.write('pass\n')
            r = ck.load_module_from_path({'path': dname, 'module_code_name': 'test', 'cfg': {'min_kernel_dep': '10'}})
            self.assertEqual(1, r['return'])

    def test_perform_action(self):
        r = ck.perform_action({})
        self.assertEqual(0, r['return'])        

        with test_util.tmp_dir(cwd=True):
            r = ck.perform_action({'cid': '#'})
            self.assertEqual(16, r['return'])
            self.assertEqual('repository is not detected in the current path', r['error'])

        with test_util.tmp_dir(cwd=True):
            r = ck.perform_action({'cids': ['#']})
            self.assertEqual(16, r['return'])
            self.assertEqual('repository is not detected in the current path', r['error'])

        with test_util.tmp_sys():
            r = ck.perform_action({'cid': 'default:test:', 'cids': ['default:test:'], 'action': 'cmd'})
            self.assertEqual(0, r['return'])
            self.assertTrue(sys.stdout.getvalue().strip().startswith('Command line:'))

    def test_get_api(self):
        r = ck.get_api({'func': 'list_actions'})
        self.assertEqual(0, r['return'])
        self.assertEqual('List actions in a module', r['title'].strip())
        self.assertTrue(r['api'].strip().startswith('Input:  {'))

        missing_file = ck.gen_uid({})['data_uid'] + '.py'
        with test_util.tmp_cfg('file_kernel_py', missing_file):
            r = ck.get_api({})
            self.assertEqual(1, r['return'])
            self.assertTrue(r['error'].strip().startswith('kernel not found in'))

        r = ck.get_api({'module_uoa': 'test'})
        self.assertEqual(1, r['return'])
        self.assertEqual('function not found', r['error'])

        with test_util.tmp_sys():
            r = ck.get_api({'module_uoa': 'test', 'func': 'cmd', 'out': 'con'})
            self.assertEqual(0, r['return'])
            self.assertTrue(r['api'].strip().startswith('Input:'))
            self.assertTrue(sys.stdout.getvalue().strip().startswith('Function: show cmd'))

        with test_util.tmp_sys():
            r = ck.get_api({'module_uoa': 'test', 'func': 'cmd', 'out': 'web'})
            self.assertEqual(0, r['return'])
            self.assertTrue(r['api'].strip().startswith('Input:'))
            self.assertTrue(sys.stdout.getvalue().strip().startswith('<B>Function:</B> show cmd'))

    def test_parse_cid(self):
        r = ck.parse_cid({'cid': 'a:b:c'})
        self.assertEqual(0, r['return'])
        self.assertEqual('a', r['repo_uoa'])
        self.assertEqual('b', r['module_uoa'])
        self.assertEqual('c', r['data_uoa'])

        r = ck.parse_cid({'cid': '#a'})
        self.assertEqual(1, r['return'])
        self.assertEqual('unknown CID format', r['error'])

        r = ck.parse_cid({'cid': '#a', 'ignore_error': 'yes'})
        self.assertEqual(0, r['return'])

        r = ck.parse_cid({'cid': '', 'cur_cid': {'repo_uoa': 'r', 'module_uoa': 'm', 'data_uoa': 'd'}})
        self.assertEqual(0, r['return'])
        self.assertEqual('r', r['repo_uoa'])
        self.assertEqual('m', r['module_uoa'])
        self.assertEqual('d', r['data_uoa'])

        r = ck.parse_cid({'cid': 'a', 'cur_cid': {'repo_uoa': 'r', 'module_uoa': 'm', 'data_uoa': 'd'}})
        self.assertEqual(0, r['return'])
        self.assertEqual('r', r['repo_uoa'])
        self.assertEqual('m', r['module_uoa'])
        self.assertEqual('a', r['data_uoa'])

        r = ck.parse_cid({'cid': 'a:b', 'cur_cid': {'repo_uoa': 'r', 'module_uoa': 'm', 'data_uoa': 'd'}})
        self.assertEqual(0, r['return'])
        self.assertEqual('r', r['repo_uoa'])
        self.assertEqual('a', r['module_uoa'])
        self.assertEqual('b', r['data_uoa'])

        r = ck.parse_cid({'cid': 'a:b:c:d', 'cur_cid': {'repo_uoa': 'r', 'module_uoa': 'm', 'data_uoa': 'd'}})
        self.assertEqual(1, r['return'])
        self.assertEqual('unknown CID format', r['error'])

    def test_delete_directory(self):
        with test_util.tmp_dir() as fname:
            os.makedirs(fname + '/a')
            os.makedirs(fname + '/b')

            with open(fname + '/a/test.txt', 'a') as f:
                f.write('abc')

            with open(fname + '/b/test.log', 'a') as f:
                f.write('12')

            r = ck.delete_directory({'path': fname})
            self.assertEqual(0, r['return'])
            self.assertFalse(os.path.isdir(fname))

    def test_get_by_flat_key(self):
        a = {'dyn_features':{'ft1':'1', 'ft2':'2'}, 'static_features':{'ft3':'3','ft4':'4'}}
        r = ck.get_by_flat_key({'dict': a, 'key': '##dyn_features#ft2'})
        self.assertEqual(0, r['return'])
        self.assertEqual('2', r['value'])

    def test_detect_cid_in_current_path(self):
        r = ck.find_path_to_repo({'repo_uoa': 'default'})
        r = ck.detect_cid_in_current_path({'path': r['path']})
        self.assertEqual(0, r['return'])
        self.assertEqual('default', r['repo_uoa'])
        self.assertEqual('default', r['repo_alias'])

    def test_uid(self):
        r = ck.uid({})
        self.assertEqual(0, r['return'])

        uid = r['data_uid']
        self.assertEqual(16, len(uid))

        chars = '0123456789abcdef'
        for c in uid:
            self.assertIn(c, chars)
        
    def test_version(self):
        r = ck.get_version({})
        self.assertEqual(0, r['return'])
        self.assertEqual(ck.__version__, r['version_str'])
        self.assertEqual(ck.__version__.split('.'), r['version'])

    def test_check_version(self):
        r = ck.check_version({'version': '1.7.4'})
        self.assertEqual(0, r['return'])
        self.assertEqual('yes', r['ok'])
        self.assertEqual(ck.__version__, r['current_version'])

    def test_convert_entry_to_cid(self):
        r = ck.convert_entry_to_cid({'repo_uoa': 'a', 'module_uoa': 'b', 'data_uoa': 'c'})
        self.assertEqual(0, r['return'])
        self.assertEqual('a:b:c', r['xcuoa'])

    def test_help(self):
        r = ck.help({})
        self.assertEqual(0, r['return'])
        self.assertTrue('Usage:' in r['help'])
        self.assertTrue('Common actions:' in r['help'])
        for ca in ck.cfg['common_actions']:
            self.assertTrue(ca in r['help'])

    def test_print_input(self):
        d = {'a': 1, 'b': [2, 3]}
        r = ck.print_input(d)
        self.assertEqual(0, r['return'])
        parsed_dict = ck.convert_json_str_to_dict({'str': r['html'], 'skip_quote_replacement': 'yes'})['dict']
        self.assertEqual(d, parsed_dict)

    def test_info(self):
        r = ck.info({'module_uoa': 'kernel'})
        self.assertEqual(0, r['return'])
        self.assertEqual('kernel', r['data_uoa'])
        self.assertEqual('kernel', r['data_alias'])

    def test_path(self):
        r = ck.find_path_to_repo({'repo_uoa': 'default'})
        r = ck.path({'path': r['path']})
        self.assertEqual(0, r['return'])
        self.assertEqual('default', r['repo_uoa'])
        self.assertEqual('default', r['repo_alias'])

    def test_find(self):
        r = ck.find({'module_uoa': 'kernel', 'data_uoa': 'default'})
        self.assertEqual(0, r['return'])
        self.assertEqual(1, r['number_of_entries'])
        self.assertEqual('kernel', r['module_uoa'])
        self.assertEqual('kernel', r['module_alias'])
        self.assertEqual('default', r['data_uoa'])
        self.assertEqual('local', r['repo_alias'])
        self.assertEqual('local', r['repo_uoa'])
        self.assertIn('repo_uid', r)
        self.assertIn('module_uid', r)
        self.assertIn('data_uid', r)
        self.assertIn('path_repo', r)
        self.assertIn('path', r)

#    TBD: cd now opens new bash so need to check how to handle that
#         need to add exit command ...
#
#    def test_cd(self):
#        r = ck.get_os_ck({})
#        plat = r['platform']
#
#        with test_util.tmp_sys():
#            r = ck.cd({'module_uoa': 'kernel', 'data_uoa': 'default'})
#            self.assertEqual(0, r['return'])
#
#            if plat == 'win':
#                self.assertEqual('cd /D ' + r['path'], r['string'])
#            else:
#                self.assertEqual('cd ' + r['path'], r['string'])
#
#            # check for fields from ck.find:
#            self.assertEqual(1, r['number_of_entries'])
#            self.assertEqual('kernel', r['module_uoa'])
#            self.assertEqual('kernel', r['module_alias'])
#            self.assertEqual('default', r['data_uoa'])
#            self.assertEqual('local', r['repo_alias'])
#            self.assertEqual('local', r['repo_uoa'])
#            self.assertIn('repo_uid', r)
#            self.assertIn('module_uid', r)
#            self.assertIn('data_uid', r)
#            self.assertIn('path_repo', r)
#            self.assertIn('path', r)

    def test_search(self):
        r = ck.search({'repo_uoa': 'default', 'module_uoa': 'kernel'})
        self.assertEqual(0, r['return'])
        lst = r['lst']
        self.assertEqual(1, len(lst))
        r = lst[0]
        self.assertEqual('kernel', r['module_uoa'])
        self.assertEqual('default', r['data_uoa'])
        self.assertEqual('default', r['repo_uoa'])
        self.assertIn('repo_uid', r)
        self.assertIn('module_uid', r)
        self.assertIn('data_uid', r)
        self.assertIn('path', r)

    def test_compare_dicts(self):
        d1 = {'a': 1, 'b': [2, 3]}
        d2 = {'b': [2, 3], 'a': 1}

        r = ck.compare_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('yes', r['equal'])

        d2['b'][1] = 4
        r = ck.compare_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('no', r['equal'])

    def test_compare_flat_dicts(self):
        d1 = {'a': 1, 'b': [2, 3]}
        d2 = {'b': [2, 3], 'a': 1}

        r = ck.compare_flat_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('yes', r['equal'])

        d2['b'][1] = 4
        r = ck.compare_flat_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('no', r['equal'])

    def test_compare_flat_dicts(self):
        d1 = {'a': 1, 'b': [2, 3]}
        d2 = {'b': [2, 3], 'a': 1}

        r = ck.compare_flat_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('yes', r['equal'])

        d2['b'][1] = 4
        r = ck.compare_flat_dicts({'dict1': d1, 'dict2': d2})
        self.assertEqual(0, r['return'])
        self.assertEqual('no', r['equal'])

    def test_find_string_in_dict_or_list(self):
        d = {'a': 1, 'b': [2, 3]}

        r = ck.find_string_in_dict_or_list({'dict': d, 'search_string': '2'})
        self.assertEqual(0, r['return'])
        self.assertEqual('yes', r['found'])

        r = ck.find_string_in_dict_or_list({'dict': d, 'search_string': '4'})
        self.assertEqual(0, r['return'])
        self.assertEqual('no', r['found'])

    def test_list_files(self):
        s = os.sep

        r = ck.list_files({'repo_uoa': 'default', 'module_uoa': 'module', 'data_uoa': 'kernel'})
        self.assertEqual(0, r['return'])
        lst = r['list']
        self.assertIn('module.py', lst)
        self.assertIn('test' + s + 'test_kernel.py', lst)
        self.assertIn('test' + s + 'test_original_tests.py', lst)
