Skip to content

Commit

Permalink
Test refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
konstin committed Oct 3, 2023
1 parent 9b2ebb5 commit b864e4f
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 228 deletions.
217 changes: 1 addition & 216 deletions test/install_wheel_rs/test_compare_pip.py
Original file line number Diff line number Diff line change
@@ -1,226 +1,11 @@
#!/usr/bin/env python
import os
import platform
import re
import shutil
import sys
import time
from argparse import ArgumentParser
from filecmp import cmpfiles
from pathlib import Path
from shutil import rmtree
from subprocess import check_call, DEVNULL
from typing import List, Union, Optional, Set

import pytest

from test.install_wheel_rs.utils import get_bin, get_root

major = 3
minor = 8


def compare_with_pip_wheels(
env_name: str,
wheels: List[Union[str, Path]],
*,
bin_name: Path = get_bin("install-wheel-rs"),
clear_rs: bool = True,
clear_pip: bool = False,
):
compare_with_pip_args(
bin_name,
env_name,
["--no-deps", *wheels],
["--major", str(major), "--minor", str(minor), *wheels],
clear_rs=clear_rs,
clear_pip=clear_pip,
)


def compare_with_pip_args(
bin_name: Path,
env_name: str,
pip_args: List[Union[str, Path]],
monotrail_args: List[Union[str, Path]],
*,
clear_rs: bool = True,
clear_pip: bool = False,
cwd: Optional[Path] = None,
content_diff_exclusions: Optional[Set[Path]] = None,
):
test_venvs = get_root().joinpath("test-venvs")
test_venvs.mkdir(exist_ok=True)
env = test_venvs.joinpath(f"{env_name}")
env_rs = test_venvs.joinpath(f"{env_name}-rs")
env_py = test_venvs.joinpath(f"{env_name}-pip")

# pip install
if clear_pip and env_py.exists():
rmtree(env_py)
if not env_py.exists():
check_call(
["virtualenv", "-p", f"{major}.{minor}", env], stdout=DEVNULL, cwd=cwd
)
start_pip = time.time()
if platform.system() == "Windows":
pip = env.joinpath("Scripts").joinpath("pip.exe")
else:
pip = env.joinpath("bin").joinpath("pip")
check_call([pip, "install", "-q", *pip_args], cwd=cwd)
stop_pip = time.time()
env.rename(env_py)

print(f"{env_name} pip install took {stop_pip - start_pip:.2f}s")

# rust install
if env_rs.exists():
rmtree(env_rs)
check_call(["virtualenv", "-p", f"{major}.{minor}", env], stdout=DEVNULL)
start_rs = time.time()
check_call(
[bin_name, *monotrail_args],
env=dict(os.environ, VIRTUAL_ENV=str(env)),
cwd=cwd,
)
stop_rs = time.time()
env.rename(env_rs)
rust_time = stop_rs - start_rs

print(f"{env_name} rs install took {rust_time :.2f}s")

diff_envs(env_name, env_py, env_rs, content_diff_exclusions=content_diff_exclusions)

if clear_rs:
shutil.rmtree(env_rs)


def diff_envs(
env_name: str,
env_py: Path,
env_rs: Path,
content_diff_exclusions: Optional[Set[Path]] = None,
):
"""Compare the contents of two virtualenvs, one filled by our implementation and one
filled by pip, poetry or another python tool we want to compare. Both were renamed
from the same dir so all path inside are the same.
* List all files and directories in both virtualenvs
* Filter out all files and folder that are known to diverge
* Check that the remaining file lists are identical
* Filter our all directories and all files known to have different contents such
as `INSTALLER`
* Read the contents from both the rs and pip env, normalize quotation marks and
newline
* Check that the file contents are identical
"""
# Filter out paths created by invoking pip and pip itself with a horrible regex
# Better matching suggestions welcome 😬
site_ignores = [
"__pycache__",
"pip",
"pip-" + "[^/]+" + re.escape(".dist-info"),
"setuptools",
"pkg_resources",
"_distutils_hack/__pycache__",
# Doesn't make sense in our case to enforce this strictly
"[a-zA-Z0-9._-]+" + re.escape(".dist-info/direct_url.json"),
# poetry doesn't seem to do this (we do currently)
"[a-zA-Z0-9._-]+" + re.escape(".dist-info/REQUESTED"),
]
if platform.system() == "Windows":
site_packages = "Lib\\site-packages\\"
else:
py_version = rf"{sys.version_info.major}.{sys.version_info.minor}"
site_packages = rf"lib/python{py_version}/site-packages/"
pattern = (
"^("
+ re.escape(site_packages)
+ "("
+ "|".join(site_ignores)
+ ")"
# TODO: .exe files should likely be identical
+ "|Scripts/.*\\.exe|install-wheel-rs.lock|.*/__pycache__(/.*)?"
+ ")"
)
if platform.system() == "Windows":
# -.-
# two for regex escaping
pattern = pattern.replace("/", "\\\\")
env_rs_entries = set()
for i in env_rs.glob("**/*"):
if re.match(pattern, str(i.relative_to(env_rs))):
continue
env_rs_entries.add(i.relative_to(env_rs))
env_py_entries = set()
for i in env_py.glob("**/*"):
if re.match(pattern, str(i.relative_to(env_py))):
continue
env_py_entries.add(i.relative_to(env_py))
symmetric_difference = env_rs_entries ^ env_py_entries
if symmetric_difference:
print(f"Differences in environment {env_name} with pattern {pattern}")
for rs_only in env_rs_entries - env_py_entries:
print(f"rust only: {rs_only}")
for pip_only in env_py_entries - env_rs_entries:
print(f"pip only: {pip_only}")
sys.exit(1)

