Skip to content

Commit ee66a6e

Browse files
authored
Merge pull request #739 from janosh/pytest-split
use pytest-split in GHA test workflow
2 parents ad4058e + 2771613 commit ee66a6e

File tree

9 files changed

+88
-32
lines changed

9 files changed

+88
-32
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json
2+
from pathlib import Path
3+
4+
split_prefix = "split-"
5+
durations_path = Path(".test_durations")
6+
7+
split_paths = Path(".").glob(f"{split_prefix}*/{durations_path.name}")
8+
9+
try:
10+
previous_durations = json.loads(durations_path.read_text())
11+
except FileNotFoundError:
12+
previous_durations = {}
13+
14+
new_durations = previous_durations.copy()
15+
16+
for path in split_paths:
17+
durations = json.loads(path.read_text())
18+
new_durations.update(durations)
19+
20+
durations_path.parent.mkdir(parents=True, exist_ok=True)
21+
durations_path.write_text(json.dumps(new_durations))

.github/workflows/test.yml

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,28 @@ jobs:
1010
test:
1111
if: github.repository_owner == 'hackingmaterials' # don't run on forks
1212
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
# pytest-split groups for automatically equally split concurrent test runners
16+
group: [1, 2, 3, 4, 5]
1317
services:
1418
mongodb:
1519
image: mongo
1620
ports:
1721
- 27017:27017
1822

1923
steps:
20-
- name: Checkout repo
21-
uses: actions/checkout@v2
24+
- name: Check out repo
25+
uses: actions/checkout@v3
2226

