Repository: jbusecke/xMIP (language: python)

Issue:
Add a new yaml for weekly full sweep of the archive



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index eabe5c3f..a4a52910 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -105,7 +105,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        variable_id: ["thetao", "o2"]
+        variable_id: ["thetao", "o2", "so", "uo"]
         experiment_id: ["historical", "ssp585"]
         grid_label: ["gn", "gr"]
     runs-on: ubuntu-latest
@@ -143,4 +143,4 @@ jobs:
       run: |
         pwd
         echo $PYTHONPATH
-        pytest --maxfail 5 --reruns 1 --reruns-delay 10 tests/test_preprocessing_cloud.py --gl ${{ matrix.grid_label }} --ei ${{ matrix.experiment_id }} --vi ${{ matrix.variable_id }}
+        pytest --reruns 1 --reruns-delay 5 tests/test_preprocessing_cloud.py --gl ${{ matrix.grid_label }} --ei ${{ matrix.experiment_id }} --vi ${{ matrix.variable_id }}
diff --git a/.github/workflows/full_archive_ci.yaml b/.github/workflows/full_archive_ci.yaml
index 5d8d28c4..988d48de 100644
--- a/.github/workflows/full_archive_ci.yaml
+++ b/.github/workflows/full_archive_ci.yaml
@@ -9,13 +9,15 @@ on:
 
 jobs:
   cloud-tests:
-    name: Build (cloud-data-tests | ${{ matrix.variable_id }} | ${{ matrix.experiment_id }} | ${{ matrix.grid_label }})
+    name: cloud-tests (${{ matrix.catalog}} catalog) | ${{ matrix.variable_id }} | ${{ matrix.experiment_id }} | ${{ matrix.grid_label }}
     strategy:
       fail-fast: false
       matrix:
-        variable_id: ["thetao", "o2", "so"]
-        experiment_id: ["historical", "piControl", "ssp585"]
-        grid_label: ["gn", "gr"]
+        variable_id: ['zos','so', 'thetao', 'uo','o2']
+        experiment_id: ['historical','piControl', 'esm-hist', 'esm-piControl',
+        'ssp245', 'ssp370','ssp585', 'ssp119']
+        grid_label: ['gn', 'gr']
+        catalog: ['main', 'testing']
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
@@ -29,8 +31,9 @@ jobs:
         key: ${{ runner.os }}-conda-${{ env.CACHE_NUMBER }}-${{ hashFiles('ci/environment-upstream-dev.yml') }}
     - uses: conda-incubator/setup-miniconda@v2
       with:
-        mamba-version: "*"
-        channels: conda-forge, defaults
+        channels: conda-forge
+        mamba-version: '*'
+        channel-priority: strict
         activate-environment: test_env_cmip6_preprocessing # Defined in ci/environment-upstream-dev.yml
         auto-update-conda: false
         python-version: 3.8
@@ -38,12 +41,16 @@ jobs:
         use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly!
     - name: Set up conda environment
       shell: bash -l {0}
-      run: |
-        python -m pip install -e .
-        conda list
+      run: python -m pip install -e . --no-deps --force-reinstall
+    - name: Conda List
+      shell: bash -l {0}
+      run: conda list
+    - name: Check Machine Config
+      shell: bash -l {0}
+      run: lscpu
     - name: Run Tests
       shell: bash -l {0}
       run: |
         pwd
         echo $PYTHONPATH
