Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
"""Tests for the remembered results in `hermes_home_key`.
|
||||
|
||||
`Path.resolve()` is a filesystem call. `hermes_home_key` sits under
|
||||
`ToolRegistry.current_scope_key()`, which runs on every registry lookup, so
|
||||
before the results were remembered the registry paid a syscall per lookup.
|
||||
|
||||
The value this returns must not change, so most of these tests compare
|
||||
against the plain uncached calculation.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import hermes_constants as hc
|
||||
|
||||
|
||||
def _uncached(path=None) -> str:
|
||||
"""The calculation as it was before results were remembered."""
|
||||
candidate = Path(path) if path is not None else hc.get_hermes_home()
|
||||
return os.path.normcase(str(candidate.expanduser().resolve(strict=False)))
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clear_cache():
|
||||
hc.reset_hermes_home_key_cache()
|
||||
yield
|
||||
hc.reset_hermes_home_key_cache()
|
||||
|
||||
|
||||
class TestSameAnswerAsBefore:
|
||||
"""Remembering a result must not change what comes back."""
|
||||
|
||||
@pytest.mark.parametrize("case", ["real", "missing", "trailing_sep", "dot_dot"])
|
||||
def test_matches_the_uncached_calculation(self, tmp_path, case):
|
||||
(tmp_path / "real").mkdir()
|
||||
target = {
|
||||
"real": str(tmp_path / "real"),
|
||||
"missing": str(tmp_path / "not_there"),
|
||||
"trailing_sep": str(tmp_path / "real") + os.sep,
|
||||
"dot_dot": str(tmp_path / "real" / ".." / "real"),
|
||||
}[case]
|
||||
assert hc.hermes_home_key(target) == _uncached(target)
|
||||
|
||||
def test_matches_for_the_default_home(self):
|
||||
assert hc.hermes_home_key() == _uncached()
|
||||
|
||||
def test_matches_for_a_tilde_path(self):
|
||||
assert hc.hermes_home_key("~") == _uncached("~")
|
||||
|
||||
def test_accepts_a_path_object(self, tmp_path):
|
||||
(tmp_path / "real").mkdir()
|
||||
assert hc.hermes_home_key(tmp_path / "real") == _uncached(tmp_path / "real")
|
||||
|
||||
def test_second_call_returns_the_same_string(self, tmp_path):
|
||||
(tmp_path / "real").mkdir()
|
||||
first = hc.hermes_home_key(str(tmp_path / "real"))
|
||||
second = hc.hermes_home_key(str(tmp_path / "real"))
|
||||
assert first == second == _uncached(str(tmp_path / "real"))
|
||||
|
||||
|
||||
class TestWhatGetsRemembered:
|
||||
def test_an_existing_path_is_remembered(self, tmp_path):
|
||||
(tmp_path / "real").mkdir()
|
||||
hc.hermes_home_key(str(tmp_path / "real"))
|
||||
assert len(hc._HOME_KEY_CACHE) == 1
|
||||
|
||||
def test_a_missing_path_is_not_remembered(self, tmp_path):
|
||||
# The answer can change once the directory is created, for example
|
||||
# when part of the path turns out to be a link, so it must not stick.
|
||||
hc.hermes_home_key(str(tmp_path / "not_there"))
|
||||
assert hc._HOME_KEY_CACHE == {}
|
||||
|
||||
def test_a_path_created_later_picks_up_the_real_answer(self, tmp_path):
|
||||
later = tmp_path / "later"
|
||||
before = hc.hermes_home_key(str(later))
|
||||
later.mkdir()
|
||||
after = hc.hermes_home_key(str(later))
|
||||
assert after == _uncached(str(later))
|
||||
assert hc._HOME_KEY_CACHE == {str(later): after}
|
||||
# On a plain directory both answers agree anyway. The point is that
|
||||
# the first one was never stored.
|
||||
assert before == after
|
||||
|
||||
def test_different_paths_get_their_own_entries(self, tmp_path):
|
||||
for name in ("a", "b", "c"):
|
||||
(tmp_path / name).mkdir()
|
||||
hc.hermes_home_key(str(tmp_path / name))
|
||||
assert len(hc._HOME_KEY_CACHE) == 3
|
||||
|
||||
def test_reset_clears_everything(self, tmp_path):
|
||||
(tmp_path / "real").mkdir()
|
||||
hc.hermes_home_key(str(tmp_path / "real"))
|
||||
assert hc._HOME_KEY_CACHE
|
||||
hc.reset_hermes_home_key_cache()
|
||||
assert hc._HOME_KEY_CACHE == {}
|
||||
|
||||
|
||||
class TestHomeChanges:
|
||||
def test_pointing_hermes_home_somewhere_else_gives_a_new_key(
|
||||
self, tmp_path, monkeypatch,
|
||||
):
|
||||
# A different home is a different input path, so it lands on its own
|
||||
# entry rather than reusing the first one.
|
||||
first = tmp_path / "home_one"
|
||||
second = tmp_path / "home_two"
|
||||
first.mkdir()
|
||||
second.mkdir()
|
||||
|
||||
monkeypatch.setenv("HERMES_HOME", str(first))
|
||||
key_one = hc.hermes_home_key()
|
||||
monkeypatch.setenv("HERMES_HOME", str(second))
|
||||
key_two = hc.hermes_home_key()
|
||||
|
||||
assert key_one != key_two
|
||||
assert key_one == _uncached(str(first))
|
||||
assert key_two == _uncached(str(second))
|
||||
|
||||
|
||||
class TestSymlinks:
|
||||
def test_a_link_resolves_to_its_target(self, tmp_path):
|
||||
target = tmp_path / "target"
|
||||
target.mkdir()
|
||||
link = tmp_path / "link"
|
||||
try:
|
||||
link.symlink_to(target, target_is_directory=True)
|
||||
except (OSError, NotImplementedError):
|
||||
pytest.skip("this platform or account cannot create symlinks")
|
||||
assert hc.hermes_home_key(str(link)) == _uncached(str(link))
|
||||
assert hc.hermes_home_key(str(link)) == hc.hermes_home_key(str(target))
|
||||
|
||||
|
||||
class TestRegistryLookupsDoNotHitTheDisk:
|
||||
def test_scope_key_resolves_the_path_once(self, monkeypatch):
|
||||
# The reason this cache exists. ToolRegistry.current_scope_key() runs
|
||||
# on every registry lookup, so it must not resolve the home path on
|
||||
# the filesystem every time.
|
||||
from tools.registry import registry
|
||||
|
||||
calls = {"n": 0}
|
||||
real_resolve = Path.resolve
|
||||
|
||||
def counting_resolve(self, *a, **kw):
|
||||
calls["n"] += 1
|
||||
return real_resolve(self, *a, **kw)
|
||||
|
||||
monkeypatch.setattr(Path, "resolve", counting_resolve)
|
||||
|
||||
registry.current_scope_key()
|
||||
after_first = calls["n"]
|
||||
for _ in range(50):
|
||||
registry.current_scope_key()
|
||||
|
||||
assert calls["n"] == after_first, (
|
||||
f"current_scope_key resolved the path on the filesystem "
|
||||
f"{calls['n'] - after_first} extra times across 50 calls"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(pytest.main([__file__, "-q"]))
|
||||
Reference in New Issue
Block a user