Spaces:
Running on Zero
Running on Zero
File size: 4,909 Bytes
5d6dcc3 d443689 5d6dcc3 d443689 5d6dcc3 d443689 5d6dcc3 d443689 5d6dcc3 f135496 d443689 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | import importlib
import os
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from render_config import RenderConfig, parse_config
from blender_runtime import _worker_environment
def test_explicit_cpu_bypasses_zerogpu_even_when_deployment_is_zerogpu(monkeypatch):
import service
monkeypatch.setenv('RENDER_BACKEND', 'zerogpu')
cfg = parse_config('{"device":"CPU"}')
assert (cfg.engine, cfg.device) == ('EEVEE', 'CPU')
assert service._backend(cfg) == 'cpu'
calls = []
monkeypatch.setattr(service, 'run_task', lambda *args: calls.append('cpu') or {'ok': True})
monkeypatch.setattr(service, '_zero_render', lambda *args: pytest.fail('CPU must not allocate a GPU'))
service._execute({}, Path('/tmp'), service._backend(cfg), cfg, Path('/tmp/mode-test.log'))
assert calls == ['cpu']
def test_default_gpu_and_cycles_only_devices_are_validated(monkeypatch):
import service
from spaces.config import Config
monkeypatch.setenv('RENDER_BACKEND', 'auto')
monkeypatch.setattr(Config, 'zero_gpu', True)
assert (RenderConfig().engine, RenderConfig().device) == ('EEVEE', 'AUTO')
assert service._backend(RenderConfig()) == 'zerogpu'
assert service._backend(RenderConfig(engine='CYCLES', device='AUTO')) == 'zerogpu'
assert service._backend(RenderConfig(engine='CYCLES', device='CPU')) == 'cpu'
with pytest.raises(ValueError, match='CUDA / OPTIX'):
RenderConfig(engine='EEVEE', device='CUDA')
def test_software_gl_environment_does_not_leak_into_gpu_jobs(monkeypatch):
monkeypatch.setenv('LIBGL_ALWAYS_SOFTWARE', 'true')
monkeypatch.setenv('GALLIUM_DRIVER', 'llvmpipe')
cpu = _worker_environment({'engine': 'EEVEE', 'device': 'CPU'})
gpu = _worker_environment({'engine': 'EEVEE', 'device': 'AUTO'})
assert cpu['LIBGL_ALWAYS_SOFTWARE'] == 'true'
assert cpu['GALLIUM_DRIVER'] == 'llvmpipe'
assert 'LIBGL_ALWAYS_SOFTWARE' not in gpu
assert 'GALLIUM_DRIVER' not in gpu
@pytest.mark.skipif(not hasattr(os, 'fork'), reason='Linux fork regression')
def test_lock_released_while_forked_worker_still_holds_descriptor(tmp_path):
import service
r, w = os.pipe()
child = None
try:
with service._lock(tmp_path):
child = os.fork()
if child == 0:
os.close(w)
os.read(r, 1)
os._exit(0)
# Child still owns an inherited descriptor, but must not own the lock.
with service._lock(tmp_path):
pass
finally:
if child:
os.write(w, b'x')
os.waitpid(child, 0)
os.close(r)
os.close(w)
def test_legacy_jobs_resume_with_cycles():
import service
assert service._config({'config': {'device': 'AUTO'}}).engine == 'CYCLES'
assert service._config({'config': {'engine': 'EEVEE', 'device': 'CPU'}}).engine == 'EEVEE'
def test_zerogpu_preserves_worker_error_details(monkeypatch):
import service
def failed_worker(*args):
raise RuntimeError('EGL initialization failed: no graphics device')
monkeypatch.setattr(service, 'run_task', failed_worker)
report = service._allocated_render({}, '/tmp/probe.log', '', 30)
assert not report['ok']
assert 'no graphics device' in report['error']
def test_eevee_gpu_probe_is_explicit_and_truthful(monkeypatch):
import service
configs = []
def probe(cfg):
configs.append(cfg)
return {'ok': False, 'gpu_verified': False, 'error': 'No EGL device'}, [], None
monkeypatch.setattr(service, '_self_test', probe)
report, _, _ = service.eevee_gpu_self_test()
assert configs[0].engine == 'EEVEE' and configs[0].device == 'AUTO'
assert not report['gpu_eevee_usable']
assert 'No EGL device' == report['error']
def test_requested_zerogpu_size_routes_exactly_once(monkeypatch):
import service
calls = []
monkeypatch.setattr(service, '_zero_render', lambda *a: calls.append('large') or {'ok': True})
monkeypatch.setattr(service, '_zero_render_xlarge', lambda *a: calls.append('xlarge') or {'ok': True})
for size in ('large', 'xlarge'):
cfg = RenderConfig(gpu_size=size)
service._execute({}, Path('/tmp'), 'zerogpu', cfg, Path('/tmp/probe.log'))
assert calls == ['large', 'xlarge']
def test_graphics_probe_preserves_requested_backend_and_size(monkeypatch):
import service
configs = []
monkeypatch.setattr(service, '_self_test', lambda cfg: (configs.append(cfg) or
{'ok': True, 'gpu_verified': True}, [], None))
report, _, _ = service.eevee_gpu_self_test('xlarge', 'vulkan')
assert configs[0].gpu_size == 'xlarge' and configs[0].graphics_backend == 'vulkan'
assert report['gpu_eevee_usable']
with pytest.raises(ValueError):
RenderConfig(graphics_backend='opengl --python unsafe.py')
|