File: TestSettingFunction.py

package info (click to toggle)
uranium 5.0.0-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,304 kB
  • sloc: python: 31,765; sh: 132; makefile: 12
file content (170 lines) | stat: -rw-r--r-- 6,901 bytes parent folder | download | duplicates (3)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# Copyright (c) 2020 Ultimaker B.V.
# Uranium is released under the terms of the LGPLv3 or higher.

import pytest

from UM.Settings.SettingFunction import SettingFunction


##  Individual test cases for the good setting functions.
#
#   Each test will be executed with each of these functions. These functions are
#   all good and should work.
setting_function_good_data = [
    "0",            # Number.
    "\"x\"",        # String.
    "True",         # Boolean.
    "foo",          # Variable.
    "math.sqrt(4)", # Function call.
    "foo * zoo"     # Two variables.
]


##  Fixture to create a setting function.
#
#   These setting functions are all built with good functions. Id est no errors
#   should occur during the creation of the fixture.
@pytest.fixture(params = setting_function_good_data)
def setting_function_good(request):
    return SettingFunction(request.param)


##  Individual test cases for the bad setting functions.
#
#   Each test will be executed with each of these functions. These functions are
#   all bad and should not work.
setting_function_bad_data = [
    "",                                                                 # Empty string.
    "os.read(os.open(\"/etc/passwd\", os.O_RDONLY), 10)",               # Function that reads your passwords from your system.
    "exec(\"os.read(os.open(\\\"/etc/passwd\\\", os.O_RDONLY), 10)\")", # Obfuscated function that reads your passwords from your system.
    "(",                                                                # Syntax error.
    "[x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == '_ImportLockContext'][0]().__enter__.__globals__['__builtins__']['__import__']('os').system(\"echo omgzomg\")",  # Obsfucated call to system
    "[x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == '_ImportLockContext'][0]().__enter__.__globals__['__' + 'builtins__']['__import__']('os').system(\"echo omgzomg\")"   #Another obsfucated call to system
    "'_'", # This string is not allowed
    "import sys",  # We don't allow importing
    "'a_a'[1]",  # Trying to circumvent the protection against underscores
    "{x:x for x in [1,2,3,4]}"  # We don't allow dict comprehension
]


##  Fixture to create a setting function.
#
#   These setting functions are all built with bad functions. Id est they should
#   give an error when creating the fixture.
@pytest.fixture(params = setting_function_bad_data)
def setting_function_bad(request):
    return SettingFunction(request.param)


##  Tests the initialisation of setting functions with good functions.
#
#   Each of these should create a good function.
def test_init_good(setting_function_good):
    assert setting_function_good is not None
    assert setting_function_good.isValid()


##  Tests the initialisation of setting functions with bad functions.
#
#   Each of these should create a bad function.
def test_init_bad(setting_function_bad):
    assert setting_function_bad is not None
    assert not setting_function_bad.isValid()


class MockValueProvider:
    ##  Creates a mock value provider.
    #
    #   This initialises a dictionary with key-value pairs.
    def __init__(self):
        self._values = {
            "foo": 5,
            "zoo": 7
        }

    ##  Provides a value.
    #
    #   \param name The key of the value to provide.
    def getProperty(self, key, property_name, context = None):
        if not (key in self._values):
            return None
        return self._values[key]


test_call_data = [
    { "code": "0",            "result": 0 },
    { "code": "\"x\"",        "result": "x" },
    { "code": "True",         "result": True },
    { "code": "foo",          "result": 5 },
    { "code": "math.sqrt(4)", "result": 2 },
    { "code": "foo * zoo",    "result": 35 }, # 5 * 7
    { "code": "",             "result": None },
    { "code": "os.read(os.open(\"/etc/passwd\", os.O_RDONLY), 10)", "result": None },
    { "code": "exec(\"os.read(os.open(\\\"/etc/passwd\\\", os.O_RDONLY), 10)\")", "result": None },
    { "code": "boo",          "result": 0 } # Variable doesn't exist.
]


##  Tests the calling of a valid setting function.
#
#   \param setting_function_good A valid setting function from a fixture.
@pytest.mark.parametrize("data", test_call_data)
def test_call(data):
    value_provider = MockValueProvider()
    function = SettingFunction(data["code"])
    assert function(value_provider) == data["result"]


##  Tests the equality operator on setting functions.
def test_eq():
    setting_function = SettingFunction("3 * 3")
    assert not (setting_function == "some string")  # Equality against something of a different type.
    assert setting_function != "some string"
    assert setting_function == setting_function # Equality against itself.
    assert not (setting_function != setting_function)

    duplicate = SettingFunction("3 * 3")  # Different instance with the same code. Should be equal!
    assert setting_function == duplicate
    assert not (setting_function != duplicate)

    same_answer = SettingFunction("9")  # Different code but the result is the same. Should NOT be equal!
    assert not (setting_function == same_answer)
    assert setting_function != same_answer


##  The individual test cases for test_getUsedSettings.
#
#   This is a list where each item is a test case. Each test case consists of a
#   dictionary with two elements: The code that represents a function, and the
#   true variables in that function (the answer).
test_getUsedSettings_data = [
    { "code": "0",       "variables": [] },
    { "code": "\"x\"",   "variables": ["x"] },
    { "code": "x",       "variables": ["x"] },
    { "code": "x * y",   "variables": ["x", "y"] },
    { "code": "sqrt(4)", "variables": [] },
    { "code": "sqrt(x)", "variables": ["x"] },
    { "code": "x * x",   "variables": ["x"] },  # Use the same variable twice.
    { "code": "sqrt('x')" , "variables": ["x"] }, # Calling functions with string parameters will mark the string parameter as a "used setting".
]


##  Tests if the function finds correctly which settings are used.
#
#   \param data A test case to test.
@pytest.mark.parametrize("data", test_getUsedSettings_data)
def test_getUsedSettings(data):
    function = SettingFunction(data["code"])
    answer = function.getUsedSettingKeys()
    assert len(answer) == len(data["variables"])
    for variable in data["variables"]:  # Check for set equality regardless of the order.
        assert variable in answer


##  Tests the conversion of a setting function to string.
def test_str():
    # Due to the simplicity of the function, it's not really necessary to make a full-blown parametrised test for this. Just two simple tests:
    function = SettingFunction("3.14156")  # Simple test case.
    assert str(function) == "=3.14156"
    function = SettingFunction("")  # Also the edge case.
    assert str(function) == "="