补阶段 1 的失败样例:软件渲染被拒、着色器编译失败只降级三维 - What: test_scene3d_gl_smoke.py 增加两项失败样例。其一:把 is_software_renderer 打桩为真后调用 GLDispatch.bind_current_context,断言抛 ThreeDUnsupported、诊断码为 gl_unsupported、且异常带回的 GLCapabilities 明确标着 software_renderer=True(板卡不得在软件光栅器上签收)。其二:把 shader_source 打桩成非法 GLSL,断言 GLRenderer.initialize 抛 diagnostic.code=shader_compile_failed 且 is_initialized 保持假;再用一个 initialize 必然失败的 renderer 构造真实 widget(走公开 API:show/repaint/processEvents),断言视图进入 unavailable、诊断已发出、第二次 repaint 是空操作而不是异常——即三维失败只降级自己,不影响应用。 - Why: 规格 §13 要求每阶段交付含失败样例;阶段 1 的两个失败路径(GL 不足、shader 编译失败)此前只有实现没有验收,而这两条恰恰是板上最可能遇到的情况。 - Verification: 真实 context 下 tests/test_scene3d_gl_smoke.py 10 项全过(原 8 + 新 2);offscreen 下该文件按设计 skip;shared 全量 187 passed、studio 全量 118 passed、ui_check 554 项 0 失败。 - Not in this change: 未改实现(这一版只是把已有失败路径钉进测试)。

This commit is contained in:
ykcheungab
2026-09-14 14:57:56 +08:00
parent 5767b4bf11
commit cad795c890
@@ -298,6 +298,69 @@ def test_release_is_idempotent_and_a_new_context_gets_the_scene_back(gl_widget,
assert _drawn(_render(widget, qt_app)).size, "the scene is not on screen again"
def test_a_software_renderer_is_refused_with_its_capabilities(gl_widget, monkeypatch):
"""A board must not be signed off on llvmpipe, and the report has to say so."""
from orisys_studio.scene3d import gl_backend
widget, _, _ = gl_widget
monkeypatch.setattr(gl_backend, "is_software_renderer", lambda vendor, renderer: True)
with pytest.raises(gl_backend.ThreeDUnsupported) as excinfo:
gl_backend.GLDispatch().bind_current_context(widget.context())
capabilities = excinfo.value.capabilities
assert capabilities.software_renderer is True
assert any(item.code == "gl_unsupported" for item in capabilities.diagnostics)
assert "software" in excinfo.value.diagnostic.message.lower()
def test_a_broken_shader_takes_the_view_down_not_the_application(gl_widget, qt_app, monkeypatch):
"""Shader compilation failure is a diagnostic and an empty view, nothing more."""
from orisys_studio.scene3d import gl_backend
from orisys_studio.scene3d.camera import OrbitCamera
from orisys_studio.scene3d.widget import Sensor3DWidget
widget, _, _ = gl_widget
capabilities = widget.capabilities()
assert capabilities is not None
# The renderer refuses to come up, and says why.
def broken_source(profile, program):
return "#version 330 core\nvoid main() { this is not glsl }\n", "#version 330 core\n"
monkeypatch.setattr(gl_backend, "shader_source", broken_source)
renderer = gl_backend.GLRenderer()
widget.makeCurrent()
try:
with pytest.raises(Exception) as excinfo:
renderer.initialize(capabilities)
finally:
widget.doneCurrent()
item = getattr(excinfo.value, "diagnostic", None)
assert item is not None and item.code == "shader_compile_failed", excinfo.value
assert not renderer.is_initialized, "a failed initialize must not claim to be up"
# The widget turns the same failure into a state: a diagnostic, an empty view,
# and no exception on the way out.
monkeypatch.undo()
class FailingRenderer(gl_backend.GLRenderer):
def initialize(self, capabilities):
raise gl_backend.error("shader_compile_failed", "forced for this test")
failing = Sensor3DWidget(FailingRenderer(), OrbitCamera())
seen: list = []
failing.diagnosticRaised.connect(seen.append)
failing.resize(120, 90)
failing.show()
failing.repaint()
qt_app.processEvents()
failing.repaint() # a second paint of a failed view must be a no-op
qt_app.processEvents()
assert failing.unavailable
assert seen and seen[0].code == "shader_compile_failed"
failing.close()
failing.deleteLater()
def test_the_probe_reports_this_machine_truthfully():
"""The probe's contract: exit 0 only for a GPU-driven context."""
import importlib.util