补阶段 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:
@@ -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"
|
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():
|
def test_the_probe_reports_this_machine_truthfully():
|
||||||
"""The probe's contract: exit 0 only for a GPU-driven context."""
|
"""The probe's contract: exit 0 only for a GPU-driven context."""
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
|||||||
Reference in New Issue
Block a user