nebius/SWE-bench-extra huggingface dataset:
Repository: reframe-hpc/reframe

Issue:
The Singularity container platform doesn't change automatically to the mounted stage directory
In the Singularity container platform the change to the mounted `stage` directory is done with https://github.com/reframe-hpc/reframe/blob/0d2c11214c914584c754afffac360bfd3c3383f8/reframe/core/containers.py#L257-L258
but `-W` doesn't change the directory like docker or sarus. From `singularity --help`:
```
  -W, --workdir string         working directory to be used for /tmp,
                               /var/tmp and $HOME (if -c/--contain was
                               also used)
```
As a result the test doesn't start in the stage directory. 

Comments:

Patch:
diff --git a/reframe/core/containers.py b/reframe/core/containers.py
index d108746b..15be3766 100644
--- a/reframe/core/containers.py
+++ b/reframe/core/containers.py
@@ -255,7 +255,7 @@ class Singularity(ContainerPlatform):
             run_opts.append('--nv')
 
         if self.workdir:
-            run_opts.append(f'-W {self.workdir}')
+            run_opts.append(f'--pwd {self.workdir}')
 
         run_opts += self.options
         if self.command:


Test patch:
diff --git a/unittests/test_containers.py b/unittests/test_containers.py
index 6518781c..ea713465 100644
--- a/unittests/test_containers.py
+++ b/unittests/test_containers.py
@@ -104,15 +104,15 @@ def expected_cmd_mount_points(container_variant):
     elif container_variant in {'Singularity', 'Singularity+nopull'}:
         return ('singularity exec -B"/path/one:/one" '
                 '-B"/path/two:/two" -B"/foo:/rfm_workdir" '
-                '-W /rfm_workdir image:tag cmd')
+                '--pwd /rfm_workdir image:tag cmd')
     elif container_variant == 'Singularity+cuda':
         return ('singularity exec -B"/path/one:/one" '
                 '-B"/path/two:/two" -B"/foo:/rfm_workdir" --nv '
-                '-W /rfm_workdir image:tag cmd')
+                '--pwd /rfm_workdir image:tag cmd')
     elif container_variant == 'Singularity+nocommand':
         return ('singularity run -B"/path/one:/one" '
                 '-B"/path/two:/two" -B"/foo:/rfm_workdir" '
-                '-W /rfm_workdir image:tag')
+                '--pwd /rfm_workdir image:tag')
 
 
 @pytest.fixture
@@ -265,7 +265,7 @@ def expected_run_with_workdir(container_variant_noopt):
             '--foo image:tag cmd1'
         )
     elif container_variant_noopt == 'Singularity':
