Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests to refactor fs #4983

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
19 changes: 14 additions & 5 deletions src/clib/lib/config/config_parser.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <filesystem>
#include <fstream>

#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -657,15 +658,23 @@ config_parse(config_parser_type *config, const char *filename,
hash_iter_free(keys);
}

if (util_file_readable(filename)) {
bool file_readable_check_succeeded = true;
try {
std::ifstream file_handler;
file_handler.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file_handler.open(filename);
} catch (std::ios_base::failure &err) {
file_readable_check_succeeded = false;
auto error_message = fmt::format(
"could not open file `{}` for parsing - {}", filename, err.what());
content->parse_errors.push_back(error_message);
}

if (file_readable_check_succeeded) {
path_stack_type *path_stack = path_stack_alloc();
config_parse__(config, content, path_stack, filename, comment_string,
include_kw, define_kw, unrecognized_behaviour, validate);
path_stack_free(path_stack);
} else {
std::string error_message =
util_alloc_sprintf("Could not open file:%s for parsing", filename);
content->parse_errors.push_back(error_message);
}

if (content->parse_errors.size() == 0)
Expand Down
16 changes: 16 additions & 0 deletions src/clib/lib/enkf/gen_kw_config.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <filesystem>
#include <numeric>

#include <stdlib.h>
#include <string.h>
Expand All @@ -9,6 +10,7 @@
#include <ert/util/vector.h>

#include <ert/config/config_parser.hpp>
#include <ert/logging.hpp>

#include <ert/enkf/config_keys.hpp>
#include <ert/enkf/enkf_defaults.hpp>
Expand All @@ -17,6 +19,7 @@
#include <ert/enkf/trans_func.hpp>

namespace fs = std::filesystem;
static auto logger = ert::get_logger("gen_kw_config");

typedef struct {
char *name;
Expand Down Expand Up @@ -102,6 +105,19 @@ void gen_kw_config_set_parameter_file(gen_kw_config_type *config,
config_content_type *content =
config_parse(parser, parameter_file, "--", NULL, NULL, NULL,
CONFIG_UNRECOGNIZED_ADD, false);
if (!content->valid) {
auto header = fmt::format(
"encountered errors while parsing GEN_KW parameter file {}",
parameter_file);
std::string errors;
for (auto &error : content->parse_errors) {
errors += error;
}
logger->warning("{}\n{}", header, errors);
}
for (auto parse_error : content->parse_errors) {
logger->warning(parse_error);
}
for (int item_index = 0; item_index < config_content_get_size(content);
item_index++) {
const config_content_node_type *node =
Expand Down
12 changes: 9 additions & 3 deletions src/ert/_c_wrappers/enkf/enkf_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,9 +488,15 @@ def sample_prior(
grid_file = self.ensembleConfig().grid_file
assert grid_file is not None
grid = xtgeo.grid_from_file(grid_file)
props = xtgeo.gridproperty_from_file(
init_file, name=parameter, grid=grid
)
try:
props = xtgeo.gridproperty_from_file(
init_file, name=parameter, grid=grid
)
except PermissionError as err:
context_message = (
f"Failed to open init file for parameter {parameter!r}"
)
raise RuntimeError(context_message) from err

data = props.values1d.data
field_config = config_node.getFieldModelConfig()
Expand Down
6 changes: 6 additions & 0 deletions src/ert/_c_wrappers/enkf/enkf_obs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from typing import Iterator, List, Optional, Union

from cwrap import BaseCClass
Expand Down Expand Up @@ -112,6 +113,11 @@ def free(self):
self._free()

def load(self, config_file: str, std_cutoff: float) -> None:
if not os.access(config_file, os.R_OK):
raise RuntimeError(
"Do not have permission to open observation "
f"config file {config_file!r}"
)
_clib.enkf_obs.load(self, config_file, std_cutoff)

@property
Expand Down
6 changes: 5 additions & 1 deletion src/ert/ensemble_evaluator/_wait_for_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

logger = logging.getLogger(__name__)

WAIT_FOR_EVALUATOR_TIMEOUT = 60


def get_ssl_context(cert: Optional[Union[str, bytes]]) -> Optional[ssl.SSLContext]:
if cert is None:
Expand Down Expand Up @@ -41,9 +43,11 @@ async def wait_for_evaluator( # pylint: disable=too-many-arguments
token: Optional[str] = None,
cert: Optional[Union[str, bytes]] = None,
healthcheck_endpoint: str = "/healthcheck",
timeout: float = 60,
timeout: Optional[float] = None,
connection_timeout: float = 2,
) -> None:
if timeout is None:
timeout = WAIT_FOR_EVALUATOR_TIMEOUT
healthcheck_url = base_url + healthcheck_endpoint
start = time.time()
sleep_time = 0.2
Expand Down
106 changes: 104 additions & 2 deletions tests/unit_tests/cli/test_integration_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import logging
import os
import shutil
import threading
Expand All @@ -9,7 +10,7 @@
import pytest

import ert.shared
from ert import LibresFacade
from ert import LibresFacade, ensemble_evaluator
from ert.__main__ import ert_parser
from ert._c_wrappers.config.config_parser import ConfigValidationError
from ert._c_wrappers.enkf import EnKFMain, ErtConfig
Expand All @@ -20,7 +21,7 @@
ITERATIVE_ENSEMBLE_SMOOTHER_MODE,
TEST_RUN_MODE,
)
from ert.cli.main import run_cli
from ert.cli.main import ErtCliError, run_cli
from ert.shared.feature_toggling import FeatureToggling
from ert.storage import open_storage

Expand Down Expand Up @@ -386,3 +387,104 @@ def test_that_prior_is_not_overwritten_in_ensemble_experiment(
else:
pd.testing.assert_frame_equal(parameter_values, prior_values)
storage.close()


def test_config_parser_fails_gracefully_on_unreadable_config_file(copy_case, caplog):
"""we cannot test on the config file directly, as the argument parser already check
if the file is readable. so we use the GEN_KW parameter file which is also parsed
using our config parser."""

copy_case("snake_oil_field")
config_file_name = "snake_oil_surface.ert"

with open(config_file_name, mode="r", encoding="utf-8") as config_file_handler:
content_lines = config_file_handler.read().splitlines()

index_line_with_gen_kw = [
index for index, line in enumerate(content_lines) if line.startswith("GEN_KW")
][0]
gen_kw_parameter_file = content_lines[index_line_with_gen_kw].split(" ")[4]
os.chmod(gen_kw_parameter_file, 0x0)
gen_kw_parameter_file_abs_path = os.path.join(os.getcwd(), gen_kw_parameter_file)
caplog.set_level(logging.WARNING)

ErtConfig.from_file(config_file_name)

assert (
f"could not open file `{gen_kw_parameter_file_abs_path}` for parsing"
in caplog.text
)


def test_field_init_file_not_readable(copy_case, monkeypatch):
monkeypatch.setattr(
ensemble_evaluator._wait_for_evaluator, "WAIT_FOR_EVALUATOR_TIMEOUT", 5
)
copy_case("snake_oil_field")
config_file_name = "snake_oil_field.ert"
field_file_rel_path = "fields/permx0.grdecl"
os.chmod(field_file_rel_path, 0x0)

try:
run_ert_test_run(config_file_name)
except ErtCliError as err:
assert "Failed to open init file for parameter 'PERMX'" in str(err)


def test_surface_init_fails_during_forward_model_callback(copy_case):
copy_case("snake_oil_field")
config_file_name = "snake_oil_surface.ert"
parameter_name = "TOP"
with open(config_file_name, mode="r+", encoding="utf-8") as config_file_handler:
content_lines = config_file_handler.read().splitlines()
index_line_with_surface_top = [
index
for index, line in enumerate(content_lines)
if line.startswith(f"SURFACE {parameter_name}")
][0]
line_with_surface_top = content_lines[index_line_with_surface_top]
breaking_line_with_surface_top = line_with_surface_top + " FORWARD_INIT:True"
content_lines[index_line_with_surface_top] = breaking_line_with_surface_top
config_file_handler.seek(0)
config_file_handler.write("\n".join(content_lines))

try:
run_ert_test_run(config_file_name)
except ErtCliError as err:
assert f"Failed to initialize parameter {parameter_name!r}" in str(err)


def test_unopenable_observation_config_fails_gracefully(copy_case):
copy_case("snake_oil_field")
config_file_name = "snake_oil_field.ert"
with open(config_file_name, mode="r", encoding="utf-8") as config_file_handler:
content_lines = config_file_handler.read().splitlines()
index_line_with_observation_config = [
index
for index, line in enumerate(content_lines)
if line.startswith("OBS_CONFIG")
][0]
line_with_observation_config = content_lines[index_line_with_observation_config]
observation_config_rel_path = line_with_observation_config.split(" ")[1]
observation_config_abs_path = os.path.join(os.getcwd(), observation_config_rel_path)
os.chmod(observation_config_abs_path, 0x0)

try:
run_ert_test_run(config_file_name)
except RuntimeError as err:
assert (
"Do not have permission to open observation config file "
f"{observation_config_abs_path!r}" in str(err)
)


def run_ert_test_run(config_file: str) -> None:
parser = ArgumentParser(prog="test_run")
parsed = ert_parser(
parser,
[
TEST_RUN_MODE,
config_file,
],
)
run_cli(parsed)