From 8fd7f9bfed753dbaa5543747569b4c2543aff03d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 17 Jul 2021 09:26:34 +0200 Subject: Merging upstream version 0.15.1. Signed-off-by: Daniel Baumann --- tests/clash_path_file | 4 +- tests/conftest.py | 1 + tests/main_path_file | 2 + tests/mock_group_file | 4 +- tests/mock_path_file | 2 +- tests/test_info.py | 4 +- tests/test_main.py | 271 ++++++++++++++++++++++++++++++++++++++------------ tests/test_utils.py | 85 +++++++++++----- 8 files changed, 281 insertions(+), 92 deletions(-) create mode 100644 tests/main_path_file (limited to 'tests') diff --git a/tests/clash_path_file b/tests/clash_path_file index 4abbfca..33eeae2 100644 --- a/tests/clash_path_file +++ b/tests/clash_path_file @@ -1,3 +1,3 @@ -/a/bcd/repo1,repo1 -/e/fgh/repo2,repo2 +/a/bcd/repo1,repo1, +/e/fgh/repo2,repo2,,--haha --pp /root/x/repo1,repo1 diff --git a/tests/conftest.py b/tests/conftest.py index b3e59ed..93576d6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ def fullpath(fname: str): PATH_FNAME = fullpath('mock_path_file') PATH_FNAME_EMPTY = fullpath('empty_path_file') PATH_FNAME_CLASH = fullpath('clash_path_file') +PATH_FNAME_MAIN = fullpath('main_path_file') GROUP_FNAME = fullpath('mock_group_file') def async_mock(): diff --git a/tests/main_path_file b/tests/main_path_file new file mode 100644 index 0000000..8e87ffb --- /dev/null +++ b/tests/main_path_file @@ -0,0 +1,2 @@ +/path/to/main/,main1,m +/xxx/xx,xx, diff --git a/tests/mock_group_file b/tests/mock_group_file index 32f0a64..1187366 100644 --- a/tests/mock_group_file +++ b/tests/mock_group_file @@ -1,2 +1,2 @@ -xx: [a, b] -yy: [a, c, d] +xx:a b +yy:a c d diff --git a/tests/mock_path_file b/tests/mock_path_file index 2a5f9f9..81dc9ef 100644 --- a/tests/mock_path_file +++ b/tests/mock_path_file @@ -1,4 +1,4 @@ /a/bcd/repo1,repo1 -/a/b/c/repo3,xxx +/a/b/c/repo3,xxx,, /e/fgh/repo2,repo2 diff --git a/tests/test_info.py b/tests/test_info.py index 025aedc..c234d78 100644 --- a/tests/test_info.py +++ b/tests/test_info.py @@ -8,9 +8,9 @@ from gita import info def test_run_quiet_diff(mock_run): mock_return = MagicMock() mock_run.return_value = mock_return - got = info.run_quiet_diff(['my', 'args']) + got = info.run_quiet_diff(['--flags'], ['my', 'args']) mock_run.assert_called_once_with( - ['git', 'diff', '--quiet', 'my', 'args'], + ['git', '--flags', 'diff', '--quiet', 'my', 'args'], stderr=subprocess.DEVNULL, ) assert got == mock_return.returncode diff --git a/tests/test_main.py b/tests/test_main.py index ad501f7..490f5d2 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,28 +1,102 @@ +import os import pytest -from unittest.mock import patch, mock_open +from unittest.mock import patch from pathlib import Path import argparse +import asyncio import shlex from gita import __main__ -from gita import utils, info +from gita import utils, info, common from conftest import ( - PATH_FNAME, PATH_FNAME_EMPTY, PATH_FNAME_CLASH, GROUP_FNAME, + PATH_FNAME, PATH_FNAME_EMPTY, PATH_FNAME_CLASH, GROUP_FNAME, PATH_FNAME_MAIN, async_mock, TEST_DIR, ) +@patch('gita.utils.get_repos', return_value={'aa'}) +def test_group_name(_): + got = __main__._group_name('xx') + assert got == 'xx' + with pytest.raises(SystemExit): + __main__._group_name('aa') + + +class TestAdd: + + @pytest.mark.parametrize('input, expected', [ + (['add', '.'], ''), + (['add', '-m', '.'], 'm'), + ]) + @patch('gita.common.get_config_fname') + def test_add(self, mock_path_fname, tmp_path, input, expected): + def side_effect(input, _=None): + return tmp_path / f'{input}.txt' + mock_path_fname.side_effect = side_effect + utils.get_repos.cache_clear() + __main__.main(input) + utils.get_repos.cache_clear() + got = utils.get_repos() + assert len(got) == 1 + assert got['gita']['type'] == expected + + @patch('gita.utils.is_git', return_value=True) + def test_add_main(self, _, tmp_path, monkeypatch, tmpdir): + def side_effect(root=None): + if root is None: + return os.path.join(tmp_path, "gita") + else: + return os.path.join(root, ".gita") + + def desc(repos, **_): + print(len(repos), repos.keys()) + assert len(repos) > 0 + for r, prop in repos.items(): + if prop['type'] == 'm': + assert 'test_add_main' in r + break + else: + assert 0, 'no main repo found' + return '' + + monkeypatch.setattr(common, 'get_config_dir', side_effect) + monkeypatch.setattr(utils, 'describe', desc) + + utils.get_repos.cache_clear() + + with tmpdir.as_cwd(): + __main__.main(['add', '-m', '.']) + utils.get_repos.cache_clear() + __main__.main(['ll']) + + +@pytest.mark.parametrize('path_fname, expected', [ + (PATH_FNAME, ''), + (PATH_FNAME_CLASH, "repo2: ['--haha', '--pp']\n"), + ]) +@patch('gita.utils.is_git', return_value=True) +@patch('gita.utils.get_groups', return_value={}) +@patch('gita.common.get_config_fname') +def test_flags(mock_path_fname, _, __, path_fname, expected, capfd): + mock_path_fname.return_value = path_fname + utils.get_repos.cache_clear() + __main__.main(['flags']) + out, err = capfd.readouterr() + assert err == '' + assert out == expected + + class TestLsLl: @patch('gita.common.get_config_fname') - def testLl(self, mock_path_fname, capfd, tmp_path): + def test_ll(self, mock_path_fname, capfd, tmp_path): """ functional test """ # avoid modifying the local configuration - def side_effect(input): + def side_effect(input, _=None): return tmp_path / f'{input}.txt' - #mock_path_fname.return_value = tmp_path / 'path_config.txt' mock_path_fname.side_effect = side_effect + utils.get_repos.cache_clear() __main__.main(['add', '.']) out, err = capfd.readouterr() assert err == '' @@ -52,11 +126,11 @@ class TestLsLl: __main__.main(['ls', 'gita']) out, err = capfd.readouterr() assert err == '' - assert out.strip() == utils.get_repos()['gita'] + assert out.strip() == utils.get_repos()['gita']['path'] - def testLs(self, monkeypatch, capfd): + def test_ls(self, monkeypatch, capfd): monkeypatch.setattr(utils, 'get_repos', - lambda: {'repo1': '/a/', 'repo2': '/b/'}) + lambda: {'repo1': {'path': '/a/'}, 'repo2': {'path': '/b/'}}) monkeypatch.setattr(utils, 'describe', lambda x: x) __main__.main(['ls']) out, err = capfd.readouterr() @@ -69,21 +143,24 @@ class TestLsLl: @pytest.mark.parametrize('path_fname, expected', [ (PATH_FNAME, - "repo1 cmaster dsu\x1b[0m msg\nrepo2 cmaster dsu\x1b[0m msg\nxxx cmaster dsu\x1b[0m msg\n"), + "repo1 cmaster dsu\x1b[0m msg \nrepo2 cmaster dsu\x1b[0m msg \nxxx cmaster dsu\x1b[0m msg \n"), (PATH_FNAME_EMPTY, ""), + (PATH_FNAME_MAIN, + '\x1b[4mmain1\x1b[0m cmaster dsu\x1b[0m msg \nxx cmaster dsu\x1b[0m msg \n'), (PATH_FNAME_CLASH, - "repo1 cmaster dsu\x1b[0m msg\nrepo2 cmaster dsu\x1b[0m msg\nx/repo1 cmaster dsu\x1b[0m msg\n" + "repo1 cmaster dsu\x1b[0m msg \nrepo2 cmaster dsu\x1b[0m msg \n" ), ]) @patch('gita.utils.is_git', return_value=True) @patch('gita.info.get_head', return_value="master") @patch('gita.info._get_repo_status', return_value=("d", "s", "u", "c")) @patch('gita.info.get_commit_msg', return_value="msg") + @patch('gita.info.get_commit_time', return_value="") @patch('gita.common.get_config_fname') - def testWithPathFiles(self, mock_path_fname, _0, _1, _2, _3, path_fname, + def test_with_path_files(self, mock_path_fname, _0, _1, _2, _3, _4, path_fname, expected, capfd): - def side_effect(input): - if input == 'repo_path': + def side_effect(input, _=None): + if input == 'repos.csv': return path_fname return f'/{input}' mock_path_fname.side_effect = side_effect @@ -95,25 +172,63 @@ class TestLsLl: assert out == expected +@pytest.mark.parametrize('input, expected', [ + ({'repo1': {'path': '/a/'}, 'repo2': {'path': '/b/'}}, ''), + ]) @patch('subprocess.run') -@patch('gita.utils.get_repos', return_value={'repo1': '/a/', 'repo2': '/b/'}) -def test_freeze(_, mock_run, capfd): +@patch('gita.utils.get_repos') +def test_freeze(mock_repos, mock_run, input, expected, capfd): + mock_repos.return_value = input __main__.main(['freeze']) assert mock_run.call_count == 2 out, err = capfd.readouterr() assert err == '' - assert out == ',repo1,/a/\n,repo2,/b/\n' + assert out == expected + + +@patch('gita.utils.parse_clone_config', return_value=[ + ['git@github.com:user/repo.git', 'repo', '/a/repo']]) +@patch('gita.utils.run_async', new=async_mock()) +@patch('subprocess.run') +def test_clone(*_): + asyncio.set_event_loop(asyncio.new_event_loop()) + args = argparse.Namespace() + args.fname = ['freeze_filename'] + args.preserve_path = None + __main__.f_clone(args) + mock_run = utils.run_async.mock + assert mock_run.call_count == 1 + cmds = ['git', 'clone', 'git@github.com:user/repo.git'] + mock_run.assert_called_once_with('repo', Path.cwd(), cmds) + + +@patch('gita.utils.parse_clone_config', return_value=[ + ['git@github.com:user/repo.git', 'repo', '/a/repo']]) +@patch('gita.utils.run_async', new=async_mock()) +@patch('subprocess.run') +def test_clone_with_preserve_path(*_): + asyncio.set_event_loop(asyncio.new_event_loop()) + args = argparse.Namespace() + args.fname = ['freeze_filename'] + args.preserve_path = True + __main__.f_clone(args) + mock_run = utils.run_async.mock + assert mock_run.call_count == 1 + cmds = ['git', 'clone', 'git@github.com:user/repo.git', '/a/repo'] + mock_run.assert_called_once_with('repo', Path.cwd(), cmds) @patch('os.path.isfile', return_value=True) @patch('gita.common.get_config_fname', return_value='some path') -@patch('gita.utils.get_repos', return_value={'repo1': '/a/', 'repo2': '/b/'}) +@patch('gita.utils.get_repos', return_value={'repo1': {'path': '/a/', 'type': ''}, + 'repo2': {'path': '/b/', 'type': ''}}) @patch('gita.utils.write_to_repo_file') def test_rm(mock_write, *_): args = argparse.Namespace() args.repo = ['repo1'] __main__.f_rm(args) - mock_write.assert_called_once_with({'repo2': '/b/'}, 'w') + mock_write.assert_called_once_with( + {'repo2': {'path': '/b/', 'type': ''}}, 'w') def test_not_add(): @@ -121,17 +236,19 @@ def test_not_add(): __main__.main(['add', '/home/some/repo/']) -@patch('gita.utils.get_repos', return_value={'repo2': '/d/efg'}) +@patch('gita.utils.get_repos', return_value={'repo2': {'path': '/d/efg', + 'flags': []}}) @patch('subprocess.run') def test_fetch(mock_run, *_): + asyncio.set_event_loop(asyncio.new_event_loop()) __main__.main(['fetch']) - mock_run.assert_called_once_with(['git', 'fetch'], cwd='/d/efg') + mock_run.assert_called_once_with(['git', 'fetch'], cwd='/d/efg', shell=False) @patch( 'gita.utils.get_repos', return_value={ - 'repo1': '/a/bc', - 'repo2': '/d/efg' + 'repo1': {'path': '/a/bc', 'flags': []}, + 'repo2': {'path': '/d/efg', 'flags': []} }) @patch('gita.utils.run_async', new=async_mock()) @patch('subprocess.run') @@ -149,28 +266,28 @@ def test_async_fetch(*_): 'diff --name-only --staged', "commit -am 'lala kaka'", ]) -@patch('gita.utils.get_repos', return_value={'repo7': 'path7'}) +@patch('gita.utils.get_repos', return_value={'repo7': {'path': 'path7', 'flags': []}}) @patch('subprocess.run') def test_superman(mock_run, _, input): mock_run.reset_mock() args = ['super', 'repo7'] + shlex.split(input) __main__.main(args) expected_cmds = ['git'] + shlex.split(input) - mock_run.assert_called_once_with(expected_cmds, cwd='path7') + mock_run.assert_called_once_with(expected_cmds, cwd='path7', shell=False) @pytest.mark.parametrize('input', [ 'diff --name-only --staged', "commit -am 'lala kaka'", ]) -@patch('gita.utils.get_repos', return_value={'repo7': 'path7'}) +@patch('gita.utils.get_repos', return_value={'repo7': {'path': 'path7', 'flags': []}}) @patch('subprocess.run') def test_shell(mock_run, _, input): mock_run.reset_mock() - args = ['shell', 'repo7'] + shlex.split(input) + args = ['shell', 'repo7', input] __main__.main(args) - expected_cmds = shlex.split(input) - mock_run.assert_called_once_with(expected_cmds, cwd='path7', check=True, stderr=-2, stdout=-1) + expected_cmds = input + mock_run.assert_called_once_with(expected_cmds, cwd='path7', check=True, shell=True, stderr=-2, stdout=-1) class TestContext: @@ -184,21 +301,21 @@ class TestContext: @patch('gita.utils.get_context', return_value=Path('gname.context')) @patch('gita.utils.get_groups', return_value={'gname': ['a', 'b']}) - def testDisplayContext(self, _, __, capfd): + def test_display_context(self, _, __, capfd): __main__.main(['context']) out, err = capfd.readouterr() assert err == '' assert 'gname: a b\n' == out @patch('gita.utils.get_context') - def testReset(self, mock_ctx): + def test_reset(self, mock_ctx): __main__.main(['context', 'none']) mock_ctx.return_value.unlink.assert_called() @patch('gita.utils.get_context', return_value=None) @patch('gita.common.get_config_dir', return_value=TEST_DIR) @patch('gita.utils.get_groups', return_value={'lala': ['b'], 'kaka': []}) - def testSetFirstTime(self, *_): + def test_set_first_time(self, *_): ctx = TEST_DIR / 'lala.context' assert not ctx.is_file() __main__.main(['context', 'lala']) @@ -208,7 +325,7 @@ class TestContext: @patch('gita.common.get_config_dir', return_value=TEST_DIR) @patch('gita.utils.get_groups', return_value={'lala': ['b'], 'kaka': []}) @patch('gita.utils.get_context') - def testSetSecondTime(self, mock_ctx, *_): + def test_set_second_time(self, mock_ctx, *_): __main__.main(['context', 'kaka']) mock_ctx.return_value.rename.assert_called() @@ -216,7 +333,7 @@ class TestContext: class TestGroupCmd: @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) - def testLs(self, _, capfd): + def test_ls(self, _, capfd): args = argparse.Namespace() args.to_group = None args.group_cmd = 'ls' @@ -227,7 +344,7 @@ class TestGroupCmd: assert 'xx yy\n' == out @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) - def testLl(self, _, capfd): + def test_ll(self, _, capfd): args = argparse.Namespace() args.to_group = None args.group_cmd = None @@ -239,7 +356,7 @@ class TestGroupCmd: @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) @patch('gita.utils.write_to_groups_file') - def testRename(self, mock_write, _): + def test_rename(self, mock_write, _): args = argparse.Namespace() args.gname = 'xx' args.new_name = 'zz' @@ -250,7 +367,7 @@ class TestGroupCmd: mock_write.assert_called_once_with(expected, 'w') @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) - def testRenameError(self, *_): + def test_rename_error(self, *_): args = argparse.Namespace() args.gname = 'xx' args.new_name = 'yy' @@ -266,7 +383,7 @@ class TestGroupCmd: @patch('gita.utils.get_repos', return_value={'a': '', 'b': '', 'c': '', 'd': ''}) @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) @patch('gita.utils.write_to_groups_file') - def testRm(self, mock_write, _, __, input, expected): + def test_rm(self, mock_write, _, __, input, expected): utils.get_groups.cache_clear() args = ['group', 'rm'] + shlex.split(input) __main__.main(args) @@ -275,7 +392,7 @@ class TestGroupCmd: @patch('gita.utils.get_repos', return_value={'a': '', 'b': '', 'c': '', 'd': ''}) @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) @patch('gita.utils.write_to_groups_file') - def testAdd(self, mock_write, *_): + def test_add(self, mock_write, *_): args = argparse.Namespace() args.to_group = ['a', 'c'] args.group_cmd = 'add' @@ -287,7 +404,7 @@ class TestGroupCmd: @patch('gita.utils.get_repos', return_value={'a': '', 'b': '', 'c': '', 'd': ''}) @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) @patch('gita.utils.write_to_groups_file') - def testAddToExisting(self, mock_write, *_): + def test_add_to_existing(self, mock_write, *_): args = argparse.Namespace() args.to_group = ['a', 'c'] args.group_cmd = 'add' @@ -300,7 +417,7 @@ class TestGroupCmd: @patch('gita.utils.get_repos', return_value={'a': '', 'b': '', 'c': '', 'd': ''}) @patch('gita.common.get_config_fname', return_value=GROUP_FNAME) @patch('gita.utils.write_to_groups_file') - def testRmRepo(self, mock_write, *_): + def test_rm_repo(self, mock_write, *_): args = argparse.Namespace() args.from_group = ['a', 'c'] args.group_cmd = 'rmrepo' @@ -310,6 +427,21 @@ class TestGroupCmd: mock_write.assert_called_once_with( {'xx': ['b'], 'yy': ['a', 'c', 'd']}, 'w') + @patch('gita.common.get_config_fname') + def test_integration(self, mock_path_fname, tmp_path, capfd): + def side_effect(input, _=None): + return tmp_path / f'{input}.csv' + mock_path_fname.side_effect = side_effect + + __main__.main('add .'.split()) + utils.get_repos.cache_clear() + __main__.main('group add gita -n test'.split()) + utils.get_groups.cache_clear() + __main__.main('ll test'.split()) + out, err = capfd.readouterr() + assert err == '' + assert 'gita' in out + @patch('gita.utils.is_git', return_value=True) @patch('gita.common.get_config_fname', return_value=PATH_FNAME) @@ -319,44 +451,61 @@ def test_rename(mock_rename, _, __): args = ['rename', 'repo1', 'abc'] __main__.main(args) mock_rename.assert_called_once_with( - {'repo1': '/a/bcd/repo1', 'repo2': '/e/fgh/repo2', - 'xxx': '/a/b/c/repo3'}, + {'repo1': {'path': '/a/bcd/repo1', 'type': '', 'flags': []}, + 'xxx': {'path': '/a/b/c/repo3', 'type': '', 'flags': []}, + 'repo2': {'path': '/e/fgh/repo2', 'type': '', 'flags': []}}, 'repo1', 'abc') class TestInfo: @patch('gita.common.get_config_fname', return_value='') - def testLl(self, _, capfd): + def test_ll(self, _, capfd): args = argparse.Namespace() args.info_cmd = None __main__.f_info(args) out, err = capfd.readouterr() - assert 'In use: branch,commit_msg\nUnused: path\n' == out + assert 'In use: branch,commit_msg,commit_time\nUnused: path\n' == out assert err == '' - @patch('gita.common.get_config_fname', return_value='') - @patch('yaml.dump') - def testAdd(self, mock_dump, _): + @patch('gita.common.get_config_fname') + def test_add(self, mock_get_fname, tmpdir): args = argparse.Namespace() args.info_cmd = 'add' args.info_item = 'path' - with patch('builtins.open', mock_open(), create=True): + with tmpdir.as_cwd(): + csv_config = Path.cwd() / 'info.csv' + mock_get_fname.return_value = csv_config __main__.f_info(args) - mock_dump.assert_called_once() - args, kwargs = mock_dump.call_args - assert args[0] == ['branch', 'commit_msg', 'path'] - assert kwargs == {'default_flow_style': None} + items = info.get_info_items() + assert items == ['branch', 'commit_msg', 'commit_time', 'path'] - @patch('gita.common.get_config_fname', return_value='') - @patch('yaml.dump') - def testRm(self, mock_dump, _): + @patch('gita.common.get_config_fname') + def test_rm(self, mock_get_fname, tmpdir): args = argparse.Namespace() args.info_cmd = 'rm' args.info_item = 'commit_msg' - with patch('builtins.open', mock_open(), create=True): + with tmpdir.as_cwd(): + csv_config = Path.cwd() / 'info.csv' + mock_get_fname.return_value = csv_config __main__.f_info(args) - mock_dump.assert_called_once() - args, kwargs = mock_dump.call_args - assert args[0] == ['branch'] - assert kwargs == {'default_flow_style': None} + items = info.get_info_items() + assert items == ['branch', 'commit_time'] + + +@patch('gita.common.get_config_fname') +def test_set_color(mock_get_fname, tmpdir): + args = argparse.Namespace() + args.color_cmd = 'set' + args.color = 'redrum' # this color doesn't exist + args.situation = 'in-sync' + with tmpdir.as_cwd(): + csv_config = Path.cwd() / 'colors.csv' + mock_get_fname.return_value = csv_config + __main__.f_color(args) + info.get_color_encoding.cache_clear() # avoid side effect + items = info.get_color_encoding() + info.get_color_encoding.cache_clear() # avoid side effect + assert items == {'no-remote': 'white', 'in-sync': 'redrum', + 'diverged': 'red', 'local-ahead': 'purple', + 'remote-ahead': 'yellow'} diff --git a/tests/test_utils.py b/tests/test_utils.py index 886ddb9..39430b0 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,7 @@ import pytest import asyncio +import subprocess +from pathlib import Path from unittest.mock import patch, mock_open from gita import utils, info @@ -8,16 +10,41 @@ from conftest import ( ) +@pytest.mark.parametrize('repo_path, paths, expected', [ + ('/a/b/c/repo', ['/a/b'], ('b', 'c')), + ]) +def test_generate_dir_hash(repo_path, paths, expected): + got = utils._generate_dir_hash(repo_path, paths) + assert got == expected + + +@pytest.mark.parametrize('repos, paths, expected', [ + ({'r1': {'path': '/a/b//repo1'}, 'r2': {'path': '/a/b/repo2'}}, + ['/a/b'], {'b': ['r1', 'r2']}), + ({'r1': {'path': '/a/b//repo1'}, 'r2': {'path': '/a/b/c/repo2'}}, + ['/a/b'], {'b': ['r1', 'r2'], 'b-c': ['r2']}), + ({'r1': {'path': '/a/b/c/repo1'}, 'r2': {'path': '/a/b/c/repo2'}}, + ['/a/b'], {'b-c': ['r1', 'r2'], 'b': ['r1', 'r2']}), + ]) +def test_auto_group(repos, paths, expected): + got = utils.auto_group(repos, paths) + assert got == expected + + @pytest.mark.parametrize('test_input, diff_return, expected', [ - ([{'abc': '/root/repo/'}, False], True, 'abc \x1b[31mrepo *+_ \x1b[0m msg'), - ([{'abc': '/root/repo/'}, True], True, 'abc repo *+_ msg'), - ([{'repo': '/root/repo2/'}, False], False, 'repo \x1b[32mrepo _ \x1b[0m msg'), + ([{'abc': {'path': '/root/repo/', 'type': '', 'flags': []}}, False], + True, 'abc \x1b[31mrepo *+_ \x1b[0m msg xx'), + ([{'abc': {'path': '/root/repo/', 'type': '', 'flags': []}}, True], + True, 'abc repo *+_ msg xx'), + ([{'repo': {'path': '/root/repo2/', 'type': '', 'flags': []}}, False], + False, 'repo \x1b[32mrepo _ \x1b[0m msg xx'), ]) def test_describe(test_input, diff_return, expected, monkeypatch): monkeypatch.setattr(info, 'get_head', lambda x: 'repo') - monkeypatch.setattr(info, 'run_quiet_diff', lambda _: diff_return) - monkeypatch.setattr(info, 'get_commit_msg', lambda _: "msg") - monkeypatch.setattr(info, 'has_untracked', lambda: True) + monkeypatch.setattr(info, 'run_quiet_diff', lambda *_: diff_return) + monkeypatch.setattr(info, 'get_commit_msg', lambda *_: "msg") + monkeypatch.setattr(info, 'get_commit_time', lambda *_: "xx") + monkeypatch.setattr(info, 'has_untracked', lambda *_: True) monkeypatch.setattr('os.chdir', lambda x: None) print('expected: ', repr(expected)) print('got: ', repr(next(utils.describe(*test_input)))) @@ -26,15 +53,14 @@ def test_describe(test_input, diff_return, expected, monkeypatch): @pytest.mark.parametrize('path_fname, expected', [ (PATH_FNAME, { - 'repo1': '/a/bcd/repo1', - 'repo2': '/e/fgh/repo2', - 'xxx': '/a/b/c/repo3', + 'repo1': {'path': '/a/bcd/repo1', 'type': '', 'flags': []}, + 'repo2': {'path': '/e/fgh/repo2', 'type': '', 'flags': []}, + 'xxx': {'path': '/a/b/c/repo3', 'type': '', 'flags': []}, }), (PATH_FNAME_EMPTY, {}), (PATH_FNAME_CLASH, { - 'repo1': '/a/bcd/repo1', - 'repo2': '/e/fgh/repo2', - 'x/repo1': '/root/x/repo1' + 'repo2': {'path': '/e/fgh/repo2', 'type': '', 'flags': ['--haha', '--pp']}, + 'repo1': {'path': '/root/x/repo1', 'type': '', 'flags': []} }), ]) @patch('gita.utils.is_git', return_value=True) @@ -70,42 +96,46 @@ def test_get_groups(mock_group_fname, group_fname, expected): @patch('os.path.getsize', return_value=True) def test_custom_push_cmd(*_): with patch('builtins.open', - mock_open(read_data='push:\n cmd: hand\n help: me')): + mock_open(read_data='{"push":{"cmd":"hand","help":"me","allow_all":true}}')): cmds = utils.get_cmds_from_files() - assert cmds['push'] == {'cmd': 'hand', 'help': 'me'} + assert cmds['push'] == {'cmd': 'hand', 'help': 'me', 'allow_all': True} @pytest.mark.parametrize( 'path_input, expected', [ - (['/home/some/repo/'], '/home/some/repo,repo\n'), # add one new + (['/home/some/repo'], '/home/some/repo,some/repo,,\r\n'), # add one new (['/home/some/repo1', '/repo2'], - {'/repo2,repo2\n/home/some/repo1,repo1\n', # add two new - '/home/some/repo1,repo1\n/repo2,repo2\n'}), # add two new + {'/repo2,repo2,,\r\n', # add two new + '/home/some/repo1,repo1,,\r\n'}), # add two new (['/home/some/repo1', '/nos/repo'], - '/home/some/repo1,repo1\n'), # add one old one new + '/home/some/repo1,repo1,,\r\n'), # add one old one new ]) @patch('os.makedirs') @patch('gita.utils.is_git', return_value=True) def test_add_repos(_0, _1, path_input, expected, monkeypatch): monkeypatch.setenv('XDG_CONFIG_HOME', '/config') with patch('builtins.open', mock_open()) as mock_file: - utils.add_repos({'repo': '/nos/repo'}, path_input) - mock_file.assert_called_with('/config/gita/repo_path', 'a+') + utils.add_repos({'repo': {'path': '/nos/repo'}}, path_input) + mock_file.assert_called_with('/config/gita/repos.csv', 'a+', newline='') handle = mock_file() if type(expected) == str: handle.write.assert_called_once_with(expected) else: - handle.write.assert_called_once() + # the write order is random + assert handle.write.call_count == 2 args, kwargs = handle.write.call_args assert args[0] in expected assert not kwargs +@patch('gita.utils.write_to_groups_file') @patch('gita.utils.write_to_repo_file') -def test_rename_repo(mock_write): - utils.rename_repo({'r1': '/a/b', 'r2': '/c/c'}, 'r2', 'xxx') - mock_write.assert_called_once_with({'r1': '/a/b', 'xxx': '/c/c'}, 'w') +def test_rename_repo(mock_write, _): + repos = {'r1': {'path': '/a/b', 'type': None}, + 'r2': {'path': '/c/c', 'type': None}} + utils.rename_repo(repos, 'r2', 'xxx') + mock_write.assert_called_once_with(repos, 'w') def test_async_output(capfd): @@ -124,3 +154,10 @@ def test_async_output(capfd): out, err = capfd.readouterr() assert err == '' assert out == 'myrepo: 0\nmyrepo: 0\n\nmyrepo: 1\nmyrepo: 1\n\nmyrepo: 2\nmyrepo: 2\n\nmyrepo: 3\nmyrepo: 3\n\n' + + +def test_is_git(tmpdir): + with tmpdir.as_cwd(): + subprocess.run('git init --bare .'.split()) + assert utils.is_git(Path.cwd()) is False + assert utils.is_git(Path.cwd(), is_bare=True) is True -- cgit v1.2.3