Skip to content

Commit

Permalink
Add more tests of error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
eivindjahren committed Jan 8, 2024
1 parent 97dcacf commit acb42a8
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 59 deletions.
77 changes: 45 additions & 32 deletions src/ert/config/_read_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def from_keyword(cls, summary_keyword: str) -> _SummaryType:
"W": cls.WELL,
}
if summary_keyword == "":
raise ValueError("Got empty summary_keyword")
raise ValueError("Got empty summary keyword")
if any(special in summary_keyword for special in SPECIAL_KEYWORDS):
return cls.OTHER
if summary_keyword[0] in KEYWORD_TYPE_MAPPING:
Expand Down Expand Up @@ -200,7 +200,7 @@ def _find_file_matching(
raise ValueError(f"Could not find any {kind} matching case path {case}")
if len(candidates) > 1:
raise ValueError(
f"Ambigous reference to {kind} in {case}, could be any of {candidates}"
f"Ambiguous reference to {kind} in {case}, could be any of {candidates}"
)
return os.path.join(dir, candidates[0])

Expand All @@ -215,10 +215,13 @@ def read_summary(
filepath: str, fetch_keys: Sequence[str]
) -> Tuple[List[str], Sequence[datetime], Any]:
summary, spec = _get_summary_filenames(filepath)
date_index, start_date, date_units, keys, indices = _read_spec(spec, fetch_keys)
fetched, time_map = _read_summary(
summary, start_date, date_units, indices, date_index
)
try:
date_index, start_date, date_units, keys, indices = _read_spec(spec, fetch_keys)
fetched, time_map = _read_summary(
summary, start_date, date_units, indices, date_index
)
except resfo.ResfoParsingError as err:
raise ValueError(f"Failed to read summary file {filepath}: {err}") from err
return (keys, time_map, fetched)


Expand All @@ -228,6 +231,14 @@ def _key2str(key: Union[bytes, str]) -> str:
return ret.strip()


def _check_vals(
kw: str, spec: str, vals: Union[npt.NDArray[Any], resfo.MESS]
) -> npt.NDArray[Any]:
if vals is resfo.MESS or isinstance(vals, resfo.MESS):
raise ValueError(f"{kw.strip()} in {spec} has incorrect type MESS")
return vals


def _read_spec(
spec: str, fetch_keys: Sequence[str]
) -> Tuple[int, datetime, DateUnit, List[str], npt.NDArray[np.int32]]:
Expand Down Expand Up @@ -274,38 +285,36 @@ def _read_spec(
break
kw = entry.read_keyword()
if kw in arrays:
vals = entry.read_array()
if vals is resfo.MESS or isinstance(vals, resfo.MESS):
raise ValueError(f"{kw} in {spec} was MESS")
arrays[kw] = vals
arrays[kw] = _check_vals(kw, spec, entry.read_array())
if kw == "DIMENS ":
vals = entry.read_array()
if vals is resfo.MESS or isinstance(vals, resfo.MESS):
raise ValueError(f"DIMENS in {spec} was MESS")
vals = _check_vals(kw, spec, entry.read_array())
size = len(vals)
n = vals[0] if size > 0 else None
nx = vals[1] if size > 1 else None
ny = vals[2] if size > 2 else None
if kw == "STARTDAT":
vals = entry.read_array()
if vals is resfo.MESS or isinstance(vals, resfo.MESS):
raise ValueError(f"Startdate in {spec} was MESS")
vals = _check_vals(kw, spec, entry.read_array())
size = len(vals)
day = vals[0] if size > 0 else 0
month = vals[1] if size > 1 else 0
year = vals[2] if size > 2 else 0
hour = vals[3] if size > 3 else 0
minute = vals[4] if size > 4 else 0
microsecond = vals[5] if size > 5 else 0
date = datetime(
day=day,
month=month,
year=year,
hour=hour,
minute=minute,
second=microsecond // 10**6,
microsecond=microsecond % 10**6,
)
try:
date = datetime(
day=day,
month=month,
year=year,
hour=hour,
minute=minute,
second=microsecond // 10**6,
microsecond=microsecond % 10**6,
)
except Exception as err:
raise ValueError(
f"SMSPEC {spec} contains invalid STARTDAT: {err}"
) from err
keywords = arrays["KEYWORDS"]
wgnames = arrays["WGNAMES "]
nums = arrays["NUMS "]
Expand All @@ -315,9 +324,9 @@ def _read_spec(
lgr_names = arrays["LGRNAMES"]

if date is None:
raise ValueError(f"keyword startdat missing in {spec}")
raise ValueError(f"Keyword startdat missing in {spec}")
if keywords is None:
raise ValueError(f"keywords missing in {spec}")
raise ValueError(f"Keywords missing in {spec}")
if n is None:
n = len(keywords)

Expand Down Expand Up @@ -367,16 +376,22 @@ def optional_get(arr: Optional[npt.NDArray[Any]], idx: int) -> Any:

units = arrays["UNITS "]
if units is None:
raise ValueError(f"keyword units missing in {spec}")
raise ValueError(f"Keyword units missing in {spec}")
if date_index is None:
raise ValueError(f"KEYWORDS did not contain TIME in {spec}")
if date_index >= len(units):
raise ValueError(f"Unit missing for TIME in {spec}")

unit_key = _key2str(units[date_index])
try:
date_unit = DateUnit[unit_key]
except KeyError:
raise ValueError(f"Unknown date unit in {spec}: {unit_key}") from None

return (
date_index,
date,
DateUnit[_key2str(units[date_index])],
date_unit,
list(keys_array),
indices_array,
)
Expand All @@ -403,9 +418,7 @@ def _read_summary(
def read_params() -> None:
nonlocal last_params, values
if last_params is not None:
vals = last_params.read_array()
if vals is resfo.MESS or isinstance(vals, resfo.MESS):
raise ValueError(f"PARAMS in {summary} was MESS")
vals = _check_vals("PARAMS", summary, last_params.read_array())
values.append(vals[indices])
dates.append(start_date + unit.make_delta(float(vals[date_index])))
last_params = None
Expand Down
1 change: 1 addition & 0 deletions tests/unit_tests/config/config_dict_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ def ert_config_values(draw, use_eclbase=booleans):
smspecs(
sum_keys=st.just(sum_keys),
start_date=st.just(Date.from_datetime(first_date)),
use_days=st.just(True),
)
)
std_cutoff = draw(small_floats)
Expand Down
15 changes: 9 additions & 6 deletions tests/unit_tests/config/summary_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,7 @@ def to_file(self, filelike, file_format: resfo.Format = resfo.Format.UNFORMATTED


@st.composite
def smspecs(
draw,
sum_keys,
start_date,
):
def smspecs(draw, sum_keys, start_date, use_days=st.booleans()):

Check warning on line 284 in tests/unit_tests/config/summary_generator.py

View workflow job for this annotation

GitHub Actions / check-style (3.11)

Do not perform function call `st.booleans` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
"""
Strategy for smspec that ensures that the TIME parameter, as required by
ert, is in the parameters list.
Expand All @@ -297,7 +293,14 @@ def smspecs(
ny = draw(small_ints)
nz = draw(small_ints)
keywords = ["TIME "] + sum_keys
units = ["DAYS "] + draw(st.lists(unit_names, min_size=n - 1, max_size=n - 1))
if draw(use_days):
units = ["DAYS "] + draw(
st.lists(unit_names, min_size=n - 1, max_size=n - 1)
)
else:
units = ["HOURS "] + draw(
st.lists(unit_names, min_size=n - 1, max_size=n - 1)
)
well_names = [":+:+:+:+"] + draw(st.lists(names, min_size=n - 1, max_size=n - 1))
if use_locals: # use local
lgrs = draw(st.lists(names, min_size=n, max_size=n))
Expand Down
109 changes: 88 additions & 21 deletions tests/unit_tests/config/test_read_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

import hypothesis.strategies as st
import pytest
import resfo
from hypothesis import given
from resdata.summary import Summary, SummaryVarType
from resfo import Format

from ert.config._read_summary import _SummaryType, make_summary_key, read_summary

Expand Down Expand Up @@ -192,12 +192,12 @@ def test_completion_summary_format_have_cell_index_and_name(
)


@given(summaries(), st.sampled_from(Format))
@given(summaries(), st.sampled_from(resfo.Format))
def test_that_reading_summaries_returns_the_contents_of_the_file(
tmp_path_factory, summary, format
):
tmp_path = tmp_path_factory.mktemp("summary")
format_specifier = "F" if format == Format.FORMATTED else ""
format_specifier = "F" if format == resfo.Format.FORMATTED else ""
smspec, unsmry = summary
unsmry.to_file(tmp_path / f"TEST.{format_specifier}UNSMRY", format)
smspec.to_file(tmp_path / f"TEST.{format_specifier}SMSPEC", format)
Expand Down Expand Up @@ -252,39 +252,106 @@ def to_date(start_date: datetime, offset: float, unit: str) -> datetime:


@pytest.mark.parametrize(
"spec_contents, smry_contents",
"spec_contents, smry_contents, error_message",
[
(b"", b""),
(b"1", b"1"),
(b"\x00\x00\x00\x10", b"1"),
(b"\x00\x00\x00\x10UNEXPECTED", b"\x00\x00\x00\x10UNEXPECTED"),
(b"", b"", "Keyword startdat missing"),
(b"1", b"1", "Failed to read summary file"),
(
b"\x00\x00\x00\x10UNEXPECTED",
b"\x00\x00\x00\x10KEYWORD1" + (2200).to_bytes(4, byteorder="big"),
),
(
b"\x00\x00\x00\x10FOOOOOOO\x00",
b"\x00\x00\x00\x10FOOOOOOO"
+ (2300).to_bytes(4, byteorder="big")
+ b"INTE\x00\x00\x00\x10"
+ b"\x00" * (4 * 2300 + 4 * 6),
b"\x00\x00\x00\x10FOOOOOOO\x00\x00\x00\x01"
+ b"INTE"
+ b"\x00\x00\x00\x10"
+ (4).to_bytes(4, signed=True, byteorder="big")
+ b"\x00" * 4,
b"",
"Keyword startdat missing",
),
(
b"\x00\x00\x00\x10FOOOOOOO\x00\x00\x00\x01"
b"\x00\x00\x00\x10STARTDAT\x00\x00\x00\x01"
+ b"INTE"
+ b"\x00\x00\x00\x10"
+ (4).to_bytes(4, signed=True, byteorder="big")
+ b"\x00" * 4
+ (4).to_bytes(4, signed=True, byteorder="big"),
b"\x00\x00\x00\x10FOOOOOOO\x00",
b"",
"contains invalid STARTDAT",
),
],
)
def test_that_incorrect_summary_files_raises_informative_errors(
smry_contents, spec_contents, tmp_path
smry_contents, spec_contents, error_message, tmp_path
):
(tmp_path / "test.UNSMRY").write_bytes(smry_contents)
(tmp_path / "test.SMSPEC").write_bytes(spec_contents)

with pytest.raises(ValueError):
with pytest.raises(ValueError, match=error_message):
read_summary(str(tmp_path / "test"), ["*"])


def test_mess_values_in_summary_files_raises_informative_errors(tmp_path):
resfo.write(tmp_path / "test.SMSPEC", [("KEYWORDS", resfo.MESS)])
(tmp_path / "test.UNSMRY").write_bytes(b"")

with pytest.raises(ValueError, match="has incorrect type MESS"):
read_summary(str(tmp_path / "test"), ["*"])


def test_empty_keywords_in_summary_files_raises_informative_errors(tmp_path):
resfo.write(
tmp_path / "test.SMSPEC",
[("STARTDAT", [31, 12, 2012, 00]), ("KEYWORDS", [" "])],
)
(tmp_path / "test.UNSMRY").write_bytes(b"")

with pytest.raises(ValueError, match="Got empty summary keyword"):
read_summary(str(tmp_path / "test"), ["*"])


def test_missing_names_keywords_in_summary_files_raises_informative_errors(
tmp_path,
):
resfo.write(
tmp_path / "test.SMSPEC",
[("STARTDAT", [31, 12, 2012, 00]), ("KEYWORDS", ["BART "])],
)
(tmp_path / "test.UNSMRY").write_bytes(b"")

with pytest.raises(
ValueError,
match="Found block keyword in summary specification without dimens keyword",
):
read_summary(str(tmp_path / "test"), ["*"])


def test_unknown_date_unit_in_summary_files_raises_informative_errors(
tmp_path,
):
resfo.write(
tmp_path / "test.SMSPEC",
[
("STARTDAT", [31, 12, 2012, 00]),
("KEYWORDS", ["TIME "]),
("UNITS ", ["ANNUAL "]),
],
)
(tmp_path / "test.UNSMRY").write_bytes(b"")

with pytest.raises(
ValueError,
match="Unknown date unit .* ANNUAL",
):
read_summary(str(tmp_path / "test"), ["*"])


def test_that_ambiguous_case_restart_raises_an_informative_error(
tmp_path,
):
(tmp_path / "test.UNSMRY").write_bytes(b"")
(tmp_path / "test.FUNSMRY").write_bytes(b"")
(tmp_path / "test.smspec").write_bytes(b"")
(tmp_path / "test.Smspec").write_bytes(b"")

with pytest.raises(
ValueError,
match="Ambiguous reference to unified summary",
):
read_summary(str(tmp_path / "test"), ["*"])

0 comments on commit acb42a8

Please sign in to comment.