Beispiel #1
0
 def _parse_actions(self, entries):
     """parse actions specified for an element
     where entries are the ones defined for this dotfile"""
     res = {
         self.key_actions_pre: [],
         self.key_actions_post: [],
     }
     for line in entries:
         fields = shlex.split(line)
         entry = fields[0]
         args = []
         if len(fields) > 1:
             args = fields[1:]
         action = None
         if self.key_actions_pre in self.actions and \
                 entry in self.actions[self.key_actions_pre]:
             kind = self.key_actions_pre
             if not args:
                 action = self.actions[self.key_actions_pre][entry]
             else:
                 a = self.actions[self.key_actions_pre][entry].action
                 action = Action(entry, kind, a, *args)
         elif self.key_actions_post in self.actions and \
                 entry in self.actions[self.key_actions_post]:
             kind = self.key_actions_post
             if not args:
                 action = self.actions[self.key_actions_post][entry]
             else:
                 a = self.actions[self.key_actions_post][entry].action
                 action = Action(entry, kind, a, *args)
         else:
             self.log.warn('unknown action \"{}\"'.format(entry))
             continue
         res[kind].append(action)
     return res
Beispiel #2
0
    def _parse(self):
        """ parse config file """
        # parse all actions
        if self.key_actions in self.content:
            if self.content[self.key_actions] is not None:
                for k, v in self.content[self.key_actions].items():
                    self.actions[k] = Action(k, v)

        # parse the profiles
        self.profiles = self.content[self.key_profiles]
        if self.profiles is None:
            self.content[self.key_profiles] = {}
            self.profiles = self.content[self.key_profiles]
        for k, v in self.profiles.items():
            if v[self.key_profiles_dots] is None:
                v[self.key_profiles_dots] = []

        # parse the configs
        self.configs = self.content[self.key_config]

        # parse the dotfiles
        if not self.content[self.key_dotfiles]:
            self.content[self.key_dotfiles] = {}
        for k, v in self.content[self.key_dotfiles].items():
            src = v[self.key_dotfiles_src]
            dst = v[self.key_dotfiles_dst]
            link = v[self.key_dotfiles_link] if self.key_dotfiles_link \
                in v else False
            entries = v[self.key_dotfiles_actions] if \
                self.key_dotfiles_actions in v else []
            actions = self._parse_actions(self.actions, entries)
            self.dotfiles[k] = Dotfile(k, dst, src, link=link, actions=actions)

        # assign dotfiles to each profile
        for k, v in self.profiles.items():
            self.prodots[k] = []
            if self.key_profiles_dots not in v:
                v[self.key_profiles_dots] = []
            if not v[self.key_profiles_dots]:
                continue
            dots = v[self.key_profiles_dots]
            if self.key_all in dots:
                self.prodots[k] = list(self.dotfiles.values())
            else:
                self.prodots[k].extend([self.dotfiles[d] for d in dots])

        # handle "include" for each profile
        for k in self.profiles.keys():
            dots = self._get_included_dotfiles(k)
            self.prodots[k].extend(dots)
            # no duplicates
            self.prodots[k] = list(set(self.prodots[k]))

        # make sure we have an absolute dotpath
        self.curdotpath = self.configs[self.key_dotpath]
        self.configs[self.key_dotpath] = self.get_abs_dotpath(self.curdotpath)
        return True
    def test_install(self):
        """Test the install function"""

        # dotpath location
        tmp = get_tempdir()
        self.assertTrue(os.path.exists(tmp))
        self.addCleanup(clean, tmp)

        # where dotfiles will be installed
        dst = get_tempdir()
        self.assertTrue(os.path.exists(dst))
        self.addCleanup(clean, dst)

        # create the dotfile in dotdrop
        f1, c1 = create_random_file(tmp)
        dst1 = os.path.join(dst, get_string(6))
        d1 = Dotfile(get_string(5), dst1, os.path.basename(f1))
        # fake a print
        self.assertTrue(str(d1) != '')
        f2, c2 = create_random_file(tmp)
        dst2 = os.path.join(dst, get_string(6))
        d2 = Dotfile(get_string(5), dst2, os.path.basename(f2))
        with open(f2, 'w') as f:
            f.write(self.TEMPLATE)
        f3, _ = create_random_file(tmp, binary=True)
        dst3 = os.path.join(dst, get_string(6))
        d3 = Dotfile(get_string(5), dst3, os.path.basename(f3))

        # create a directory dotfile
        dir1 = os.path.join(tmp, 'somedir')
        create_dir(dir1)
        fd, _ = create_random_file(dir1)
        dstd = os.path.join(dst, get_string(6))
        ddot = Dotfile(get_string(5), dstd, os.path.basename(dir1))

        # to test backup
        f4, c4 = create_random_file(tmp)
        dst4 = os.path.join(dst, get_string(6))
        d4 = Dotfile(key=get_string(6), dst=dst4, src=os.path.basename(f4))
        with open(dst4, 'w') as f:
            f.write(get_string(16))

        # to test link
        f5, c5 = create_random_file(tmp)
        dst5 = os.path.join(dst, get_string(6))
        self.addCleanup(clean, dst5)
        d5 = Dotfile(get_string(6), dst5, os.path.basename(f5), link=True)

        # create the dotfile directories in dotdrop
        dir1 = create_dir(os.path.join(tmp, get_string(6)))
        self.assertTrue(os.path.exists(dir1))
        self.addCleanup(clean, dir1)
        dst6 = os.path.join(dst, get_string(6))
        # fill with files
        sub1, _ = create_random_file(dir1, template=True)
        self.assertTrue(os.path.exists(sub1))
        sub2, _ = create_random_file(dir1)
        self.assertTrue(os.path.exists(sub2))
        # make up the dotfile
        d6 = Dotfile(get_string(6), dst6, os.path.basename(dir1))

        # to test symlink directories
        dir2 = create_dir(os.path.join(tmp, get_string(6)))
        self.assertTrue(os.path.exists(dir2))
        self.addCleanup(clean, dir2)
        dst7 = os.path.join(dst, get_string(6))
        # fill with files
        sub3, _ = create_random_file(dir2)
        self.assertTrue(os.path.exists(sub3))
        sub4, _ = create_random_file(dir2)
        self.assertTrue(os.path.exists(sub4))
        # make up the dotfile
        d7 = Dotfile(get_string(6), dst7, os.path.basename(dir2), link=True)

        # to test actions
        value = get_string(12)
        fact = '/tmp/action'
        self.addCleanup(clean, fact)
        act1 = Action('testaction', 'post',
                      'echo "{}" > {}'.format(value, fact))
        f8, c8 = create_random_file(tmp)
        dst8 = os.path.join(dst, get_string(6))
        d8 = Dotfile(get_string(6), dst8, os.path.basename(f8), actions=[act1])

        # to test transformations
        trans1 = 'trans1'
        trans2 = 'trans2'
        cmd = 'cat {0} | sed \'s/%s/%s/g\' > {1}' % (trans1, trans2)
        tr = Action('testtrans', 'post', cmd)
        f9, c9 = create_random_file(tmp, content=trans1)
        dst9 = os.path.join(dst, get_string(6))
        d9 = Dotfile(get_string(6), dst9, os.path.basename(f9), trans_r=tr)

        # to test template
        f10, _ = create_random_file(tmp, content='{{@@ header() @@}}')
        dst10 = os.path.join(dst, get_string(6))
        d10 = Dotfile(get_string(6), dst10, os.path.basename(f10))

        # generate the config and stuff
        profile = get_string(5)
        confpath = os.path.join(tmp, self.CONFIG_NAME)
        dotfiles = [d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, ddot]
        self.fake_config(confpath, dotfiles, profile, tmp, [act1], [tr])
        conf = Cfg(confpath)
        self.assertTrue(conf is not None)

        # install them
        conf, opts = load_config(confpath, profile)
        opts['safe'] = False
        opts['debug'] = True
        opts['showdiff'] = True
        opts['variables'] = {}
        cmd_install(opts, conf)

        # now compare the generated files
        self.assertTrue(os.path.exists(dst1))
        self.assertTrue(os.path.exists(dst2))
        self.assertTrue(os.path.exists(dst3))
        self.assertTrue(os.path.exists(dst5))
        self.assertTrue(os.path.exists(dst6))
        self.assertTrue(os.path.exists(dst7))
        self.assertTrue(os.path.exists(dst8))
        self.assertTrue(os.path.exists(dst10))
        self.assertTrue(os.path.exists(fd))

        # check if 'dst5' is a link whose target is 'f5'
        self.assertTrue(os.path.islink(dst5))
        self.assertTrue(os.path.realpath(dst5) == os.path.realpath(f5))

        # check if 'dst7' is a link whose target is 'dir2'
        self.assertTrue(os.path.islink(dst7))
        self.assertTrue(os.path.realpath(dst7) == os.path.realpath(dir2))

        # make sure backup is there
        b = dst4 + Installer.BACKUP_SUFFIX
        self.assertTrue(os.path.exists(b))

        self.assertTrue(filecmp.cmp(f1, dst1, shallow=True))
        f2content = open(dst2, 'r').read()
        self.assertTrue(f2content == self.RESULT)
        self.assertTrue(filecmp.cmp(f3, dst3, shallow=True))

        # test action has been executed
        self.assertTrue(os.path.exists(fact))
        self.assertTrue(str(act1) != '')
        actcontent = open(fact, 'r').read().rstrip()
        self.assertTrue(actcontent == value)

        # test transformation has been done
        self.assertTrue(os.path.exists(dst9))
        transcontent = open(dst9, 'r').read().rstrip()
        self.assertTrue(transcontent == trans2)

        # test template has been remplaced
        self.assertTrue(os.path.exists(dst10))
        tempcontent = open(dst10, 'r').read().rstrip()
        self.assertTrue(tempcontent == header())
