示例#1
0
class TestConfig(MacsyTest):
    def setUp(self):
        self.defaults = MacsyDefaults()
        self.parsed_args = Namespace()

    def test_str_2_tuple(self):
        s = 'set_1/Flagellum 12 set_1/t4ss 13'
        expected = [('set_1/Flagellum', '12'), ('set_1/t4ss', '13')]
        cfg = Config(self.defaults, self.parsed_args)
        self.assertListEqual(cfg._str_2_tuple(s), expected)

        with self.assertRaises(ValueError) as ctx:
            s = 'set_1/Flagellum 12 set_1/t4ss'
            cfg._str_2_tuple(s)
        self.assertEqual(
            str(ctx.exception),
            f"You must provide a list of model name and value separated by spaces: {s}"
        )

    def test_config_file_2_dict(self):
        cfg = Config(self.defaults, self.parsed_args)
        res = cfg._config_file_2_dict(self.defaults, ['nimportnaoik'])
        self.assertDictEqual({}, res)

        cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_test_conf.conf'))
        res = cfg._config_file_2_dict(self.defaults, [cfg_file])
        expected = {
            'db_type': 'gembase',
            'inter_gene_max_space': 'set_1/T2SS 2 set_1/Flagellum 4',
            'min_mandatory_genes_required': 'set_1/T2SS 5 set_1/Flagellum 9',
            'replicon_topology': 'circular',
            'sequence_db': '/path/to/sequence/bank/fasta_file',
            'topology_file': '/the/path/to/the/topology/to/use'
        }
        self.assertDictEqual(expected, res)

        bad_cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_test_bad_conf.conf'))
        with self.assertRaises(ParsingError):
            cfg._config_file_2_dict(self.defaults, [bad_cfg_file])

    def test_Config(self):
        cfg = Config(self.defaults, self.parsed_args)
        methods_needing_args = {
            'inter_gene_max_space': None,
            'max_nb_genes': None,
            'min_genes_required': None,
            'min_mandatory_genes_required': None,
            'multi_loci': None
        }

        for opt, val in self.defaults.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertFalse(cfg.multi_loci('whatever'))
            elif opt in methods_needing_args:
                self.assertEqual(
                    getattr(cfg, opt)('whatever'),
                    val,
                    msg=
                    f"test of '{opt}' failed : expected{getattr(cfg, opt)('whatever')} !=  got {val}"
                )
            else:
                self.assertEqual(
                    getattr(cfg, opt)(),
                    val,
                    msg=
                    f"test of '{opt}' failed : expected{getattr(cfg, opt)()} !=  got {val}"
                )

    def test_Config_file(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'max_nb_genes': [('set_1/Flagellum', 6), ('set_1/T3SS', 3)],
            'min_genes_required': [('set_1/Flagellum', 8), ('set_1/T4SS', 4)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 12),
                                             ('set_1/T6SS', 6)],
            'multi_loci': {'set_1/Flagellum', 'T4SS'}
        }

        self.parsed_args.cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        cfg = Config(self.defaults, self.parsed_args)

        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values['cfg_file'] = self.parsed_args.cfg_file
        expected_values.update(methods_needing_args)

        for opt, val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in methods_needing_args:
                for model, genes in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), genes)
            else:
                self.assertEqual(getattr(cfg, opt)(), val)

    def test_Config_file_bad_values(self):
        ori_conf_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        config_parser = ConfigParser()
        config_parser.read(ori_conf_file)
        config_parser.add_section('general')
        config_parser.set('general', 'worker', 'foo')
        with tempfile.TemporaryDirectory() as tmpdirname:
            dest_conf_file = os.path.join(tmpdirname, 'macsyfinder.conf')
            with open(dest_conf_file, 'w') as cfg_file:
                config_parser.write(cfg_file)

            import macsypy.config
            macsyconf = macsypy.config.__MACSY_CONF__
            macsypy.config.__MACSY_CONF__ = tmpdirname
            with self.assertRaises(ValueError) as ctx:
                Config(self.defaults, self.parsed_args)
            self.assertEqual(
                str(ctx.exception),
                "Invalid value in config_file for option 'worker': "
                "invalid literal for int() with base 10: 'foo'")

    def test_Config_default_conf_file(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'max_nb_genes': [('set_1/Flagellum', 6), ('set_1/T3SS', 3)],
            'min_genes_required': [('set_1/Flagellum', 8), ('set_1/T4SS', 4)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 12),
                                             ('set_1/T6SS', 6)],
            'multi_loci': {'set_1/Flagellum', 'set_1/T4SS'}
        }
        with tempfile.TemporaryDirectory() as tmpdirname:
            ori_conf_file = self.find_data(
                os.path.join('conf_files', 'macsy_models.conf'))
            dest_conf_file = os.path.join(tmpdirname, 'macsyfinder.conf')
            shutil.copy(ori_conf_file, dest_conf_file)
            import macsypy.config
            macsyconf = macsypy.config.__MACSY_CONF__
            macsypy.config.__MACSY_CONF__ = tmpdirname
            try:
                cfg = Config(self.defaults, self.parsed_args)

                expected_values = {k: v for k, v in self.defaults.items()}
                expected_values.update(methods_needing_args)
                for opt, val in expected_values.items():
                    if opt == 'out_dir':
                        self.assertEqual(
                            cfg.out_dir(),
                            os.path.join(
                                cfg.res_search_dir(),
                                f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
                    elif opt == 'multi_loci':
                        self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                        self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                        self.assertFalse(cfg.multi_loci('set_1/T6SS'))
                    elif opt in methods_needing_args:
                        for model, genes in expected_values[opt]:
                            self.assertEqual(getattr(cfg, opt)(model), genes)
                    else:
                        self.assertEqual(getattr(cfg, opt)(), val)
            finally:
                macsypy.config.__MACSY_CONF__ = macsyconf

    def test_Config_args(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', '14'),
                                     ('set_1/T2SS', '12')],
            'max_nb_genes': [('set_1/Flagellum', '16'), ('set_1/T3SS', '13')],
            'min_genes_required': [('set_1/Flagellum', '18'),
                                   ('set_1/T4SS', '14')],
            'min_mandatory_genes_required': [('set_1/Flagellum', '22'),
                                             ('set_1/T6SS', '16')],
            'multi_loci':
            'set_1/Flagellum, set_1/T4SS',
        }
        for opt, value in methods_needing_args.items():
            setattr(self.parsed_args, opt, value)

        simple_opt = {
            'hmmer': 'foo',
            'i_evalue_sel': 20,
            'replicon_topology': 'linear',
            'db_type': 'gembase',
            'sequence_db': self.find_data(os.path.join('base',
                                                       'test_1.fasta')),
            'topology_file': __file__  # test only the existence of a file
        }

        for opt, val in simple_opt.items():
            setattr(self.parsed_args, opt, val)

        cfg = Config(self.defaults, self.parsed_args)

        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values.update(methods_needing_args)
        expected_values.update(simple_opt)
        for opt, val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in methods_needing_args:
                for model, genes in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), int(genes))

            else:
                self.assertEqual(
                    getattr(cfg, opt)(),
                    val,
                    msg=
                    f"{opt} failed: expected: val '{val}' != got '{getattr(cfg, opt)}'"
                )

    def test_Config_file_n_args(self):
        cfg_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', '4'),
                                     ('set_1/T2SS', '2')],
            'max_nb_genes': [('set_1/Flagellum', '6'), ('set_1/T3SS', '3')],
            'min_genes_required': [('set_1/Flagellum', '8'),
                                   ('set_1/T4SS', '4')],
            'min_mandatory_genes_required': [('set_1/Flagellum', '12'),
                                             ('set_1/T6SS', '6')],
            'multi_loci':
            'set_1/Flagellum, set_1/T4SS',
        }

        self.parsed_args.cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values['cfg_file'] = self.parsed_args.cfg_file
        expected_values.update(cfg_needing_args)

        cmd_needing_args = {
            'min_genes_required': [('set_1/Flagellum', 18), ('T4SS', 14)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 22),
                                             ('set_1/T6SS', 16)],
        }
        for opt, value in cmd_needing_args.items():
            setattr(self.parsed_args, opt,
                    ' '.join([f"{m} {v}" for m, v in value]))

        simple_opt = {'hmmer': 'foo', 'i_evalue_sel': 20, 'db_type': 'gembase'}
        for opt, val in simple_opt.items():
            setattr(self.parsed_args, opt, val)

        cfg = Config(self.defaults, self.parsed_args)

        expected_values.update(cmd_needing_args)
        expected_values.update(simple_opt)

        for opt, exp_val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f"macsyfinder-{strftime('%Y%m%d_%H-%M-%S')}"))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in cfg_needing_args:
                for model, val in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), int(val))

            else:
                self.assertEqual(getattr(cfg, opt)(), exp_val)

    def test_bad_values(self):
        invalid_syntax = {
            'inter_gene_max_space': 'set_1/Flagellum 4 2',
            'max_nb_genes': 'set_1/Flagellum set_1/T3SS 3',
            'min_genes_required': 'set_1/Flagellum set_1/T4SS 4',
            'min_mandatory_genes_required': '12 set_1/T6SS 6',
        }

        for opt, val in invalid_syntax.items():
            args = Namespace()
            setattr(args, opt, val)
            with self.assertRaises(ValueError) as ctx:
                Config(self.defaults, args)
            self.assertEqual(
                str(ctx.exception),
                f"Invalid syntax for '{opt}': You must provide a list of model name "
                f"and value separated by spaces: {val}.")

        int_error = {
            'inter_gene_max_space': 'set_1/Flagellum 4.2 set_1/T2SS 2',
            'max_nb_genes': 'set_1/Flagellum 4 set_1/T3SS FOO',
            'min_genes_required': 'set_1/Flagellum FOO set_1/T4SS 4',
            'min_mandatory_genes_required':
            'set_1/Flagellum 12 set_1/T6SS 6.4',
        }

        for opt, val in int_error.items():
            args = Namespace()
            setattr(args, opt, val)
            with self.assertRaises(ValueError):
                Config(self.defaults, args)

    def test_bad_db_type(self):
        self.parsed_args.db_type = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(str(ctx.exception),
                         "db_type as unauthorized value : 'FOO'.")

    def test_bad_topology(self):
        self.parsed_args.replicon_topology = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(str(ctx.exception),
                         "replicon_topology as unauthorized value : 'FOO'.")

    def test_bad_topology_file(self):
        self.parsed_args.topology_file = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "topology_file 'FOO' does not exists or is not a file.")

    def test_bad_sequence_db(self):
        self.parsed_args.sequence_db = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "sequence_db 'FOO' does not exists or is not a file.")

    def test_bad_models_dir(self):
        self.parsed_args.models_dir = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "models_dir 'FOO' does not exists or is not a directory.")

    def test_save(self):
        self.parsed_args.max_nb_genes = [['Set_1/T2SS', 5],
                                         ['set_1/Flagelum', 12]]
        self.parsed_args.multi_loci = 'Set_1/T2SS,set_1/Flagelum'
        self.parsed_args.models = [['Set_1', 'T9SS', 'T3SS', 'T4SS_typeI']]
        cfg = Config(self.defaults, self.parsed_args)
        expected = {k: v for k, v in cfg._options.items() if v is not None}
        expected['max_nb_genes'] = 'Set_1/T2SS 5 set_1/Flagelum 12'
        expected['models'] = [('models_1', 'Set_1 T9SS T3SS T4SS_typeI')]
        with tempfile.TemporaryDirectory() as tmpdirname:
            cfg_path = os.path.join(tmpdirname, 'macsyfinder.conf')
            cfg.save(path_or_buf=cfg_path)
            saved_opt = cfg._config_file_2_dict(self.defaults, [cfg_path])
            saved_opt['multi_loci'] = {
                v
                for v in
                [v.strip() for v in saved_opt['multi_loci'].split(',')] if v
            }
            self.maxDiff = None
            self.assertDictEqual(saved_opt, expected)

    def test_out_dir(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(
            cfg.out_dir(),
            os.path.join(cfg.res_search_dir(),
                         f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
        self.parsed_args.out_dir = 'foo'
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.out_dir(), 'foo')

    def test_working_dir(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.out_dir(), cfg.working_dir())

    def test_previous_n_sequence_db(self):
        self.parsed_args.previous_run = self.find_data(
            os.path.join('data_set', 'results'))
        self.parsed_args.sequence_db = self.find_data(
            os.path.join('base', 'test_1.fasta'))
        with self.catch_log() as log:
            cfg = Config(self.defaults, self.parsed_args)
            catch_msg = log.get_value().strip()
        self.assertEqual(cfg.sequence_db(), 'tests/data/base/test_1.fasta')
        self.assertEqual(
            f"ignore sequence_db '{self.parsed_args.sequence_db}' "
            f"use sequence_db from previous_run '{self.parsed_args.previous_run}'.",
            catch_msg)

    def test_previous_wo_cfg(self):
        self.parsed_args.previous_run = self.find_data(
            os.path.join('data_set'))
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            f"No config file found in dir {self.parsed_args.previous_run}")

    def test_no_cut_ga(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertFalse(cfg.no_cut_ga())
        self.parsed_args.no_cut_ga = True
        cfg = Config(self.defaults, self.parsed_args)
        self.assertTrue(cfg.no_cut_ga())

    def test_e_value_search(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(self.defaults.e_value_search, cfg.e_value_search())

        self.parsed_args.e_value_search = 1.0
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.e_value_search(), 1.0)

    def test_hit_weights(self):
        cfg = Config(self.defaults, self.parsed_args)
        default = {
            k: self.defaults[f"{k}_weight"]
            for k in ('mandatory', 'accessory', 'neutral', 'itself',
                      'exchangeable', 'loner_multi_system')
        }
        self.assertDictEqual(default, cfg.hit_weights())

        self.parsed_args.mandatory_weight = 2
        self.parsed_args.accessory_weight = 1
        self.parsed_args.exchangeable_weight = 0.5
        self.parsed_args.loner_multi_system_weight = 0.2

        cfg = Config(self.defaults, self.parsed_args)
        expected = {
            'mandatory': 2,
            'accessory': 1,
            'neutral': self.defaults.neutral_weight,
            'itself': self.defaults.itself_weight,
            'exchangeable': .5,
            'loner_multi_system': .2
        }
        self.assertDictEqual(cfg.hit_weights(), expected)
示例#2
0
class TestConfig(MacsyTest):
    def setUp(self):
        self._current_dir = os.getcwd()
        self.tmp_dir = os.path.join(tempfile.gettempdir(),
                                    'test_macsyfinder_Config')
        if os.path.exists(self.tmp_dir):
            shutil.rmtree(self.tmp_dir)
        os.mkdir(self.tmp_dir)
        self.defaults = MacsyDefaults()
        self.parsed_args = Namespace()

    def tearDown(self):
        os.chdir(self._current_dir)
        try:
            shutil.rmtree(self.tmp_dir)
            #pass
        except Exception as err:
            pass

    def test_str_2_tuple(self):
        s = 'set_1/Flagellum 12 set_1/t4ss 13'
        expected = [('set_1/Flagellum', '12'), ('set_1/t4ss', '13')]
        cfg = Config(self.defaults, self.parsed_args)
        self.assertListEqual(cfg._str_2_tuple(s), expected)

        with self.assertRaises(ValueError) as ctx:
            s = 'set_1/Flagellum 12 set_1/t4ss'
            cfg._str_2_tuple(s)
        self.assertEqual(
            str(ctx.exception),
            f"You must provide a list of model name and value separated by spaces: {s}"
        )

    def test_set_option(self):
        cfg = Config(self.defaults, self.parsed_args)
        opts = {'GOOD': "GOOD", "BAD": None}
        self.assertFalse('GOOD' in cfg._options)
        self.assertFalse('BAD' in cfg._options)
        cfg._set_options(opts)
        self.assertTrue('GOOD' in cfg._options)
        self.assertEqual(cfg._options['GOOD'], 'GOOD')
        self.assertFalse('BAD' in cfg._options)

    def test_set_log_level(self):
        cfg = Config(self.defaults, self.parsed_args)
        cfg._set_log_level(20)
        self.assertEqual(cfg.log_level(), 20)
        cfg._set_log_level('WARNING')
        self.assertEqual(cfg.log_level(), 30)
        with self.assertRaises(ValueError) as ctx:
            cfg._set_log_level('FOO')
        self.assertEqual(str(ctx.exception),
                         "Invalid value for log_level: FOO.")

    def test_config_file_2_dict(self):
        cfg = Config(self.defaults, self.parsed_args)
        res = cfg._config_file_2_dict('nimportnaoik')
        self.assertDictEqual({}, res)

        cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_test_conf.conf'))
        res = cfg._config_file_2_dict(cfg_file)
        expected = {
            'db_type': 'gembase',
            'inter_gene_max_space': 'set_1/T2SS 2 set_1/Flagellum 4',
            'min_mandatory_genes_required': 'set_1/T2SS 5 set_1/Flagellum 9',
            'replicon_topology': 'circular',
            'sequence_db': '/path/to/sequence/bank/fasta_file',
            'topology_file': '/the/path/to/the/topology/to/use'
        }
        self.assertDictEqual(expected, res)

        bad_cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_test_bad_conf.conf'))
        with self.assertRaises(ParsingError):
            cfg._config_file_2_dict(bad_cfg_file)

    def test_Config(self):
        cfg = Config(self.defaults, self.parsed_args)
        methods_needing_args = {
            'inter_gene_max_space': None,
            'max_nb_genes': None,
            'min_genes_required': None,
            'min_mandatory_genes_required': None,
            'multi_loci': None
        }

        for opt, val in self.defaults.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertFalse(cfg.multi_loci('whatever'))
            elif opt in methods_needing_args:
                self.assertEqual(
                    getattr(cfg, opt)('whatever'),
                    val,
                    msg=
                    f"test of '{opt}' failed : expected {getattr(cfg, opt)('whatever')} !=  got {val}"
                )
            elif opt == 'models_dir':
                val = self.defaults['system_models_dir']
                self.assertEqual(
                    getattr(cfg, opt)(),
                    val,
                    msg=
                    f"test of '{opt}' failed : expected {getattr(cfg, opt)()} !=  got {val}"
                )
            else:
                self.assertEqual(
                    getattr(cfg, opt)(),
                    val,
                    msg=
                    f"test of '{opt}' failed : expected {getattr(cfg, opt)()} !=  got {val}"
                )

    def test_cmd_config_file(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'max_nb_genes': [('set_1/Flagellum', 6), ('set_1/T3SS', 3)],
            'min_genes_required': [('set_1/Flagellum', 8), ('set_1/T4SS', 4)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 12),
                                             ('set_1/T6SS', 6)],
            'multi_loci': {'set_1/Flagellum', 'T4SS'}
        }

        self.parsed_args.cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        cfg = Config(self.defaults, self.parsed_args)

        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values['cfg_file'] = self.parsed_args.cfg_file
        expected_values.update(methods_needing_args)

        for opt, val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in methods_needing_args:
                for model, genes in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), genes)
            elif opt == 'models_dir':
                self.assertEqual(
                    getattr(cfg, opt)(), self.defaults['system_models_dir'])
            else:
                self.assertEqual(getattr(cfg, opt)(),
                                 val,
                                 msg=f"error with {opt}")

        self.parsed_args.cfg_file = 'niportnaoik'
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(str(ctx.exception),
                         "Config file niportnaoik not found.")

    def test_project_config_file(self):
        os.chdir(self.tmp_dir)
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'max_nb_genes': [('set_1/Flagellum', 6), ('set_1/T3SS', 3)],
            'min_genes_required': [('set_1/Flagellum', 8), ('set_1/T4SS', 4)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 12),
                                             ('set_1/T6SS', 6)],
            'multi_loci': {'set_1/Flagellum', 'T4SS'}
        }
        hmmer_opts_in_file = {'coverage_profile': 0.8, 'e_value_search': 0.12}

        try:
            shutil.copyfile(
                self.find_data(os.path.join('conf_files', 'project.conf')),
                os.path.join(self.tmp_dir, 'macsyfinder.conf'))
            cfg = Config(self.defaults, self.parsed_args)

            expected_values = {k: v for k, v in self.defaults.items()}
            expected_values.update(methods_needing_args)
            expected_values.update(hmmer_opts_in_file)

            for opt, val in expected_values.items():
                if opt == 'out_dir':
                    self.assertEqual(
                        cfg.out_dir(),
                        os.path.join(
                            cfg.res_search_dir(),
                            f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
                elif opt == 'multi_loci':
                    self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                    self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                    self.assertFalse(cfg.multi_loci('set_1/T6SS'))
                elif opt in methods_needing_args:
                    for model, genes in expected_values[opt]:
                        self.assertEqual(getattr(cfg, opt)(model), genes)
                elif opt == 'models_dir':
                    self.assertEqual(
                        getattr(cfg, opt)(),
                        self.defaults['system_models_dir'])
                else:
                    self.assertEqual(getattr(cfg, opt)(), val)
        except:
            os.chdir(self._current_dir)

    def test_Config_file_bad_values(self):
        ori_conf_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        config_parser = ConfigParser()
        config_parser.read(ori_conf_file)
        config_parser.add_section('general')
        config_parser.set('general', 'worker', 'foo')
        with tempfile.TemporaryDirectory() as tmpdirname:
            dest_conf_file = os.path.join(tmpdirname, 'macsyfinder.conf')
            with open(dest_conf_file, 'w') as cfg_file:
                config_parser.write(cfg_file)
            self.parsed_args.cfg_file = dest_conf_file
            with self.assertRaises(ValueError) as ctx:
                Config(self.defaults, self.parsed_args)
            self.assertEqual(
                str(ctx.exception),
                "Invalid value in config_file for option 'worker': "
                "invalid literal for int() with base 10: 'foo'")

    def test_Config_default_conf_file(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'max_nb_genes': [('set_1/Flagellum', 6), ('set_1/T3SS', 3)],
            'min_genes_required': [('set_1/Flagellum', 8), ('set_1/T4SS', 4)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 12),
                                             ('set_1/T6SS', 6)],
            'multi_loci': {'set_1/Flagellum', 'set_1/T4SS'}
        }
        with tempfile.TemporaryDirectory() as tmpdirname:
            ori_conf_file = self.find_data(
                os.path.join('conf_files', 'macsy_models.conf'))
            dest_conf_file = os.path.join(tmpdirname, 'macsyfinder.conf')
            shutil.copy(ori_conf_file, dest_conf_file)
            os.environ['MACSY_CONF'] = dest_conf_file
            virtual_env = os.environ.get("VIRTUAL_ENV")
            del os.environ["VIRTUAL_ENV"]
            try:
                cfg = Config(self.defaults, self.parsed_args)

                expected_values = {k: v for k, v in self.defaults.items()}
                expected_values.update(methods_needing_args)
                for opt, val in expected_values.items():
                    if opt == 'out_dir':
                        self.assertEqual(
                            cfg.out_dir(),
                            os.path.join(
                                cfg.res_search_dir(),
                                f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
                    elif opt == 'multi_loci':
                        self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                        self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                        self.assertFalse(cfg.multi_loci('set_1/T6SS'))
                    elif opt in methods_needing_args:
                        for model, genes in expected_values[opt]:
                            self.assertEqual(getattr(cfg, opt)(model), genes)
                    elif opt == 'models_dir':
                        self.assertEqual(
                            getattr(cfg, opt)(),
                            self.defaults['system_models_dir'])
                    else:
                        self.assertEqual(getattr(cfg, opt)(), val)
            finally:
                os.environ["VIRTUAL_ENV"] = virtual_env

    def test_Config_conf_file_virtualenv(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', 4),
                                     ('set_1/T2SS', 2)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 9),
                                             ('set_1/T2SS', 5)]
        }
        modified_args = {
            'db_type': 'gembase',
            'coverage_profile': 0.1,
            'replicon_topology': 'circular'
        }

        with tempfile.TemporaryDirectory() as tmpdirname:
            ori_conf_file = self.find_data(
                os.path.join('conf_files', 'macsy_virtualenv_test.conf'))
            conf_dir = os.path.join(tmpdirname, 'etc', 'macsyfinder')
            os.makedirs(conf_dir)
            dest_conf_file = os.path.join(conf_dir, 'macsyfinder.conf')
            shutil.copy(ori_conf_file, dest_conf_file)
            virtual_env = os.environ.get("VIRTUAL_ENV")

            os.environ['VIRTUAL_ENV'] = tmpdirname
            try:
                cfg = Config(self.defaults, self.parsed_args)

                expected_values = {k: v for k, v in self.defaults.items()}
                expected_values.update(methods_needing_args)
                expected_values.update(modified_args)
                for opt, val in expected_values.items():
                    if opt == 'out_dir':
                        self.assertEqual(
                            cfg.out_dir(),
                            os.path.join(
                                cfg.res_search_dir(),
                                f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
                    elif opt in ('max_nb_genes', 'min_genes_required',
                                 'multi_loci'):  # not set in cfg file
                        pass
                    elif opt in methods_needing_args:
                        for model, genes in expected_values[opt]:
                            self.assertEqual(getattr(cfg, opt)(model), genes)
                    elif opt == 'models_dir':
                        self.assertEqual(
                            getattr(cfg, opt)(),
                            self.defaults['system_models_dir'])
                    else:
                        self.assertEqual(getattr(cfg, opt)(), val)
            finally:
                if virtual_env:
                    os.environ["VIRTUAL_ENV"] = virtual_env

    def test_Config_args(self):
        methods_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', '14'),
                                     ('set_1/T2SS', '12')],
            'max_nb_genes': [('set_1/Flagellum', '16'), ('set_1/T3SS', '13')],
            'min_genes_required': [('set_1/Flagellum', '18'),
                                   ('set_1/T4SS', '14')],
            'min_mandatory_genes_required': [('set_1/Flagellum', '22'),
                                             ('set_1/T6SS', '16')],
            'multi_loci':
            'set_1/Flagellum, set_1/T4SS',
        }
        for opt, value in methods_needing_args.items():
            setattr(self.parsed_args, opt, value)

        simple_opt = {
            'hmmer': 'foo',
            'i_evalue_sel': 20,
            'replicon_topology': 'linear',
            'db_type': 'gembase',
            'sequence_db': self.find_data(os.path.join('base',
                                                       'test_1.fasta')),
            'topology_file': __file__  # test only the existence of a file
        }

        for opt, val in simple_opt.items():
            setattr(self.parsed_args, opt, val)

        cfg = Config(self.defaults, self.parsed_args)

        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values.update(methods_needing_args)
        expected_values.update(simple_opt)
        for opt, val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in methods_needing_args:
                for model, genes in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), int(genes))
            elif opt == 'models_dir':
                self.assertEqual(
                    getattr(cfg, opt)(), self.defaults['system_models_dir'])
            else:
                self.assertEqual(
                    getattr(cfg, opt)(),
                    val,
                    msg=
                    f"{opt} failed: expected: val '{val}' != got '{getattr(cfg, opt)()}'"
                )

    def test_Config_file_n_args(self):
        cfg_needing_args = {
            'inter_gene_max_space': [('set_1/Flagellum', '4'),
                                     ('set_1/T2SS', '2')],
            'max_nb_genes': [('set_1/Flagellum', '6'), ('set_1/T3SS', '3')],
            'min_genes_required': [('set_1/Flagellum', '8'),
                                   ('set_1/T4SS', '4')],
            'min_mandatory_genes_required': [('set_1/Flagellum', '12'),
                                             ('set_1/T6SS', '6')],
            'multi_loci':
            'set_1/Flagellum, set_1/T4SS',
        }

        self.parsed_args.cfg_file = self.find_data(
            os.path.join('conf_files', 'macsy_models.conf'))
        expected_values = {k: v for k, v in self.defaults.items()}
        expected_values['cfg_file'] = self.parsed_args.cfg_file
        expected_values.update(cfg_needing_args)

        cmd_needing_args = {
            'min_genes_required': [('set_1/Flagellum', 18), ('T4SS', 14)],
            'min_mandatory_genes_required': [('set_1/Flagellum', 22),
                                             ('set_1/T6SS', 16)],
        }
        for opt, value in cmd_needing_args.items():
            setattr(self.parsed_args, opt,
                    ' '.join([f"{m} {v}" for m, v in value]))

        simple_opt = {'hmmer': 'foo', 'i_evalue_sel': 20, 'db_type': 'gembase'}
        for opt, val in simple_opt.items():
            setattr(self.parsed_args, opt, val)

        cfg = Config(self.defaults, self.parsed_args)

        expected_values.update(cmd_needing_args)
        expected_values.update(simple_opt)

        for opt, exp_val in expected_values.items():
            if opt == 'out_dir':
                self.assertEqual(
                    cfg.out_dir(),
                    os.path.join(cfg.res_search_dir(),
                                 f"macsyfinder-{strftime('%Y%m%d_%H-%M-%S')}"))
            elif opt == 'multi_loci':
                self.assertTrue(cfg.multi_loci('set_1/Flagellum'))
                self.assertTrue(cfg.multi_loci('set_1/T4SS'))
                self.assertFalse(cfg.multi_loci('set_1/T6SS'))
            elif opt in cfg_needing_args:
                for model, val in expected_values[opt]:
                    self.assertEqual(getattr(cfg, opt)(model), int(val))
            elif opt == 'models_dir':
                self.assertEqual(
                    getattr(cfg, opt)(), self.defaults['system_models_dir'])
            else:
                self.assertEqual(getattr(cfg, opt)(), exp_val)

    def test_model_conf(self):
        self.parsed_args.models_dir = self.find_data('models')
        self.parsed_args.models = "Model_w_conf all"
        cfg = Config(self.defaults, self.parsed_args)
        expected_weights = {
            'mandatory': 13.0,
            'accessory': 14.0,
            'neutral': 0.0,
            'itself': 11.0,
            'exchangeable': 12.0,
            'out_of_cluster': 10.0
        }
        self.assertDictEqual(cfg.hit_weights(), expected_weights)
        self.assertEqual(cfg.i_evalue_sel(), 0.012)
        self.assertEqual(cfg.e_value_search(), 0.12)
        self.assertEqual(cfg.coverage_profile(), 0.55)
        self.assertTrue(cfg.no_cut_ga())

    def test_bad_values(self):
        invalid_syntax = {
            'inter_gene_max_space': 'set_1/Flagellum 4 2',
            'max_nb_genes': 'set_1/Flagellum set_1/T3SS 3',
            'min_genes_required': 'set_1/Flagellum set_1/T4SS 4',
            'min_mandatory_genes_required': '12 set_1/T6SS 6',
        }

        for opt, val in invalid_syntax.items():
            args = Namespace()
            setattr(args, opt, val)
            with self.assertRaises(ValueError) as ctx:
                Config(self.defaults, args)
            self.assertEqual(
                str(ctx.exception),
                f"Invalid syntax for '{opt}': You must provide a list of model name "
                f"and value separated by spaces: {val}.")

        int_error = {
            'inter_gene_max_space': 'set_1/Flagellum 4.2 set_1/T2SS 2',
            'max_nb_genes': 'set_1/Flagellum 4 set_1/T3SS FOO',
            'min_genes_required': 'set_1/Flagellum FOO set_1/T4SS 4',
            'min_mandatory_genes_required':
            'set_1/Flagellum 12 set_1/T6SS 6.4',
        }

        for opt, val in int_error.items():
            args = Namespace()
            setattr(args, opt, val)
            with self.assertRaises(ValueError):
                Config(self.defaults, args)

    def test_bad_db_type(self):
        self.parsed_args.db_type = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(str(ctx.exception),
                         "db_type as unauthorized value : 'FOO'.")

    def test_bad_topology(self):
        self.parsed_args.replicon_topology = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(str(ctx.exception),
                         "replicon_topology as unauthorized value : 'FOO'.")

    def test_bad_topology_file(self):
        self.parsed_args.topology_file = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "topology_file 'FOO' does not exists or is not a file.")

    def test_bad_sequence_db(self):
        self.parsed_args.sequence_db = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "sequence_db 'FOO' does not exists or is not a file.")

    def test_bad_models_dir(self):
        self.parsed_args.models_dir = "FOO"
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            "models_dir 'FOO' does not exists or is not a directory.")

    def test_save(self):
        self.parsed_args.max_nb_genes = [['Set_1/T2SS', 5],
                                         ['set_1/Flagelum', 12]]
        self.parsed_args.multi_loci = 'Set_1/T2SS,set_1/Flagelum'
        self.parsed_args.models = ['Set_1', 'T9SS', 'T3SS', 'T4SS_typeI']
        cfg = Config(self.defaults, self.parsed_args)
        expected = {k: v for k, v in cfg._options.items() if v is not None}
        expected['max_nb_genes'] = 'Set_1/T2SS 5 set_1/Flagelum 12'
        expected['models'] = 'Set_1 T9SS T3SS T4SS_typeI'
        # save in file 'macsyfinder.conf'
        with tempfile.TemporaryDirectory() as tmpdirname:
            cfg_path = os.path.join(tmpdirname, 'macsyfinder.conf')
            cfg.save(path_or_buf=cfg_path)
            cfg.save(path_or_buf='/tmp/macsyfinder.conf')
            new_args = Namespace()
            new_args.cfg_file = cfg_path
            restored_cfg = Config(self.defaults, new_args)
            self.maxDiff = None
            # the option cfg-file differ from the 2 configs
            # None in cfg
            # cfg_path in restored_cfg
            self.assertEqual(restored_cfg._options['cfg_file'], cfg_path)
            del (cfg._options['cfg_file'])
            del (restored_cfg._options['cfg_file'])
            self.assertDictEqual(cfg._options, restored_cfg._options)

    def test_out_dir(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(
            cfg.out_dir(),
            os.path.join(cfg.res_search_dir(),
                         f'macsyfinder-{strftime("%Y%m%d_%H-%M-%S")}'))
        self.parsed_args.out_dir = 'foo'
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.out_dir(), 'foo')

    def test_working_dir(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.out_dir(), cfg.working_dir())

    def test_models_dir(self):
        self.parsed_args.models_dir = self.tmp_dir
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.models_dir(), [self.tmp_dir])

    def test_previous_n_sequence_db(self):
        self.parsed_args.previous_run = self.find_data(
            os.path.join('data_set', 'results_1'))
        sequence_db = self.find_data(os.path.join('base', 'test_1.fasta'))
        self.parsed_args.sequence_db = sequence_db
        with self.catch_log() as log:
            cfg = Config(self.defaults, self.parsed_args)
            # The config set the parsed_args.sequence_db to None
            catch_msg = log.get_value().strip()
        self.assertEqual(cfg.sequence_db(),
                         'tests/data/base/VICH001.B.00001.C001.prt')
        self.maxDiff = None
        self.assertEqual(
            f"ignore sequence_db '{sequence_db}' use sequence_db from previous_run "
            f"'{os.path.abspath(cfg.previous_run())}'.", catch_msg)

    def test_previous_wo_cfg(self):
        self.parsed_args.previous_run = self.find_data(
            os.path.join('data_set'))
        with self.assertRaises(ValueError) as ctx:
            Config(self.defaults, self.parsed_args)
        self.assertEqual(
            str(ctx.exception),
            f"No config file found in dir {self.parsed_args.previous_run}")

    def test_no_cut_ga(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertFalse(cfg.no_cut_ga())
        self.parsed_args.no_cut_ga = True
        cfg = Config(self.defaults, self.parsed_args)
        self.assertTrue(cfg.no_cut_ga())

    def test_e_value_search(self):
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(self.defaults.e_value_search, cfg.e_value_search())

        self.parsed_args.e_value_search = 1.0
        cfg = Config(self.defaults, self.parsed_args)
        self.assertEqual(cfg.e_value_search(), 1.0)

    def test_hit_weights(self):
        cfg = Config(self.defaults, self.parsed_args)
        default = {
            k: self.defaults[f"{k}_weight"]
            for k in ('mandatory', 'accessory', 'neutral', 'itself',
                      'exchangeable', 'out_of_cluster')
        }
        self.assertDictEqual(default, cfg.hit_weights())

        self.parsed_args.mandatory_weight = 2
        self.parsed_args.accessory_weight = 1
        self.parsed_args.exchangeable_weight = 0.5
        self.parsed_args.out_of_cluster_weight = 0.2

        cfg = Config(self.defaults, self.parsed_args)
        expected = {
            'mandatory': 2,
            'accessory': 1,
            'neutral': self.defaults.neutral_weight,
            'itself': self.defaults.itself_weight,
            'exchangeable': .5,
            'out_of_cluster': .2
        }
        self.assertDictEqual(cfg.hit_weights(), expected)