1from unittest.mock import MagicMock, patch
2
3import pytest
4
5from UM.Math.Color import Color
6from UM.Math.Matrix import Matrix
7from UM.Mesh.MeshBuilder import MeshBuilder
8from UM.Mesh.MeshData import MeshData
9from UM.View.RenderBatch import RenderBatch
10
11
12test_addItem_data = [
13    {"item": {"transformation": Matrix(), "mesh": MeshData()}, "should_add": True},
14    {"item": {"transformation": None, "mesh": MeshData()}, "should_add": False},
15    {"item": {"transformation": None, "mesh": None}, "should_add": False},
16    {"item": {"transformation": Matrix(), "mesh": None}, "should_add": False},
17    {"item": {"transformation": Matrix(), "mesh": MeshData(), "uniforms": {}}, "should_add": True},
18]
19
20
21test_compare_data = [
22    {"item1": {}, "item2": {"sort": 1}},
23    {"item1": {}, "item2": {"sort": 1}},
24    {"item1": {"type": RenderBatch.RenderType.Solid, "sort": 0}, "item2": {"sort": 20, "type":RenderBatch.RenderType.NoType}},  # Solid trumps notype, even if sort is higher
25    {"item1": {"type": RenderBatch.RenderType.Transparent, "sort": 0}, "item2": {"sort": 20, "type":RenderBatch.RenderType.NoType}}
26]
27
28
29def test_createRenderBatch():
30    mocked_shader = MagicMock()
31    with patch("UM.View.GL.OpenGL.OpenGL.getInstance"):
32        render_batch = RenderBatch(mocked_shader)
33
34
35    #  Ensure that the proper defaults are set.
36    assert render_batch.renderType == RenderBatch.RenderType.Solid
37    assert render_batch.renderMode == RenderBatch.RenderMode.Triangles
38    assert render_batch.shader == mocked_shader
39    assert not render_batch.backfaceCull
40    assert render_batch.renderRange is None
41    assert render_batch.items == []
42
43
44@pytest.mark.parametrize("data", test_addItem_data)
45def test_addItem(data):
46    mocked_shader = MagicMock()
47    with patch("UM.View.GL.OpenGL.OpenGL.getInstance"):
48        render_batch = RenderBatch(mocked_shader)
49
50    render_batch.addItem(**data["item"])
51
52    if data["should_add"]:
53        assert len(render_batch.items) != 0
54
55
56@pytest.mark.parametrize("data", test_compare_data)
57def test_compare(data):
58    mocked_shader = MagicMock()
59    with patch("UM.View.GL.OpenGL.OpenGL.getInstance"):
60        render_batch_1 = RenderBatch(mocked_shader, **data["item1"])
61        render_batch_2 = RenderBatch(mocked_shader, **data["item2"])
62    assert render_batch_1 < render_batch_2
63
64
65def test_render():
66    mocked_shader = MagicMock()
67    with patch("UM.View.GL.OpenGL.OpenGL.getInstance"):
68        render_batch = RenderBatch(mocked_shader)
69
70    # Render without a camera shouldn't cause any effect.
71    render_batch.render(None)
72    assert mocked_shader.bind.call_count == 0
73
74    # Rendering with a camera should cause the shader to be bound and released (even if the batch is empty)
75    mocked_camera = MagicMock()
76    mocked_camera.getWorldTransformation = MagicMock(return_value = Matrix())
77    mocked_camera.getViewProjectionMatrix = MagicMock(return_value=Matrix())
78    with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"):
79        render_batch.render(mocked_camera)
80    assert mocked_shader.bind.call_count == 1
81    assert mocked_shader.release.call_count == 1
82
83    # Actualy render with an item in the batch
84    mb = MeshBuilder()
85    mb.addPyramid(10, 10, 10, color=Color(0.0, 1.0, 0.0, 1.0))
86    mb.calculateNormals()
87    mesh_data = mb.build()
88    render_batch.addItem(Matrix(), mesh_data, {})
89    with patch("UM.View.GL.OpenGL.OpenGL.getInstance"):
90        with patch("UM.View.GL.OpenGLContext.OpenGLContext.properties"):
91            render_batch.render(mocked_camera)
92    assert mocked_shader.bind.call_count == 2
93    assert mocked_shader.release.call_count == 2