Beispiel #4
0
    def _parse(self):
        """parse config file"""
        # parse all actions
        if self.key_actions in self.content:
            if self.content[self.key_actions] is not None:
                for k, v in self.content[self.key_actions].items():
                    # loop through all actions
                    if k in [self.key_actions_pre, self.key_actions_post]:
                        # parse pre/post actions
                        items = self.content[self.key_actions][k].items()
                        for k2, v2 in items:
                            if k not in self.actions:
                                self.actions[k] = {}
                            self.actions[k][k2] = Action(k2, k, v2)
                    else:
                        # parse naked actions as post actions
                        if self.key_actions_post not in self.actions:
                            self.actions[self.key_actions_post] = {}
                        self.actions[self.key_actions_post][k] = Action(
                            k, '', v)

        # parse read transformations
        if self.key_trans_r in self.content:
            if self.content[self.key_trans_r] is not None:
                for k, v in self.content[self.key_trans_r].items():
                    self.trans_r[k] = Transform(k, v)

        # parse write transformations
        if self.key_trans_w in self.content:
            if self.content[self.key_trans_w] is not None:
                for k, v in self.content[self.key_trans_w].items():
                    self.trans_w[k] = Transform(k, v)

        # parse the profiles
        self.lnk_profiles = self.content[self.key_profiles]
        if self.lnk_profiles is None:
            # ensures self.lnk_profiles is a dict
            self.content[self.key_profiles] = {}
            self.lnk_profiles = self.content[self.key_profiles]
        for k, v in self.lnk_profiles.items():
            if self.key_profiles_dots in v and \
                    v[self.key_profiles_dots] is None:
                # if has the dotfiles entry but is empty
                # ensures it's an empty list
                v[self.key_profiles_dots] = []

        # parse the settings
        self.lnk_settings = self.content[self.key_settings]
        self._complete_settings()

        # parse the dotfiles
        # and construct the dict of objects per dotfile key
        if not self.content[self.key_dotfiles]:
            # ensures the dotfiles entry is a dict
            self.content[self.key_dotfiles] = {}
        for k, v in self.content[self.key_dotfiles].items():
            src = os.path.normpath(v[self.key_dotfiles_src])
            dst = os.path.normpath(v[self.key_dotfiles_dst])

            # Fail if both `link` and `link_children` present
            if self.key_dotfiles_link in v \
                    and self.key_dotfiles_link_children in v:
                msg = 'only one of `link` or `link_children` allowed per'
                msg += ' dotfile, error on dotfile "{}".'
                self.log.err(msg.format(k))

            # Otherwise, get link type
            link = LinkTypes.NOLINK
            if self.key_dotfiles_link in v and v[self.key_dotfiles_link]:
                link = LinkTypes.PARENTS
            if self.key_dotfiles_link_children in v \
                    and v[self.key_dotfiles_link_children]:
                link = LinkTypes.CHILDREN

            noempty = v[self.key_dotfiles_noempty] if \
                self.key_dotfiles_noempty \
                in v else self.lnk_settings[self.key_ignoreempty]
            itsactions = v[self.key_dotfiles_actions] if \
                self.key_dotfiles_actions in v else []
            actions = self._parse_actions(itsactions)

            # parse read transformation
            itstrans_r = v[self.key_dotfiles_trans_r] if \
                self.key_dotfiles_trans_r in v else None
            trans_r = None
            if itstrans_r:
                if type(itstrans_r) is list:
                    msg = 'One transformation allowed per dotfile'
                    msg += ', error on dotfile \"{}\"'
                    self.log.err(msg.format(k))
                    msg = 'Please modify your config file to: \"trans: {}\"'
                    self.log.err(msg.format(itstrans_r[0]))
                    msg = 'see https://github.com/deadc0de6/dotdrop/wiki/transformations#config-error-with-transformation-list'  # noqa
                    self.log.err(msg)
                    return False
                trans_r = self._parse_trans(itstrans_r, read=True)
                if not trans_r:
                    msg = 'unknown trans \"{}\" for \"{}\"'
                    self.log.err(msg.format(itstrans_r, k))
                    return False

            # parse write transformation
            itstrans_w = v[self.key_dotfiles_trans_w] if \
                self.key_dotfiles_trans_w in v else None
            trans_w = None
            if itstrans_w:
                if type(itstrans_w) is list:
                    msg = 'One write transformation allowed per dotfile'
                    msg += ', error on dotfile \"{}\"'
                    self.log.err(msg.format(k))
                    msg = 'Please modify your config file: \"trans_write: {}\"'
                    self.log.err(msg.format(itstrans_w[0]))
                    msg = 'see https://github.com/deadc0de6/dotdrop/wiki/transformations#config-error-with-transformation-list'  # noqa
                    self.log.err(msg)
                    return False
                trans_w = self._parse_trans(itstrans_w, read=False)
                if not trans_w:
                    msg = 'unknown trans_write \"{}\" for \"{}\"'
                    self.log.err(msg.format(itstrans_w, k))
                    return False

            # disable transformation when link is true
            if link != LinkTypes.NOLINK and (trans_r or trans_w):
                msg = 'transformations disabled for \"{}\"'.format(dst)
                msg += ' because link is True'
                self.log.warn(msg)
                trans_r = None
                trans_w = None

            # parse cmpignore pattern
            cmpignores = v[self.key_dotfiles_cmpignore] if \
                self.key_dotfiles_cmpignore in v else []

            # parse upignore pattern
            upignores = v[self.key_dotfiles_upignore] if \
                self.key_dotfiles_upignore in v else []

            # create new dotfile
            self.dotfiles[k] = Dotfile(k,
                                       dst,
                                       src,
                                       link=link,
                                       actions=actions,
                                       trans_r=trans_r,
                                       trans_w=trans_w,
                                       cmpignore=cmpignores,
                                       noempty=noempty,
                                       upignore=upignores)

        # assign dotfiles to each profile
        for k, v in self.lnk_profiles.items():
            self.prodots[k] = []
            if self.key_profiles_dots not in v:
                # ensures is a list
                v[self.key_profiles_dots] = []
            if not v[self.key_profiles_dots]:
                continue
            dots = v[self.key_profiles_dots]
            if self.key_all in dots:
                # add all if key ALL is used
                self.prodots[k] = list(self.dotfiles.values())
            else:
                # add the dotfiles
                for d in dots:
                    if d not in self.dotfiles:
                        msg = 'unknown dotfile \"{}\" for {}'.format(d, k)
                        self.log.err(msg)
                        continue
                    self.prodots[k].append(self.dotfiles[d])

        # handle "include" for each profile
        for k in self.lnk_profiles.keys():
            dots = self._get_included_dotfiles(k)
            self.prodots[k].extend(dots)
            # remove duplicates if any
            self.prodots[k] = list(set(self.prodots[k]))

        # make sure we have an absolute dotpath
        self.curdotpath = self.lnk_settings[self.key_dotpath]
        self.lnk_settings[self.key_dotpath] = \
            self._abs_path(self.curdotpath)

        # make sure we have an absolute workdir
        self.curworkdir = self.lnk_settings[self.key_workdir]
        self.lnk_settings[self.key_workdir] = \
            self._abs_path(self.curworkdir)

        return True