23-
- name: Setup Python
24-
uses: actions/setup-python@v2
27+
- name: Set up python
28+
uses: actions/setup-python@v3
2529
with:
2630
python-version: 3.8
31+
cache: pip
32+
cache-dependency-path: |
33+
setup.py
34+
requirements-ci.txt
2735
2836
- name: Install OpenBabel
2937
run: |
@@ -32,19 +40,54 @@ jobs:
3240
# https://github.com/openbabel/openbabel/issues/2408#issuecomment-1014466193
3341
sudo ln -s /usr/include/openbabel3 /usr/local/include/openbabel3
3442
35-
- name: Cache pip
36-
uses: actions/cache@v2
37-
with:
38-
path: ~/.cache/pip
39-
key: ${{ runner.os }}-pip-${{ hashFiles('requirements-ci.txt', 'setup.py') }}
40-
restore-keys: |
41-
${{ runner.os }}-pip-
42-
4343
- name: Install dependencies
4444
run: |
4545
pip install -r requirements-ci.txt
4646
pip install .[complete]
4747
48-
- name: pytest
48+
- name: Get durations from cache
49+
uses: actions/cache@v2
50+
with:
51+
path: test_durations
52+
# the key must never match, even when restarting workflows, as that
53+
# will cause durations to get out of sync between groups, the
54+
# combined durations will be loaded if available
55+
key: test-durations-split-${{ github.run_id }}-${{ github.run_number}}-${{ matrix.group }}
56+
restore-keys: |
57+
test-durations-combined-${{ github.sha }}
58+
test-durations-combined
59+
60+
- name: Run tests
4961
run: |
50-
pytest --ignore=atomate/qchem/test_files --cov=atomate --cov-report html:coverage_reports atomate
62+
pytest --splits 5 --group ${{ matrix.group }} --store-durations \
63+
--cov=atomate --cov-report html:coverage_reports atomate
64+
65+
- name: Upload partial durations
66+
uses: actions/upload-artifact@v2
67+
with:
68+
name: split-${{ matrix.group }}
69+
path: .test_durations
70+
71+
update_durations:
72+
name: Combine and update test durations
73+
runs-on: ubuntu-latest
74+
needs: test
75+
steps:
76+
- name: Check out repo
77+
uses: actions/checkout@v2
78+
79+
- name: Get durations from cache
80+
uses: actions/cache@v2
81+
with:
82+
path: .test_durations
83+
# key won't match during the first run for the given commit, but
84+
# restore-key will if there's a previous stored durations file,
85+
# so cache will both be loaded and stored
86+
key: test-durations-combined-${{ github.sha }}
87+
restore-keys: test-durations-combined
88+
89+
- name: Download artifacts
90+
uses: actions/download-artifact@v2
91+
92+
- name: Combine test durations
93+
run: python3 .github/workflows/combine_durations.py

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pip-log.txt
3131
# Unit test / coverage reports
3232
.coverage
3333
.tox
34-
nosetests.xml
34+
.test_durations
3535
*.mypy_cache/*
3636

3737
# Translations

atomate/common/firetasks/tests/test_parse_outputs.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ def __init__(self):
2121
def assimilate(self, path):
2222
return {"drone": "Test Drone", "dir_name": "/test", "state": "successful"}
2323

24-
def get_valid_paths(self, path):
25-
return path
26-
2724

2825
class TestToDbTask(AtomateTest):
2926
def test_ToDbTask(self):

atomate/lammps/drones.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def assimilate(
8383
dump_files = [dump_files] if isinstance(dump_files, str) else dump_files
8484

8585
# input set
86-
lmps_input = LammpsInputSet.from_file(
86+
lmps_input = LammpsInputSet.from_file( # noqa: F821
8787
"lammps", input_file, {}, data_file, data_filename
8888
)
8989

@@ -94,7 +94,7 @@ def assimilate(
9494
dumps.append((df, LammpsDump.from_file(os.path.join(path, df))))
9595

9696
# log
97-
log = LammpsLog(log_file=log_file)
97+
log = LammpsLog(log_file=log_file) # noqa: F821
9898

9999
logger.info(f"Getting task doc for base dir :{path}")
100100
d = self.generate_doc(path, lmps_input, log, dumps)
@@ -163,9 +163,6 @@ def generate_doc(self, dir_name, lmps_input, log, dumps):
163163
)
164164
return None
165165

166-
def get_valid_paths(self, path):
167-
return [path]
168-
169166
def as_dict(self):
170167
init_args = {
171168
"additional_fields": self.additional_fields,

atomate/qchem/drones.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,3 @@ def validate_doc(self, d):
503503
diff = v.difference(set(d.get(k, d).keys()))
504504
if diff:
505505
logger.warning(f"The keys {diff} in {k} not set")
506-
507-
@staticmethod
508-
def get_valid_paths(self, path):
509-
return [path]

atomate/vasp/drones.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def generate_doc(self, dir_name, vasprun_files, outcar_files):
420420
d["output"][k] = d_calc_final["output"][k]
421421

422422
# store optical data, overwrites the LOPTICS data
423-
if d["input"]["incar"].get("ALGO") == 'CHI':
423+
if d["input"]["incar"].get("ALGO") == "CHI":
424424
for k in ["optical_absorption_coeff", "dielectric"]:
425425
d["output"][k] = d_calc_final["output"][k]
426426

@@ -486,12 +486,12 @@ def process_vasprun(self, dir_name, taskname, filename):
486486
d["output"][k] = d["output"].pop(v)
487487

488488
# Process bandstructure and DOS
489-
if self.bandstructure_mode is not False: # noqa
489+
if self.bandstructure_mode is not False:
490490
bs = self.process_bandstructure(vrun)
491491
if bs:
492492
d["bandstructure"] = bs
493493

494-
if self.parse_dos is not False: # noqa
494+
if self.parse_dos is not False:
495495
dos = self.process_dos(vrun)
496496
if dos:
497497
d["dos"] = dos
@@ -588,7 +588,7 @@ def process_vasprun(self, dir_name, taskname, filename):
588588
d["output"]["optical_absorption_coeff"] = vrun.optical_absorption_coeff
589589

590590
# parse output from response function
591-
if vrun.incar.get("ALGO") == 'CHI':
591+
if vrun.incar.get("ALGO") == "CHI":
592592
dielectric = vrun.dielectric
593593
d["output"]["dielectric"] = dict(
594594
energy=dielectric[0], real=dielectric[1], imag=dielectric[2]

atomate/vasp/firetasks/lobster_tasks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ def __init__(self, *args, **kwargs):
231231
f"Check that you did not misspell it."
232232
)
233233

234-
def _find_gz_file(self, filename):
234+
@staticmethod
235+
def _find_gz_file(filename):
235236
gz_filename = filename + ".gz"
236237
if os.path.exists(gz_filename):
237238
return gz_filename

requirements-ci.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ coverage==5.5
33
moto==2.0.10
44
pytest-cov==2.12.1
55
pytest==6.2.4
6+
pytest-split

0 commit comments

Comments
 (0)