add basic functionality and corresponding tests
This commit is contained in:
2
tests/_test_data/config.toml
Normal file
2
tests/_test_data/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[test]
|
||||
entry = 'test123'
|
||||
12
tests/conftest.py
Normal file
12
tests/conftest.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def root_data_folder() -> Path:
|
||||
pth = Path.cwd() / "tests/_test_data/"
|
||||
assert pth.exists()
|
||||
assert pth.is_dir()
|
||||
|
||||
return pth
|
||||
36
tests/test_configs.py
Normal file
36
tests/test_configs.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from dopt_basics import configs
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def config_file(root_data_folder) -> Path:
|
||||
pth = root_data_folder / "config.toml"
|
||||
assert pth.exists()
|
||||
assert pth.is_file()
|
||||
|
||||
return pth
|
||||
|
||||
|
||||
def test_load_toml_SuccessPath(config_file):
|
||||
cfg = configs.load_toml(config_file)
|
||||
assert isinstance(cfg, dict)
|
||||
assert "test" in cfg
|
||||
assert cfg["test"]["entry"] == "test123"
|
||||
|
||||
|
||||
def test_load_toml_SuccessStringPath(config_file):
|
||||
str_pth = str(config_file)
|
||||
cfg = configs.load_toml(str_pth)
|
||||
assert isinstance(cfg, dict)
|
||||
assert "test" in cfg
|
||||
assert cfg["test"]["entry"] == "test123"
|
||||
|
||||
|
||||
def test_load_toml_FailWrongPath(tmp_path):
|
||||
wrong_pth = tmp_path / "config.toml"
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
_ = configs.load_toml(wrong_pth)
|
||||
64
tests/test_datastructures.py
Normal file
64
tests/test_datastructures.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import pytest
|
||||
|
||||
from dopt_basics import datastructures as dst
|
||||
|
||||
|
||||
def test_flatten():
|
||||
nested_iterable = ([1, 2], [[3], [4, 5]], [6, [7, 8, 9]])
|
||||
target = tuple(i for i in range(1, 10))
|
||||
|
||||
ret_iter = dst.flatten(nested_iterable)
|
||||
ret = tuple(ret_iter)
|
||||
assert ret == target
|
||||
|
||||
|
||||
def test_DualDict():
|
||||
base_dict: dict[str, int] = {"test1": 1, "test2": 2, "test3": 3}
|
||||
inverted_dict: dict[int, str] = {1: "test1", 2: "test2", 3: "test3"}
|
||||
assert all((key == inverted_dict[value] for key, value in base_dict.items()))
|
||||
|
||||
dual_dict: dst.DualDict[str, int] = dst.DualDict(test1=1, test2=2, test3=3)
|
||||
|
||||
assert all((key in dual_dict for key in base_dict.keys()))
|
||||
assert all((base_dict[key] == dual_dict[key] for key in base_dict.keys()))
|
||||
assert all((key == dual_dict.inverted[value] for key, value in base_dict.items()))
|
||||
assert all(
|
||||
(inverted_dict[key] == dual_dict.inverted[key] for key in inverted_dict.keys())
|
||||
)
|
||||
base_dict["test_add"] = 5
|
||||
dual_dict["test_add"] = 5
|
||||
assert len(base_dict) == len(dual_dict)
|
||||
assert len(dual_dict) == len(dual_dict.inverted)
|
||||
del base_dict["test_add"]
|
||||
del dual_dict["test_add"]
|
||||
assert len(base_dict) == len(dual_dict)
|
||||
assert len(dual_dict) == len(dual_dict.inverted)
|
||||
|
||||
for key_base, key_dd in zip(base_dict, dual_dict):
|
||||
assert key_base == key_dd
|
||||
|
||||
|
||||
def test_DualDict_update_Success():
|
||||
base_dict: dict[str, int] = {"test1": 1, "test2": 2, "test3": 3}
|
||||
dual_dict: dst.DualDict[str, int] = dst.DualDict(test1=1, test2=2, test3=3)
|
||||
|
||||
update = dict(test3=4, test4=5)
|
||||
base_dict.update(**update)
|
||||
dual_dict.update(**update)
|
||||
|
||||
assert all((key in dual_dict for key in base_dict.keys()))
|
||||
assert all((base_dict[key] == dual_dict[key] for key in base_dict.keys()))
|
||||
assert all((key == dual_dict.inverted[value] for key, value in base_dict.items()))
|
||||
|
||||
|
||||
def test_DualDict_update_FailIdenticalValues():
|
||||
base_dict: dict[str, int] = {"test1": 1, "test2": 2, "test3": 3}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
_: dst.DualDict[str, int] = dst.DualDict(test1=1, test2=3, test3=3)
|
||||
|
||||
dual_dict: dst.DualDict[str, int] = dst.DualDict(test1=1, test2=2, test3=3)
|
||||
update = dict(test3=4, test4=4)
|
||||
base_dict.update(**update)
|
||||
with pytest.raises(ValueError):
|
||||
dual_dict.update(**update)
|
||||
148
tests/test_datetime.py
Normal file
148
tests/test_datetime.py
Normal file
@@ -0,0 +1,148 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from dopt_basics import datetime as datetime_
|
||||
from dopt_basics.datetime import TIMEZONE_CEST, TimeUnitsTimedelta
|
||||
|
||||
|
||||
def test_dt_with_UTC():
|
||||
year = 2024
|
||||
month = 3
|
||||
day = 28
|
||||
hour = 3
|
||||
minute = 0
|
||||
dt_target = datetime(year, month, day, hour, minute, tzinfo=UTC)
|
||||
dt_ret = datetime_.dt_with_tz_UTC(year, month, day, hour, minute)
|
||||
|
||||
assert dt_target == dt_ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"time_unit, expected",
|
||||
[
|
||||
("hours", timedelta(hours=2.0)),
|
||||
("minutes", timedelta(minutes=2.0)),
|
||||
("seconds", timedelta(seconds=2.0)),
|
||||
("milliseconds", timedelta(milliseconds=2.0)),
|
||||
("microseconds", timedelta(microseconds=2.0)),
|
||||
(TimeUnitsTimedelta.HOURS, timedelta(hours=2.0)),
|
||||
(TimeUnitsTimedelta.MINUTES, timedelta(minutes=2.0)),
|
||||
],
|
||||
)
|
||||
def test_timedelta_from_val_Success(time_unit, expected):
|
||||
val = 2.0
|
||||
td = datetime_.timedelta_from_val(val, time_unit)
|
||||
assert td == expected
|
||||
|
||||
|
||||
def test_timedelta_from_val_FailWrongTimeUnit():
|
||||
val = 2.0
|
||||
time_unit = "years"
|
||||
with pytest.raises(ValueError):
|
||||
datetime_.timedelta_from_val(val, time_unit) # type: ignore
|
||||
|
||||
|
||||
def test_round_td_by_seconds():
|
||||
hours = 2.0
|
||||
minutes = 30.0
|
||||
seconds = 30.0
|
||||
microseconds = 600
|
||||
td = timedelta(hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds)
|
||||
rounded_td = datetime_.round_td_by_seconds(td, round_to_next_seconds=1)
|
||||
assert rounded_td == timedelta(hours=2.0, minutes=30.0, seconds=30.0)
|
||||
|
||||
|
||||
def test_current_time_tz():
|
||||
tz = datetime_.TIMEZONE_UTC
|
||||
mock_dt = datetime(2024, 6, 1, 12, 15, 30, 1000, tzinfo=tz)
|
||||
with patch("dopt_basics.datetime.Datetime") as mock_obj:
|
||||
mock_obj.now.return_value = mock_dt
|
||||
ret = datetime_.current_time_tz(cut_microseconds=False)
|
||||
assert ret.tzinfo is not None
|
||||
assert ret == mock_dt
|
||||
|
||||
with patch("dopt_basics.datetime.Datetime") as mock_obj:
|
||||
mock_obj.now.return_value = mock_dt
|
||||
ret = datetime_.current_time_tz(cut_microseconds=True)
|
||||
target = datetime(2024, 6, 1, 12, 15, 30, tzinfo=tz)
|
||||
assert ret.tzinfo is not None
|
||||
assert ret == target
|
||||
|
||||
|
||||
def test_get_timestamp_WithTime():
|
||||
tz = datetime_.TIMEZONE_UTC
|
||||
mock_dt = datetime(2024, 6, 1, 12, 15, 30, 1000, tzinfo=tz)
|
||||
|
||||
with patch("dopt_basics.datetime.Datetime") as mock_obj:
|
||||
mock_obj.now.return_value = mock_dt
|
||||
ret = datetime_.get_timestamp(tz=tz, with_time=True)
|
||||
|
||||
target = "2024-06-01--12-15-30"
|
||||
assert ret == target
|
||||
|
||||
|
||||
def test_get_timestamp_WithoutTime():
|
||||
tz = datetime_.TIMEZONE_UTC
|
||||
mock_dt = datetime(2024, 6, 1, 12, 15, 30, 1000, tzinfo=tz)
|
||||
|
||||
with patch("dopt_basics.datetime.Datetime") as mock_obj:
|
||||
mock_obj.now.return_value = mock_dt
|
||||
ret = datetime_.get_timestamp(tz=tz, with_time=False)
|
||||
|
||||
target = "2024-06-01"
|
||||
assert ret == target
|
||||
|
||||
|
||||
def test_add_timedelta_FailWithoutTZInfo():
|
||||
year = 2024
|
||||
month = 3
|
||||
day = 30
|
||||
hour = 3
|
||||
minute = 0
|
||||
dt = datetime(year, month, day, hour, minute)
|
||||
td = timedelta(hours=2.0)
|
||||
with pytest.raises(ValueError):
|
||||
datetime_.add_timedelta_with_tz(dt, td)
|
||||
|
||||
|
||||
def test_add_timedelta_with_tz():
|
||||
year = 2024
|
||||
month = 3
|
||||
day = 30
|
||||
hour = 23
|
||||
minute = 0
|
||||
dt = datetime(year, month, day, hour, minute, tzinfo=TIMEZONE_CEST)
|
||||
td = timedelta(hours=6.0)
|
||||
new_dt = datetime_.add_timedelta_with_tz(dt, td)
|
||||
assert new_dt == datetime(2024, 3, 31, 6, 0, tzinfo=TIMEZONE_CEST)
|
||||
|
||||
|
||||
def test_validate_dt_UTC_Success():
|
||||
dt = datetime(2024, 3, 30, 0, 0, tzinfo=UTC)
|
||||
datetime_.validate_dt_UTC(dt)
|
||||
|
||||
|
||||
def test_validate_dt_FailWrongTZInfo():
|
||||
dt = datetime(2024, 3, 30, 0, 0, tzinfo=TIMEZONE_CEST)
|
||||
with pytest.raises(ValueError):
|
||||
datetime_.validate_dt_UTC(dt)
|
||||
|
||||
|
||||
def test_dt_to_timezone_Success():
|
||||
dt = datetime(2024, 3, 30, 2, 0, tzinfo=UTC)
|
||||
new_dt = datetime_.dt_to_timezone(dt, TIMEZONE_CEST)
|
||||
assert new_dt == datetime(2024, 3, 30, 3, tzinfo=TIMEZONE_CEST)
|
||||
|
||||
|
||||
def test_dt_to_timezone_FailWithoutTZInfo():
|
||||
dt = datetime(2024, 3, 30, 2, 0)
|
||||
with pytest.raises(ValueError):
|
||||
datetime_.dt_to_timezone(dt, TIMEZONE_CEST)
|
||||
|
||||
|
||||
def test_cut_microseconds():
|
||||
dt = datetime(2024, 3, 30, 2, 0, 0, 600)
|
||||
new_dt = datetime_.cut_dt_microseconds(dt)
|
||||
assert new_dt == datetime(2024, 3, 30, 2, 0, 0, 0)
|
||||
17
tests/test_enums.py
Normal file
17
tests/test_enums.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import enum
|
||||
|
||||
from dopt_basics import enums
|
||||
|
||||
|
||||
def test_enum_str_values_as_frzset():
|
||||
class TestEnum(enum.StrEnum):
|
||||
T1 = enum.auto()
|
||||
T2 = enum.auto()
|
||||
T3 = enum.auto()
|
||||
|
||||
target_vals = frozenset(("t1", "t2", "t3"))
|
||||
extracted_vals = enums.enum_str_values_as_frzset(TestEnum)
|
||||
|
||||
diff = target_vals.difference(extracted_vals)
|
||||
|
||||
assert len(diff) == 0
|
||||
207
tests/test_paths.py
Normal file
207
tests/test_paths.py
Normal file
@@ -0,0 +1,207 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from dopt_basics import paths
|
||||
|
||||
FILE_SEARCH = "test.txt"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def base_folder(tmp_path_factory) -> Path:
|
||||
folder_structure = "path/to/base/folder/"
|
||||
pth = tmp_path_factory.mktemp("search")
|
||||
pth = pth / folder_structure
|
||||
pth.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return pth
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def target_file_pth(base_folder) -> Path:
|
||||
# place in folder 'path' of TMP path
|
||||
target_folder = base_folder.parents[2]
|
||||
target_file = target_folder / FILE_SEARCH
|
||||
with open(target_file, "w") as file:
|
||||
file.write("TEST")
|
||||
|
||||
return target_file
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"delete_existing",
|
||||
[True, False],
|
||||
)
|
||||
def test_create_folder(tmp_path, delete_existing):
|
||||
target_dir = tmp_path / "test"
|
||||
assert not target_dir.exists()
|
||||
paths.create_folder(target_dir, delete_existing=delete_existing)
|
||||
assert target_dir.exists()
|
||||
assert target_dir.is_dir()
|
||||
paths.create_folder(target_dir, delete_existing=delete_existing)
|
||||
assert target_dir.exists()
|
||||
assert target_dir.is_dir()
|
||||
|
||||
|
||||
def test_prepare_save_path_SuccessWithCreate(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = ("target", "dir")
|
||||
filename = None
|
||||
suffix = None
|
||||
target_pth = tmp_path / "/".join(dirs)
|
||||
res_pth = paths.prepare_save_path(base_folder, dirs, filename, suffix, create_folder=True)
|
||||
assert res_pth.exists()
|
||||
assert res_pth == target_pth
|
||||
|
||||
|
||||
def test_prepare_save_path_SuccessWithCreateTimestamp(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = ("target", "dir")
|
||||
filename = "test"
|
||||
suffix = ".pkl"
|
||||
res_pth = paths.prepare_save_path(
|
||||
base_folder, dirs, filename, suffix, create_folder=True, include_timestamp=True
|
||||
)
|
||||
assert res_pth.parent.exists()
|
||||
|
||||
|
||||
def test_prepare_save_path_SuccessWithoutCreate(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = ("target", "dir")
|
||||
filename = None
|
||||
suffix = None
|
||||
target_pth = tmp_path / "/".join(dirs)
|
||||
res_pth = paths.prepare_save_path(
|
||||
base_folder, dirs, filename, suffix, create_folder=False
|
||||
)
|
||||
assert not res_pth.exists()
|
||||
assert res_pth == target_pth
|
||||
|
||||
|
||||
def test_prepare_save_path_FailNoTargets(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = None
|
||||
filename = None
|
||||
suffix = None
|
||||
with pytest.raises(ValueError):
|
||||
_ = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
)
|
||||
|
||||
|
||||
def test_prepare_save_path_FailNoFilenameSuffix(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = None
|
||||
filename = None
|
||||
suffix = "pkl"
|
||||
with pytest.raises(ValueError):
|
||||
_ = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
)
|
||||
|
||||
filename = "test"
|
||||
suffix = None
|
||||
with pytest.raises(ValueError):
|
||||
_ = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
)
|
||||
|
||||
|
||||
def test_prepare_save_path_FailTimestampWithoutFilename(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = ["test"]
|
||||
filename = None
|
||||
suffix = None
|
||||
with pytest.raises(ValueError):
|
||||
_ = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
include_timestamp=True,
|
||||
)
|
||||
|
||||
|
||||
def test_prepare_save_path_FailBadSuffix(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = None
|
||||
filename = "test"
|
||||
suffix = "."
|
||||
with pytest.raises(ValueError):
|
||||
_ = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
include_timestamp=False,
|
||||
)
|
||||
|
||||
|
||||
def test_prepare_save_path_SuccessSuffixAddDot(tmp_path):
|
||||
base_folder = tmp_path
|
||||
dirs = None
|
||||
filename = "test"
|
||||
suffix = "pkl"
|
||||
target_path = tmp_path / f"{filename}.{suffix}"
|
||||
ret_path = paths.prepare_save_path(
|
||||
base_folder,
|
||||
dirs,
|
||||
filename,
|
||||
suffix,
|
||||
create_folder=False,
|
||||
include_timestamp=False,
|
||||
)
|
||||
assert ret_path == target_path
|
||||
|
||||
|
||||
def test_search_cwd(monkeypatch, base_folder, target_file_pth):
|
||||
monkeypatch.setattr(Path, "cwd", lambda: base_folder)
|
||||
assert Path.cwd() == base_folder
|
||||
ret = paths.search_cwd(FILE_SEARCH)
|
||||
assert ret is None
|
||||
|
||||
target_folder = target_file_pth.parent
|
||||
monkeypatch.setattr(Path, "cwd", lambda: target_folder)
|
||||
assert Path.cwd() == target_folder
|
||||
ret = paths.search_cwd(FILE_SEARCH)
|
||||
assert ret is not None
|
||||
assert ret == target_file_pth
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stop_folder_name", ["to", "base", None])
|
||||
def test_search_file_iterative(base_folder, target_file_pth, stop_folder_name):
|
||||
# target in parent of 'to': 'path'
|
||||
ret = paths.search_file_iterative(base_folder, FILE_SEARCH, stop_folder_name)
|
||||
if stop_folder_name == "to" or stop_folder_name is None:
|
||||
assert ret is not None
|
||||
assert ret.name == FILE_SEARCH
|
||||
assert ret == target_file_pth
|
||||
elif stop_folder_name == "base":
|
||||
assert ret is None
|
||||
|
||||
|
||||
def test_search_folder_path(base_folder):
|
||||
stop_folder = "123" # should not exist
|
||||
found = paths.search_folder_path(base_folder, stop_folder_name=stop_folder)
|
||||
assert found is None
|
||||
stop_folder = "to"
|
||||
found = paths.search_folder_path(base_folder, stop_folder_name=stop_folder)
|
||||
assert found is not None
|
||||
assert found.name == "path"
|
||||
stop_folder = None
|
||||
found = paths.search_folder_path(base_folder, stop_folder_name=stop_folder)
|
||||
assert found is None
|
||||
Reference in New Issue
Block a user