Beispiel #5
0
    def _load(self):
        """load lower level config"""
        self.cfgyaml = CfgYaml(self.path,
                               self.profile_key,
                               debug=self.debug)

        # settings
        self.settings = Settings.parse(None, self.cfgyaml.settings)

        # dotfiles
        self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles)
        if self.debug:
            self._debug_list('dotfiles', self.dotfiles)

        # profiles
        self.profiles = Profile.parse_dict(self.cfgyaml.profiles)
        if self.debug:
            self._debug_list('profiles', self.profiles)

        # actions
        self.actions = Action.parse_dict(self.cfgyaml.actions)
        if self.debug:
            self._debug_list('actions', self.actions)

        # trans_r
        self.trans_r = Transform.parse_dict(self.cfgyaml.trans_r)
        if self.debug:
            self._debug_list('trans_r', self.trans_r)

        # trans_w
        self.trans_w = Transform.parse_dict(self.cfgyaml.trans_w)
        if self.debug:
            self._debug_list('trans_w', self.trans_w)

        # variables
        self.variables = self.cfgyaml.variables
        if self.debug:
            self._debug_dict('variables', self.variables)

        # patch dotfiles in profiles
        self._patch_keys_to_objs(self.profiles,
                                 "dotfiles", self.get_dotfile)

        # patch action in dotfiles actions
        self._patch_keys_to_objs(self.dotfiles,
                                 "actions", self._get_action_w_args)
        # patch action in profiles actions
        self._patch_keys_to_objs(self.profiles,
                                 "actions", self._get_action_w_args)

        # patch actions in settings default_actions
        self._patch_keys_to_objs([self.settings],
                                 "default_actions", self._get_action_w_args)
        if self.debug:
            msg = 'default actions: {}'.format(self.settings.default_actions)
            self.log.dbg(msg)

        # patch trans_w/trans_r in dotfiles
        self._patch_keys_to_objs(self.dotfiles,
                                 "trans_r",
                                 self._get_trans_w_args(self._get_trans_r),
                                 islist=False)
        self._patch_keys_to_objs(self.dotfiles,
                                 "trans_w",
                                 self._get_trans_w_args(self._get_trans_w),
                                 islist=False)
