1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-09 18:44:16 +00:00

Testing merge funcationalities in import_configs

This commit is contained in:
Davide Laezza
2019-04-22 00:38:33 +02:00
parent bcf89478ba
commit 124336ed57
4 changed files with 305 additions and 65 deletions

View File

@@ -14,6 +14,7 @@ from dotdrop.logger import Logger
class Cmd: class Cmd:
eq_ignore = ('log',)
def __init__(self, key, action): def __init__(self, key, action):
"""constructor """constructor
@@ -31,7 +32,17 @@ class Cmd:
return 'cmd({})'.format(self.__str__()) return 'cmd({})'.format(self.__str__())
def __eq__(self, other): def __eq__(self, other):
return self.__dict__ == other.__dict__ self_dict = {
k: v
for k, v in self.__dict__.items()
if k not in self.eq_ignore
}
other_dict = {
k: v
for k, v in other.__dict__.items()
if k not in self.eq_ignore
}
return self_dict == other_dict
def __hash__(self): def __hash__(self):
return hash(self.key) ^ hash(self.action) return hash(self.key) ^ hash(self.action)

View File

@@ -154,6 +154,9 @@ class Cfg:
if not self._load_config(profile=profile): if not self._load_config(profile=profile):
raise ValueError('config is not valid') raise ValueError('config is not valid')
def __eq__(self, other):
return self.cfgpath == other.cfgpath
def eval_dotfiles(self, profile, variables, debug=False): def eval_dotfiles(self, profile, variables, debug=False):
"""resolve dotfiles src/dst/actions templating for this profile""" """resolve dotfiles src/dst/actions templating for this profile"""
t = Templategen(variables=variables) t = Templategen(variables=variables)
@@ -297,7 +300,11 @@ class Cfg:
# If read transformations are None, replaces them with empty dict # If read transformations are None, replaces them with empty dict
try: try:
read_trans = self.content[self.key_trans_r] or {} read_trans = self.content[self.key_trans_r] or {}
self.trans_r = {k: Transform(k, v) for k, v in read_trans.items()} self.trans_r.update({
k: Transform(k, v)
for k, v
in read_trans.items()
})
except KeyError: except KeyError:
pass pass
@@ -305,7 +312,11 @@ class Cfg:
# If write transformations are None, replaces them with empty dict # If write transformations are None, replaces them with empty dict
try: try:
read_trans = self.content[self.key_trans_w] or {} read_trans = self.content[self.key_trans_w] or {}
self.trans_w = {k: Transform(k, v) for k, v in read_trans.items()} self.trans_w.update({
k: Transform(k, v)
for k, v
in read_trans.items()
})
except KeyError: except KeyError:
pass pass
@@ -462,6 +473,14 @@ class Cfg:
raise ValueError( raise ValueError(
'external config file not found: {}'.format(config_path)) 'external config file not found: {}'.format(config_path))
list_settings = (
(k, v)
for k, v in ext_config.lnk_settings.items()
if isinstance(v, list)
)
for k, v in list_settings:
self.lnk_settings[k] += v
ext_members = ( ext_members = (
(name, member) (name, member)
for name, member in inspect.getmembers(ext_config, is_dict) for name, member in inspect.getmembers(ext_config, is_dict)

View File

@@ -5,10 +5,12 @@ helpers for the unittests
""" """
import os import os
import random
import shutil import shutil
import string import string
import random
import tempfile import tempfile
from unittest import TestCase
import yaml import yaml
from dotdrop.options import Options, ENV_NODEBUG from dotdrop.options import Options, ENV_NODEBUG
@@ -18,6 +20,30 @@ from dotdrop.utils import strip_home
TMPSUFFIX = '-dotdrop-tests' TMPSUFFIX = '-dotdrop-tests'
class SubsetTestCase(TestCase):
def assertIsSubset(self, sub, sup):
for subKey, subValue in sub.items():
self.assertIn(subKey, sup)
supValue = sup[subKey]
if isinstance(subValue, str):
self.assertEquals(subValue, supValue)
continue
if isinstance(subValue, dict):
self.assertIsSubset(subValue, supValue)
continue
try:
iter(subValue)
self.assertTrue(all(
subItem in supValue
for subItem in subValue
))
except TypeError:
self.assertEquals(subValue, supValue)
def clean(path): def clean(path):
"""Delete file or directory""" """Delete file or directory"""
if not os.path.exists(path): if not os.path.exists(path):
@@ -157,7 +183,8 @@ def yaml_dashed_list(items, indent=0):
def create_fake_config(directory, configname='config.yaml', def create_fake_config(directory, configname='config.yaml',
dotpath='dotfiles', backup=True, create=True, dotpath='dotfiles', backup=True, create=True,
import_configs=()): import_configs=(), import_actions=(),
import_variables=()):
"""Create a fake config file""" """Create a fake config file"""
path = os.path.join(directory, configname) path = os.path.join(directory, configname)
workdir = os.path.join(directory, 'workdir') workdir = os.path.join(directory, 'workdir')
@@ -167,16 +194,36 @@ def create_fake_config(directory, configname='config.yaml',
f.write(' create: {}\n'.format(str(create))) f.write(' create: {}\n'.format(str(create)))
f.write(' dotpath: {}\n'.format(dotpath)) f.write(' dotpath: {}\n'.format(dotpath))
f.write(' workdir: {}\n'.format(workdir)) f.write(' workdir: {}\n'.format(workdir))
if import_actions:
f.write(' import_actions:\n')
f.write(yaml_dashed_list(import_actions, 4))
if import_configs: if import_configs:
f.write(' import_configs:\n') f.write(' import_configs:\n')
f.write(yaml_dashed_list(import_configs, 4)) f.write(yaml_dashed_list(import_configs, 4))
if import_variables:
f.write(' import_variables:\n')
f.write(yaml_dashed_list(import_variables, 4))
f.write('dotfiles:\n') f.write('dotfiles:\n')
f.write('profiles:\n') f.write('profiles:\n')
f.write('actions:\n') f.write('actions:\n')
return path return path
def populate_fake_config(config, dotfiles=(), profiles=()): def create_yaml_keyval(pairs, parent_dir=None, top_key=None):
if top_key:
pairs = {top_key: pairs}
if not parent_dir:
parent_dir = get_tempdir()
fd, file_name = tempfile.mkstemp(dir=parent_dir, suffix='.yaml', text=True)
with os.fdopen(fd, 'w') as f:
yaml.safe_dump(pairs, f)
return file_name
def populate_fake_config(config, dotfiles=(), profiles=(), actions=(),
trans=(), trans_write=(), variables=(),
dynvariables=()):
"""Adds some juicy content to config files""" """Adds some juicy content to config files"""
is_path = isinstance(config, str) is_path = isinstance(config, str)
if is_path: if is_path:
@@ -186,6 +233,11 @@ def populate_fake_config(config, dotfiles=(), profiles=()):
config['dotfiles'] = dotfiles config['dotfiles'] = dotfiles
config['profiles'] = profiles config['profiles'] = profiles
config['actions'] = actions
config['trans'] = trans
config['trans_write'] = trans_write
config['variables'] = variables
config['dynvariables'] = dynvariables
if is_path: if is_path:
with open(config_path, 'w') as config_file: with open(config_path, 'w') as config_file:

View File

@@ -13,11 +13,12 @@ import yaml
from dotdrop.config import Cfg from dotdrop.config import Cfg
from dotdrop.options import Options from dotdrop.options import Options
from dotdrop.linktypes import LinkTypes from dotdrop.linktypes import LinkTypes
from tests.helpers import get_tempdir, clean, \ from tests.helpers import (SubsetTestCase, _fake_args, clean,
create_fake_config, _fake_args, populate_fake_config create_fake_config, create_yaml_keyval, get_tempdir,
populate_fake_config)
class TestConfig(unittest.TestCase): class TestConfig(SubsetTestCase):
CONFIG_BACKUP = False CONFIG_BACKUP = False
CONFIG_CREATE = True CONFIG_CREATE = True
@@ -200,78 +201,235 @@ profiles:
conf = Cfg(confpath) conf = Cfg(confpath)
self.assertTrue(conf is not None) self.assertTrue(conf is not None)
def test_include_profiles(self): def test_import_configs_merge(self):
"""Test import_configs when all config keys merge."""
tmp = get_tempdir() tmp = get_tempdir()
self.assertTrue(os.path.exists(tmp)) self.assertTrue(os.path.exists(tmp))
self.addCleanup(clean, tmp) self.addCleanup(clean, tmp)
# create the imported base config file vars_ed = {
imported = create_fake_config(tmp, 'variables': {
configname=self.CONFIG_NAME_2, 'a_var_ed': '33',
dotpath=self.CONFIG_DOTPATH, },
backup=self.CONFIG_BACKUP, 'dynvariables': {
create=self.CONFIG_CREATE) 'a_dynvar_ed': 'echo 33',
# create the importing base config file },
importing = create_fake_config(tmp, }
configname=self.CONFIG_NAME, vars_ing = {
dotpath=self.CONFIG_DOTPATH, 'variables': {
backup=self.CONFIG_BACKUP, 'a_var_ing': 'dd',
create=self.CONFIG_CREATE, },
import_configs=(imported,)) 'dynvariables': {
'a_dynvar_ing': 'echo dd',
},
}
vars_ed_file = create_yaml_keyval(vars_ed, tmp)
vars_ing_file = create_yaml_keyval(vars_ing, tmp)
# keys imported = {
keys = { 'config': {
'dotfile1': 'f_vimrc', 'dotpath': 'importing',
'dotfile2': 'f_xinitrc', 'import_variables': [vars_ed_file],
'profile1': 'host1', },
'profile2': 'host2', 'dotfiles': {
} 'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'},
},
'profiles': {
'host1': {
'dotfiles': ['f_vimrc'],
},
},
'actions': {
'pre': {
'a_pre_log_ed': 'echo pre 2',
},
'post': {
'a_post_log_ed': 'echo post 2',
},
'a_log_ed': 'echo 2',
},
'trans': {
't_log_ed': 'echo 3',
},
'trans_write': {
'tw_log_ed': 'echo 4',
},
'variables': {
'v_log_ed': '42',
},
'dynvariables': {
'dv_log_ed': 'echo 5',
},
}
importing = {
'config': {
'dotpath': 'importing',
'import_variables': [vars_ing_file],
},
'dotfiles': {
'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'},
},
'profiles': {
'host2': {
'dotfiles': ['f_xinitrc'],
'include': ['host1'],
},
},
'actions': {
'pre': {
'a_pre_log_ing': 'echo pre a',
},
'post': {
'a_post_log_ing': 'echo post a',
},
'a_log_ing': 'echo a',
},
'trans': {
't_log_ing': 'echo b',
},
'trans_write': {
'tw_log_ing': 'echo c',
},
'variables': {
'v_log_ing': 'd',
},
'dynvariables': {
'dv_log_ing': 'echo e',
},
}
# create the imported base config file
imported_path = create_fake_config(tmp,
configname=self.CONFIG_NAME_2,
**imported['config'])
# create the importing base config file
importing_path = create_fake_config(tmp,
configname=self.CONFIG_NAME,
import_configs=(imported_path,),
**importing['config'])
# edit the imported config # edit the imported config
dotfiles_imported = { populate_fake_config(imported_path, **{
keys['dotfile1']: {'dst': '~/.vimrc', 'src': 'vimrc'}, k: v
} for k, v in imported.items()
profiles_imported = { if k != 'config'
keys['profile1']: {'dotfiles': [keys['dotfile1']]}, })
}
populate_fake_config(imported,
dotfiles=dotfiles_imported,
profiles=profiles_imported)
# edit the importing config # edit the importing config
dotfiles_importing = { populate_fake_config(importing_path, **{
keys['dotfile2']: {'dst': '~/.vimrc', 'src': 'vimrc'}, k: v
} for k, v in importing.items()
profiles_importing = { if k != 'config'
keys['profile2']: { })
'dotfiles': [keys['dotfile2']],
'include': [keys['profile1']],
}
}
populate_fake_config(importing,
dotfiles=dotfiles_importing,
profiles=profiles_importing)
# do the tests # do the tests
importing_cfg = Cfg(importing) importing_cfg = Cfg(importing_path)
imported_cfg = Cfg(imported_path)
self.assertIsNotNone(importing_cfg) self.assertIsNotNone(importing_cfg)
self.assertIsNotNone(imported_cfg)
# test profile # test settings
profiles = importing_cfg.get_profiles() self.assertIsSubset(imported_cfg.lnk_settings,
self.assertIn(keys['profile2'], profiles) importing_cfg.lnk_settings)
# test profiles
self.assertIsSubset(imported_cfg.lnk_profiles,
importing_cfg.lnk_profiles)
# test dotfiles # test dotfiles
importing_cfg_dotfiles = [ self.assertIsSubset(imported_cfg.dotfiles,
(dotfile.key, {'src': dotfile.src, 'dst': dotfile.dst}) importing_cfg.dotfiles)
for dotfile in importing_cfg.prodots[keys['profile2']]
]
self.assertIn( # test actions
(keys['dotfile2'], dotfiles_importing[keys['dotfile2']]), self.assertIsSubset(imported_cfg.actions['pre'],
importing_cfg_dotfiles) importing_cfg.actions['pre'])
self.assertIn( self.assertIsSubset(imported_cfg.actions['post'],
(keys['dotfile1'], dotfiles_imported[keys['dotfile1']]), importing_cfg.actions['post'])
importing_cfg_dotfiles)
# test transactions
self.assertIsSubset(imported_cfg.trans_r,
importing_cfg.trans_r)
self.assertIsSubset(imported_cfg.trans_w,
importing_cfg.trans_w)
# test transactions
self.assertIsSubset(imported_cfg.trans_r,
importing_cfg.trans_r)
self.assertIsSubset(imported_cfg.trans_w,
importing_cfg.trans_w)
# test prodots
self.assertIsSubset(imported_cfg.prodots,
importing_cfg.prodots)
# test ext_variables
self.assertIsSubset(imported_cfg.ext_variables,
importing_cfg.ext_variables)
# test ext_dynvariables
self.assertIsSubset(imported_cfg.ext_dynvariables,
importing_cfg.ext_dynvariables)
def test_import_configs_override(self):
imported = {
'config': {
'dotpath': 'imported',
'backup': False,
},
'dotfiles': {
'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'},
},
'profiles': {
'host1': {
'dotfiles': ['f_vimrc'],
},
},
'actions': {
'a_log': 'echo 2',
},
'trans': {
't_log': 'echo 3',
},
'trans_write': {
'tw_log': 'echo 4',
},
'variables': {
'v_log': '42',
},
'dynvariables': {
'dv_log': 'echo 5',
},
}
importing = {
'config': {
'dotpath': 'importing',
'backup': True,
},
'dotfiles': {
'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'},
},
'profiles': {
'host2': {
'dotfiles': ['f_xinitrc'],
'include': ['host1'],
},
},
'actions': {
'a_log': 'echo a',
},
'trans': {
't_log': 'echo b',
},
'trans_write': {
'tw_log': 'echo c',
},
'variables': {
'v_log': 'd',
},
'dynvariables': {
'dv_log': 'echo e',
},
}
def main(): def main():