Skip to content

Commit

Permalink
TST: Add create_case_metadata integration test
Browse files Browse the repository at this point in the history
This test runs ert with a minimal configuration that just ensures the
workflow functions correctly and the fmu case metadata is exported. It
does not do more rigorous validating of the exported data.
  • Loading branch information
mferrera committed Feb 23, 2024
1 parent 4a19f6c commit 0f1e546
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 119 deletions.
Empty file added tests/__init__.py
Empty file.
153 changes: 42 additions & 111 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""The conftest.py, providing magical fixtures to tests."""
import datetime
import inspect
import json
import logging
import os
import shutil
from functools import wraps
from pathlib import Path

import fmu.dataio as dio
Expand All @@ -18,9 +16,10 @@
from fmu.dataio.dataio import ExportData, read_metadata
from fmu.dataio.datastructure.configuration import global_configuration

from .utils import _metadata_examples

logger = logging.getLogger(__name__)

ROOTPWD = Path(".").absolute()
RUN1 = "tests/data/drogon/ertrun1/realization-0/iter-0"
RUN2 = "tests/data/drogon/ertrun1"
RUN_PRED = "tests/data/drogon/ertrun1/realization-0/pred"
Expand Down Expand Up @@ -55,18 +54,9 @@ def set_environ_inside_rms(monkeypatch):
monkeypatch.setattr("fmu.dataio._utils.detect_inside_rms", lambda: True)


def inside_rms(func):
@pytest.mark.usefixtures("set_export_data_inside_rms", "set_environ_inside_rms")
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)

return wrapper


@pytest.fixture(name="testroot", scope="session")
def fixture_testroot():
return ROOTPWD
@pytest.fixture(scope="session")
def rootpath(request):
return request.config.rootpath


def _fmu_run1_env_variables(monkeypatch, usepath="", case_only=False):
Expand All @@ -83,11 +73,11 @@ def _fmu_run1_env_variables(monkeypatch, usepath="", case_only=False):


@pytest.fixture(name="fmurun", scope="function")
def fixture_fmurun(tmp_path_factory, monkeypatch):
def fixture_fmurun(tmp_path_factory, monkeypatch, rootpath):
"""A tmp folder structure for testing; here a new fmurun without case metadata."""
tmppath = tmp_path_factory.mktemp("data")
newpath = tmppath / RUN1
shutil.copytree(ROOTPWD / RUN1, newpath)
shutil.copytree(rootpath / RUN1, newpath)

_fmu_run1_env_variables(monkeypatch, usepath=newpath, case_only=False)

Expand All @@ -96,11 +86,11 @@ def fixture_fmurun(tmp_path_factory, monkeypatch):


@pytest.fixture(name="fmurun_prehook", scope="function")
def fixture_fmurun_prehook(tmp_path_factory, monkeypatch):
def fixture_fmurun_prehook(tmp_path_factory, monkeypatch, rootpath):
"""A tmp folder structure for testing; here a new fmurun without case metadata."""
tmppath = tmp_path_factory.mktemp("data")
newpath = tmppath / RUN2
shutil.copytree(ROOTPWD / RUN2, newpath)
shutil.copytree(rootpath / RUN2, newpath)

_fmu_run1_env_variables(monkeypatch, usepath=newpath, case_only=True)

Expand All @@ -109,11 +99,11 @@ def fixture_fmurun_prehook(tmp_path_factory, monkeypatch):


@pytest.fixture(name="fmurun_w_casemetadata", scope="function")
def fixture_fmurun_w_casemetadata(tmp_path_factory, monkeypatch):
def fixture_fmurun_w_casemetadata(tmp_path_factory, monkeypatch, rootpath):
"""Create a tmp folder structure for testing; here existing fmurun w/ case meta!"""
tmppath = tmp_path_factory.mktemp("data3")
newpath = tmppath / RUN2
shutil.copytree(ROOTPWD / RUN2, newpath)
shutil.copytree(rootpath / RUN2, newpath)
rootpath = newpath / "realization-0/iter-0"

_fmu_run1_env_variables(monkeypatch, usepath=rootpath, case_only=False)
Expand All @@ -123,11 +113,11 @@ def fixture_fmurun_w_casemetadata(tmp_path_factory, monkeypatch):


@pytest.fixture(name="fmurun_w_casemetadata_pred", scope="function")
def fixture_fmurun_w_casemetadata_pred(tmp_path_factory, monkeypatch):
def fixture_fmurun_w_casemetadata_pred(tmp_path_factory, monkeypatch, rootpath):
"""Create a tmp folder structure for testing; here existing fmurun w/ case meta!"""
tmppath = tmp_path_factory.mktemp("data3")
newpath = tmppath / RUN2
shutil.copytree(ROOTPWD / RUN2, newpath)
shutil.copytree(rootpath / RUN2, newpath)
rootpath = newpath / "realization-0/pred"

_fmu_run1_env_variables(monkeypatch, usepath=rootpath, case_only=False)
Expand All @@ -137,44 +127,40 @@ def fixture_fmurun_w_casemetadata_pred(tmp_path_factory, monkeypatch):


@pytest.fixture(name="fmurun_pred", scope="session")
def fixture_fmurun_pred(tmp_path_factory):
def fixture_fmurun_pred(tmp_path_factory, rootpath):
"""Create a tmp folder structure for testing; here a new fmurun for prediction."""
tmppath = tmp_path_factory.mktemp("data_pred")
newpath = tmppath / RUN_PRED
shutil.copytree(ROOTPWD / RUN_PRED, newpath)
shutil.copytree(rootpath / RUN_PRED, newpath)
logger.debug("Ran %s", _current_function_name())
return newpath


@pytest.fixture(name="rmsrun_fmu_w_casemetadata", scope="session")
def fixture_rmsrun_fmu_w_casemetadata(tmp_path_factory):
def fixture_rmsrun_fmu_w_casemetadata(tmp_path_factory, rootpath):
"""Create a tmp folder structure for testing; here existing fmurun w/ case meta!
Then we locate the folder to the ...rms/model folder, pretending running RMS
in a FMU setup where case metadata are present
"""
tmppath = tmp_path_factory.mktemp("data3")
newpath = tmppath / RUN2
shutil.copytree(ROOTPWD / RUN2, newpath)
shutil.copytree(rootpath / RUN2, newpath)
rmspath = newpath / "realization-0/iter-0/rms/model"
rmspath.mkdir(parents=True, exist_ok=True)
logger.debug("Active folder is %s", rmspath)
logger.debug("Ran %s", _current_function_name())
return rmspath


@pytest.fixture(name="rmssetup", scope="module")
def fixture_rmssetup(tmp_path_factory):
@pytest.fixture(scope="module")
def rmssetup(tmp_path_factory, global_config2_path):
"""Create the folder structure to mimic RMS project."""

tmppath = tmp_path_factory.mktemp("revision")
rmspath = tmppath / "rms/model"
rmspath.mkdir(parents=True, exist_ok=True)

# copy a global config here
shutil.copy(
ROOTPWD / "tests/data/drogon/global_config2/global_variables.yml", rmspath
)
shutil.copy(global_config2_path, rmspath)

logger.debug("Ran %s", _current_function_name())

Expand All @@ -196,7 +182,7 @@ def fixture_rmsglobalconfig(rmssetup):


@pytest.fixture(name="globalvars_norwegian_letters", scope="module")
def fixture_globalvars_norwegian_letters(tmp_path_factory):
def fixture_globalvars_norwegian_letters(tmp_path_factory, rootpath):
"""Read a global config with norwegian special letters w/ fmu.config utilities."""

tmppath = tmp_path_factory.mktemp("revisionxx")
Expand All @@ -207,7 +193,7 @@ def fixture_globalvars_norwegian_letters(tmp_path_factory):

# copy a global config with nowr letters here
shutil.copy(
ROOTPWD / "tests/data/drogon/global_config2" / gname,
rootpath / "tests/data/drogon/global_config2" / gname,
rmspath,
)

Expand Down Expand Up @@ -282,10 +268,10 @@ def fixture_globalconfig1():
).model_dump()


@pytest.fixture(name="globalconfig_asfile", scope="module")
def fixture_globalconfig_asfile() -> str:
"""Global config as file for use with environment variable"""
return ROOTPWD / "tests/data/drogon/global_config2/global_variables.yml"
@pytest.fixture(scope="module")
def global_config2_path(rootpath) -> Path:
"""The path to the second global config."""
return rootpath / "tests/data/drogon/global_config2/global_variables.yml"


@pytest.fixture(name="edataobj1", scope="module")
Expand All @@ -310,18 +296,11 @@ def fixture_edataobj1(globalconfig1):
return eobj


@pytest.fixture(name="globalconfig2", scope="module")
def fixture_globalconfig2() -> dict:
@pytest.fixture(scope="module")
def globalconfig2(global_config2_path) -> dict:
"""More advanced global config from file state variable in ExportData class."""
globvar = {}
with open(
ROOTPWD / "tests/data/drogon/global_config2/global_variables.yml",
encoding="utf-8",
) as stream:
globvar = yaml.safe_load(stream)

logger.debug("Ran %s", _current_function_name())
return globvar
with open(global_config2_path, encoding="utf-8") as stream:
return yaml.safe_load(stream)


@pytest.fixture(name="edataobj2", scope="function")
Expand Down Expand Up @@ -359,37 +338,24 @@ def fixture_edataobj2(globalconfig2):
# ======================================================================================


@pytest.fixture(name="schema_080", scope="session")
def fixture_schema_080():
@pytest.fixture(scope="session")
def schema_080(rootpath):
"""Return 0.8.0 version of schema as json."""

return _parse_json(ROOTPWD / "schema/definitions/0.8.0/schema/fmu_results.json")
with open(
rootpath / "schema/definitions/0.8.0/schema/fmu_results.json", encoding="utf-8"
) as f:
return json.load(f)


@pytest.fixture(scope="session")
def metadata_examples():
"""Parse all metadata examples.
Returns:
Dict: Dictionary with filename as key, file contents as value.
"""

# hard code 0.8.0 for now
return {
path.name: _isoformat_all_datetimes(_parse_yaml(str(path)))
for path in ROOTPWD.glob("schema/definitions/0.8.0/examples/*.yml")
}


@pytest.fixture(name="metadata_examples", scope="session")
def fixture_metadata_examples():
"""Parse all metadata examples.
Returns:
Dict: Dictionary with filename as key, file contents as value.
"""
return metadata_examples()
return _metadata_examples()


# ======================================================================================
Expand Down Expand Up @@ -577,16 +543,15 @@ def fixture_summary():


@pytest.fixture(name="drogon_summary")
def fixture_drogon_sum():
def fixture_drogon_sum(rootpath):
"""Return pyarrow table
Returns:
pa.Table: table with summary data
"""
from pyarrow import feather

path = ROOTPWD / "tests/data/drogon/tabular/summary.arrow"
return feather.read_table(path)
return feather.read_table(rootpath / "tests/data/drogon/tabular/summary.arrow")


@pytest.fixture(name="mock_volumes")
Expand All @@ -607,7 +572,7 @@ def fixture_mock_volumes():


@pytest.fixture(name="drogon_volumes")
def fixture_drogon_volumes():
def fixture_drogon_volumes(rootpath):
"""Return pyarrow table
Returns:
Expand All @@ -617,40 +582,6 @@ def fixture_drogon_volumes():

