"""Tests for the `genomic_config.base_config` module. These also indirectly
test the `genomic_config.ini_config` module. These will probably need
re-structuring as other parsers are added.
"""
from genomic_config import genomic_config, base_config, ini_config, regions
import os
import pytest
import pprint as pp


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_config_file_direct(tmpdir, tmpfile):
    """Test that when a genomic config file is actually provided (and not
    obtained from a default location) that the file is correctly passed through
    and returned.
    """
    # Pass through, give a file and get the same one back
    confile = genomic_config.get_config_file(config_file=tmpfile)
    assert confile == tmpfile, "incorrect file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "ext",
    # Each element in this list represents a single parameter
    [".ini", ".cnf", ".cfg"]
)
def test_get_config_file_home(tmpdir, ext):
    """The to get a configuration file location from the root of the home
    directory. This tests the various allowed file extensions for a genomic
    config file are found.
    """
    try:
        # Remove the environment so it won't be found in the next test below,
        # just in case it has been set elsewhere or in the tests elsewhere
        del os.environ[base_config.GENOMIC_CONFIG_ENV]
    except KeyError:
        # not set
        pass

    # We fake a home file in our tmp dir and then alter the HOME environment
    # to see if we can find it
    fake_home_file = os.path.join(
        tmpdir, "{0}{1}".format(base_config.GENOMIC_CONFIG_DEFAULT_NAME, ext)
    )
    open(fake_home_file, 'wt').close()

    os.environ['HOME'] = str(tmpdir)
    confile = genomic_config.get_config_file()
    assert confile == fake_home_file, "incorrect file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_config_file_env(tmpdir, tmpfile):
    """Test to get a configuration file location from the environment variable
    """
    # Now test that it correctly uses the environment variable
    os.environ[base_config.GENOMIC_CONFIG_ENV] = tmpfile

    # Pass through, give a file and get the same one back
    confile = genomic_config.get_config_file()
    assert confile == tmpfile, "incorrect file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_config_file_fail(tmpdir):
    """Test that a file not found error is raised when a genomic config file is
    not provided and is not present in any of the default locations.
    """
    try:
        # Remove the environment so it won't be found in the next test below,
        # just in case it has been set elsewhere or in the tests elsewhere
        del os.environ[base_config.GENOMIC_CONFIG_ENV]
    except KeyError:
        # not set
        pass

    os.environ['HOME'] = str(tmpdir)

    with pytest.raises(FileNotFoundError) as e:
        genomic_config.get_config_file()

    assert e.value.args[0] == "can't locate genomic configuration file", \
        "wrong error message"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_defaults(config_sections):
    """Test that the expected fields are present in the blank default config.
    """
    default_config = base_config._BaseConfig.get_config_defaults()

    for test_key in config_sections:
        assert test_key in default_config
        assert isinstance(default_config[test_key], dict)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_open_cm(config_file):
    """Test that we can open the config file with the context manager.
    """
    with genomic_config.open(config_file) as cfg:
        assert cfg.seed == 1984, "wrong seed value"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_open(config_file):
    """Test that we can open the config file without the context manager.
    """
    cfg = genomic_config.open(config_file)
    seed = cfg.seed
    cfg.close()

    assert seed == 1984, "wrong seed value"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_open_ini(config_file):
    """Test that we can open the config file directly from the
    `genomic_config.ini_config.IniConfig` with a context manager.
    """
    parser = ini_config.IniConfig.open_ini(config_file)
    with ini_config.IniConfig(parser) as cfg:
        assert cfg.seed == 1984, "wrong seed value"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_seed(config_file):
    """Test that the expected random seed is returned.
    """
    with genomic_config.open(config_file) as cfg:
        assert cfg.seed == 1984, "wrong seed value"

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_version(config_file):
    """Test that the expected random seed is returned.
    """
    with genomic_config.open(config_file) as cfg:
        assert cfg.version == "template-0.1", "wrong version value"

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "name,exp_result",
    # Each element in this list represents a single parameter
    [
        ('European', ['UKBB_EUR', 'ALFA_EUR', 'GNOMAD31_NFE',
                      'GNOMAD2EX_NFE', '1KG_EUR', '1KG_CEU']),
    ]
)
def test_population_map(config_file, name, exp_result):
    """Test that the correct population mapping is returned.
    """
    with genomic_config.open(config_file) as cfg:
        assert cfg.get_population_map(name) == exp_result, "wrong populations"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_available_populations(config_file):
    """Test that all the available populations are returned from the config
    file.
    """
    exp_result = ['European', 'British', 'Finnish', 'African',
                  'AfricanAmerican', 'AfricanCaribbean', 'EastAsian',
                  'LatinAfricanAmerican', 'LatinAmerican', 'Asian',
                  'SouthAsian', 'Amish', 'AshkenaziJewish', 'MiddleEastern',
                  'BangladeshBengali', 'ChineseDai', 'ChineseHan',
                  'ChineseSouthHan', 'Colombian', 'NigerianEsan',
                  'IndianGujarati', 'GambianWestern', 'IndianTelugu',
                  'SpanishIberian', 'Japanese', 'Vietnamese',
                  'KenyianLuhya', 'SierraLeoneMende', 'Mexican',
                  'Peruvians', 'PakistaniPunjabi', 'PuertoRicans',
                  'SriLankanTamil', 'Italian', 'NigerianYoruban']
    with genomic_config.open(config_file) as cfg:
        assert cfg.get_available_populations() == exp_result, "wrong populations"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "synonym,species",
    # Each element in this list represents a single parameter
    [
        ('homo_sapiens', 'human'),
        ('homo sapiens', 'human'),
        ('people', 'human'),
        ('man', 'human'),
        ('woman', 'human'),
        ('human', 'human'),
        ('humans', 'human'),
        ('muppets', 'muppets')
    ]
)
def test_species_synonyms(config_file, synonym, species):
    """Test that the species synonym mappings are as expected. This tests a set
    of mappings that are defined within a fixture.
    """
    with genomic_config.open(config_file) as cfg:
        assert cfg.get_species_synonym(synonym) == species, "wrong species"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly_syn,assembly,species",
    # Each element in this list represents a single parameter
    [
        ('hg19', 'b37', 'human'),
        ('hg19', 'b37', 'people'),
        ('grch38', 'b38', 'homo_sapiens')
    ]
)
def test_assembly_synonyms(config_file, assembly_syn, assembly, species):
    """Test that the assembly synonym mappings are as expected. Note that in
    this test we are also using species synonym mapping, so everything should
    work irrespective of the species definition (as they all have synonyms in
    the config file).
    """
    with genomic_config.open(config_file, use_species_synonyms=True) as cfg:
        assembly_mapping = cfg.get_assembly_synonym(species, assembly_syn)
        assert assembly_mapping == assembly, "wrong assembly"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly,species,name,exp_results",
    # Each element in this list represents a single parameter
    [
        ('b38', 'human', 'PYTEST', ['1', '2']),
        ('b37', 'human', 'PYTEST', None),
    ]
)
def test_chr_name_synonyms(config_file, assembly, species, name, exp_results):
    """Test that the assembly synonym mappings are as expected. Note that in
    this test we are also using species synonym mapping, so everything should
    work irrespective of the species definition (as they all have synonyms in
    the config file).
    """
    with genomic_config.open(config_file, use_species_synonyms=True) as cfg:
        if exp_results is not None:
            chr_synonyms = cfg.get_chr_name_synonyms(species, assembly, name)
            assert list(chr_synonyms.values()) == exp_results
        else:
            with pytest.raises(KeyError) as e:
                cfg.get_chr_name_synonyms(species, assembly, name)
            assert e.value.args[0] == \
                   f"unknown chromosome synonym name: {name} ({species}:{assembly})"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly,species,name,exp_results",
    # Each element in this list represents a single parameter
    [
        ('b38', 'human', 'DEFAULT',
         ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
          '11', '12', '13', '14', '15', '16', '17', '18',
          '19', '20', '21', '22', 'X', 'Y', 'MT']),
        ('b37', 'human', 'DEFAULT', None),
    ]
)
def test_chr_name_sort_order(config_file, assembly, species, name, exp_results):
    """Test that the assembly synonym mappings are as expected. Note that in
    this test we are also using species synonym mapping, so everything should
    work irrespective of the species definition (as they all have synonyms in
    the config file).
    """
    with genomic_config.open(config_file, use_species_synonyms=True) as cfg:
        if exp_results is not None:
            sort_order = cfg.get_chr_name_sort_order(species, assembly, name)
            assert sort_order == exp_results
        else:
            with pytest.raises(KeyError) as e:
                cfg.get_chr_name_sort_order(species, assembly, name)
            assert e.value.args[0] == \
                   f"unknown chromosome sort order name: {name} ({species}:{assembly})"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly,species,name,error",
    # Each element in this list represents a single parameter
    [
        ('b38', 'human', 'DEFAULT', True),
        ('b38', 'human', 'DEFAULT', False),
    ]
)
def test_chr_name_sort_key(config_file, assembly, species, name, error):
    """Test that the sort key function is working as expected.
    """
    with genomic_config.open(config_file, use_species_synonyms=True) as cfg:
        sort_order = cfg.get_chr_name_sort_order(species, assembly, name)
        func = cfg.get_chr_name_sort_key(species, assembly, name, error=error)

        for i in ['1', '10', 'X', 'MT', 'N']:
            try:
                exp_idx = sort_order.index(i)
                assert exp_idx == func(i)
            except ValueError:
                if error is True:
                    with pytest.raises(KeyError) as e:
                        func(i)
                else:
                    idx = func(i)
                    assert idx == len(sort_order)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_available_samples(config_file, expected_samples):
    """Test that we get the correct sample set names from the config file.
    """
    expected_samples = [i[0] for i in expected_samples]
    with genomic_config.open(config_file) as cfg:
        sample_sets = cfg.get_available_samples()
        assert sorted(sample_sets) == expected_samples


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_get_samples(config_file, expected_samples):
    """Test that we get the correct sample set names (i.e. sample IDs) from the
    config file.
    """
    with genomic_config.open(config_file) as cfg:
        for sample_set_name, expected_count in expected_samples:
            sample_ids = cfg.get_samples(sample_set_name)
            assert len(sample_ids) == expected_count, \
                "wrong sample count: {0}".format(sample_set_name)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "source_assembly,target_assembly,file_basename",
    # Each element in this list represents a single parameter
    [
        ('hg35', 'grch37', 'homo_sapiens_b35_grch37.chain'),
        ('b35', 'GRCh38', 'homo_sapiens_b35_GRCh38.chain'),
        ('hg17', 'ncbi36', 'homo_sapiens_b35_ncbi36.chain'),
        ('b37', 'b35', 'homo_sapiens_grch37_b35.chain'),
        ('grch37', 'b38', 'homo_sapiens_grch37_GRCh38.chain'),
        ('hg19', 'ncbi36', 'homo_sapiens_grch37_ncbi36.chain'),
        ('grch38', 'b35', 'homo_sapiens_GRCh38_b35.chain'),
        ('GRCh38', 'b37', 'homo_sapiens_GRCh38_grch37.chain'),
        ('b38', 'ncbi36', 'homo_sapiens_GRCh38_ncbi36.chain'),
        ('ncbi36', 'hg35', 'homo_sapiens_ncbi36_b35.chain'),
        ('ncbi36', 'GRCh37', 'homo_sapiens_ncbi36_grch37.chain'),
        ('ncbi36', 'hg38', 'homo_sapiens_ncbi36_GRCh38.chain')
    ]
)
def test_get_chain(full_config_file, source_assembly, target_assembly,
                   file_basename):
    """Test that we get the correct chain files returned. This has
    parameterised tests for content that is provided in the
    ``full_config_file`` fixture.
    """
    with genomic_config.open(full_config_file) as cfg:
        chain_file = cfg.get_chain_file(
            'human', source_assembly, target_assembly
        )
        assert os.path.basename(chain_file) == file_basename, \
            "wrong file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_no_chain(missing_chain):
    """Test that we get the correct error raised when we get a chain file
    defined in the config but the actual file does not exist and
    ``validate_files=True``.
    """
    with pytest.raises(FileNotFoundError) as e:
        with genomic_config.open(missing_chain):
            pass
        assert e.value.args[0].startswith(
            "FileNotFoundError: problem with 'chain file':"
        ), "wrong error raised"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly,name,file_basename",
    # Each element in this list represents a single parameter
    [
        ('hg35', 'all', 'homo_sapiens_b35_all_mapping_file.vcf.gz'),
        ('b35', 'common', 'homo_sapiens_b35_common_mapping_file.vcf.gz'),
        ('hg17', 'common', 'homo_sapiens_b35_common_mapping_file.vcf.gz')
    ]
)
def test_get_mapping(full_config_file, assembly, name, file_basename):
    """Test that we get the correct mapping files returned. This has
    parameterised tests for content that is provided in the
    ``full_config_file`` fixture.
    """
    with genomic_config.open(full_config_file) as cfg:
        mapping_file = cfg.get_mapping_file(
            'human', assembly, name
        )
        assert os.path.basename(mapping_file) == file_basename, \
            "wrong file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_is_remote():
    """Test that the `genomic_config.base_config._BaseConfig.is_remote`
    classmethod returns the expected results when passed remote paths and local
    paths.
    """
    test_path = '/my/test/path.txt'

    for i in base_config.REMOTE_SCHEMES:
        remote_path = "{0}:/{1}".format(i, test_path)
        assert base_config._BaseConfig.is_remote(remote_path) is True

    # Now test the absolute path
    assert base_config._BaseConfig.is_remote(test_path) is False

    # Relative path
    assert base_config._BaseConfig.is_remote('path.txt') is False

    # Relative path (to home)
    assert base_config._BaseConfig.is_remote('~/path.txt') is False

    # Relative path (to dir)
    assert base_config._BaseConfig.is_remote('../path.txt') is False


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "assembly, name, file_basename",
    # Each element in this list represents a single parameter
    [
        ('b35', 'local', 'homo_sapiens_b35_ref_genome.faa.gz'),
        ('grch37', 'local', 'homo_sapiens_grch37_ref_genome.faa.gz'),
        ('GRCh38', 'local', 'homo_sapiens_GRCh38_ref_genome.faa.gz'),
        ('b38', 'remote', 'Homo_sapiens.GRCh38.dna.toplevel.fa.gz'),
        ('ncbi36', 'local', 'homo_sapiens_ncbi36_ref_genome.faa.gz')
    ]
)
def test_ref_genome(full_config_file, assembly, name, file_basename):
    """Test that the correct reference genome file is returned from the
    config file.
    """
    with genomic_config.open(full_config_file) as cfg:
        ref_genome = cfg.get_reference_genome(
            'human', assembly, name
        )
        assert os.path.basename(ref_genome) == file_basename, \
            "wrong file returned"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "test_file",
    # Each element in this list represents a single parameter
    [
        'https://remote/file/human{CHR}_{START}_{END}',
        'http://remote/file/human{CHR}_{START}_{END}',
        'ftp://remote/file/human{CHR}_{START}_{END}'
    ]
)
def test_remote_implicit_hint(test_file):
    """Test that any implicit remote hinting correctly errors out
    """
    with pytest.raises(TypeError) as e:
        base_config._BaseConfig.get_hinting(test_file, None)
    assert e.value.args[0].startswith(
        "implicit hinting in remote location:"
    ), "wrong error raised"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "test_file, hint_key, hint_result",
    # Each element in this list represents a single parameter
    [
        ('https://remote/file/human_chr1.vcf.gz', "1", ("1", None, None)),
        ('http://remote/file/human_chrMT.vcf.gz', "MT", ("MT", None, None)),
        (
            'ftp://remote/file/human_chr2_12345-12745.vcf.gz',
            "2:12345-12745", ("2", 12345, 12745)
        )
    ]
)
def test_remote_explicit_hinting(test_file, hint_key, hint_result):
    """Test that remote explicit hinting works as expected. This tests the
    `genomic_config.base_config._BaseConfig.get_hinting` class method and
    indirectly tests the class method
    `genomic_config.base_config._BaseConfig._get_explicit_hint`
    """
    hints = base_config._BaseConfig.get_hinting(hint_key, test_file)
    assert len(hints) == 1, "incorrect number of hints"
    hint, is_remote, file_name = hints[0]
    assert is_remote, "should be remote"
    assert hint == hint_result, "unexpected hint result"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "test_file, hint_key, hint_result",
    # Each element in this list represents a single parameter
    [
        ('/local/file/human_chrY.vcf.gz', "Y", ("Y", None, None)),
        ('/human_chrX.vcf.gz', "X", ("X", None, None)),
        (
            'local/file/human_chunk1.vcf.gz',
            "CHR_HSCHR16_1_CTG1:1234578-1274530",
            ("CHR_HSCHR16_1_CTG1", 1234578, 1274530)
        )
    ]
)
def test_local_explicit_hinting(test_file, hint_key, hint_result):
    """Test that remote explicit hinting works as expected. This tests the
    `genomic_config.base_config._BaseConfig.get_hinting` class method and
    indirectly tests the class method
    `genomic_config.base_config._BaseConfig._get_explicit_hint`
    """
    hints = base_config._BaseConfig.get_hinting(hint_key, test_file)
    assert len(hints) == 1, "incorrect number of hints"
    hint, is_remote, file_name = hints[0]
    assert not is_remote, "should not be remote"
    assert hint == hint_result, "unexpected hint result"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_local_inplicit_chr_hinting(implicit_hint_chr):
    """Test that local implicit chromosome hinting is working as expected. This
    tests the `genomic_config.base_config._BaseConfig.get_hinting` class method
    and indirectly tests the class method
    `genomic_config.base_config._BaseConfig._get_implicit_hint`
    """
    implicit_hint, expected_results = implicit_hint_chr
    _implicit_hint_test(implicit_hint, expected_results)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def test_local_inplicit_full_hinting(implicit_hint_full):
    """Test that local implicit chromosome hinting is working as expected. This
    tests the `genomic_config.base_config._BaseConfig.get_hinting` class method
    and indirectly tests the class method
    `genomic_config.base_config._BaseConfig._get_implicit_hint`
    """
    implicit_hint, expected_results = implicit_hint_full
    _implicit_hint_test(implicit_hint, expected_results)


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def _implicit_hint_test(implicit_hint, expected_results):
    """Helper function to carry out implict hint tests.
    """
    hints = base_config._BaseConfig.get_hinting(implicit_hint, None)
    expected_results.sort()
    hints.sort(key=lambda x: x[0])
    assert len(hints) == len(expected_results), "wrong number of hints"
    for i in range(len(hints)):
        hint, is_remote, file_name = hints[i]
        assert not is_remote, "should not be remote"
        assert hint == expected_results[i], "wrong hint"


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@pytest.mark.parametrize(
    # The variable names by which the parameters in the list will be
    # available in the test function
    "species, assembly, cohort_name, file_format, expected_result",
    # Each element in this list represents a single parameter
    [
        (
            'human', 'b37', 'NO_HINT', 'vcf', [
                (
                    (None, None, None),
                    'NO_HINT_homo_sapiens_grch37.cohort.vcf'
                )
            ]
        ),
        (
            'human', 'b37', 'IMPLICIT', 'vcf', [
                (
                    ('3', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chr3_whole_chr.cohort.vcf'
                ),
                (
                    ('4', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chr4_whole_chr.cohort.vcf'
                ),
                (
                    ('2', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chr2_whole_chr.cohort.vcf'
                ),
                (
                    ('Y', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chrY_whole_chr.cohort.vcf'
                ),
                (
                    ('MT', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chrMT_whole_chr.cohort.vcf'
                ),
                (
                    ('X', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chrX_whole_chr.cohort.vcf'
                ),
                (
                    ('1', None, None),
                    'IMPLICIT_homo_sapiens_grch37_chr1_whole_chr.cohort.vcf'
                ),
                (
                    ('1', 12345, 22345),
                    'IMPLICIT_homo_sapiens_grch37_chr1_12345_22345_chunk.cohort.vcf'
                ),
                (
                    ('Y', 50000, 100000),
                    'IMPLICIT_homo_sapiens_grch37_chrY_50000_100000_chunk.cohort.vcf'
                ),
                (
                    ('1', 300000, 400000),
                    'IMPLICIT_homo_sapiens_grch37_chr1_300000_400000_chunk.cohort.vcf'
                ),
                (
                    ('MT', 1, 50000),
                    'IMPLICIT_homo_sapiens_grch37_chrMT_1_50000_chunk.cohort.vcf'
                )
            ]
        )
    ]
)
def test_cohorts(full_config_file, species, assembly, cohort_name,
                 file_format, expected_result):
    """Test that the cohorts are being correctly stored into a
    `genomic_config.regions.RegionFileFetch` object and the correct
    `genomic_config.regions.RegionFileFetch` object can be retrieved and it's
    contents match what is expected. This uses ``full_config_file`` fixture and
    the expected results hard set but are defined based on the data generated
    by that fixture.
    """
    with genomic_config.open(full_config_file) as cfg:
        provider = cfg.get_cohort_region_provider(
            species, assembly, cohort_name, file_format
        )

        assert isinstance(provider, regions.RegionFileFetch), \
            "wrong type returned"

        result = sorted(
            [(region_def, os.path.basename(file_name))
             for region_def, file_name in provider.to_list()],
            key=lambda x: x[1]
        )
        expected_result.sort(key=lambda x: x[1])
        assert result == expected_result, "incorrect results"
