1from unittest.mock import MagicMock, call
2
3import pytest
4from PyQt5.QtGui import QOpenGLShader
5
6from UM.View.GL.ShaderProgram import ShaderProgram, InvalidShaderProgramError
7import os
8
9def test_ShaderProgramInit():
10    # Creating it just shouldn't fail.
11    shader = ShaderProgram()
12
13
14def test_loadEmpty():
15    shader = ShaderProgram()
16    with pytest.raises(InvalidShaderProgramError):
17        shader.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Shaders", "empty.shader"))
18
19
20def test_loadInvalid():
21    shader = ShaderProgram()
22    with pytest.raises(InvalidShaderProgramError):
23        shader.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Shaders", "invalid.shader"))
24
25    with pytest.raises(InvalidShaderProgramError):
26        shader.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Shaders", "invalid2.shader"))
27
28def test_load():
29    shader = ShaderProgram()
30
31    mocked_shader_program = MagicMock()
32
33    shader._shader_program = mocked_shader_program
34    shader.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Shaders", "test.shader"))
35
36    # It should be called 3 times, once for vertex, once for fragment and once for geometry
37    call_arg_list = mocked_shader_program.addShaderFromSourceCode.call_args_list
38    assert call(QOpenGLShader.Vertex, "vertex_code") in call_arg_list
39    assert call(QOpenGLShader.Fragment, "fragment_code") in call_arg_list
40    assert call(QOpenGLShader.Geometry, "geometry_code") in call_arg_list
41
42
43def test_bindAndRelease():
44    shader = ShaderProgram()
45
46    mocked_shader_program = MagicMock()
47    shader._shader_program = mocked_shader_program
48    shader.bind()
49    assert mocked_shader_program.bind.call_count == 1
50
51    # Doing it without releasing in between shouldn't cause another bind.
52    shader.bind()
53    assert mocked_shader_program.bind.call_count == 1
54
55    shader.release()
56    assert mocked_shader_program.release.call_count == 1
57
58    shader.release()
59    assert mocked_shader_program.release.call_count == 1
60
61    # We left it unbound, so now binding should work.
62    shader.bind()
63    assert mocked_shader_program.bind.call_count == 2
64
65
66enable_attribute_data = [("int", 2, 3, 0x1404, 1),
67                         ("float", 5, 9, 0x1406, 1),
68                         ("vector2f", 2, 32, 0x1406, 2),
69                         ("vector3f", 2000, 90, 0x1406, 3),
70                         ("vector4f", 12, 1, 0x1406, 4)]
71
72
73@pytest.mark.parametrize("type, offset, stride, type_result, tuple_size", enable_attribute_data)
74def test_enableDisableAttribute(type, offset, stride, type_result, tuple_size):
75    shader = ShaderProgram()
76
77    mocked_shader_program = MagicMock()
78    shader._shader_program = mocked_shader_program
79
80    attribute_index = MagicMock()
81
82    mocked_shader_program.attributeLocation = MagicMock(return_value = attribute_index)
83
84    shader.enableAttribute("blorp", type, offset, stride)
85
86    # Validate
87    mocked_shader_program.setAttributeBuffer.assert_called_once_with(attribute_index, type_result, offset, tuple_size, stride)
88    mocked_shader_program.enableAttributeArray.assert_called_once_with(attribute_index)
89
90    # Disable it again
91    shader.disableAttribute("blorp")
92    mocked_shader_program.disableAttributeArray.assert_called_once_with(attribute_index)
93
94    # Disable unknown attribute
95    shader.disableAttribute("BEEP")
96    mocked_shader_program.disableAttributeArray.assert_called_once_with(attribute_index)
97
98