start = time.time()
# Filter out directories and files known to have diverging contents
common = []
for entry in env_rs_entries:
if env_py.joinpath(entry).is_dir():
continue
# Used for source distribution
if content_diff_exclusions and entry in content_diff_exclusions:
continue
# Ignore INSTALLER and subsequently RECORD
# TODO(konstin): Use RECORD checker from monotrail on both
if str(entry).startswith(site_packages):
in_python_path = Path(str(entry).replace(site_packages, "", 1))
if len(in_python_path.parts) == 2:
[folder, file] = in_python_path.parts
if folder.endswith(".dist-info") and file in ["INSTALLER", "RECORD"]:
continue

common.append(entry)

# Fast path for the majority of files that is byte-by-bytes identical
_match, mismatch, errors = cmpfiles(env_py, env_rs, common)

assert not errors, errors

# Slow path to compare files again after some normalization
diverging = []
for entry in mismatch:
# Ignore platform newline differences
content_py = env_py.joinpath(entry).read_bytes().replace(b"\r", b"")
content_rs = env_rs.joinpath(entry).read_bytes().replace(b"\r", b"")

# These depend on the tool versions, make them equal across versions
if (platform.system() == "Windows" and entry.parent.name == "Scripts") or (
platform.system() != "Windows" and entry.parent.name == "bin"
):
content_py = content_py.replace(b"'", b'"')
content_rs = content_rs.replace(b"'", b'"')

if content_py != content_rs:
diverging.append(entry)
end = time.time()
# This is currently slow, remove this print once it is fast
print(f"Comparing files took {end - start:.2f}")

if diverging:
for path in diverging:
# clickable links for debugging
print(f"# {path}\n", file=sys.stderr)
print(
f"{env_py.joinpath(path).relative_to(os.getcwd())}:1", file=sys.stderr
)
print(
f"{env_rs.joinpath(path).relative_to(os.getcwd())}:1", file=sys.stderr
)
raise AssertionError(f"Diverging path:\n{diverging}")
from test.install_wheel_rs.utils import get_root, compare_with_pip_wheels


@pytest.mark.skipif(sys.platform != "linux", reason="linux only wheel")
Expand Down
5 changes: 2 additions & 3 deletions test/install_wheel_rs/test_compare_poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
from subprocess import check_call, DEVNULL, CalledProcessError
from typing import List, Optional

from test.install_wheel_rs.test_compare_pip import diff_envs
from test.install_wheel_rs.utils import get_bin, get_root
from test.install_wheel_rs.utils import get_bin, get_root, diff_envs


def compare_with_poetry(
Expand Down Expand Up @@ -66,7 +65,7 @@ def compare_with_poetry(
if env_rs.exists():
rmtree(env_rs)
check_call(["virtualenv", "-p", "3.8", env], stdout=DEVNULL)
monotrail = monotrail or get_bin()
monotrail = monotrail or get_bin("monotrail")
start_rs = time.time()
call = [monotrail, "poetry-install"]
if no_dev:
Expand Down
3 changes: 1 addition & 2 deletions test/install_wheel_rs/test_piptests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
Use the wheels from pip's tests to cover the edge cases
"""

from test.install_wheel_rs.test_compare_pip import compare_with_pip_wheels
from test.install_wheel_rs.utils import get_root
from test.install_wheel_rs.utils import get_root, compare_with_pip_wheels


def test_piptests():
Expand Down
3 changes: 1 addition & 2 deletions test/install_wheel_rs/test_popular.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"""
from subprocess import check_call

from test.install_wheel_rs.test_compare_pip import compare_with_pip_wheels
from test.install_wheel_rs.utils import get_root
from test.install_wheel_rs.utils import get_root, compare_with_pip_wheels


def test_popular():
Expand Down
2 changes: 1 addition & 1 deletion test/install_wheel_rs/test_tqdm.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_tqdm():
.joinpath("popular-wheels")
.joinpath("tqdm-4.62.3-py2.py3-none-any.whl")
)
check_call([get_bin(), "wheel-install", tqdm_wheel], env=env)
check_call([get_bin("monotrail"), "wheel-install", tqdm_wheel], env=env)
check_call(
[
python,
Expand Down
Loading

0 comments on commit b864e4f

Please sign in to comment.