def test__symlink_pointing_at_cycle__throws(fs, links_db): # pylint: disable=unused-argument """Catch when referencing a symlink cycle""" if not platform.WINDOWS: cycle_path, target = expand('~/foo'), 'foo' fs.create_symlink(cycle_path.exp, target) #| with raises(OSError, match='Too many levels of symbolic links'): links.ln('~/leaf', cycle_path.orig)
def test__symlink_pointing_at_self__throws(fs, links_db): # pylint: disable=unused-argument """Catch accidental creation of self-referential symlinks""" file_path = expand('~/foo') fs.create_file(file_path.exp) # TODO: add parent-dir symlinks to look through before hitting `foo`, to # exercise the `realpath` stuff. #| with raises(exceptions.PathInvalidError, match='Symlink points at itself'): links.ln(file_path.orig, file_path.orig)
def test__tracked_link_exists_with_correct_target__ignores( caplog, fs, links_db): """Creating an existing symlink should do nothing""" caplog.set_level(logging.DEBUG) file_path, link_path, target = expand('~/file.txt'), expand( '~/link'), 'file.txt' db_serial = 5 fs.create_file(file_path.exp) fs.create_symlink(link_path.exp, target) links_db.db.serial = db_serial links_db.add(link_path.exp, target, db_serial - 1) #| result = links.ln(link_path.orig, file_path.orig) entry = links_db.find(link_path.exp) #| assert 'Skipping managed symlink' in caplog.text assert result == LinkResult.LINK_OK assert entry == { 'link': link_path.exp, 'target': target, 'last': db_serial }
def test__link_not_exist_and_target_exists__shortens_creates_and_tracks( caplog, fs, links_db): """Basic behavior of creating new symlinks""" caplog.set_level(logging.DEBUG) file_contents = 'abc' file_path = expand('~/path/to/actual/file.txt') link_path = expand('~/path/to/the/.link') target = '../actual/file.txt' db_serial = 12 fs.create_file(file_path.exp, contents=file_contents) links_db.db.serial = db_serial #| result = links.ln(link_path.orig, file_path.orig) entry = links_db.find(link_path.exp) #| assert 'Creating symlink' in caplog.text assert 'Creating parent folder' in caplog.text assert result == LinkResult.LINK_OK assert entry == { 'link': link_path.exp, 'target': target, 'last': db_serial } assert os.readlink(link_path.exp).replace( '\\', '/') == target, 'link is shortened and not absolute' assert fs.resolve(link_path.exp).path.replace( '\\', '/') == file_path.exp, 'link resolves correctly' assert open(link_path.exp, 'r').read() == file_contents, 'link target matches expected'
def test__tracked_link_exists_with_different_target__updates( caplog, fs, links_db): """A symlink we were tracking has changed in spec, so update the symlink""" caplog.set_level(logging.DEBUG) file_moved_path, link_path = expand('~/moved.txt'), expand('~/link') target_existing, target_moved = 'existing.txt', 'moved.txt' db_serial = 30 # symlink and db entry point to old location fs.create_symlink(link_path.exp, target_existing) links_db.db.serial = db_serial links_db.add(link_path.exp, target_existing, db_serial - 1) # but the file has been 'moved' already to its new location fs.create_file(file_moved_path.exp) #| result = links.ln(link_path.orig, file_moved_path.orig) entry = links_db.find(link_path.exp) #| assert 'Moving managed symlink' in caplog.text assert result == LinkResult.LINK_OK assert entry['target'] == target_moved
def test__untracked_link_exists_with_correct_target__tracks_and_ignores( caplog, fs, links_db): """Should take over an already-correct symlink""" caplog.set_level(logging.DEBUG) file_path, link_path, target = expand('~/file.txt'), expand( '~/link'), 'file.txt' db_serial = 20 fs.create_file(file_path.exp) fs.create_symlink(link_path.exp, target) links_db.db.serial = db_serial #| result = links.ln(link_path.orig, file_path.orig) entry = links_db.find(link_path.exp) #| assert 'Taking ownership of existing symlink' in caplog.text assert result == LinkResult.LINK_OK assert entry == { 'link': link_path.exp, 'target': target, 'last': db_serial }
def test__target_not_exist__ignores(caplog, links_db): """Ignore symlinks referring to nonexistent target paths (will be very common across OS's)""" caplog.set_level(logging.DEBUG) link_path = '~/link' #| result = links.ln(link_path, '~/no-file.txt') entry = links_db.find(link_path) #| assert 'does not exist; skipping' in caplog.text assert result == LinkResult.NO_TARGET assert entry is None assert not os.path.exists(f'{HOME}/link')
def test__untracked_link_exists_with_different_target__returns_mismatch( caplog, fs, links_db): # pylint: disable=unused-argument """Symlink already exists, but is not managed and is pointing somewhere unexpected""" caplog.set_level(logging.DEBUG) file_path, link_path = expand('~/file.txt'), expand('~/link') fs.create_file(file_path.exp) fs.create_symlink(link_path.exp, 'otherfile.txt') #| result = links.ln(link_path.orig, file_path.orig) #| assert 'Unmanaged symlink found' in caplog.text assert result == LinkResult.LINK_MISMATCH
def test__dup_target_and_link__throws(fs, links_db): # pylint: disable=unused-argument """Catch accidental duplication of symlinks""" file_path, file2_path, link_path = expand('~/file.txt'), expand( '~/file2.txt'), expand('~/link') fs.create_file(file_path.exp) fs.create_file(file2_path.exp) links.ln(link_path.orig, file_path.orig) with raises(exceptions.UpdotError, match='Duplicate creation of symlink'): links.ln(link_path.orig, file_path.orig) with raises(exceptions.UpdotError, match='Duplicate creation of symlink'): links.ln(link_path.orig, file2_path.orig)
def test__basic_integration_scenario__succeeds(links_db): # pylint: disable=unused-argument # pylint: disable=bad-whitespace #| # mkdir('~/bin') # mkdir('~/go/bin') # mkdir('~/.ssh') PROJ = '~/proj' ln('~/dotfiles/config', '~/.config') ln('~/Common/Private', '~/dotfiles/private') ln('~/dotfiles/private/ssh/authorized_keys', '~/.ssh/authorized_keys') # ln('~/.config/tmux/tmux.conf', '~/.tmux.conf', if_app='tmux') # ln('~/.config/pdb/pdbrc.py', '~/.pdbrc.py', if_app='python') # ln('~/.config/hyper/hyper.js', '~/.hyper.js', if_app='hyper') ln('~/dotfiles/special/zsh/zshenv', '~/.zshenv') ln('~/.config/termux', '~/.termux') ln('~/storage/shared/Sync/Common', '~/Common') ln('~/dotfiles/special/cygwin/profile', '~/.profile') ln('~/dotfiles/special/cygwin/minttyrc', '~/.minttyrc') ln('~/.config/git/config-windows', '~/.gitconfig') ln('~/.config/hg/hgrc', '~/.hgrc') ln('~/.config/omnisharp', '~/.omnisharp') ln('~/dotfiles/private/openvpn/config', '~/OpenVPN/config') ln('~/Common/_Settings/gimp-2.8', '.gimp-2.8') ln('~/Common/_Settings/Ssh', '.ssh') ln('~/Common/_Settings/minttyrc.txt', '.minttyrc') ln('~/Common/Visual Studio 2017', 'Documents/Visual Studio 2017') ln('~/Common/WindowsPowerShell', 'Documents/WindowsPowerShell') if platform.WINDOWS: APPDATA = os.getenv('APPDATA') ln('~/dotfiles/special/vscode/User', f'{APPDATA}/Code/User') ln('~/Games/Factorio', f'{APPDATA}/Factorio') ln('~/Programs/Everything', f'{APPDATA}/Everything') ln(f'{PROJ}/unity-meta', '~/unity-meta')