return Table.from_pandas(
pd.read_csv(
ROOTPWD / "tests/data/drogon/tabular/geogrid--vol.csv",
rootpath / "tests/data/drogon/tabular/geogrid--vol.csv",
)
)


# ======================================================================================
# Utilities
# ======================================================================================


def _parse_json(schema_path):
"""Parse the schema, return JSON"""
with open(schema_path, encoding="utf-8") as stream:
return json.load(stream)


def _parse_yaml(yaml_path):
"""Parse the filename as json, return data"""
with open(yaml_path, encoding="utf-8") as stream:
data = yaml.safe_load(stream)

return _isoformat_all_datetimes(data)


def _isoformat_all_datetimes(indate):
"""Recursive function to isoformat all datetimes in a dictionary"""

if isinstance(indate, list):
return [_isoformat_all_datetimes(i) for i in indate]

if isinstance(indate, dict):
return {key: _isoformat_all_datetimes(indate[key]) for key in indate}

if isinstance(indate, (datetime.datetime, datetime.date)):
return indate.isoformat()

return indate
Empty file.
62 changes: 62 additions & 0 deletions tests/test_integration/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import pathlib
import shutil
from textwrap import dedent

import pytest


@pytest.fixture
def base_ert_config() -> str:
return dedent(
r"""
DEFINE <USER> user
DEFINE <SCRATCH> $DATAIO_TMP_PATH/scratch
DEFINE <CASE_DIR> snakeoil
DEFINE <SUMO_ENV> dev
DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>
NUM_REALIZATIONS 5
QUEUE_SYSTEM LOCAL
QUEUE_OPTION LOCAL MAX_RUNNING 5
RANDOM_SEED 123456
RUNPATH <SCRATCH>/<USER>/<CASE_DIR>/realization-<IENS>/iter-<ITER>/
"""
)


