Repository: nilearn/nilearn

Issue:
nilearn.interfaces.fmriprep.load_confounds not able to load compcor regressors
When using fmriprep version > 21.0.0 and a confound strategy that attempts to load 'WM' or 'CSF' acompcor regressors, no regressors are loaded. 

The reason seems to be that the naming convention on fmriprep's side was changed in fmriprep version 21.0.0 (Dec 2021, see [fmriprep changelog](https://fmriprep.org/en/stable/changes.html#december-14-2021) "FIX: Split aCompCor into aCompCor, cCompCor, wCompCor (# 2523)"). The regressors for aCompCor on the anatomical masks 'WM' and 'CSF' have been renamed. 


Nilearn version:
0.9.2.dev
### Expected behavior
Load acompcor regressors computed on separate anatomical masks

### Actual behavior
return empty list

### Steps and code to reproduce bug

```python
confounds, _=fmriprep.load_confounds(
            img_path, strategy=['compcor','high_pass'],compcor='anat_separated')
# confounds does not contain acompcor regressors although they exist
```

Code that needs to be changed (?)
https://github.com/nilearn/nilearn/blob/a2bca9df73bd397894fca7bc193519904767bf79/nilearn/interfaces/fmriprep/load_confounds_compcor.py#L4-L6
````
prefix_compcor = {"temporal_anat": ["t", "a"],
                  "temporal": ["t"],
                  "anat": ["a","c","w"]}
````