Beispiel #6
0
    def _parse(self):
        """parse config file"""
        # parse all actions
        if self.key_actions in self.content:
            if self.content[self.key_actions] is not None:
                for k, v in self.content[self.key_actions].items():
                    # loop through all actions
                    if k in [self.key_actions_pre, self.key_actions_post]:
                        # parse pre/post actions
                        items = self.content[self.key_actions][k].items()
                        for k2, v2 in items:
                            if k not in self.actions:
                                self.actions[k] = {}
                            self.actions[k][k2] = Action(k2, v2)
                    else:
                        # parse naked actions as post actions
                        if self.key_actions_post not in self.actions:
                            self.actions[self.key_actions_post] = {}
                        self.actions[self.key_actions_post][k] = Action(k, v)

        # parse all transformations
        if self.key_trans in self.content:
            if self.content[self.key_trans] is not None:
                for k, v in self.content[self.key_trans].items():
                    self.trans[k] = Transform(k, v)

        # parse the profiles
        self.lnk_profiles = self.content[self.key_profiles]
        if self.lnk_profiles is None:
            # ensures self.lnk_profiles is a dict
            self.content[self.key_profiles] = {}
            self.lnk_profiles = self.content[self.key_profiles]
        for k, v in self.lnk_profiles.items():
            if self.key_profiles_dots in v and \
                    v[self.key_profiles_dots] is None:
                # if has the dotfiles entry but is empty
                # ensures it's an empty list
                v[self.key_profiles_dots] = []

        # parse the settings
        self.lnk_settings = self.content[self.key_settings]
        self._complete_settings()

        # parse the dotfiles
        # and construct the dict of objects per dotfile key
        if not self.content[self.key_dotfiles]:
            # ensures the dotfiles entry is a dict
            self.content[self.key_dotfiles] = {}
        for k, v in self.content[self.key_dotfiles].items():
            src = v[self.key_dotfiles_src]
            dst = v[self.key_dotfiles_dst]
            link = v[self.key_dotfiles_link] if self.key_dotfiles_link \
                in v else self.default_link
            itsactions = v[self.key_dotfiles_actions] if \
                self.key_dotfiles_actions in v else []
            actions = self._parse_actions(itsactions)
            itstrans = v[self.key_dotfiles_trans] if \
                self.key_dotfiles_trans in v else []
            trans = self._parse_trans(itstrans)
            if len(trans) > 0 and link:
                msg = 'transformations disabled for \"{}\"'.format(dst)
                msg += ' because link is True'
                self.log.warn(msg)
                trans = []
            self.dotfiles[k] = Dotfile(k,
                                       dst,
                                       src,
                                       link=link,
                                       actions=actions,
                                       trans=trans)

        # assign dotfiles to each profile
        for k, v in self.lnk_profiles.items():
            self.prodots[k] = []
            if self.key_profiles_dots not in v:
                # ensures is a list
                v[self.key_profiles_dots] = []
            if not v[self.key_profiles_dots]:
                continue
            dots = v[self.key_profiles_dots]
            if self.key_all in dots:
                # add all if key ALL is used
                self.prodots[k] = list(self.dotfiles.values())
            else:
                # add the dotfiles
                self.prodots[k].extend([self.dotfiles[d] for d in dots])

        # handle "include" for each profile
        for k in self.lnk_profiles.keys():
            dots = self._get_included_dotfiles(k)
            self.prodots[k].extend(dots)
            # remove duplicates if any
            self.prodots[k] = list(set(self.prodots[k]))

        # make sure we have an absolute dotpath
        self.curdotpath = self.lnk_settings[self.key_dotpath]
        self.lnk_settings[self.key_dotpath] = self.abs_dotpath(self.curdotpath)
        return True
