Test Failed
Pull Request — master (#173)
by Jan
02:52
created

OvalNode.find_node_with_id()   B

Complexity

Conditions 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 18
ccs 0
cts 10
cp 0
rs 8.6666
c 0
b 0
f 0
cc 6
nop 2
crap 42
1
"""
2
    This file contains a OvalNode class, which is one node of OVAL tree
3
"""
4
5
from .evaluate import (is_notapp_result, oval_operator_and, oval_operator_one,
6
                       oval_operator_or, oval_operator_xor)
7
8
9
class OvalNode():
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
10
    """The Oval Node object is one node of the OVAL tree.
11
       The graphic representation of the OVAL tree is the OVAL graph.
12
13
    Attributes:
14
        node_id (str): id of node
15
        node_type (str): type node
16
        value (str): value of node for operator and, or, one etc... and for value true,
17
        false, error etc...
18
        negation (bool): value indicating whether the node is negated
19
        comment (str): some comment about node
20
        tag (str): tag specifies if the node represents OVAL test,
21
        OVAL definition or XCCDF rule
22
        test_result_details (dict|None): information about test
23
        children ([OvalNode]): children of node
24
    """
25
26
    def __init__(self, **kwargs):
27
        """This metode construct OvalNode and validate values of parameteres.
28
29
        Required args:
30
            node_id (str|int): identifies node
31
            node_type (str): type of node (value or operator)
32
            value (str): value of node
33
34
        Optional args:
35
            negation (bool): value indicating whether the node is negated (empty eq False)
36
            comment (str): text about node (empty eq None)
37
            tag (str): tag specifies if the node represents OVAL test,
38
            OVAL definition or XCCDF rule (empty eq None)
39
            test_result_details (dict|None): information about test (empty eq None)
40
            children ([OvalNode]): array of children of node (empty eq empty array)
41
42
        Raises:
43
            KeyError, TypeError, ValueError
44
        """
45
        try:
46
            self.node_id = kwargs['node_id']
47
            self.node_type = self._validate_type(kwargs['node_type'])
48
            self.value = self._validate_value(self.node_type, kwargs['value'])
49
50
            self._check_missing_children_for_operator(
51
                kwargs.get('children', None))
52
            self.negation = self._validate_negation(
53
                kwargs.get('negation', False))
54
        except KeyError as key_error:
55
            raise Exception("Missing required argument!") from key_error
56
57
        self.comment = kwargs.get('comment', None)
58
        self.tag = kwargs.get('tag', None)
59
        self.test_result_details = kwargs.get('test_result_details', None)
60
61
        input_children = kwargs.get('children', None)
62
        self.children = []
63
        if input_children:
64
            for child in input_children:
65
                self.add_child(child)
66
67
    @staticmethod
68
    def _validate_negation(input_negation):
69
        if not isinstance(input_negation, bool):
70
            raise TypeError("Wrong value of negation argument!")
71
        return input_negation
72
73
    @staticmethod
74
    def _validate_type(input_node_type):
75
        node_type = input_node_type.lower()
76
        if node_type not in ("value", "operator"):
77
            raise TypeError("Wrong value of node_type argument!")
78
        return node_type
79
80
    @staticmethod
81
    def _validate_value(input_node_type, input_value):
82
        value = input_value.lower()
83
84
        allowed_values = [
85
            "true",
86
            "false",
87
            "error",
88
            "unknown",
89
            "noteval",
90
            "notappl"]
91
        allowed_operators = ["or", "and", "one", "xor"]
92
93
        if input_node_type == "value" and value not in allowed_values:
94
            raise TypeError(
95
                "Wrong value of argument value for value node!")
96
97
        if input_node_type == "operator" and value not in allowed_operators:
98
            raise TypeError(
99
                "Wrong value of argument value for operator node!")
100
101
        return value
102
103
    def _check_missing_children_for_operator(self, children):
0 ignored issues
show
Coding Style Naming introduced by
Method name "_check_missing_children_for_operator" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
104
        if children is None and self.node_type == "operator":
105
            raise ValueError(
106
                "The operator node must have a child!")
107
108
    def __repr__(self):
109
        return self.value
110
111
    def add_child(self, node):
112
        """Adds a OvalNode (node) as child of OvalNode.
113
114
        Args:
115
            node (OvalNode): New node
116
117
        Raises:
118
            ValueError
119
        """
120
        if self.node_type == "operator":
121
            assert isinstance(node, OvalNode)
122
            self.children.append(node)
123
            return
124
        raise ValueError(
125
            "The value node cannot contain any child!")
126
127
    def _get_result_counts(self):
128
        result = {
129
            'true_cnt': 0,
130
            'false_cnt': 0,
131
            'error_cnt': 0,
132
            'unknown_cnt': 0,
133
            'noteval_cnt': 0,
134
            'notappl_cnt': 0
135
        }
136
137
        for child in self.children:
138
            if child.value == 'true' and not child.negation:
139
                result['true_cnt'] += 1
140
            elif child.value == 'true' and child.negation:
141
                result['false_cnt'] += 1
142
            elif child.value == 'false' and not child.negation:
143
                result['false_cnt'] += 1
144
            elif child.value == 'false' and child.negation:
145
                result['true_cnt'] += 1
146
            else:
147
                if child.node_type == "operator":
148
                    result[child.evaluate_tree() + "_cnt"] += 1
149
                else:
150
                    result[child.value + "_cnt"] += 1
151
        return result
152
153
    def evaluate_tree(self):
154
        """Evaluates the entire OVAL tree.
155
156
        Returns:
157
            str. return values::
158
159
                true
160
                false
161
                error
162
                unknown
163
                noteval
164
                notappl
165
        """
166
        result = self._get_result_counts()
167
        out_result = None
168
        if is_notapp_result(result):
169
            out_result = "notappl"
170
        else:
171
            if self.value == "or":
172
                out_result = oval_operator_or(result)
173
            elif self.value == "and":
174
                out_result = oval_operator_and(result)
175
            elif self.value == "one":
176
                out_result = oval_operator_one(result)
177
            elif self.value == "xor":
178
                out_result = oval_operator_xor(result)
179
180
        if out_result == 'true' and self.negation:
181
            out_result = 'false'
182
        elif out_result == 'false' and self.negation:
183
            out_result = 'true'
184
185
        return out_result
186
187
    def find_node_with_id(self, node_id):
188
        """Finds an OVAL tree node by ID.
189
190
        Args:
191
            node_id (str): ID of node
192
193
        Returns:
194
            OvalNode or None.
195
        """
196
        if self.node_id == node_id:
197
            return self
198
        for child in self.children:
199
            if child.node_id == node_id:
200
                return child
201
        for child in self.children:
202
            if child.children != []:
203
                return child.find_node_with_id(node_id)
204
        return None
205
206
    def add_to_tree(self, node_id, new_node):
207
        """Adds a OvalNode (new_node) below OvalNode with node_id.
208
209
        Args:
210
            node_id (str): ID of node
211
            newNode (OvalNode): New node
212
213
        Returns:
214
            bool. The return values::
215
216
                true -- Success!
217
                false -- Not found.
218
        """
219
        node = self.find_node_with_id(node_id)
220
        if node is not None:
221
            node.add_child(new_node)
222
            return True
223
        return False
224
225
    def change_tree_value(self, node_id, value):
226
        """Changes the value of OvalNode with node_id.
227
228
        Args:
229
            node_id (str): ID of node
230
            value (str): value of node::
231
232
                true
233
                false
234
                error
235
                unknown
236
                noteval
237
                notappl
238
239
        Returns:
240
            bool. The return values::
241
242
                true -- Success!
243
                false -- Not found.
244
        """
245
        node = self.find_node_with_id(node_id)
246
        if node is not None:
247
            self._validate_value(node.node_type, value)
248
            node.value = value
249
            return True
250
        return False
251