Discussion:
Thanks for the thorough report @askieslinger!
I will need to further investigate the how compcor components are generated and fix it with a solution that's backwards compatible.
Do you happen to have data processed by 21.x series? 
The confound tsv and json are crucial for creating the test.
Thanks for the quick reply. 
[nilearn_example.zip](https://github.com/nilearn/nilearn/files/8805813/nilearn_example.zip)

This includes the confounds tsv and json file for one subject.

Resolving patch:
diff --git a/nilearn/interfaces/fmriprep/load_confounds_compcor.py b/nilearn/interfaces/fmriprep/load_confounds_compcor.py
--- a/nilearn/interfaces/fmriprep/load_confounds_compcor.py
+++ b/nilearn/interfaces/fmriprep/load_confounds_compcor.py
@@ -1,9 +1,12 @@
 """Helper function for _load_compcor."""
 
 
-prefix_compcor = {"temporal_anat": ["t", "a"],
+prefix_compcor = {"temporal_anat_combined": ["t", "a"],
+                  "temporal_anat_separated": ["t", "a", "w", "c"],
                   "temporal": ["t"],
-                  "anat": ["a"]}
+                  "anat_combined": ["a"],
+                  "anat_separated": ["a", "w", "c"],
+                  }
 anat_masker = {"combined": ["combined"],
                "separated": ["WM", "CSF"],
                None: None}
@@ -32,8 +35,7 @@ def _find_compcor(confounds_json, compcor, n_compcor):
 
     collector = []
     for prefix in prefix_set:
-        # all possible compcor confounds in order, mixing different
-        # types of mask
+        # all possible compcor confounds in order
         all_compcor_name = [
             comp
             for comp in confounds_json.keys()
@@ -94,16 +96,11 @@ def _check_compcor_method(compcor):
     anat_mask : :obj:`list`
         List of anatomical masks to use for acompcor.
     """
-    compcor_type = compcor.split("_")
-    if len(compcor_type) > 1:
-        compcor_type = "_".join(compcor_type[:-1])
-        anat_mask_type = compcor.split("_")[-1:][0]
-    else:
-        compcor_type = compcor_type[0]
-        anat_mask_type = None
     # get relevant prefix from compcor strategy
-    prefix_set = prefix_compcor[compcor_type]
+    prefix_set = prefix_compcor[compcor]
     # get relevant compcor mask
+    check_masktype = compcor.split("_")
+    anat_mask_type = None if len(check_masktype) == 1 else check_masktype[-1]
     anat_mask = anat_masker[anat_mask_type]
     return prefix_set, anat_mask
 
diff --git a/nilearn/interfaces/fmriprep/load_confounds_utils.py b/nilearn/interfaces/fmriprep/load_confounds_utils.py
--- a/nilearn/interfaces/fmriprep/load_confounds_utils.py
+++ b/nilearn/interfaces/fmriprep/load_confounds_utils.py
@@ -334,9 +334,9 @@ def _load_confounds_file_as_dataframe(confounds_raw_path):
     # header format. 1.0.x and 1.1.x series uses camel case
     if any(is_camel_case(col_name) for col_name in confounds_raw.columns):
         raise ValueError(
-            "The confound file contains header in camel case."
-            "This is likely the output from 1.0.x and 1.1.x "
-            "series. We only support fmriprep outputs >= 1.2.0."
+            "The confound file contains header in camel case. "
+            "This is likely the output from 1.0.x and 1.1.x series. "
+            "We only support fmriprep outputs >= 1.2.0."
             f"{confounds_raw.columns}"
         )
 


Test patch:
diff --git a/nilearn/interfaces/fmriprep/tests/_testing.py b/nilearn/interfaces/fmriprep/tests/_testing.py
--- a/nilearn/interfaces/fmriprep/tests/_testing.py
+++ b/nilearn/interfaces/fmriprep/tests/_testing.py
@@ -45,17 +45,18 @@
 }
 
 
-def get_testdata_path(non_steady_state=True):
+def get_testdata_path(non_steady_state=True, fmriprep_version="1.4.x"):
     """Get file path for the confound regressors."""
-    derivative = "regressors"
+    derivative = "regressors" if fmriprep_version != "21.x.x" else "timeseries"
     path_data = os.path.join(os.path.dirname(
         load_confounds_utils.__file__), "data")
+    suffix = "test-v21" if fmriprep_version == "21.x.x" else "test"
     if non_steady_state:
         return [
             os.path.join(path_data, filename)
             for filename in [
-                f"test_desc-confounds_{derivative}.tsv",
-                f"test_desc-confounds_{derivative}.json",
+                f"{suffix}_desc-confounds_{derivative}.tsv",
+                f"{suffix}_desc-confounds_{derivative}.json",
             ]
         ]
     else:
@@ -74,18 +75,18 @@ def create_tmp_filepath(
     bids_fields=None,
     copy_confounds=False,
     copy_json=False,
-    old_derivative_suffix=False
+    fmriprep_version="1.4.x"
 ):
     entities_to_include = [
         *_bids_entities()["raw"],
         *_bids_entities()["derivatives"]
     ]
     if bids_fields is None:
-        bids_fields = {"entities": {"sub": "test01",
+        bids_fields = {"entities": {"sub": fmriprep_version.replace(".", ""),
                                     "task": "test"}}
 
     """Create test files in temporary directory."""
-    derivative = "regressors" if old_derivative_suffix else "timeseries"
+    derivative = "regressors" if fmriprep_version == "1.2.x" else "timeseries"
 
     # confound files
     bids_fields["entities"]["desc"] = "confounds"
@@ -98,7 +99,7 @@ def create_tmp_filepath(
     tmp_conf = base_path / confounds_filename
 
     if copy_confounds:
-        conf, meta = get_legal_confound()
+        conf, meta = get_legal_confound(fmriprep_version=fmriprep_version)
         conf.to_csv(tmp_conf, sep="\t", index=False)
     else:
         tmp_conf.touch()
@@ -110,7 +111,7 @@ def create_tmp_filepath(
             entities_to_include=entities_to_include
         )
         tmp_meta = base_path / confounds_sidecar
-        conf, meta = get_legal_confound()
+        conf, meta = get_legal_confound(fmriprep_version=fmriprep_version)
         with open(tmp_meta, "w") as file:
             json.dump(meta, file, indent=2)
 
@@ -140,11 +141,12 @@ def create_tmp_filepath(
     return tmp_img, tmp_conf
 
 
-def get_legal_confound(non_steady_state=True):
+def get_legal_confound(non_steady_state=True, fmriprep_version="1.4.x"):
     """Load the valid confound files for manipulation."""
-    conf, meta = get_testdata_path(non_steady_state=non_steady_state)
+    conf, meta = get_testdata_path(non_steady_state=non_steady_state,
+                                   fmriprep_version=fmriprep_version)
     conf = pd.read_csv(conf, delimiter="\t", encoding="utf-8")
-    with open(meta) as file:
+    with open(meta, "r") as file:
         meta = json.load(file)
     return conf, meta
 
diff --git a/nilearn/interfaces/fmriprep/tests/test_load_confounds.py b/nilearn/interfaces/fmriprep/tests/test_load_confounds.py
--- a/nilearn/interfaces/fmriprep/tests/test_load_confounds.py
+++ b/nilearn/interfaces/fmriprep/tests/test_load_confounds.py
@@ -111,6 +111,7 @@ def _regression(confounds, tmp_path):
     assert tseries_clean.shape[0] == confounds.shape[0]
 
 
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
 @pytest.mark.filterwarnings("ignore")
 @pytest.mark.parametrize(
     "test_strategy,param",
@@ -125,11 +126,16 @@ def _regression(confounds, tmp_path):
         (("ica_aroma", ), {"ica_aroma": "basic"}),
     ],
 )
-def test_nilearn_regress(tmp_path, test_strategy, param):
+def test_nilearn_regress(tmp_path, test_strategy, param, fmriprep_version):
     """Try regressing out all motion types without sample mask."""
     img_nii, _ = create_tmp_filepath(
-        tmp_path, copy_confounds=True, copy_json=True
+        tmp_path,
+        copy_confounds=True,
+        copy_json=True,
+        fmriprep_version=fmriprep_version
     )
+    if fmriprep_version == "21.x.x" and test_strategy == ("ica_aroma", ):
+        return
     confounds, _ = load_confounds(img_nii, strategy=test_strategy, **param)
     _regression(confounds, tmp_path)
 
@@ -236,18 +242,24 @@ def test_nilearn_standardize(tmp_path, standardize_signal,
     assert corr.mean() > 0.8
 
 
-def test_confounds2df(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_confounds2df(tmp_path, fmriprep_version):
     """Check auto-detect of confonds from an fMRI nii image."""
-    img_nii, _ = create_tmp_filepath(tmp_path, copy_confounds=True)
+    img_nii, _ = create_tmp_filepath(tmp_path,
+                                     copy_confounds=True,
+                                     fmriprep_version=fmriprep_version)
     confounds, _ = load_confounds(img_nii)
     assert "trans_x" in confounds.columns
 
 
-def test_load_single_confounds_file(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_load_single_confounds_file(tmp_path, fmriprep_version):
     """Check that the load_confounds function returns the same confounds \
     as _load_single_confounds_file."""
-    nii_file, confounds_file = create_tmp_filepath(tmp_path,
-                                                   copy_confounds=True)
+    nii_file, confounds_file = create_tmp_filepath(
+        tmp_path,
+        copy_confounds=True,
+        fmriprep_version=fmriprep_version)
 
     # get defaults from load_confounds
     import inspect
@@ -292,12 +304,15 @@ def expected_suffixes(motion):
     return SUFFIXES[expectation[motion]]
 
 
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
 @pytest.mark.parametrize("motion", ["basic", "derivatives", "power2", "full"])
 @pytest.mark.parametrize(
     "param", ["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]
 )
-def test_motion(tmp_path, motion, param, expected_suffixes):
-    img_nii, _ = create_tmp_filepath(tmp_path, copy_confounds=True)
+def test_motion(tmp_path, motion, param, expected_suffixes, fmriprep_version):
+    img_nii, _ = create_tmp_filepath(tmp_path,
+                                     copy_confounds=True,
+                                     fmriprep_version=fmriprep_version)
     conf, _ = load_confounds(
         img_nii, strategy=("motion", ), motion=motion
     )
@@ -308,13 +323,26 @@ def test_motion(tmp_path, motion, param, expected_suffixes):
             assert f"{param}{suff}" not in conf.columns
 
 
-@pytest.mark.parametrize("compcor,n_compcor,test_keyword,test_n",
-                         [("anat_combined", 2, "a_comp_cor_", 2),
-                          ("anat_combined", "all", "a_comp_cor_", 57),
-                          ("temporal", "all", "t_comp_cor_", 6)])
-def test_n_compcor(tmp_path, compcor, n_compcor, test_keyword, test_n):
+@pytest.mark.parametrize(
+    "compcor, n_compcor, test_keyword, test_n, fmriprep_version",
+    [("anat_combined", 2, "a_comp_cor_", 2, "1.4.x"),
+     ("anat_separated", 2, "a_comp_cor_", 4, "1.4.x"),
+     ("anat_combined", "all", "a_comp_cor_", 57, "1.4.x"),
+     ("temporal", "all", "t_comp_cor_", 6, "1.4.x"),
+     ("anat_combined", 2, "a_comp_cor_", 2, "21.x.x"),
+     ("anat_separated", "all", "w_comp_cor_", 4, "21.x.x"),
+     ("temporal_anat_separated", "all", "c_comp_cor_", 3, "21.x.x"),
+     ("temporal", "all", "t_comp_cor_", 3, "21.x.x")])
+def test_n_compcor(tmp_path, compcor,
+                   n_compcor,
+                   test_keyword,
+                   test_n,
+                   fmriprep_version):
     img_nii, _ = create_tmp_filepath(
-        tmp_path, copy_confounds=True, copy_json=True
+        tmp_path,
+        copy_confounds=True,
+        copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     conf, _ = load_confounds(
         img_nii, strategy=("high_pass", "compcor", ), compcor=compcor,
@@ -323,33 +351,43 @@ def test_n_compcor(tmp_path, compcor, n_compcor, test_keyword, test_n):
     assert sum(True for col in conf.columns if test_keyword in col) == test_n
 
 
-def test_not_found_exception(tmp_path):
-    """Check various file or parameter missing scenario."""
-    # Create invalid confound file in temporary dir
-    img_missing_confounds, bad_conf = create_tmp_filepath(
-        tmp_path, copy_confounds=True, copy_json=False
-    )
-    missing_params = ["trans_y", "trans_x_derivative1", "rot_z_power2"]
-    missing_keywords = ["cosine", "global_signal"]
+missing_params = ["trans_y", "trans_x_derivative1", "rot_z_power2"]
+missing_keywords = ["cosine", "global_signal"]
+
 
-    leagal_confounds = pd.read_csv(bad_conf, delimiter="\t", encoding="utf-8")
+def _remove_confounds(conf_file):
+    legal_confounds = pd.read_csv(conf_file, delimiter="\t", encoding="utf-8")
     remove_columns = []
     for missing_kw in missing_keywords:
         remove_columns += [
             col_name
-            for col_name in leagal_confounds.columns
+            for col_name in legal_confounds.columns
             if missing_kw in col_name
         ]
 
     aroma = [
         col_name
-        for col_name in leagal_confounds.columns
+        for col_name in legal_confounds.columns
         if "aroma" in col_name
     ]
-    missing_confounds = leagal_confounds.drop(
+    missing_confounds = legal_confounds.drop(
         columns=missing_params + remove_columns + aroma
     )
-    missing_confounds.to_csv(bad_conf, sep="\t", index=False)
+    missing_confounds.to_csv(conf_file, sep="\t", index=False)
+
+
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_not_found_exception(tmp_path, fmriprep_version):
+    """Check various file or parameter missing scenario."""
+    # Create invalid confound file in temporary dir
+    img_missing_confounds, bad_conf = create_tmp_filepath(
+        tmp_path,
+        copy_confounds=True,
+        copy_json=False,
+        fmriprep_version=fmriprep_version
+    )
+
+    _remove_confounds(bad_conf)
 
     with pytest.raises(ValueError) as exc_info:
         load_confounds(
@@ -359,6 +397,7 @@ def test_not_found_exception(tmp_path):
             motion="full",
         )
     assert f"{missing_params}" in exc_info.value.args[0]
+
     # missing cosine if it's not present in the file it's fine
     assert f"{missing_keywords[-1:]}" in exc_info.value.args[0]
 
@@ -378,6 +417,20 @@ def test_not_found_exception(tmp_path):
             compcor="blah"
         )
 
+
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_not_found_exception_ica_aroma(tmp_path, fmriprep_version):
+    """Check various file or parameter for ICA-AROMA strategy."""
+    # Create invalid confound file in temporary dir
+    img_missing_confounds, bad_conf = create_tmp_filepath(
+        tmp_path,
+        copy_confounds=True,
+        copy_json=False,
+        fmriprep_version=fmriprep_version
+    )
+
+    _remove_confounds(bad_conf)
+
     # Aggressive ICA-AROMA strategy requires
     # default nifti and noise ICs in confound file
     # correct nifti but missing noise regressor
@@ -387,11 +440,12 @@ def test_not_found_exception(tmp_path):
         )
     assert "ica_aroma" in exc_info.value.args[0]
 
-    # Aggressive ICA-AROMA strategy requires
-    # default nifti
+    # Default nifti
     aroma_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="ica_aroma", bids_fields={"entities":
-                                                       {"sub": "icaAroma"}}
+        tmp_path, image_type="ica_aroma",
+        bids_fields={"entities":
+                     {"sub": "icaAroma"}},
+        fmriprep_version=fmriprep_version
     )
     with pytest.raises(ValueError) as exc_info:
         load_confounds(
@@ -414,12 +468,15 @@ def test_not_found_exception(tmp_path):
     assert "Could not find associated confound file." in exc_info.value.args[0]
 
 
-def test_non_steady_state(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_non_steady_state(tmp_path, fmriprep_version):
     """Warn when 'non_steady_state' is in strategy."""
     # supplying 'non_steady_state' in strategy is not necessary
     # check warning is correctly raised
     img, _ = create_tmp_filepath(
-        tmp_path, copy_confounds=True
+        tmp_path,
+        copy_confounds=True,
+        fmriprep_version=fmriprep_version
     )
     warning_message = (r"Non-steady state")
     with pytest.warns(UserWarning, match=warning_message):
@@ -453,13 +510,13 @@ def test_invalid_filetype(tmp_path, rng):
     """Invalid file types/associated files for load method."""
     bad_nii, bad_conf = create_tmp_filepath(tmp_path,
                                             copy_confounds=True,
-                                            old_derivative_suffix=False)
+                                            fmriprep_version="1.4.x")
     _, _ = load_confounds(bad_nii)
 
     # more than one legal filename for confounds
-    add_conf = "sub-test01_task-test_desc-confounds_regressors.tsv"
-    leagal_confounds, _ = get_legal_confound()
-    leagal_confounds.to_csv(tmp_path / add_conf, sep="\t", index=False)
+    add_conf = "sub-14x_task-test_desc-confounds_regressors.tsv"
+    legal_confounds, _ = get_legal_confound()
+    legal_confounds.to_csv(tmp_path / add_conf, sep="\t", index=False)
     with pytest.raises(ValueError) as info:
         load_confounds(bad_nii)
     assert "more than one" in str(info.value)
@@ -473,10 +530,10 @@ def test_invalid_filetype(tmp_path, rng):
     assert "The confound file contains no header." in str(error_log.value)
 
     # invalid fmriprep version: old camel case header (<1.2)
-    leagal_confounds, _ = get_legal_confound()
-    camel_confounds = leagal_confounds.copy()
+    legal_confounds, _ = get_legal_confound()
+    camel_confounds = legal_confounds.copy()
     camel_confounds.columns = [
-        to_camel_case(col_name) for col_name in leagal_confounds.columns
+        to_camel_case(col_name) for col_name in legal_confounds.columns
     ]
     camel_confounds.to_csv(bad_conf, sep="\t", index=False)
     with pytest.raises(ValueError) as error_log:
@@ -492,13 +549,20 @@ def test_invalid_filetype(tmp_path, rng):
         load_confounds(bad_nii)
 
 
-def test_ica_aroma(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x"])
+def test_ica_aroma(tmp_path, fmriprep_version):
     """Test ICA AROMA related file input."""
     aroma_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="ica_aroma", copy_confounds=True
+        tmp_path,
+        image_type="ica_aroma",
+        copy_confounds=True,
+        fmriprep_version=fmriprep_version
     )
     regular_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="regular", copy_confounds=True
+        tmp_path,
+        image_type="regular",
+        copy_confounds=True,
+        fmriprep_version=fmriprep_version
     )
     # Aggressive strategy
     conf, _ = load_confounds(
@@ -522,26 +586,36 @@ def test_ica_aroma(tmp_path):
     assert "Current input: invalid" in exc_info.value.args[0]
 
 
-def test_sample_mask(tmp_path):
+@pytest.mark.parametrize(
+    "fmriprep_version, scrubbed_time_points, non_steady_outliers",
+    [("1.4.x", 8, 1), ("21.x.x", 30, 3)])
+def test_sample_mask(tmp_path,
+                     fmriprep_version,
+                     scrubbed_time_points,
+                     non_steady_outliers):
     """Test load method and sample mask."""
     regular_nii, regular_conf = create_tmp_filepath(
-        tmp_path, image_type="regular", copy_confounds=True
+        tmp_path,
+        image_type="regular",
+        copy_confounds=True,
+        fmriprep_version=fmriprep_version
     )
 
     reg, mask = load_confounds(
         regular_nii, strategy=("motion", "scrub"), scrub=5, fd_threshold=0.15
     )
-    # the current test data has 6 time points marked as motion outliers,
+    # the "1.4.x" test data has 6 time points marked as motion outliers,
     # and one nonsteady state (overlap with the first motion outlier)
-    # 2 time points removed due to the "full" srubbing strategy (remove segment
-    # shorter than 5 volumes)
-    assert reg.shape[0] - len(mask) == 8
+    # 2 time points removed due to the "full" srubbing strategy
+    # (remove segment shorter than 5 volumes)
+    assert reg.shape[0] - len(mask) == scrubbed_time_points
+
     # nilearn requires unmasked confound regressors
     assert reg.shape[0] == 30
 
     # non steady state will always be removed
     reg, mask = load_confounds(regular_nii, strategy=("motion", ))
-    assert reg.shape[0] - len(mask) == 1
+    assert reg.shape[0] - len(mask) == non_steady_outliers
 
     # When no non-steady state volumes are present
     conf_data, _ = get_legal_confound(non_steady_state=False)
diff --git a/nilearn/interfaces/fmriprep/tests/test_load_confounds_components.py b/nilearn/interfaces/fmriprep/tests/test_load_confounds_components.py
--- a/nilearn/interfaces/fmriprep/tests/test_load_confounds_components.py
+++ b/nilearn/interfaces/fmriprep/tests/test_load_confounds_components.py
@@ -23,17 +23,23 @@ def expected_parameters(strategy_keywords):
         return False
 
 
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
 @pytest.mark.parametrize(
     "strategy_keywords",
     ["motion", "wm_csf", "global_signal", "compcor", "ica_aroma", "high_pass"],
 )
-def test_missing_keywords(tmp_path, strategy_keywords, expected_parameters):
+def test_missing_keywords(
+    tmp_path, strategy_keywords, expected_parameters, fmriprep_version
+):
     """
     Check the strategy keywords are raising errors correctly in low and
     high level functions with the exception of `high_pass`.
     """
     img, bad_conf = create_tmp_filepath(
-        tmp_path, copy_confounds=True, copy_json=True
+        tmp_path,
+        copy_confounds=True,
+        copy_json=True,
+        fmriprep_version=fmriprep_version,
     )
     legal_confounds = pd.read_csv(bad_conf, delimiter="\t", encoding="utf-8")
     meta_json = bad_conf.parent / bad_conf.name.replace("tsv", "json")
diff --git a/nilearn/interfaces/fmriprep/tests/test_load_confounds_strategy.py b/nilearn/interfaces/fmriprep/tests/test_load_confounds_strategy.py
--- a/nilearn/interfaces/fmriprep/tests/test_load_confounds_strategy.py
+++ b/nilearn/interfaces/fmriprep/tests/test_load_confounds_strategy.py
@@ -10,15 +10,23 @@
 from nilearn.interfaces.fmriprep.tests._testing import create_tmp_filepath
 
 
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
 @pytest.mark.parametrize("denoise_strategy,image_type",
                          [("simple", "regular"),
                           ("scrubbing", "regular"),
                           ("compcor", "regular"),
                           ("ica_aroma", "ica_aroma")])
-def test_load_confounds_strategy(tmp_path, denoise_strategy, image_type):
+def test_load_confounds_strategy(tmp_path,
+                                 denoise_strategy,
+                                 image_type,
+                                 fmriprep_version):
     """Smoke test with no extra inputs."""
     file_nii, _ = create_tmp_filepath(
-        tmp_path, image_type=image_type, copy_confounds=True, copy_json=True
+        tmp_path,
+        image_type=image_type,
+        copy_confounds=True,
+        copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     confounds, _ = load_confounds_strategy(
         file_nii, denoise_strategy=denoise_strategy
@@ -26,15 +34,17 @@ def test_load_confounds_strategy(tmp_path, denoise_strategy, image_type):
     assert isinstance(confounds, pd.DataFrame)
 
 
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
 @pytest.mark.parametrize("denoise_strategy,image_type",
                          [("simple", "regular"),
                           ("scrubbing", "regular"),
                           ("compcor", "regular"),
                           ("ica_aroma", "ica_aroma")])
-def test_strategies(tmp_path, denoise_strategy, image_type):
+def test_strategies(tmp_path, denoise_strategy, image_type, fmriprep_version):
     """Check defaults setting of each preset strategy."""
     file_nii, _ = create_tmp_filepath(
-        tmp_path, image_type=image_type, copy_confounds=True, copy_json=True
+        tmp_path, image_type=image_type, copy_confounds=True, copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     confounds, _ = load_confounds_strategy(
         file_nii, denoise_strategy=denoise_strategy
@@ -54,7 +64,7 @@ def test_strategies(tmp_path, denoise_strategy, image_type):
     'wm_csf': ["csf$", "white_matter$"],
     'global': ["global_signal$"],
     'global_signal': ["global_signal$"],
-    'compcor': ["a_comp_cor_+"],
+    'compcor': ["[tawc]_comp_cor_+"],
 }
 
 
@@ -75,26 +85,37 @@ def _get_headers(denoise_strategy):
     return list_check
 
 
-def test_strategy_scrubbing(tmp_path):
+@pytest.mark.parametrize(
+    "fmriprep_version, volumes_left, len_sample_mask",
+    [("1.4.x", 22, 29), ("21.x.x", 0, 5)])
+def test_strategy_scrubbing(tmp_path,
+                            fmriprep_version,
+                            volumes_left,
+                            len_sample_mask):
     """Check user specified input for scrubbing strategy."""
     file_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="regular", copy_confounds=True, copy_json=True
+        tmp_path, image_type="regular", copy_confounds=True, copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     confounds, sample_mask = load_confounds_strategy(
         file_nii, denoise_strategy="scrubbing", fd_threshold=0.15
     )
+    # For "1.4.x"
     # out of 30 vols, should have 6 motion outliers from scrubbing,
-    # and 2 vol removed by srubbing strategy "full"
-    assert len(sample_mask) == 22
+    # and 2 vol removed by scrubbing strategy "full"
+    assert len(sample_mask) == volumes_left
+
     # shape of confound regressors untouched
     assert confounds.shape[0] == 30
+
     # also load confounds with very liberal scrubbing thresholds
     # this should not produce an error
     confounds, sample_mask = load_confounds_strategy(
         file_nii, denoise_strategy="scrubbing",
         fd_threshold=1, std_dvars_threshold=5
     )
-    assert len(sample_mask) == 29  # only non-steady volumes removed
+    # only non-steady volumes removed
+    assert len(sample_mask) == len_sample_mask
 
     # maker sure global signal works
     confounds, sample_mask = load_confounds_strategy(
@@ -108,25 +129,32 @@ def test_strategy_scrubbing(tmp_path):
         assert check in confounds.columns
 
 
-def test_strategy_compcor(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_strategy_compcor(tmp_path, fmriprep_version):
     """Check user specified input for compcor strategy."""
     file_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="regular", copy_confounds=True, copy_json=True
+        tmp_path, image_type="regular", copy_confounds=True, copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     confounds, _ = load_confounds_strategy(
         file_nii, denoise_strategy="compcor"
     )
     compcor_col_str_anat = "".join(confounds.columns)
     assert "t_comp_cor_" not in compcor_col_str_anat
-    assert (
-        "a_comp_cor_57" not in compcor_col_str_anat
-    )  # this one comes from the white matter mask
+    # this one comes from the white matter mask
+    expected = ("a_comp_cor_57" if fmriprep_version == "1.4.x"
+                else "w_comp_cor_00")
+    assert expected not in compcor_col_str_anat
 
 
-def test_irrelevant_input(tmp_path):
+@pytest.mark.parametrize("fmriprep_version", ["1.4.x", "21.x.x"])
+def test_irrelevant_input(tmp_path, fmriprep_version):
     """Check invalid input raising correct warning or error message."""
     file_nii, _ = create_tmp_filepath(
-        tmp_path, image_type="regular", copy_confounds=True, copy_json=True
+        tmp_path, image_type="regular",
+        copy_confounds=True,
+        copy_json=True,
+        fmriprep_version=fmriprep_version
     )
     warning_message = (r"parameters accepted: \['motion', 'wm_csf', "
                        "'global_signal', 'demean']")
diff --git a/nilearn/interfaces/fmriprep/tests/test_load_confounds_utils.py b/nilearn/interfaces/fmriprep/tests/test_load_confounds_utils.py
--- a/nilearn/interfaces/fmriprep/tests/test_load_confounds_utils.py
+++ b/nilearn/interfaces/fmriprep/tests/test_load_confounds_utils.py
@@ -30,19 +30,17 @@ def test_sanitize_confounds(inputs, flag):
     assert singleflag is flag
 
 
-@pytest.mark.parametrize("flag", [True, False])
+@pytest.mark.parametrize("flag,keyword",
+                         [("1.2.x", "_desc-confounds_regressors"),
+                          ("1.4.x", "_desc-confounds_timeseries"),
+                          ("21.x.x", "21xx")])
 @pytest.mark.parametrize(
     "image_type", ["regular", "native", "res", "cifti", "den", "part", "gifti"]
 )
-def test_get_file_name(tmp_path, flag, image_type):
+def test_get_file_name(tmp_path, flag, keyword, image_type):
     """Test _get_file_name."""
-    if flag:
-        suffix = "_desc-confounds_regressors"
-    else:
-        suffix = "_desc-confounds_timeseries"
-
     if image_type == "part":
-        kwargs = {"bids_fields": {"entities": {"sub": "test01",
+        kwargs = {"bids_fields": {"entities": {"sub": flag.replace(".", ""),
                                                "task": "test",
                                                "part": "mag",
                                                "run": "01"}}}
@@ -52,9 +50,10 @@ def test_get_file_name(tmp_path, flag, image_type):
     img, _ = create_tmp_filepath(
         tmp_path,
         image_type=image_type,
-        old_derivative_suffix=flag,
+        fmriprep_version=flag,
         **kwargs,
     )
 
     conf = _get_file_name(img)
-    assert suffix in conf
+
+    assert keyword in conf