Skip to content

Commit e2e7842

Browse files
authored
Merge pull request #35 from amq92/slurm-test
Add slurm github action
2 parents 3237685 + dd65406 commit e2e7842

File tree

4 files changed

+63
-143
lines changed

4 files changed

+63
-143
lines changed

.github/workflows/python-run-tests.yml

+35-11
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,39 @@ on:
1010
jobs:
1111
build:
1212
runs-on: ubuntu-latest
13+
services:
14+
mysql:
15+
image: mysql:8.0
16+
env:
17+
MYSQL_ROOT_PASSWORD: root
18+
ports:
19+
- "8888:3306"
20+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
21+
1322
steps:
14-
- uses: actions/checkout@v4
15-
- name: Set up Python
16-
uses: actions/setup-python@v4
17-
with:
18-
python-version: '3.x'
19-
- name: Install dependencies
20-
run: |
21-
python -m pip install --upgrade pip
22-
pip install build
23-
- name: Run tests with unittest
24-
run: python -m unittest -v
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
26+
- name: Set up SLURM
27+
uses: koesterlab/setup-slurm-action@v1
28+
29+
- name: Set up Python
30+
uses: actions/setup-python@v4
31+
with:
32+
python-version: '3.x'
33+
34+
- name: Install dependencies
35+
run: |
36+
python -m pip install --upgrade pip
37+
pip install build
38+
39+
- name: Copy files to SLURM node
40+
run: |
41+
mkdir -p $HOME/slurm_workspace
42+
rsync -av --progress . $HOME/slurm_workspace/
43+
ls -lah $HOME/slurm_workspace # Debugging step
44+
45+
- name: Run tests with unittest
46+
run: |
47+
cd $HOME/slurm_workspace
48+
python -m unittest -v

