Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ select = [
# raise vanilla args
"TRY002",
# pytest-style
#"PT",
"PT",
]
# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
Expand Down
19 changes: 8 additions & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

"""Helper np.arrays used by various tests."""

from collections.abc import Generator
from typing import Any

import pytest
from power_grid_model import initialize_array

Expand Down Expand Up @@ -35,20 +32,20 @@ def get_installed_graph_models() -> dict[str, type[BaseGraphModel]]:


@pytest.fixture
def grid() -> Generator[Grid, Any, None]:
def grid() -> Grid:
"""A grid fixture that will be parametrized"""
yield Grid.empty()
return Grid.empty()


@pytest.fixture
def graph() -> Generator[RustworkxGraphModel, Any, None]:
def graph() -> RustworkxGraphModel:
"""A graph fixture that will be parametrized"""
yield RustworkxGraphModel()
return RustworkxGraphModel()


@pytest.fixture
def fancy_test_array():
yield FancyTestArray(
return FancyTestArray(
id=[1, 2, 3],
test_int=[3, 0, 4],
test_float=[4.0, 4.0, 1.0],
Expand All @@ -59,17 +56,17 @@ def fancy_test_array():

@pytest.fixture
def basic_grid(grid: Grid):
yield build_basic_grid(grid)
return build_basic_grid(grid)


@pytest.fixture
def grid_with_3wt(grid: Grid):
yield build_basic_grid_with_three_winding(grid)
return build_basic_grid_with_three_winding(grid)


@pytest.fixture
def topologically_full_grid(grid: Grid):
yield build_topologically_full_grid(grid)
return build_topologically_full_grid(grid)


@pytest.fixture
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/model/arrays/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ def test_setitem_as_fancy_array_with_mask(fancy_test_array: FancyTestArray):

def test_setitem_as_fancy_array_with_mask_too_large(fancy_test_array: FancyTestArray):
mask = np.array([True, False, True])
with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match="NumPy boolean array indexing assignment cannot assign 3 input values to the 2 output values"
" where the mask is true",
):
fancy_test_array[mask] = FancyTestArray.zeros(3)


Expand Down
13 changes: 7 additions & 6 deletions tests/unit/model/arrays/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MPL-2.0

import re
from typing import ClassVar

import numpy as np
Expand Down Expand Up @@ -82,7 +83,7 @@ def test_build_from_kwargs():


def test_build_from_kwargs_with_missing_input_fields():
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Missing required columns"):
FancyTestArray(
id=[1, 2, 3],
test_int=[3, 0, 4],
Expand All @@ -93,7 +94,7 @@ def test_build_from_kwargs_with_missing_input_fields():


def test_build_from_kwargs_with_different_input_lengths():
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Size of column 'test_bool' does not match other columns.")):
FancyTestArray(
id=[1, 2, 3],
test_int=[3, 0, 4],
Expand All @@ -105,7 +106,7 @@ def test_build_from_kwargs_with_different_input_lengths():

def test_build_from_kwargs_with_different_input_lengths_with_defaults():
# Also fail when defaults are defined
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Size of column 'test_bool' does not match other columns.")):
DefaultedFancyTestArray(
id=[1, 2, 3],
test_int=[3, 0, 4],
Expand Down Expand Up @@ -221,20 +222,20 @@ def test_from_structured_subarray_with_defaults(fancy_test_array: FancyTestArray


def test_from_structured_subarray_no_defaults(fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Missing required columns"):
ExtendedFancyTestArrayNoDefaults(fancy_test_array.data)


def test_from_sub_ndarray_with_defaults(fancy_test_array: FancyTestArray):
# defaults are not supported when working with unstructured arrays
sub_ndarray = np.array(fancy_test_array.tolist())
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Cannot convert array of shape (3, 5) into 7 columns.")):
ExtendedFancyTestArray(sub_ndarray)


def test_from_sub_ndarray_no_defaults(fancy_test_array: FancyTestArray):
sub_ndarray = np.array(fancy_test_array.tolist())
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Cannot convert array of shape (3, 5) into 7 columns.")):
ExtendedFancyTestArrayNoDefaults(sub_ndarray)


Expand Down
34 changes: 28 additions & 6 deletions tests/unit/model/arrays/test_modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: MPL-2.0

import re

import numpy as np
import pytest
from numpy.testing import assert_array_equal
Expand All @@ -28,7 +30,9 @@ def test_reorder_by_test_str(self, fancy_test_array: FancyTestArray):
assert_array_equal(reordered.test_str, ["d", "a", "c"])

def test_reorder_mismatched_length(self, fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(
ValueError, match=re.escape("Cannot re-order array: mismatch between new_order and values in 'id'-column.")
):
fancy_test_array.re_order([3, 1])


Expand Down Expand Up @@ -60,15 +64,27 @@ def test_update_by_id_multiple_columns(self, fancy_test_array: FancyTestArray):
assert_array_equal(fancy_test_array.test_int, [88, 0, 99])

def test_update_by_id_invalid_column(self, fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="no field of name non_existing_column"):
fancy_test_array.update_by_id([1, 3], non_existing_column=123)

def test_update_by_id_invalid_length(self, fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match=re.escape(
"Cannot update FancyTestArray. NumPy boolean array indexing assignment cannot "
"assign 3 input values to the 2 output values where the mask is true"
),
):
fancy_test_array.update_by_id([1, 3], test_str=["e", "f", "g"])

def test_update_by_id_invalid_id(self, fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match=re.escape(
"Cannot update FancyTestArray. One or more ids do not exist."
" Provide allow_missing=True if this is intended."
),
):
fancy_test_array.update_by_id([1, 4], test_str="e")

def test_update_by_id_invalid_id_allow_missing(self, fancy_test_array: FancyTestArray):
Expand All @@ -77,11 +93,17 @@ def test_update_by_id_invalid_id_allow_missing(self, fancy_test_array: FancyTest

def test_update_by_id_no_id_column(self):
fancy_non_id_array = FancyNonIdArray.zeros(10)
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Cannot update FancyNonIdArray. no field of name id")):
fancy_non_id_array.update_by_id([1, 4], test_str="e")

def test_update_by_id_non_existing_id(self, fancy_test_array: FancyTestArray):
with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match=re.escape(
"Cannot update FancyTestArray. One or more ids do not exist. "
"Provide allow_missing=True if this is intended."
),
):
fancy_test_array.update_by_id([1, 4], test_str="e")


Expand Down
6 changes: 3 additions & 3 deletions tests/unit/model/containers/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_check_ids_two_arrays_with_conflict():

assert len(list(container.all_arrays())) == 2

with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Duplicates found within _TwoArraysContainer!"):
container.check_ids()


Expand All @@ -122,7 +122,7 @@ def test_check_ids_two_arrays_with_conflict_in_same_array():

assert len(list(container.all_arrays())) == 2

with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Duplicates found within _TwoArraysContainer!"):
container.check_ids()


Expand Down Expand Up @@ -210,7 +210,7 @@ def test_rebuild_ids():
def test_rebuild_ids_with_duplicates():
grid = Grid.from_txt("1 2 12")
grid.node.id = [1, 12] # Duplicate IDs within different arrays same array
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Duplicate ids found between arrays (LineArray)")):
grid.rebuild_ids()


Expand Down
4 changes: 2 additions & 2 deletions tests/unit/model/graphs/test_graph_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def test_all_paths_no_path(self, graph_with_5_nodes: BaseGraphModel):

class TestFindFundamentalCycles:
@pytest.mark.parametrize(
"additional_edges, nodes_in_cycles",
("additional_edges", "nodes_in_cycles"),
[
([], set()),
([(2, 5)], {1, 2, 5}),
Expand Down Expand Up @@ -359,7 +359,7 @@ def test_find_first_connected(self, graph_with_2_routes: BaseGraphModel):

def test_find_first_connected_same_node(self, graph_with_2_routes: BaseGraphModel):
graph = graph_with_2_routes
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="node_id cannot be in candidate_node_ids"):
graph.find_first_connected(1, candidate_node_ids=[1, 3, 5])

def test_find_first_connected_no_match(self, graph_with_2_routes: BaseGraphModel):
Expand Down
13 changes: 9 additions & 4 deletions tests/unit/model/grids/serialization/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""Comprehensive unit tests for Grid serialization with power-grid-model compatibility."""

import json
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import ClassVar
Expand Down Expand Up @@ -108,7 +109,7 @@ def extended_grid():
class TestSerializationRoundtrips:
"""Test serialization across different formats and configurations"""

@pytest.mark.parametrize("grid_fixture", ("basic_grid", "grid"))
@pytest.mark.parametrize("grid_fixture", ["basic_grid", "grid"])
def test_serialization_roundtrip(self, request, grid_fixture: str, tmp_path: Path):
"""Test serialization roundtrip

Expand All @@ -124,7 +125,7 @@ def test_serialization_roundtrip(self, request, grid_fixture: str, tmp_path: Pat
loaded_grid = Grid.deserialize(path)
assert loaded_grid == grid

@pytest.mark.parametrize("grid_fixture", ("basic_grid", "grid"))
@pytest.mark.parametrize("grid_fixture", ["basic_grid", "grid"])
def test_pgm_roundtrip(self, request, grid_fixture: str, tmp_path: Path):
"""Test roundtrip serialization for PGM-compatible grid"""
# Grid
Expand Down Expand Up @@ -323,7 +324,7 @@ def test_missing_required_array_field(self, tmp_path: Path):
with Path(path).open("w", encoding="utf-8") as f:
json.dump({"data": missing_array_data}, f)

with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Missing required columns: {'u_rated'}")):
Grid.deserialize(path)

def test_some_records_miss_data(self, tmp_path):
Expand All @@ -335,5 +336,9 @@ def test_some_records_miss_data(self, tmp_path):
with Path(path).open("w", encoding="utf-8") as f:
json.dump({"data": incomplete_data}, f)

with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match=r"Some records in column '(id|u_rated)' have missing values. "
"For defaulted columns, either provide all values or none.",
):
Grid.deserialize(path)
5 changes: 3 additions & 2 deletions tests/unit/model/grids/serialization/test_string.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0
import re
from pathlib import Path

import numpy as np
Expand Down Expand Up @@ -65,11 +66,11 @@ def test_from_txt_with_branch_ids(self):
np.testing.assert_array_equal([95, 91, 92, 93, 94, 96, 97, 98], grid.branches.id)

def test_from_txt_with_conflicting_ids(self):
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="Source nodes and regular nodes have overlapping ids"):
Grid.from_txt("S1 2", "1 3")

def test_from_txt_with_invalid_line(self):
with pytest.raises(ValueError):
with pytest.raises(ValueError, match=re.escape("Text line 'S1' is invalid. Skipping...")):
Grid.from_txt("S1")

def test_from_txt_parallel_lines_without_ids(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/model/grids/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_from_extended_grid():
assert grid.graphs.complete_graph.nr_nodes == len(grid.branches)

assert extended_grid.id_counter == grid.id_counter
assert extended_grid.max_id == grid.max_id
assert extended_grid.id_counter == grid.id_counter


def test_basic_grid_fixture(basic_grid: Grid):
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/model/grids/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0
import re

import pytest

from power_grid_model_ds import Grid
Expand Down Expand Up @@ -62,7 +64,10 @@ def test_merge_grid_with_some_identical_lines(self, grid1: Grid, grid2: Grid):
grid1.check_ids()

def test_merge_grid_with_some_identical_lines_failing(self, grid1: Grid, grid2: Grid):
with pytest.raises(ValueError):
with pytest.raises(
ValueError,
match=re.escape("Asset ids are not unique after merging! Use mode='recalculate_ids' to avoid this."),
):
grid1.merge(grid2, mode="keep_ids")

def test_merge_grid_into_a_grid_of_a_different_class(self, grid1: Grid):
Expand Down
Loading
Loading