Skip to content

Commit 73ddc7f

Browse files
authored
Add unit testing (#84)
* Fixed outputs.py module after improved testing * Moved testing to test_general.py Added explicitly input for methods in ModelSystem and testing Added testing for _set_system_branch_depth and TODO for the functions in these testings which should be implemented in the datamodel * Added explicit input and fix testing for atoms_state.py * Added coverage in README * Added testing for model_method.py classes related with TB * Added explicitly input in functions for numerical_settings.py and its testing * Fix imports using abspath to package * Added sh script to generate coverage.txt in the project * Deleted coverage.txt file and add it to gitignore * Added Ahmed changes to the pipeline for comment on pr from coverage Added uv to pip install in pipeline * Fixed README with more info on the script, uv
1 parent e032021 commit 73ddc7f

23 files changed

+1431
-490
lines changed

.github/workflows/actions.yaml

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,55 @@
11
name: install-and-test
22
on: [push]
3+
4+
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
5+
# `contents` is for permission to the contents of the repository.
6+
# `pull-requests` is for permission to pull request
7+
permissions:
8+
contents: write
9+
checks: write
10+
pull-requests: write
11+
312
jobs:
413
install-and-test:
514
runs-on: ubuntu-latest
615
steps:
7-
- uses: actions/checkout@v3
8-
- name: Set up Python 3.9
9-
uses: actions/setup-python@v2
10-
with:
11-
python-version: 3.9
12-
- name: Install dependencies
13-
run: |
14-
pip install --upgrade pip
15-
pip install '.[dev]' --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
16-
pip install coverage coveralls
17-
- name: mypy
18-
run: |
19-
python -m mypy --ignore-missing-imports --follow-imports=silent --no-strict-optional src/nomad_simulations tests
20-
- name: Test with pytest
21-
run: |
22-
python -m coverage run -m pytest -sv tests
23-
- name: Submit to coveralls
24-
continue-on-error: true
25-
env:
26-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27-
run: |
28-
coveralls --service=github
16+
- uses: actions/checkout@v3
17+
- name: Set up Python 3.9
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: 3.9
21+
- name: Install dependencies
22+
run: |
23+
pip install uv
24+
uv pip install -e '.[dev]' --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple --system
25+
- name: mypy
26+
run: |
27+
python -m mypy --ignore-missing-imports --follow-imports=silent --no-strict-optional src/nomad_simulations tests
28+
- name: Build coverage file
29+
run: |
30+
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=src tests | tee pytest-coverage.txt
31+
- name: Pytest coverage comment
32+
uses: MishaKav/pytest-coverage-comment@main
33+
with:
34+
pytest-coverage-path: pytest-coverage.txt
35+
junitxml-path: pytest.xml
2936
build-and-install:
3037
runs-on: ubuntu-latest
3138
steps:
32-
- uses: actions/checkout@v3
33-
- name: Set up Python 3.9
34-
uses: actions/setup-python@v2
35-
with:
36-
python-version: 3.9
37-
- name: Build the package
38-
run: |
39-
pip install --upgrade pip
40-
pip install build
41-
python -m build --sdist
42-
- name: Install the package
43-
run: |
44-
pip install dist/*.tar.gz --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
39+
- uses: actions/checkout@v3
40+
- name: Set up Python 3.9
41+
uses: actions/setup-python@v2
42+
with:
43+
python-version: 3.9
44+
- name: Build the package
45+
run: |
46+
pip install uv
47+
uv pip install --upgrade pip --system
48+
uv pip install build --system
49+
python -m build --sdist
50+
- name: Install the package
51+
run: |
52+
uv pip install dist/*.tar.gz --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple --system
4553
ruff-linting:
4654
runs-on: ubuntu-latest
4755
steps:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ htmlcov/
4545
.cache
4646
nosetests.xml
4747
coverage.xml
48+
coverage.txt
4849
*.cover
4950
*.py,cover
5051
.hypothesis/

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ python3.9 -m venv .pyenv
2121
. .pyenv/bin/activate
2222
```
2323

24+
We recommend installing `uv` for fast pip installation of the packages:
25+
```sh
26+
pip install uv
27+
```
28+
2429
Install the `nomad-lab` package:
2530

2631
```sh
2732
pip install --upgrade pip
28-
pip install '.[dev]' --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
33+
uv pip install '.[dev]' --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
2934
```
3035

3136
**Note!**
@@ -37,25 +42,29 @@ sure to include NOMAD's internal package registry (via `--index-url` in the abov
3742
You can run local tests using the `pytest` package:
3843

3944
```sh
40-
python -m pytest -sv
45+
python -m pytest -sv tests
4146
```
4247

4348
where the `-s` and `-v` options toggle the output verbosity.
4449

45-
Our CI/CD pipeline produces a more comprehensive test report using `coverage` and `coveralls` packages.
46-
To emulate this locally, perform:
50+
Our CI/CD pipeline produces a more comprehensive test report using `coverage` and `coveralls` packages. We suggest you to generate your own coverage reports locally by doing:
4751

4852
```sh
4953
pip install coverage coveralls
50-
python -m coverage run -m pytest -sv
54+
python -m pytest --cov=src tests
55+
```
56+
57+
You can also run the script to generate a local file `coverage.txt` with the same information by doing:
58+
```sh
59+
./scripts/generate_coverage_txt.sh
5160
```
5261

5362
## Development
5463

5564
The plugin is still under development. If you would like to contribute, install the package in editable mode (with the added `-e` flag) with the development dependencies:
5665

5766
```sh
58-
pip install -e .[dev] --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
67+
uv pip install -e .[dev] --index-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi/simple
5968
```
6069

6170
### Setting up plugin on your local installation

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ dependencies = [
3131
[project.optional-dependencies]
3232
dev = [
3333
'mypy==1.0.1',
34-
'pytest==3.10.0',
35-
'pytest-timeout==1.4.2',
36-
'pytest-cov==2.7.1',
34+
'pytest',
35+
'pytest-timeout',
36+
'pytest-cov',
3737
'ruff',
3838
"structlog==22.3.0",
3939
"lxml_html_clean>=0.1.0",

scripts/generate_coverage_txt.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
# Run pytest with coverage
4+
python -m pytest --cov=src | tee coverage.txt
5+
6+
# Append the generation message
7+
echo -e "\n\n\nGenerated using './scripts/generate_coverage_txt.sh' in the terminal in the root folder of the project" >> coverage.txt

src/nomad_simulations/atoms_state.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from nomad.datamodel.metainfo.basesections import Entity
2929
from nomad.datamodel.metainfo.annotations import ELNAnnotation
3030

31-
from .utils import RussellSaundersState
31+
from nomad_simulations.utils import RussellSaundersState
3232

3333

3434
class OrbitalsState(Entity):
@@ -280,7 +280,7 @@ def resolve_degeneracy(self) -> Optional[int]:
280280
for jj in self.j_quantum_number:
281281
if self.mj_quantum_number is not None:
282282
mjs = RussellSaundersState.generate_MJs(
283-
self.j_quantum_number[0], rising=True
283+
J=self.j_quantum_number[0], rising=True
284284
)
285285
degeneracy += len(
286286
[mj for mj in mjs if mj in self.mj_quantum_number]
@@ -293,15 +293,15 @@ def normalize(self, archive, logger) -> None:
293293
super().normalize(archive, logger)
294294

295295
# General checks for physical quantum numbers and symbols
296-
if not self.validate_quantum_numbers(logger):
296+
if not self.validate_quantum_numbers(logger=logger):
297297
logger.error('The quantum numbers are not physical.')
298298
return
299299

300300
# Resolving the quantum numbers and symbols if not available
301301
for quantum_name in ['l', 'ml', 'ms']:
302302
for quantum_type in ['number', 'symbol']:
303303
quantity = self.resolve_number_and_symbol(
304-
quantum_name, quantum_type, logger
304+
quantum_name=quantum_name, quantum_type=quantum_type, logger=logger
305305
)
306306
if getattr(self, f'{quantum_name}_quantum_{quantum_type}') is None:
307307
setattr(self, f'{quantum_name}_quantum_{quantum_type}', quantity)
@@ -383,7 +383,7 @@ def normalize(self, archive, logger) -> None:
383383
self.n_excited_electrons = None
384384
self.orbital_ref.degeneracy = 1
385385
if self.orbital_ref.occupation is None:
386-
self.orbital_ref.occupation = self.resolve_occupation(logger)
386+
self.orbital_ref.occupation = self.resolve_occupation(logger=logger)
387387

388388

389389
class HubbardInteractions(ArchiveSection):
@@ -552,11 +552,11 @@ def normalize(self, archive, logger) -> None:
552552
self.u_interaction,
553553
self.u_interorbital_interaction,
554554
self.j_hunds_coupling,
555-
) = self.resolve_u_interactions(logger)
555+
) = self.resolve_u_interactions(logger=logger)
556556

557557
# If u_effective is not available, calculate it
558558
if self.u_effective is None:
559-
self.u_effective = self.resolve_u_effective(logger)
559+
self.u_effective = self.resolve_u_effective(logger=logger)
560560

561561
# Check if length of `orbitals_ref` is the same as the length of `umn`:
562562
if self.u_matrix is not None and self.orbitals_ref is not None:
@@ -652,6 +652,6 @@ def normalize(self, archive, logger) -> None:
652652

653653
# Get chemical_symbol from atomic_number and viceversa
654654
if self.chemical_symbol is None:
655-
self.chemical_symbol = self.resolve_chemical_symbol(logger)
655+
self.chemical_symbol = self.resolve_chemical_symbol(logger=logger)
656656
if self.atomic_number is None:
657-
self.atomic_number = self.resolve_atomic_number(logger)
657+
self.atomic_number = self.resolve_atomic_number(logger=logger)

src/nomad_simulations/general.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,16 @@
1818

1919
import numpy as np
2020
from typing import List
21-
from structlog.stdlib import BoundLogger
2221

23-
from nomad.units import ureg
24-
from nomad.metainfo import SubSection, Quantity, MEnum, Section, Datetime
22+
from nomad.metainfo import SubSection, Quantity, Section, Datetime
2523
from nomad.datamodel.metainfo.annotations import ELNAnnotation
2624
from nomad.datamodel.data import EntryData
2725
from nomad.datamodel.metainfo.basesections import Entity, Activity
2826

29-
from .model_system import ModelSystem
30-
from .model_method import ModelMethod
31-
from .outputs import Outputs
32-
from .utils import is_not_representative, get_composition
27+
from nomad_simulations.model_system import ModelSystem
28+
from nomad_simulations.model_method import ModelMethod
29+
from nomad_simulations.outputs import Outputs
30+
from nomad_simulations.utils import is_not_representative, get_composition
3331

3432

3533
class Program(Entity):
@@ -178,7 +176,9 @@ def _set_system_branch_depth(
178176
):
179177
for system_child in system_parent.model_system:
180178
system_child.branch_depth = branch_depth + 1
181-
self._set_system_branch_depth(system_child, branch_depth + 1)
179+
self._set_system_branch_depth(
180+
system_parent=system_child, branch_depth=branch_depth + 1
181+
)
182182

183183
def resolve_composition_formula(self, system_parent: ModelSystem) -> None:
184184
"""Determine and set the composition formula for `system_parent` and all of its
@@ -217,7 +217,9 @@ def set_composition_formula(
217217
for subsystem in subsystems
218218
]
219219
if system.composition_formula is None:
220-
system.composition_formula = get_composition(subsystem_labels)
220+
system.composition_formula = get_composition(
221+
children_names=subsystem_labels
222+
)
221223

222224
def get_composition_recurs(system: ModelSystem, atom_labels: List[str]) -> None:
223225
"""Traverse the system hierarchy downward and set the branch composition for
@@ -229,10 +231,12 @@ def get_composition_recurs(system: ModelSystem, atom_labels: List[str]) -> None:
229231
to the atom indices stored in system.
230232
"""
231233
subsystems = system.model_system
232-
set_composition_formula(system, subsystems, atom_labels)
234+
set_composition_formula(
235+
system=system, subsystems=subsystems, atom_labels=atom_labels
236+
)
233237
if subsystems:
234238
for subsystem in subsystems:
235-
get_composition_recurs(subsystem, atom_labels)
239+
get_composition_recurs(system=subsystem, atom_labels=atom_labels)
236240

237241
atoms_state = (
238242
system_parent.cell[0].atoms_state if system_parent.cell is not None else []
@@ -242,7 +246,7 @@ def get_composition_recurs(system: ModelSystem, atom_labels: List[str]) -> None:
242246
if atoms_state is not None
243247
else []
244248
)
245-
get_composition_recurs(system_parent, atom_labels)
249+
get_composition_recurs(system=system_parent, atom_labels=atom_labels)
246250

247251
def normalize(self, archive, logger) -> None:
248252
super(EntryData, self).normalize(archive, logger)
@@ -263,8 +267,8 @@ def normalize(self, archive, logger) -> None:
263267
system_parent.branch_depth = 0
264268
if len(system_parent.model_system) == 0:
265269
continue
266-
self._set_system_branch_depth(system_parent)
270+
self._set_system_branch_depth(system_parent=system_parent)
267271

268-
if is_not_representative(system_parent, logger):
272+
if is_not_representative(model_system=system_parent, logger=logger):
269273
continue
270-
self.resolve_composition_formula(system_parent)
274+
self.resolve_composition_formula(system_parent=system_parent)

0 commit comments

Comments
 (0)