@pytest.fixture
def fmu_snakeoil_project(tmp_path, monkeypatch, base_ert_config, global_config2_path):
"""Makes a skeleton FMU project structure into a tmp_path, copying global_config2
into it with a basic ert config that can be appended onto."""
monkeypatch.setenv("DATAIO_TMP_PATH", str(tmp_path))

os.makedirs(tmp_path / "eclipse/model")
for app in ("ert", "rms"):
os.makedirs(tmp_path / f"{app}/bin")
os.makedirs(tmp_path / f"{app}/input")
os.makedirs(tmp_path / f"{app}/model")
os.makedirs(tmp_path / "rms/model/snakeoil.rms13.1.2")

os.makedirs(tmp_path / "fmuconfig/output")
shutil.copy(global_config2_path, tmp_path / "fmuconfig/output/")

os.makedirs(tmp_path / "ert/bin/workflows")
pathlib.Path(tmp_path / "ert/bin/workflows/xhook_create_case_metadata").write_text(
"WF_CREATE_CASE_METADATA "
"<SCRATCH>/<USER>/<CASE_DIR> " # ert case root
"<CONFIG_PATH> " # ert config path
"<CASE_DIR> " # ert case dir
"<USER>", # ert username
encoding="utf-8",
)

pathlib.Path(tmp_path / "ert/model/snakeoil.ert").write_text(
base_ert_config, encoding="utf-8"
)

return tmp_path
Loading

0 comments on commit 0f1e546

Please sign in to comment.