-        pytest --maxfail 5 tests/test_preprocessing_cloud.py --gl ${{ matrix.grid_label }} --ei ${{ matrix.experiment_id }} --vi ${{ matrix.variable_id }}
+        pytest --reruns 1 --reruns-delay 10 --maxfail 20 tests/test_preprocessing_cloud.py --gl ${{ matrix.grid_label }} --ei ${{ matrix.experiment_id }} --vi ${{ matrix.variable_id }} --cat ${{ matrix.catalog }}
diff --git a/README.md b/README.md
index 6478a5d1..2eeed424 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 [![conda-forge](https://img.shields.io/conda/dn/conda-forge/cmip6_preprocessing?label=conda-forge)](https://anaconda.org/conda-forge/cmip6_preprocessing)
 [![Pypi](https://img.shields.io/pypi/v/cmip6_preprocessing.svg)](https://pypi.org/project/cmip6_preprocessing)
 [![Build Status](https://img.shields.io/github/workflow/status/jbusecke/cmip6_preprocessing/CI?logo=github)](https://github.com/jbusecke/cmip6_preprocessing/actions)
+![Full Archive CI](https://github.com/jbusecke/cmip6_preprocessing/workflows/Full%20Archive%20CI/badge.svg)
 [![codecov](https://codecov.io/gh/jbusecke/cmip6_preprocessing/branch/master/graph/badge.svg)](https://codecov.io/gh/jbusecke/cmip6_preprocessing)
 [![License:MIT](https://img.shields.io/badge/License-MIT-lightgray.svg?style=flt-square)](https://opensource.org/licenses/MIT)
 [![DOI](https://zenodo.org/badge/215606850.svg)](https://zenodo.org/badge/latestdoi/215606850)
diff --git a/cmip6_preprocessing/utils.py b/cmip6_preprocessing/utils.py
index a3f67c06..bb63cefb 100644
--- a/cmip6_preprocessing/utils.py
+++ b/cmip6_preprocessing/utils.py
@@ -4,12 +4,51 @@
     intake = None
 
 
-def google_cmip_col():
+def google_cmip_col(catalog="main"):
     """A tiny utility function to point to the 'official' pangeo cmip6 cloud files."""
     if intake is None:
         raise ValueError(
             "This functionality requires intake-esm. Install with `conda install -c conda-forge intake-esm"
         )
-    return intake.open_esm_datastore(
-        "https://cmip6.storage.googleapis.com/pangeo-cmip6.json"
-    )
+    if catalog == "main":
+        return intake.open_esm_datastore(
+            "https://storage.googleapis.com/cmip6/pangeo-cmip6.json"
+        )
+    elif catalog == "testing":
+        return intake.open_esm_datastore(
+            "https://storage.googleapis.com/cmip6/pangeo-cmip6-testing.json"
+        )
+    else:
+        raise ValueError("Catalog not recognized. Should be `main` or `testing`")
+
+
+def model_id_match(match_list, id_tuple):
+    """Matches `id_tuple` to the list of tuples `exception_list`, which can contain
+    wildcards (match any entry) and lists (match any entry that is in the list).
+
+    Parameters
+    ----------
+    match_list : list
+        list of tuples with id strings corresponding to e.g. `source_id`, `grid_label`...
+    id_tuple : tuple
+        single tuple with id strings.
+    """
+    # Check the size of tuples
+    if any([len(t) != len(id_tuple) for t in match_list]):
+        raise ValueError(
+            "Each tuple in `match_list` must have the same number of elements as `match_id`"
+        )
+
+    match_list_checked = []
+    for ml in match_list:
+        ml_processed = []
+        for i in range(len(ml)):
+            match_element = ml[i]
+            if isinstance(match_element, str) and match_element != "*":
+                match_element = [match_element]
+            if id_tuple[i] in match_element or match_element == "*":
+                ml_processed.append(True)
+            else:
+                ml_processed.append(False)
+        match_list_checked.append(all(ml_processed))
+    return any(match_list_checked)
diff --git a/pyproject.toml b/pyproject.toml
index a35b614d..d74456bd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,7 +24,7 @@ known_third_party = ["fsspec", "matplotlib", "numpy", "pandas", "pkg_resources",
 
 [tool.pytest.ini_options]
 minversion = "6.0"
-addopts = "-v -n auto"
+addopts = " -vv -rXfE -n auto"
 # only test the root level, otherwise it picks up the tests of the project template
 testpaths = [
     "tests",


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 4b88e858..43f2e104 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,6 +2,7 @@
 
 
 def pytest_addoption(parser):
+    parser.addoption("--cat", action="store", default="main")
     parser.addoption("--vi", action="store", default="thetao")
     parser.addoption("--gl", action="store", default="gn")
     parser.addoption("--ei", action="store", default="historical")
diff --git a/tests/test_preprocessing_cloud.py b/tests/test_preprocessing_cloud.py
index 35d66c60..f715d4ab 100644
--- a/tests/test_preprocessing_cloud.py
+++ b/tests/test_preprocessing_cloud.py
@@ -9,7 +9,7 @@
 
 from cmip6_preprocessing.grids import combine_staggered_grid
 from cmip6_preprocessing.preprocessing import combined_preprocessing
-from cmip6_preprocessing.utils import google_cmip_col
+from cmip6_preprocessing.utils import google_cmip_col, model_id_match
 
 
 pytest.importorskip("gcsfs")
@@ -24,7 +24,9 @@ def diagnose_doubles(data):
         print(f"Missing values Indicies[{missing}]/ Values[{missing_values}]")
 
 
-def data(source_id, variable_id, experiment_id, grid_label, use_intake_esm):
+def data(
+    source_id, variable_id, experiment_id, grid_label, use_intake_esm, catalog="main"
+):
     zarr_kwargs = {
         "consolidated": True,
         "decode_times": False,
@@ -32,7 +34,7 @@ def data(source_id, variable_id, experiment_id, grid_label, use_intake_esm):
         # "use_cftime": True,
     }
 
-    cat = google_cmip_col().search(
+    cat = google_cmip_col(catalog=catalog).search(
         source_id=source_id,
         experiment_id=experiment_id,
         variable_id=variable_id,
@@ -69,6 +71,7 @@ def all_models():
     df = google_cmip_col().df
     all_models = df["source_id"].unique()
     all_models = tuple(np.sort(all_models))
+    # all_models = tuple(["EC-Earth3"])
     return all_models
 
 
@@ -89,7 +92,7 @@ def pytest_generate_tests(metafunc):
     # This is called for every test. Only get/set command line arguments
     # if the argument is specified in the list of test "fixturenames".
 
-    for name in ["vi", "gl", "ei"]:
+    for name in ["vi", "gl", "ei", "cat"]:
 
         option_value = getattr(metafunc.config.option, name)
 
@@ -106,15 +109,71 @@ def pytest_generate_tests(metafunc):
 
 ########################### Most basic test #########################
 
+# Try to combine some of the failures
+
+## We dont support these at all
+not_supported_failures = [
+    ("AWI-ESM-1-1-LR", "*", "*", "gn"),
+    ("AWI-CM-1-1-MR", "*", "*", "gn"),
+]
+
+## basic problems when trying to concat with intake-esm
+intake_concat_failures = [
+    (
+        "CanESM5",
+        [
+            "uo",
+            "so",
+            "thetao",
+        ],
+        "ssp245",
+        "gn",
+    ),
+    (
+        "CanESM5",
+        ["zos"],
+        [
+            "ssp245",
+            "ssp585",
+        ],
+        "gn",
+    ),
+    (
+        "E3SM-1-0",
+        ["so", "o2", "zos"],
+        ["historical", "ssp585"],
+        "gr",
+    ),  # issues with time concatenation
+    (
+        "IPSL-CM6A-LR",
+        ["thetao", "o2", "so"],
+        "historical",
+        "gn",
+    ),  # IPSL has an issue with `lev` dims concatting]
+    (
+        "NorESM2-MM",
+        ["uo", "so"],
+        "historical",
+        "gr",
+    ),  # time concatting
+    (
+        "NorESM2-MM",
+        ["so"],
+        "historical",
+        "gn",
+    ),
+]
+
 # this fixture has to be redifined every time to account for different fail cases for each test
 @pytest.fixture
-def spec_check_dim_coord_values_wo_intake(request, gl, vi, ei):
-    expected_failures = [
-        ("AWI-ESM-1-1-LR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-LR", "thetao", "ssp585", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "ssp585", "gn"),
-        # TODO: would be nice to have a "*" matching...
+def spec_check_dim_coord_values_wo_intake(request, gl, vi, ei, cat):
+    expected_failures = not_supported_failures + [
+        ("GISS-E2-2-G", "uo", "piControl", "gn"),
+        ("GISS-E2-1-G", "uo", "*", "gn"),
+        ("GISS-E2-1-G-CC", "uo", "*", "gn"),
+        ("MIROC6", "uo", "*", "gn"),
+        ("MIROC-ES2L", "uo", "*", "gn"),
+        ("FGOALS-f3-L", ["thetao"], "piControl", "gn"),
         # (
         #     "GFDL-CM4",
         #     "thetao",
@@ -123,9 +182,9 @@ def spec_check_dim_coord_values_wo_intake(request, gl, vi, ei):
         # ),  # this should not fail and should trigger an xpass (I just use this for dev purposes to check
         #     # the strict option)
     ]
-    spec = (request.param, vi, ei, gl)
+    spec = (request.param, vi, ei, gl, cat)
     request.param = spec
-    if request.param in expected_failures:
+    if model_id_match(expected_failures, request.param[0:-1]):
         request.node.add_marker(pytest.mark.xfail(strict=True))
     return request
 
@@ -141,11 +200,14 @@ def test_check_dim_coord_values_wo_intake(
         variable_id,
         experiment_id,
         grid_label,
+        catalog,
     ) = spec_check_dim_coord_values_wo_intake.param
 
     # there must be a better way to build this at the class level and then tear it down again
     # I can probably get this done with fixtures, but I dont know how atm
-    ds, _ = data(source_id, variable_id, experiment_id, grid_label, False)
+    ds, _ = data(
+        source_id, variable_id, experiment_id, grid_label, False, catalog=catalog
+    )
 
     if ds is None:
         pytest.skip(
@@ -179,26 +241,25 @@ def test_check_dim_coord_values_wo_intake(
 
 # this fixture has to be redifined every time to account for different fail cases for each test
 @pytest.fixture
-def spec_check_dim_coord_values(request, gl, vi, ei):
-    expected_failures = [
-        ("AWI-ESM-1-1-LR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-LR", "thetao", "ssp585", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "ssp585", "gn"),
-        # TODO: would be nice to have a "*" matching...
-        (
-            "IPSL-CM6A-LR",
-            "thetao",
-            "historical",
-            "gn",
-        ),  # IPSL has an issue with `lev` dims concatting
-        ("IPSL-CM6A-LR", "o2", "historical", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gr"),
-    ]
-    spec = (request.param, vi, ei, gl)
+def spec_check_dim_coord_values(request, gl, vi, ei, cat):
+    expected_failures = (
+        not_supported_failures
+        + intake_concat_failures
+        + [
+            ("GISS-E2-2-G", "uo", "piControl", "gn"),
+            ("GISS-E2-1-G-CC", "uo", "*", "gn"),
+            ("GISS-E2-1-G", "uo", "*", "gn"),
+            ("MIROC-ES2L", "uo", "*", "gn"),
+            ("MIROC6", "uo", "*", "gn"),
+            ("NorESM2-MM", ["uo", "zos"], "historical", "gn"),
+            ("NorESM2-MM", "thetao", "historical", "gn"),
+            ("NorESM2-MM", "thetao", "historical", "gr"),
+            ("FGOALS-f3-L", ["thetao"], "piControl", "gn"),
+        ]
+    )
+    spec = (request.param, vi, ei, gl, cat)
     request.param = spec
-    if request.param in expected_failures:
+    if model_id_match(expected_failures, request.param[0:-1]):
         request.node.add_marker(pytest.mark.xfail(strict=True))
     return request
 
@@ -212,10 +273,13 @@ def test_check_dim_coord_values(
         variable_id,
         experiment_id,
         grid_label,
+        catalog,
     ) = spec_check_dim_coord_values.param
     # there must be a better way to build this at the class level and then tear it down again
     # I can probably get this done with fixtures, but I dont know how atm
-    ds, cat = data(source_id, variable_id, experiment_id, grid_label, True)
+    ds, cat = data(
+        source_id, variable_id, experiment_id, grid_label, True, catalog=catalog
+    )
 
     if ds is None:
         pytest.skip(
@@ -252,40 +316,43 @@ def test_check_dim_coord_values(
 
 # this fixture has to be redifined every time to account for different fail cases for each test
 @pytest.fixture
-def spec_check_bounds_verticies(request, gl, vi, ei):
-    expected_failures = [
-        ("AWI-ESM-1-1-LR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-MR", "thetao", "ssp585", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "ssp585", "gn"),
-        ("FGOALS-f3-L", "thetao", "historical", "gn"),
-        ("FGOALS-f3-L", "thetao", "ssp585", "gn"),
-        ("FGOALS-g3", "thetao", "historical", "gn"),
-        ("FGOALS-g3", "thetao", "ssp585", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gr"),
-        ("IPSL-CM6A-LR", "thetao", "historical", "gn"),
-        ("IPSL-CM6A-LR", "o2", "historical", "gn"),
-    ]
-    spec = (request.param, vi, ei, gl)
+def spec_check_bounds_verticies(request, gl, vi, ei, cat):
+    expected_failures = (
+        not_supported_failures
+        + intake_concat_failures
+        + [
+            ("FGOALS-f3-L", ["thetao", "so", "uo", "zos"], "*", "gn"),
+            ("FGOALS-g3", ["thetao", "so", "uo", "zos"], "*", "gn"),
+            ("NorESM2-MM", ["thetao", "uo", "zos"], "historical", "gn"),
+            ("NorESM2-MM", ["thetao", "so"], "historical", "gr"),
+            ("IPSL-CM6A-LR", ["thetao", "o2"], "historical", "gn"),
+            ("NESM3", "uo", "*", "gn"),
+            ("IITM-ESM", ["so", "uo", "thetao"], "piControl", "gn"),
+            ("GFDL-CM4", "uo", "*", "gn"),
+            ("EC-Earth3", "uo", "*", "gn"),
+            ("EC-Earth3-LR", "uo", "piControl", "gn"),
+            ("EC-Earth3-Veg", "uo", "*", "gn"),
+        ]
+    )
+    spec = (request.param, vi, ei, gl, cat)
     request.param = spec
-    if request.param in expected_failures:
+    if model_id_match(expected_failures, request.param[0:-1]):
         request.node.add_marker(pytest.mark.xfail(strict=True))
     return request
 
 
 @pytest.mark.parametrize("spec_check_bounds_verticies", test_models, indirect=True)
-def test_check_bounds_verticies(
-    spec_check_bounds_verticies,
-):
+def test_check_bounds_verticies(spec_check_bounds_verticies):
     (
         source_id,
         variable_id,
         experiment_id,
         grid_label,
+        catalog,
     ) = spec_check_bounds_verticies.param
-    ds, cat = data(source_id, variable_id, experiment_id, grid_label, True)
+    ds, cat = data(
+        source_id, variable_id, experiment_id, grid_label, True, catalog=catalog
+    )
 
     if ds is None:
         pytest.skip(
@@ -341,30 +408,41 @@ def test_check_bounds_verticies(
 
 # this fixture has to be redifined every time to account for different fail cases for each test
 @pytest.fixture
-def spec_check_grid(request, gl, vi, ei):
-    expected_failures = [
-        ("AWI-ESM-1-1-LR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-ESM-1-1-MR", "thetao", "ssp585", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "historical", "gn"),
-        ("AWI-CM-1-1-MR", "thetao", "ssp585", "gn"),
-        ("CMCC-CM2-SR5", "thetao", "historical", "gn"),
-        ("CMCC-CM2-SR5", "thetao", "ssp585", "gn"),
-        ("FGOALS-f3-L", "thetao", "historical", "gn"),
-        ("FGOALS-f3-L", "thetao", "ssp585", "gn"),
-        ("FGOALS-g3", "thetao", "historical", "gn"),
-        ("FGOALS-g3", "thetao", "ssp585", "gn"),
-        ("MPI-ESM-1-2-HAM", "thetao", "historical", "gn"),
-        ("MPI-ESM-1-2-HAM", "o2", "historical", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gn"),
-        ("NorESM2-MM", "thetao", "historical", "gr"),
-        ("IPSL-CM6A-LR", "thetao", "historical", "gn"),
-        ("IPSL-CM6A-LR", "o2", "historical", "gn"),
-    ]
-    spec = (request.param, vi, ei, gl)
+def spec_check_grid(request, gl, vi, ei, cat):
+    expected_failures = (
+        not_supported_failures
+        + intake_concat_failures
+        + [
+            ("CMCC-ESM2", "zos", "historical", "gn"),
+            ("CMCC-CM2-SR5", "*", "*", "gn"),
+            ("CMCC-CM2-HR4", "*", "*", "gn"),
+            ("FGOALS-f3-L", "*", "*", "gn"),
+            ("FGOALS-g3", "*", "*", "gn"),
+            ("E3SM-1-0", ["so", "thetao", "o2"], "*", "gn"),
+            (
+                "E3SM-1-0",
+                ["zos"],
+                ["historical", "ssp585", "ssp245", "ssp370", "esm-hist"],
+                "gr",
+            ),
+            (
+                "EC-Earth3-AerChem",
+                ["so", "thetao", "zos"],
+                ["historical", "piControl", "ssp370"],
+                "gn",
+            ),
+            ("EC-Earth3-Veg", "zos", "historical", "gr"),
+            ("MPI-ESM-1-2-HAM", "*", "*", "gn"),
+            ("NorESM2-MM", "*", "historical", "gn"),
+            ("NorESM2-MM", ["thetao", "so", "uo"], "historical", "gr"),
+            ("IITM-ESM", "*", "*", "gn"),
+            ("GFDL-CM4", ["uo"], "*", "gn"),
+        ]
+    )
+    spec = (request.param, vi, ei, gl, cat)
     request.param = spec
-    if request.param in expected_failures:
-        request.node.add_marker(pytest.mark.xfail(strict=True))
+    if model_id_match(expected_failures, request.param[0:-1]):
+        request.node.add_marker(pytest.mark.xfail(strict=True, reason=""))
     return request
 
 
@@ -372,9 +450,11 @@ def spec_check_grid(request, gl, vi, ei):
 def test_check_grid(
     spec_check_grid,
 ):
-    source_id, variable_id, experiment_id, grid_label = spec_check_grid.param
+    source_id, variable_id, experiment_id, grid_label, catalog = spec_check_grid.param
 
-    ds, cat = data(source_id, variable_id, experiment_id, grid_label, True)
+    ds, cat = data(
+        source_id, variable_id, experiment_id, grid_label, True, catalog=catalog
+    )
 
     if ds is None:
         pytest.skip(
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8b713b92..5b119a0f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,11 +1,44 @@
 import pytest
 
-from cmip6_preprocessing.utils import google_cmip_col
+from cmip6_preprocessing.utils import google_cmip_col, model_id_match
 
 
 pytest.importorskip("intake")
 
 
 def test_google_cmip_col():
-    col = google_cmip_col()
+    col = google_cmip_col(catalog="main")
     assert col.catalog_file == "https://storage.googleapis.com/cmip6/pangeo-cmip6.csv"
+    col = google_cmip_col(catalog="testing")
+    assert (
+        col.catalog_file
+        == "https://storage.googleapis.com/cmip6/pangeo-cmip6-testing.csv"
+    )
+
+
+def test_model_id_match():
+
+    # wrong amount of elements
+    with pytest.raises(ValueError):
+
+        model_id_match([("A", "a", "aa"), ("A", "a", "aa", "aaa")], ("A", "a", "aa"))
+
+    with pytest.raises(ValueError):
+        model_id_match([("A", "a", "aa"), ("B", "a", "aa")], ("A", "a", "aa", "aaa"))
+
+    assert model_id_match([("A", "a", "aa"), ("B", "a", "aa")], ("A", "a", "aa"))
+    assert ~model_id_match([("A", ["b", "c"], "aa")], ("A", "a", "aa"))
+    assert ~model_id_match([("A", ["b", "c"], "aa")], ("A", "a", "aa"))
+    assert ~model_id_match(
+        [("EC-Earth3-AerChem", ["so"], "historical", "gn")],
+        ("EC-Earth3", ["so"], "historical", "gn"),
+    )
+    assert ~model_id_match([("A", "a", "aa"), ("B", "a", "aa")], ("AA", "a", "aa"))
+    assert ~model_id_match([("AA", "a", "aa"), ("B", "a", "aa")], ("A", "a", "aa"))
+    assert ~model_id_match([(["AA"], "a", "aa"), ("B", "a", "aa")], ("A", "a", "aa"))
+    assert ~model_id_match([(["AA"], ["a"], "aa"), ("B", "a", "aa")], ("A", "a", "aa"))
+    assert model_id_match([("*", "a", "aa")], ("whatever", "a", "aa"))
+    assert model_id_match([(["bb", "b"], "a", "aa")], ("b", "a", "aa"))
+    assert model_id_match(
+        [(["bb", "b"], "a", "aa"), (["bb", "b"], "c", "cc")], ("bb", "a", "aa")
+    )