-        return ('singularity exec -B\"/foo:/rfm_workdir\" -W foodir '
+        return ('singularity exec -B\"/foo:/rfm_workdir\" --pwd foodir '
                 '--foo image:tag cmd1')
Repository: reframe-hpc/reframe

Issue:
Nodelist in failure report adds a `None` at the end
This is very easy to reproduce:

```
./bin/reframe -c unittests/resources/checks/hellocheck.py -S 'prerun_cmds=set -e;foo' -r
```

```
FAILURE INFO for HelloTest
  * Expanded name: HelloTest
  * Description: C Hello World test
  * System partition: generic:default
  * Environment: builtin
  * Stage directory: /Users/karakasv/Repositories/reframe/stage/generic/default/builtin/HelloTest
  * Node list: tresa.localNone
  * Job type: local (id=80795)
  * Dependencies (conceptual): []
  * Dependencies (actual): []
  * Maintainers: ['VK']
  * Failing phase: sanity
  * Rerun with '-n /2b3e4546 -p builtin --system generic:default -r'
  * Reason: sanity error: pattern 'Hello, World\\!' not found in 'rfm_job.out'
```

Comments:

Patch:
diff --git a/reframe/utility/__init__.py b/reframe/utility/__init__.py
index aa678bce..f6b7d664 100644
--- a/reframe/utility/__init__.py
+++ b/reframe/utility/__init__.py
@@ -836,6 +836,9 @@ class _NodeGroup:
         self.__nodes.append(nid)
 
     def __str__(self):
+        if not self.__nodes:
+            return self.__name
+
         abbrev = []
         encoded = _rl_encode(_delta_encode(self.nodes))
         for unit in encoded:
@@ -920,7 +923,8 @@ def nodelist_abbrev(nodes):
         basename, width, nid = _parse_node(n)
         ng = _NodeGroup(basename, width)
         node_groups.setdefault(ng, ng)
-        node_groups[ng].add(nid)
+        if nid is not None:
+            node_groups[ng].add(nid)
 
     return ','.join(str(ng) for ng in node_groups)
 


Test patch:
diff --git a/unittests/test_utility.py b/unittests/test_utility.py
index 4e9f8fe7..d3392b8d 100644
--- a/unittests/test_utility.py
+++ b/unittests/test_utility.py
@@ -1800,6 +1800,8 @@ def test_nodelist_utilities():
     assert nodelist(['nid01', 'nid10', 'nid20']) == 'nid01,nid10,nid20'
     assert nodelist([]) == ''
     assert nodelist(['nid001']) == 'nid001'
+    assert nodelist(['node']) == 'node'
+    assert nodelist(['nid001', 'node', 'nid002']) == 'nid00[1-2],node'
 
     # Test the reverse operation
     assert expand('nid00[0-4],nid01[0-4],nid02[0-4]') == sorted(nid_nodes)
Repository: reframe-hpc/reframe

Issue:
Issue a warning if an execution mode requested with `--mode` is not found
Currently no warning is issued: `reframe --mode=foo -l`.

Comments:

Patch:
diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py
index 9a465ea2..54436755 100644
--- a/reframe/frontend/cli.py
+++ b/reframe/frontend/cli.py
@@ -737,16 +737,22 @@ def main():
 
         # Update options from the selected execution mode
         if options.mode:
-            mode_args = site_config.get(f'modes/@{options.mode}/options')
-
-            # We lexically split the mode options, because otherwise spaces
-            # will be treated as part of the option argument; see GH bug #1554
-            mode_args = list(itertools.chain.from_iterable(shlex.split(m)
-                                                           for m in mode_args))
-            # Parse the mode's options and reparse the command-line
-            options = argparser.parse_args(mode_args)
-            options = argparser.parse_args(namespace=options.cmd_options)
-            options.update_config(site_config)
+            mode = site_config.get(f'modes/@{options.mode}')
+            if mode is None:
+                printer.warning(f'invalid mode: {options.mode!r}; ignoring...')
+            else:
+                mode_args = site_config.get(f'modes/@{options.mode}/options')
+
+                # We lexically split the mode options, because otherwise spaces
+                # will be treated as part of the option argument;
+                # see GH bug #1554
+                mode_args = list(
+                    itertools.chain.from_iterable(shlex.split(m)
+                                                  for m in mode_args))
+                # Parse the mode's options and reparse the command-line
+                options = argparser.parse_args(mode_args)
+                options = argparser.parse_args(namespace=options.cmd_options)
+                options.update_config(site_config)
 
         logging.configure_logging(site_config)
     except (OSError, errors.ConfigError) as e:


Test patch:
diff --git a/unittests/test_cli.py b/unittests/test_cli.py
index bfdb59f3..adfe5d0f 100644
--- a/unittests/test_cli.py
+++ b/unittests/test_cli.py
@@ -478,6 +478,20 @@ def test_execution_modes(run_reframe):
     assert 'Ran 2/2 test case' in stdout
 
 
+def test_invalid_mode_warning(run_reframe):
+    mode = 'invalid'
+    returncode, stdout, stderr = run_reframe(
+        action='list',
+        checkpath=[],
+        environs=[],
+        local=False,
+        mode=mode
+    )
+    assert 'Traceback' not in stdout
+    assert 'Traceback' not in stderr
+    assert f'invalid mode: {mode!r}; ignoring' in stdout
+
+
 def test_timestamp_option(run_reframe):
     timefmt = time.strftime('xxx_%F')
     returncode, stdout, _ = run_reframe(
Repository: reframe-hpc/reframe

Issue:
Cleanup is not called for fixtures that are referenced also in test that are not run
This can be reproduced with the `fixtures_simple.py` example. The fixture setup is the following there:

```bash
./bin/reframe -c unittests/resources/checks_unlisted/fixtures_simple.py -l
```

```console
- TestB /cc291487
    ^HelloTest ~generic 'ff /98821229
    ^HelloFixture ~generic 'f /93cd42f0
- TestA /f41565ab
    ^HelloFixture ~generic 'f /93cd42f0
- HelloTest /2b3e4546
```

The `HelloFixture` is used by `TestA` and `TestB`. If we execute all the tests, the `HelloFixture`'s stage directory is cleaned up as well:

```bash
ls stage/generic/default/builtin/
# empty
```

However, if we only select `TestA` to run, then the `HelloFixture`'s cleanup is not called and the stage directory is not deleted:

```bash
./bin/reframe -c unittests/resources/checks_unlisted/fixtures_simple.py -n ^TestA -r
ls stage/generic/default/builtin/
# HelloFixture_93cd42f0/
```

The reason for that is that the reference count for the fixtures or, generally, the dependencies, is not updated after we filter the tests. As a result, when `TestA` finishes the reference count is 1, so it is not cleaned up. But since we don't execute `TestB`, the resources of `HelloFixture` are leaked.

Comments:

Patch:
diff --git a/reframe/frontend/dependencies.py b/reframe/frontend/dependencies.py
index 2b15ca87..d26f72af 100644
--- a/reframe/frontend/dependencies.py
+++ b/reframe/frontend/dependencies.py
@@ -205,6 +205,14 @@ def prune_deps(graph, testcases, max_depth=None):
                 if adj not in pruned_graph:
                     unvisited.append(adj)
 
+    # Re-calculate the in-degree of the pruned graph nodes
+    for u in pruned_graph:
+        u.in_degree = 0
+
+    for u, adjacent in pruned_graph.items():
+        for v in adjacent:
+            v.in_degree += 1
+
     return pruned_graph
 
 


Test patch:
diff --git a/unittests/test_dependencies.py b/unittests/test_dependencies.py
index d4ffa3b0..2a15e1e3 100644
--- a/unittests/test_dependencies.py
+++ b/unittests/test_dependencies.py
@@ -697,6 +697,14 @@ def test_prune_deps(default_exec_ctx):
             assert len(pruned_deps[node('t5')]) == 0
             assert len(pruned_deps[node('t0')]) == 0
 
+            # Check the degree of the nodes of the pruned graph
+            assert in_degree(pruned_deps, node('t0')) == 1
+            assert in_degree(pruned_deps, node('t1')) == 2
+            assert in_degree(pruned_deps, node('t2')) == 1
+            assert in_degree(pruned_deps, node('t3')) == 0
+            assert in_degree(pruned_deps, node('t5')) == 1
+            assert in_degree(pruned_deps, node('t7')) == 0
+
 
 def test_toposort(default_exec_ctx):
     #
Repository: reframe-hpc/reframe

Issue:
Update the default `--timestamp` value so as not to use `:`
The paths containing colons are problematic for CMake and Bash's autocompletion does not like them either. It seems that we can still have an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) compliant default timestamp without dashes and colons: `20230422T151055` instead of the current `2023-04-22T15:10:55`

Comments:

Patch:
diff --git a/reframe/core/runtime.py b/reframe/core/runtime.py
index 8048876f..9ff0db00 100644
--- a/reframe/core/runtime.py
+++ b/reframe/core/runtime.py
@@ -9,7 +9,7 @@
 
 import os
 import functools
-from datetime import datetime
+import time
 
 import reframe.core.config as config
 import reframe.utility.osext as osext
@@ -31,7 +31,7 @@ class RuntimeContext:
         self._site_config = site_config
         self._system = System.create(site_config)
         self._current_run = 0
-        self._timestamp = datetime.now()
+        self._timestamp = time.localtime()
 
     def _makedir(self, *dirs, wipeout=False):
         ret = os.path.join(*dirs)
@@ -111,7 +111,7 @@ class RuntimeContext:
     @property
     def timestamp(self):
         timefmt = self.site_config.get('general/0/timestamp_dirs')
-        return self._timestamp.strftime(timefmt)
+        return time.strftime(timefmt, self._timestamp)
 
     @property
     def output_prefix(self):
diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py
index b3ea5af7..e0ed7da3 100644
--- a/reframe/frontend/cli.py
+++ b/reframe/frontend/cli.py
@@ -286,7 +286,7 @@ def main():
         envvar='RFM_SAVE_LOG_FILES', configvar='general/save_log_files'
     )
     output_options.add_argument(
-        '--timestamp', action='store', nargs='?', const='%FT%T',
+        '--timestamp', action='store', nargs='?', const='%y%m%dT%H%M%S%z',
         metavar='TIMEFMT',
         help=('Append a timestamp to the output and stage directory prefixes '
               '(default: "%%FT%%T")'),


Test patch:
diff --git a/unittests/test_cli.py b/unittests/test_cli.py
index d2e9589e..14ba5fc5 100644
--- a/unittests/test_cli.py
+++ b/unittests/test_cli.py
@@ -553,14 +553,17 @@ def test_timestamp_option(run_reframe):
 
 
 def test_timestamp_option_default(run_reframe):
-    timefmt_date_part = time.strftime('%FT')
     returncode, stdout, _ = run_reframe(
         checkpath=['unittests/resources/checks'],
         action='list',
         more_options=['-R', '--timestamp']
     )
     assert returncode == 0
-    assert timefmt_date_part in stdout
+
+    matches = re.findall(
+        r'(stage|output) directory: .*\/(\d{6}T\d{6}\+\d{4})', stdout
+    )
+    assert len(matches) == 2
 
 
 def test_list_empty_prgenvs_check_and_options(run_reframe):
Repository: reframe-hpc/reframe

Issue:
`--describe` option shows `reference` as `null` even if it is defined
Example:

```python
@rfm.simple_test
class stream_test(rfm.RegressionTest):
    valid_systems = ['*']
    valid_prog_environs = ['+openmp']
    build_system = 'SingleSource'
    sourcepath = 'stream.c'
    reference = {
        'tresa': {
            'copy_bandwidth': (23000, -0.05, None, 'MB/s')
        }
    }

    ...
```

```bash
./bin/reframe -C tutorials/cscs-webinar-2022/config/mysettings.py -c tutorials/cscs-webinar-2022/tests/stream6.py --describe | jq .[].reference
```

```console
null
null
```

Comments:

Patch:
diff --git a/reframe/utility/__init__.py b/reframe/utility/__init__.py
index 12edb01e..a381abea 100644
--- a/reframe/utility/__init__.py
+++ b/reframe/utility/__init__.py
@@ -1275,6 +1275,9 @@ class ScopedDict(UserDict):
     def __missing__(self, key):
         raise KeyError(str(key))
 
+    def __rfm_json_encode__(self):
+        return self.data
+
 
 @functools.total_ordering
 class OrderedSet(collections.abc.MutableSet):


Test patch:
diff --git a/unittests/test_utility.py b/unittests/test_utility.py
index b89236ce..224d4dc0 100644
--- a/unittests/test_utility.py
+++ b/unittests/test_utility.py
@@ -799,7 +799,6 @@ def test_scoped_dict_construction():
         'a': {'k1': 3, 'k2': 4},
         'b': {'k3': 5}
     }
-    namespace_dict = reframe.utility.ScopedDict()
     namespace_dict = reframe.utility.ScopedDict(d)
 
     # Change local dict and verify that the stored values are not affected
@@ -1088,6 +1087,17 @@ def test_scoped_dict_update():
     assert scoped_dict == scoped_dict_alt
 
 
+def test_scoped_dict_json_enc():
+    import json
+
+    d = {
+        'a': {'k1': 3, 'k2': 4},
+        'b': {'k3': 5}
+    }
+    ns_dict = reframe.utility.ScopedDict(d)
+    assert d == json.loads(jsonext.dumps(ns_dict))
+
+
 def test_sequence_view():
     l = util.SequenceView([1, 2, 2])
     assert 1 == l[0]
Repository: reframe-hpc/reframe

Issue:
Passing an invalid mode should issue an error not a warning
Currently, passing an inexistent mode with the `--mode` option, only a warning will be issued and the option will be ignored. This is error prone, as reframe continues execution as if the option wasn't passed.

Comments:

Patch:
diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py
index 8c59a680..2a257c11 100644
--- a/reframe/frontend/cli.py
+++ b/reframe/frontend/cli.py
@@ -791,7 +791,7 @@ def main():
         if options.mode:
             mode = site_config.get(f'modes/@{options.mode}')
             if mode is None:
-                printer.warning(f'invalid mode: {options.mode!r}; ignoring...')
+                raise errors.ReframeError(f'invalid mode: {options.mode!r}')
             else:
                 mode_args = site_config.get(f'modes/@{options.mode}/options')
 
@@ -811,6 +811,10 @@ def main():
         printer.error(f'failed to load configuration: {e}')
         printer.info(logfiles_message())
         sys.exit(1)
+    except errors.ReframeError as e:
+        printer.error(str(e))
+        printer.info(logfiles_message())
+        sys.exit(1)
 
     printer.colorize = site_config.get('general/0/colorize')
     if not restrict_logging():


Test patch:
diff --git a/unittests/test_cli.py b/unittests/test_cli.py
index d5d8f148..b468658f 100644
--- a/unittests/test_cli.py
+++ b/unittests/test_cli.py
@@ -527,7 +527,7 @@ def test_execution_modes(run_reframe, run_action):
     assert 'Ran 1/1 test case' in stdout
 
 
-def test_invalid_mode_warning(run_reframe):
+def test_invalid_mode_error(run_reframe):
     mode = 'invalid'
     returncode, stdout, stderr = run_reframe(
         action='list',
@@ -538,7 +538,8 @@ def test_invalid_mode_warning(run_reframe):
     )
     assert 'Traceback' not in stdout
     assert 'Traceback' not in stderr
-    assert f'invalid mode: {mode!r}; ignoring' in stdout
+    assert returncode == 1
+    assert f'ERROR: invalid mode: {mode!r}' in stdout
 
 
 def test_timestamp_option(run_reframe):