CONTRIBUTING.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ We welcome contributions! Before submitting changes, please follow these guideli
2020
```
2121
4. Test/validate changes (see [Testing](#testing)).
2222
5. Update documentation if needed.
23-
6. Open a PR with a clear description of changes and test results.
23+
6. Open a Pull Request with a clear description of changes and test results.
2424

2525
## Code Style
2626
- Format code with [`ruff`](https://docs.astral.sh/ruff/).
2727
- Add type hints for new functions/methods.
2828
- Keep docstrings consistent with existing code.
2929

3030
## Testing
31-
- Testing on a real Slurm cluster is **highly desired**.
3231
- Add unit tests to validate any change in functionality.
32+
- Testing on a real Slurm cluster is **highly desired**.
33+
- A simple Slurm cluster is setup as an automatic action for any Pull Request.
3334

3435
## Questions?
3536
- Open a GitHub issue.

test/test_cli.py

+8-41
Original file line numberDiff line numberDiff line change
@@ -55,52 +55,19 @@ def test_03_using_equal_and_spaces(self):
5555
)
5656

5757
def __run_test(self, args):
58-
stdout = run_cli(args)
59-
script, job_id = parse_stdout(stdout)
58+
with io.StringIO() as buffer:
59+
with contextlib.redirect_stdout(buffer):
60+
with patch.object(sys, "argv", args):
61+
cli()
62+
stdout = buffer.getvalue()
6063

61-
out_file = f"slurm-{job_id}.out"
62-
while True: # wait for job to finalize
63-
if os.path.isfile(out_file):
64-
break
65-
with open(out_file, "r") as fid:
66-
contents = fid.read()
67-
os.remove(out_file)
64+
*script, _, job_msg = stdout.strip().split("\n")
65+
script = "\n".join(script).strip()
66+
job_id = int(job_msg.replace("Submitted batch job ", ""))
6867

6968
self.assertEqual(self.script, script)
70-
self.assertIn("Hello!", contents)
7169
self.assertIn(f"Submitted batch job {job_id}", stdout)
7270

7371

74-
def run_cli(testargs):
75-
with io.StringIO() as buffer:
76-
with contextlib.redirect_stdout(buffer):
77-
with patch.object(sys, "argv", testargs):
78-
if shutil.which("sbatch") is not None:
79-
cli()
80-
else:
81-
with patch("subprocess.run", subprocess_sbatch):
82-
cli()
83-
stdout = buffer.getvalue()
84-
return stdout
85-
86-
87-
def subprocess_sbatch(*args, **kwargs):
88-
job_id = 1234
89-
out_file = f"slurm-{job_id}.out"
90-
with open(out_file, "w") as fid:
91-
fid.write("Hello!!!\n")
92-
stdout = f"Submitted batch job {job_id}"
93-
return subprocess.CompletedProcess(
94-
*args, returncode=1, stdout=stdout.encode("utf-8")
95-
)
96-
97-
98-
def parse_stdout(stdout):
99-
*script, _, job_msg = stdout.strip().split("\n")
100-
script = "\n".join(script).strip()
101-
job_id = int(job_msg.replace("Submitted batch job ", ""))
102-
return script, job_id
103-
104-
10572
if __name__ == "__main__":
10673
unittest.main()

test/test_core.py

+17-89
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import shutil
66
import subprocess
77
import unittest
8-
from unittest.mock import patch
98

109
from simple_slurm import Slurm
1110

@@ -209,34 +208,15 @@ def test_13_output_env_vars(self):
209208

210209
def test_14_srun_returncode(self):
211210
slurm = Slurm(contiguous=True)
212-
if shutil.which("srun") is not None:
213-
code = slurm.srun("echo Hello!")
214-
else:
215-
with patch("subprocess.run", subprocess_srun):
216-
code = slurm.srun("echo Hello!")
211+
code = slurm.srun("echo Hello!")
217212
self.assertEqual(code, 0)
218213

219214
def test_15_sbatch_execution(self):
220-
with io.StringIO() as buffer:
221-
with contextlib.redirect_stdout(buffer):
222-
slurm = Slurm(contiguous=True)
223-
if shutil.which("sbatch") is not None:
224-
job_id = slurm.sbatch("echo Hello!")
225-
else:
226-
with patch("subprocess.run", subprocess_sbatch):
227-
job_id = slurm.sbatch("echo Hello!")
228-
stdout = buffer.getvalue()
215+
slurm = Slurm(contiguous=True)
216+
job_id, stdout = self.__run_sbatch(slurm)
229217

230-
out_file = f"slurm-{job_id}.out"
231-
while True: # wait for job to finalize
232-
if os.path.isfile(out_file):
233-
break
234-
with open(out_file, "r") as fid:
235-
contents = fid.read()
236-
os.remove(out_file)
237218
self.assertFalse(slurm.is_parsable)
238219
self.assertIsInstance(job_id, int)
239-
self.assertIn("Hello!", contents)
240220
self.assertIn(f"Submitted batch job {job_id}", stdout)
241221

242222
def test_16_parse_timedelta(self):
@@ -281,36 +261,19 @@ def test_18_false_boolean_arguments(self):
281261

282262
def test_19_sbatch_execution_with_job_file(self):
283263
job_file = "script.sh"
284-
with io.StringIO() as buffer:
285-
with contextlib.redirect_stdout(buffer):
286-
slurm = Slurm(contiguous=True)
287-
if shutil.which("sbatch") is not None:
288-
job_id = slurm.sbatch("echo Hello!", job_file=job_file)
289-
else:
290-
with patch("subprocess.run", subprocess_sbatch):
291-
job_id = slurm.sbatch("echo Hello!", job_file=job_file)
292-
stdout = buffer.getvalue()
293264

265+
slurm = Slurm(contiguous=True)
266+
job_id, stdout = self.__run_sbatch(slurm, job_file=job_file)
267+
268+
self.assertFalse(slurm.is_parsable)
294269
self.assertIsInstance(job_id, int)
270+
self.assertIn(f"Submitted batch job {job_id}", stdout)
295271

296-
out_file = f"slurm-{job_id}.out"
297-
while True: # wait for job to finalize
298-
if os.path.isfile(out_file):
299-
break
300-
# Assert the script was written correctly
301272
with open(job_file, "r") as fid:
302273
job_contents = fid.read()
303274
os.remove(job_file)
304275

305-
self.assertEqual(job_contents, self.job_file_test_19)
306-
307-
# Assert the script was executed correctly
308-
with open(out_file, "r") as fid:
309-
contents = fid.read()
310-
os.remove(out_file)
311-
312-
self.assertIn("Hello!", contents)
313-
self.assertIn(f"Submitted batch job {job_id}", stdout)
276+
self.assertEqual(self.job_file_test_19, job_contents)
314277

315278
def test_20_add_cmd_single(self):
316279
slurm = Slurm(
@@ -367,54 +330,19 @@ def test_21_add_cmd_multiple(self):
367330
self.assertEqual(self.script + "\n" + self.commands, str(slurm))
368331

369332
def test_22_parsable_sbatch_execution(self):
370-
with io.StringIO() as buffer:
371-
with contextlib.redirect_stdout(buffer):
372-
slurm = Slurm(contiguous=True, parsable=True)
373-
if shutil.which("sbatch") is not None:
374-
job_id = slurm.sbatch("echo Hello!")
375-
else:
376-
with patch("subprocess.run", subprocess_sbatch_parsable):
377-
job_id = slurm.sbatch("echo Hello!")
378-
stdout = buffer.getvalue()
333+
slurm = Slurm(contiguous=True, parsable=True)
334+
job_id, stdout = self.__run_sbatch(slurm)
379335

380-
out_file = f"slurm-{job_id}.out"
381-
while True: # wait for job to finalize
382-
if os.path.isfile(out_file):
383-
break
384-
with open(out_file, "r") as fid:
385-
contents = fid.read()
386-
os.remove(out_file)
387336
self.assertTrue(slurm.is_parsable)
388337
self.assertIsInstance(job_id, int)
389-
self.assertIn("Hello!", contents)
390338
self.assertEqual(f"{job_id}\n", stdout)
391339

392-
393-
def subprocess_srun(*args, **kwargs):
394-
print("Hello!!!")
395-
return subprocess.CompletedProcess(*args, returncode=0)
396-
397-
398-
def subprocess_sbatch(*args, **kwargs):
399-
job_id = 1234
400-
out_file = f"slurm-{job_id}.out"
401-
with open(out_file, "w") as fid:
402-
fid.write("Hello!!!\n")
403-
stdout = f"Submitted batch job {job_id}"
404-
return subprocess.CompletedProcess(
405-
*args, returncode=1, stdout=stdout.encode("utf-8")
406-
)
407-
408-
409-
def subprocess_sbatch_parsable(*args, **kwargs):
410-
job_id = 1234
411-
out_file = f"slurm-{job_id}.out"
412-
with open(out_file, "w") as fid:
413-
fid.write("Hello!!!\n")
414-
stdout = str(job_id)
415-
return subprocess.CompletedProcess(
416-
*args, returncode=0, stdout=stdout.encode("utf-8")
417-
)
340+
def __run_sbatch(self, slurm, *args, **kwargs):
341+
with io.StringIO() as buffer:
342+
with contextlib.redirect_stdout(buffer):
343+
job_id = slurm.sbatch("echo Hello!", *args, **kwargs)
344+
stdout = buffer.getvalue()
345+
return job_id, stdout
418346

419347

420348
if __name__ == "__main__":

0 commit comments

Comments
 (0)