mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
192 lines
6.4 KiB
Python
192 lines
6.4 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Copyright (c) 2025 Allaun
|
|
|
|
"""Tests for nodupe/tools/gpu/__init__.py - GPU Backend implementations."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
# Import the GPU backend classes
|
|
from nodupe.tools.gpu import (
|
|
CPUFallbackBackend,
|
|
CUDABackend,
|
|
GPUBackend,
|
|
MetalBackend,
|
|
create_gpu_backend,
|
|
get_gpu_backend,
|
|
)
|
|
|
|
|
|
class TestGPUBackend:
|
|
"""Test abstract GPUBackend class."""
|
|
|
|
def test_is_abstract(self):
|
|
"""GPUBackend cannot be instantiated directly."""
|
|
with pytest.raises(TypeError):
|
|
GPUBackend()
|
|
|
|
|
|
class TestCPUFallbackBackend:
|
|
"""Test CPUFallbackBackend class."""
|
|
|
|
def test_cpu_backend_creation(self):
|
|
"""CPUFallbackBackend can be created."""
|
|
backend = CPUFallbackBackend()
|
|
assert backend is not None
|
|
|
|
def test_is_available(self):
|
|
"""CPUFallbackBackend is always available."""
|
|
backend = CPUFallbackBackend()
|
|
assert backend.is_available() is True
|
|
|
|
def test_get_device_info(self):
|
|
"""CPUFallbackBackend returns device info."""
|
|
backend = CPUFallbackBackend()
|
|
info = backend.get_device_info()
|
|
assert isinstance(info, dict)
|
|
assert info['type'] == 'cpu'
|
|
|
|
def test_compute_embeddings_empty_list(self):
|
|
"""CPUFallbackBackend handles empty list."""
|
|
backend = CPUFallbackBackend()
|
|
embeddings = backend.compute_embeddings([])
|
|
assert embeddings == []
|
|
|
|
def test_compute_embeddings_lists(self):
|
|
"""CPUFallbackBackend generates embeddings for lists."""
|
|
backend = CPUFallbackBackend()
|
|
embeddings = backend.compute_embeddings([[1, 2, 3], [4, 5, 6]])
|
|
assert len(embeddings) == 2
|
|
|
|
def test_compute_embeddings_numpy_arrays(self):
|
|
"""CPUFallbackBackend generates embeddings for numpy arrays."""
|
|
backend = CPUFallbackBackend()
|
|
arr1 = np.array([1.0, 2.0, 3.0])
|
|
arr2 = np.array([4.0, 5.0, 6.0])
|
|
embeddings = backend.compute_embeddings([arr1, arr2])
|
|
assert len(embeddings) == 2
|
|
|
|
def test_compute_embeddings_other_types(self):
|
|
"""CPUFallbackBackend handles other types."""
|
|
backend = CPUFallbackBackend()
|
|
embeddings = backend.compute_embeddings([42, "string", None])
|
|
assert len(embeddings) == 3
|
|
|
|
def test_matrix_multiply_basic(self):
|
|
"""CPUFallbackBackend performs matrix multiplication."""
|
|
backend = CPUFallbackBackend()
|
|
a = [[1, 2], [3, 4]]
|
|
b = [[5, 6], [7, 8]]
|
|
result = backend.matrix_multiply(a, b)
|
|
# [[1*5+2*7, 1*6+2*8], [3*5+4*7, 3*6+4*8]] = [[19, 22], [43, 50]]
|
|
assert result[0][0] == 19
|
|
assert result[0][1] == 22
|
|
assert result[1][0] == 43
|
|
assert result[1][1] == 50
|
|
|
|
def test_matrix_multiply_empty(self):
|
|
"""CPUFallbackBackend handles empty matrices."""
|
|
backend = CPUFallbackBackend()
|
|
result = backend.matrix_multiply([], [])
|
|
# Empty input returns 0.0 or empty
|
|
assert result == [] or result == 0.0
|
|
|
|
def test_matrix_multiply_invalid(self):
|
|
"""CPUFallbackBackend handles invalid matrices."""
|
|
backend = CPUFallbackBackend()
|
|
result = backend.matrix_multiply([[1, 2]], [[3], [4]]) # Incompatible dimensions
|
|
# Should return empty or handle gracefully
|
|
assert isinstance(result, list)
|
|
|
|
|
|
class TestCUDABackend:
|
|
"""Test CUDABackend class."""
|
|
|
|
def test_cuda_backend_creation(self):
|
|
"""CUDABackend can be created."""
|
|
backend = CUDABackend()
|
|
assert backend is not None
|
|
|
|
def test_cuda_backend_with_device_id(self):
|
|
"""CUDABackend can be created with device_id."""
|
|
backend = CUDABackend(device_id=1)
|
|
assert backend.device_id == 1
|
|
|
|
def test_cuda_backend_not_available(self):
|
|
"""CUDABackend is not available without CUDA."""
|
|
backend = CUDABackend()
|
|
# Falls back to CPU when CUDA is not available
|
|
assert backend.is_available() is False
|
|
|
|
def test_get_device_info_no_device(self):
|
|
"""CUDABackend returns empty dict when not available."""
|
|
backend = CUDABackend()
|
|
info = backend.get_device_info()
|
|
assert info == {}
|
|
|
|
|
|
class TestMetalBackend:
|
|
"""Test MetalBackend class."""
|
|
|
|
def test_metal_backend_creation(self):
|
|
"""MetalBackend can be created."""
|
|
backend = MetalBackend()
|
|
assert backend is not None
|
|
|
|
def test_metal_backend_not_available(self):
|
|
"""MetalBackend is not available without Metal."""
|
|
backend = MetalBackend()
|
|
# Falls back to CPU when Metal is not available
|
|
assert backend.is_available() is False
|
|
|
|
def test_get_device_info_no_device(self):
|
|
"""MetalBackend returns empty dict when not available."""
|
|
backend = MetalBackend()
|
|
info = backend.get_device_info()
|
|
assert info == {}
|
|
|
|
|
|
class TestCreateGpuBackend:
|
|
"""Test create_gpu_backend factory function."""
|
|
|
|
def test_create_gpu_backend_auto(self):
|
|
"""create_gpu_backend with 'auto' returns CPUFallbackBackend."""
|
|
backend = create_gpu_backend('auto')
|
|
assert isinstance(backend, CPUFallbackBackend)
|
|
|
|
def test_create_gpu_backend_cpu(self):
|
|
"""create_gpu_backend with 'cpu' returns CPUFallbackBackend."""
|
|
backend = create_gpu_backend('cpu')
|
|
assert isinstance(backend, CPUFallbackBackend)
|
|
|
|
def test_create_gpu_backend_cuda(self):
|
|
"""create_gpu_backend with 'cuda' returns CUDABackend."""
|
|
backend = create_gpu_backend('cuda')
|
|
assert isinstance(backend, CUDABackend)
|
|
|
|
def test_create_gpu_backend_metal(self):
|
|
"""create_gpu_backend with 'metal' returns MetalBackend."""
|
|
backend = create_gpu_backend('metal')
|
|
assert isinstance(backend, MetalBackend)
|
|
|
|
def test_create_gpu_backend_unknown_type(self):
|
|
"""create_gpu_backend raises ValueError for unknown type."""
|
|
with pytest.raises(ValueError, match="Unknown GPU backend type"):
|
|
create_gpu_backend('unknown')
|
|
|
|
|
|
class TestGetGpuBackend:
|
|
"""Test get_gpu_backend function."""
|
|
|
|
def test_get_backend_returns_backend(self):
|
|
"""get_gpu_backend returns a GPUBackend."""
|
|
backend = get_gpu_backend()
|
|
assert isinstance(backend, GPUBackend)
|
|
|
|
def test_get_backend_singleton(self):
|
|
"""get_gpu_backend returns the same instance."""
|
|
backend1 = get_gpu_backend()
|
|
backend2 = get_gpu_backend()
|
|
assert backend1 is backend2
|