Beispiel #7
0
    def test_install(self):
        '''Test the install function'''

        # dotpath location
        tmp = get_tempfolder()
        self.assertTrue(os.path.exists(tmp))
        self.addCleanup(clean, tmp)

        # where dotfiles will be installed
        dst = get_tempfolder()
        self.assertTrue(os.path.exists(dst))
        self.addCleanup(clean, dst)

        # create the dotfile in dotdrop
        f1, c1 = create_random_file(tmp)
        dst1 = os.path.join(dst, get_string(6))
        d1 = Dotfile(get_string(5), dst1, os.path.basename(f1))
        # fake a print
        self.assertTrue(str(d1) != '')
        f2, c2 = create_random_file(tmp)
        dst2 = os.path.join(dst, get_string(6))
        d2 = Dotfile(get_string(5), dst2, os.path.basename(f2))
        with open(f2, 'w') as f:
            f.write(self.TEMPLATE)
        f3, _ = create_random_file(tmp, binary=True)
        dst3 = os.path.join(dst, get_string(6))
        d3 = Dotfile(get_string(5), dst3, os.path.basename(f3))

        # to test backup
        f4, c4 = create_random_file(tmp)
        dst4 = os.path.join(dst, get_string(6))
        d4 = Dotfile(get_string(6), dst4, os.path.basename(f4))
        with open(dst4, 'w') as f:
            f.write(get_string(16))

        # to test link
        f5, c5 = create_random_file(tmp)
        dst5 = os.path.join(dst, get_string(6))
        self.addCleanup(clean, dst5)
        d5 = Dotfile(get_string(6), dst5, os.path.basename(f5), link=True)

        # create the dotfile folders in dotdrop
        dir1 = create_dir(os.path.join(tmp, get_string(6)))
        self.assertTrue(os.path.exists(dir1))
        self.addCleanup(clean, dir1)
        dst6 = os.path.join(dst, get_string(6))
        # fill with files
        sub1, _ = create_random_file(dir1)
        self.assertTrue(os.path.exists(sub1))
        sub2, _ = create_random_file(dir1)
        self.assertTrue(os.path.exists(sub2))
        # make up the dotfile
        d6 = Dotfile(get_string(6), dst6, os.path.basename(dir1))

        # to test symlink folders
        dir2 = create_dir(os.path.join(tmp, get_string(6)))
        self.assertTrue(os.path.exists(dir2))
        self.addCleanup(clean, dir2)
        dst7 = os.path.join(dst, get_string(6))
        # fill with files
        sub3, _ = create_random_file(dir2)
        self.assertTrue(os.path.exists(sub3))
        sub4, _ = create_random_file(dir2)
        self.assertTrue(os.path.exists(sub4))
        # make up the dotfile
        d7 = Dotfile(get_string(6), dst7, os.path.basename(dir2), link=True)

        # to test actions
        value = get_string(12)
        fact = '/tmp/action'
        act1 = Action('testaction', 'echo "%s" > %s' % (value, fact))
        f8, c8 = create_random_file(tmp)
        dst8 = os.path.join(dst, get_string(6))
        d8 = Dotfile(get_string(6), dst8, os.path.basename(f8), actions=[act1])

        # generate the config and stuff
        profile = get_string(5)
        confpath = os.path.join(tmp, self.CONFIG_NAME)
        self.fake_config(confpath, [d1, d2, d3, d4, d5, d6, d7, d8], profile,
                         tmp, [act1])
        conf = Cfg(confpath)
        self.assertTrue(conf is not None)

        # install them
        conf, opts = load_config(confpath, profile)
        opts['safe'] = False
        opts['quiet'] = True
        install(opts, conf)

        # now compare the generated files
        self.assertTrue(os.path.exists(dst1))
        self.assertTrue(os.path.exists(dst2))
        self.assertTrue(os.path.exists(dst3))
        self.assertTrue(os.path.exists(dst5))
        self.assertTrue(os.path.exists(dst6))
        self.assertTrue(os.path.exists(dst7))
        self.assertTrue(os.path.exists(dst8))

        # check if 'dst5' is a link whose target is 'f5'
        self.assertTrue(os.path.islink(dst5))
        self.assertTrue(os.path.realpath(dst5) == os.path.realpath(f5))

        # check if 'dst7' is a link whose target is 'dir2'
        self.assertTrue(os.path.islink(dst7))
        self.assertTrue(os.path.realpath(dst7) == os.path.realpath(dir2))

        # make sure backup is there
        b = dst4 + Installer.BACKUP_SUFFIX
        self.assertTrue(os.path.exists(b))

        self.assertTrue(filecmp.cmp(f1, dst1, shallow=True))
        f2content = open(dst2, 'r').read()
        self.assertTrue(f2content == self.RESULT)
        self.assertTrue(filecmp.cmp(f3, dst3, shallow=True))

        # test action has been executed
        self.assertTrue(os.path.exists(fact))
        self.assertTrue(str(act1) != '')
        actcontent = open(fact, 'r').read().rstrip()
        self.assertTrue(actcontent == value)