chore: import upstream snapshot with attribution
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from invokeai.backend.util.build_line import build_line
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["x1", "y1", "x2", "y2", "x3", "y3"],
|
||||
[
|
||||
(0, 0, 1, 1, 2, 2), # y = x
|
||||
(0, 1, 1, 2, 2, 3), # y = x + 1
|
||||
(0, 0, 1, 2, 2, 4), # y = 2x
|
||||
(0, 1, 1, 0, 2, -1), # y = -x + 1
|
||||
(0, 5, 1, 5, 2, 5), # y = 0
|
||||
],
|
||||
)
|
||||
def test_build_line(x1: float, y1: float, x2: float, y2: float, x3: float, y3: float):
|
||||
assert math.isclose(build_line(x1, y1, x2, y2)(x3), y3, rel_tol=1e-9)
|
||||
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Test abstract device class.
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from invokeai.app.services.config import get_config
|
||||
from invokeai.backend.util.devices import TorchDevice, choose_precision, choose_torch_device, torch_dtype
|
||||
|
||||
devices = ["cpu", "cuda:0", "cuda:1", "cuda:2", "mps"]
|
||||
device_types_cpu = [("cpu", torch.float32), ("cuda:0", torch.float32), ("mps", torch.float32)]
|
||||
device_types_cuda = [("cpu", torch.float32), ("cuda:0", torch.float16), ("mps", torch.float32)]
|
||||
device_types_mps = [("cpu", torch.float32), ("cuda:0", torch.float32), ("mps", torch.float16)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_name", devices)
|
||||
def test_device_choice(device_name):
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
torch_device = TorchDevice.choose_torch_device()
|
||||
assert torch_device == torch.device(device_name)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_dtype_pair", device_types_cpu)
|
||||
def test_device_dtype_cpu(device_dtype_pair):
|
||||
with (
|
||||
patch("torch.cuda.is_available", return_value=False),
|
||||
patch("torch.backends.mps.is_available", return_value=False),
|
||||
):
|
||||
device_name, dtype = device_dtype_pair
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
torch_dtype = TorchDevice.choose_torch_dtype()
|
||||
assert torch_dtype == dtype
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_dtype_pair", device_types_cuda)
|
||||
def test_device_dtype_cuda(device_dtype_pair):
|
||||
with (
|
||||
patch("torch.cuda.is_available", return_value=True),
|
||||
patch("torch.cuda.get_device_name", return_value="RTX4070"),
|
||||
patch("torch.backends.mps.is_available", return_value=False),
|
||||
):
|
||||
device_name, dtype = device_dtype_pair
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
torch_dtype = TorchDevice.choose_torch_dtype()
|
||||
assert torch_dtype == dtype
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_dtype_pair", device_types_mps)
|
||||
def test_device_dtype_mps(device_dtype_pair):
|
||||
with (
|
||||
patch("torch.cuda.is_available", return_value=False),
|
||||
patch("torch.backends.mps.is_available", return_value=True),
|
||||
):
|
||||
device_name, dtype = device_dtype_pair
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
torch_dtype = TorchDevice.choose_torch_dtype()
|
||||
assert torch_dtype == dtype
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_dtype_pair", device_types_cuda)
|
||||
def test_device_dtype_override(device_dtype_pair):
|
||||
with (
|
||||
patch("torch.cuda.get_device_name", return_value="RTX4070"),
|
||||
patch("torch.cuda.is_available", return_value=True),
|
||||
patch("torch.backends.mps.is_available", return_value=False),
|
||||
):
|
||||
device_name, dtype = device_dtype_pair
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
config.precision = "float32"
|
||||
torch_dtype = TorchDevice.choose_torch_dtype()
|
||||
assert torch_dtype == torch.float32
|
||||
|
||||
|
||||
def test_normalize():
|
||||
assert (
|
||||
TorchDevice.normalize("cuda") == torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cuda")
|
||||
)
|
||||
assert (
|
||||
TorchDevice.normalize("cuda:0") == torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cuda")
|
||||
)
|
||||
assert (
|
||||
TorchDevice.normalize("cuda:1") == torch.device("cuda:1") if torch.cuda.is_available() else torch.device("cuda")
|
||||
)
|
||||
assert TorchDevice.normalize("mps") == torch.device("mps")
|
||||
assert TorchDevice.normalize("cpu") == torch.device("cpu")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_name", devices)
|
||||
def test_legacy_device_choice(device_name):
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
with pytest.deprecated_call():
|
||||
torch_device = choose_torch_device()
|
||||
assert torch_device == torch.device(device_name)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("device_dtype_pair", device_types_cpu)
|
||||
def test_legacy_device_dtype_cpu(device_dtype_pair):
|
||||
with (
|
||||
patch("torch.cuda.is_available", return_value=False),
|
||||
patch("torch.backends.mps.is_available", return_value=False),
|
||||
patch("torch.cuda.get_device_name", return_value="RTX9090"),
|
||||
):
|
||||
device_name, dtype = device_dtype_pair
|
||||
config = get_config()
|
||||
config.device = device_name
|
||||
with pytest.deprecated_call():
|
||||
torch_device = choose_torch_device()
|
||||
returned_dtype = torch_dtype(torch_device)
|
||||
assert returned_dtype == dtype
|
||||
|
||||
|
||||
def test_legacy_precision_name():
|
||||
config = get_config()
|
||||
config.precision = "auto"
|
||||
with (
|
||||
pytest.deprecated_call(),
|
||||
patch("torch.cuda.is_available", return_value=True),
|
||||
patch("torch.backends.mps.is_available", return_value=True),
|
||||
patch("torch.cuda.get_device_name", return_value="RTX9090"),
|
||||
):
|
||||
assert "float16" == choose_precision(torch.device("cuda"))
|
||||
assert "float16" == choose_precision(torch.device("mps"))
|
||||
assert "float32" == choose_precision(torch.device("cpu"))
|
||||
|
||||
|
||||
# ===== choose_anima_inference_dtype (config.precision honoring) ============
|
||||
|
||||
|
||||
def test_choose_anima_inference_dtype_float16():
|
||||
"""precision='float16' returns torch.float16 without touching hardware."""
|
||||
config = get_config()
|
||||
config.precision = "float16"
|
||||
result = TorchDevice.choose_anima_inference_dtype(torch.device("cpu"))
|
||||
assert result is torch.float16
|
||||
|
||||
|
||||
def test_choose_anima_inference_dtype_bfloat16():
|
||||
"""precision='bfloat16' returns torch.bfloat16 without touching hardware."""
|
||||
config = get_config()
|
||||
config.precision = "bfloat16"
|
||||
result = TorchDevice.choose_anima_inference_dtype(torch.device("cpu"))
|
||||
assert result is torch.bfloat16
|
||||
|
||||
|
||||
def test_choose_anima_inference_dtype_float32():
|
||||
"""precision='float32' returns torch.float32 without touching hardware."""
|
||||
config = get_config()
|
||||
config.precision = "float32"
|
||||
result = TorchDevice.choose_anima_inference_dtype(torch.device("cpu"))
|
||||
assert result is torch.float32
|
||||
|
||||
|
||||
def test_choose_anima_inference_dtype_auto_delegates_to_safe_dtype():
|
||||
"""precision='auto' delegates to choose_bfloat16_safe_dtype (current behavior)."""
|
||||
config = get_config()
|
||||
config.precision = "auto"
|
||||
device = torch.device("cpu")
|
||||
sentinel = torch.bfloat16
|
||||
with patch.object(TorchDevice, "choose_bfloat16_safe_dtype", return_value=sentinel) as mock_safe:
|
||||
result = TorchDevice.choose_anima_inference_dtype(device)
|
||||
assert result is sentinel
|
||||
mock_safe.assert_called_once_with(device)
|
||||
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Test interaction of logging with configuration system.
|
||||
"""
|
||||
|
||||
import io
|
||||
import logging
|
||||
import re
|
||||
|
||||
from invokeai.app.services.config import InvokeAIAppConfig
|
||||
from invokeai.backend.util.logging import LOG_FORMATTERS, InvokeAILogger
|
||||
|
||||
|
||||
# test formatting
|
||||
# Would prefer to use the capfd/capsys fixture here, but it is broken
|
||||
# when used with the logging module: https://github.com/pytest-dev/pytest/issue
|
||||
def test_formatting():
|
||||
logger = InvokeAILogger.get_logger()
|
||||
stream = io.StringIO()
|
||||
handler = logging.StreamHandler(stream)
|
||||
handler.setFormatter(LOG_FORMATTERS["plain"]())
|
||||
logger.addHandler(handler)
|
||||
logger.info("test1")
|
||||
output = stream.getvalue()
|
||||
assert re.search(r"\[InvokeAI\]::INFO --> test1$", output)
|
||||
|
||||
handler.setFormatter(LOG_FORMATTERS["legacy"]())
|
||||
logger.info("test2")
|
||||
output = stream.getvalue()
|
||||
assert re.search(r">> test2$", output)
|
||||
|
||||
|
||||
# test independence of two loggers with different names
|
||||
def test_independence():
|
||||
logger1 = InvokeAILogger.get_logger()
|
||||
logger2 = InvokeAILogger.get_logger("Test")
|
||||
assert logger1.name == "InvokeAI"
|
||||
assert logger2.name == "Test"
|
||||
assert logger1.level == logging.INFO
|
||||
assert logger2.level == logging.INFO
|
||||
logger2.setLevel(logging.DEBUG)
|
||||
assert logger1.level == logging.INFO
|
||||
assert logger2.level == logging.DEBUG
|
||||
|
||||
|
||||
# test that the logger is returned from two similar get_logger() calls
|
||||
def test_retrieval():
|
||||
logger1 = InvokeAILogger.get_logger()
|
||||
logger2 = InvokeAILogger.get_logger()
|
||||
logger3 = InvokeAILogger.get_logger("Test")
|
||||
assert logger1 == logger2
|
||||
assert logger1 != logger3
|
||||
|
||||
|
||||
# test that the configuration is used to set the initial logging level
|
||||
def test_config():
|
||||
config = InvokeAIAppConfig(log_level="debug")
|
||||
logger1 = InvokeAILogger.get_logger("DebugTest", config=config)
|
||||
assert logger1.level == logging.DEBUG
|
||||
@@ -0,0 +1,88 @@
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from invokeai.backend.util.mask import to_standard_float_mask
|
||||
|
||||
|
||||
def test_to_standard_float_mask_wrong_ndim():
|
||||
with pytest.raises(ValueError):
|
||||
to_standard_float_mask(mask=torch.zeros((1, 1, 5, 10)), out_dtype=torch.float32)
|
||||
|
||||
|
||||
def test_to_standard_float_mask_wrong_shape():
|
||||
with pytest.raises(ValueError):
|
||||
to_standard_float_mask(mask=torch.zeros((2, 5, 10)), out_dtype=torch.float32)
|
||||
|
||||
|
||||
def check_mask_result(mask: torch.Tensor, expected_mask: torch.Tensor):
|
||||
"""Helper function to check the result of `to_standard_float_mask()`."""
|
||||
assert mask.shape == expected_mask.shape
|
||||
assert mask.dtype == expected_mask.dtype
|
||||
assert torch.allclose(mask, expected_mask)
|
||||
|
||||
|
||||
def test_to_standard_float_mask_ndim_2():
|
||||
"""Test the case where the input mask has shape (h, w)."""
|
||||
mask = torch.zeros((3, 2), dtype=torch.float32)
|
||||
mask[0, 0] = 1.0
|
||||
mask[1, 1] = 1.0
|
||||
|
||||
expected_mask = torch.zeros((1, 3, 2), dtype=torch.float32)
|
||||
expected_mask[0, 0, 0] = 1.0
|
||||
expected_mask[0, 1, 1] = 1.0
|
||||
|
||||
new_mask = to_standard_float_mask(mask=mask, out_dtype=torch.float32)
|
||||
|
||||
check_mask_result(mask=new_mask, expected_mask=expected_mask)
|
||||
|
||||
|
||||
def test_to_standard_float_mask_ndim_3():
|
||||
"""Test the case where the input mask has shape (1, h, w)."""
|
||||
mask = torch.zeros((1, 3, 2), dtype=torch.float32)
|
||||
mask[0, 0, 0] = 1.0
|
||||
mask[0, 1, 1] = 1.0
|
||||
|
||||
expected_mask = torch.zeros((1, 3, 2), dtype=torch.float32)
|
||||
expected_mask[0, 0, 0] = 1.0
|
||||
expected_mask[0, 1, 1] = 1.0
|
||||
|
||||
new_mask = to_standard_float_mask(mask=mask, out_dtype=torch.float32)
|
||||
|
||||
check_mask_result(mask=new_mask, expected_mask=expected_mask)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"out_dtype",
|
||||
[torch.float32, torch.float16],
|
||||
)
|
||||
def test_to_standard_float_mask_bool_to_float(out_dtype: torch.dtype):
|
||||
"""Test the case where the input mask has dtype bool."""
|
||||
mask = torch.zeros((3, 2), dtype=torch.bool)
|
||||
mask[0, 0] = True
|
||||
mask[1, 1] = True
|
||||
|
||||
expected_mask = torch.zeros((1, 3, 2), dtype=out_dtype)
|
||||
expected_mask[0, 0, 0] = 1.0
|
||||
expected_mask[0, 1, 1] = 1.0
|
||||
|
||||
new_mask = to_standard_float_mask(mask=mask, out_dtype=out_dtype)
|
||||
|
||||
check_mask_result(mask=new_mask, expected_mask=expected_mask)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"out_dtype",
|
||||
[torch.float32, torch.float16],
|
||||
)
|
||||
def test_to_standard_float_mask_float_to_float(out_dtype: torch.dtype):
|
||||
"""Test the case where the input mask has type float (but not all values are 0.0 or 1.0)."""
|
||||
mask = torch.zeros((3, 2), dtype=torch.float32)
|
||||
mask[0, 0] = 0.1 # Should be converted to 0.0
|
||||
mask[0, 1] = 0.9 # Should be converted to 1.0
|
||||
|
||||
expected_mask = torch.zeros((1, 3, 2), dtype=out_dtype)
|
||||
expected_mask[0, 0, 1] = 1.0
|
||||
|
||||
new_mask = to_standard_float_mask(mask=mask, out_dtype=out_dtype)
|
||||
|
||||
check_mask_result(mask=new_mask, expected_mask=expected_mask)
|
||||
Reference in New Issue
Block a user