Merge ~emitorino/review-tools:multi_value_attrs_in_decl into review-tools:master
- Git
- lp:~emitorino/review-tools
- multi_value_attrs_in_decl
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 8f1d06564faf23c9ddfef696007bc871715ec79a | ||||
Proposed branch: | ~emitorino/review-tools:multi_value_attrs_in_decl | ||||
Merge into: | review-tools:master | ||||
Diff against target: |
568 lines (+448/-66) 2 files modified
reviewtools/sr_declaration.py (+49/-63) reviewtools/tests/test_sr_declaration.py (+399/-3) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Samuele Pedroni (community) | Approve | ||
Alex Murray | Approve | ||
Review via email: mp+427054@code.launchpad.net |
Commit message
- sr_declaration.py: add support for multi value attrs declarations
- test_sr_
Description of the change
The current code considers if the attribute in the constraint is a list, just one must match (it is
considered a list of alternatives, aka, a list of OR constraints).
As per https:/
Whats pending to do is to confirm if nested list attrs should be allowed. We can create a follow up MP to include it.
I added several test scenarios including the cases I could think of that should/should not be allowed after this change. Please suggest more that I could be missing so we make sure we dont break this important functionality.
The current test_sr_
Samuele Pedroni (pedronis) wrote : | # |
question about more deep/recursive situations
Emilia Torino (emitorino) wrote : | # |
Adding support for nested lists in declaration attrs
Samuele Pedroni (pedronis) wrote : | # |
lgtm, wondering if the code can be simplified now though, as the code for lists _chec_attrib and the top loop do similar things ?
Emilia Torino (emitorino) wrote : | # |
> lgtm, wondering if the code can be simplified now though, as the code for
> lists _chec_attrib and the top loop do similar things ?
Thanks for this comment. I indeed simplified the rules processing code but had to extend a bit more the _chec_attrib to support considering lists in "against" (rules) for non-lists in val (snap decl).
Preview Diff
1 | diff --git a/reviewtools/sr_declaration.py b/reviewtools/sr_declaration.py | |||
2 | index 58f5f3b..61bd313 100644 | |||
3 | --- a/reviewtools/sr_declaration.py | |||
4 | +++ b/reviewtools/sr_declaration.py | |||
5 | @@ -811,61 +811,49 @@ class SnapReviewDeclaration(SnapReview): | |||
6 | 811 | if type(val) not in [str, list, dict, bool]: | 811 | if type(val) not in [str, list, dict, bool]: |
7 | 812 | raise SnapDeclarationException("unknown type '%s'" % val) | 812 | raise SnapDeclarationException("unknown type '%s'" % val) |
8 | 813 | 813 | ||
9 | 814 | # Keep in mind by this point each OR constraint is being iterated | ||
10 | 815 | # through such that 'against' as a list is a nested list. | ||
11 | 816 | # For now, punt on nested lists since they are impractical in use | ||
12 | 817 | # since they are a list of OR options where one option must match | ||
13 | 818 | # all of val, but if you have that you may as well just use a | ||
14 | 819 | # string. Eg for this snap.yaml: | ||
15 | 820 | # snap.yaml: | ||
16 | 821 | # plugs: | ||
17 | 822 | # foo: | ||
18 | 823 | # bar: [ baz, norf ] | ||
19 | 824 | # | ||
20 | 825 | # a snap decl that uses a list for 'bar' might be: | ||
21 | 826 | # snap decl: | ||
22 | 827 | # foo: | ||
23 | 828 | # plug-attributes: | ||
24 | 829 | # bar: | ||
25 | 830 | # - baz|norf | ||
26 | 831 | # - something|else | ||
27 | 832 | # | ||
28 | 833 | # but, 'something|else' is pointless so it will never match, so the | ||
29 | 834 | # decl should be rewritten more simply as: | ||
30 | 835 | # snap decl: | ||
31 | 836 | # foo: | ||
32 | 837 | # plug-attributes: | ||
33 | 838 | # bar: baz|norf | ||
34 | 839 | # Importantly, the type of the attribute in the snap decl means | ||
35 | 840 | # something different than the type in snap.yaml | ||
36 | 841 | if isinstance(against, list): | ||
37 | 842 | raise SnapDeclarationException( | ||
38 | 843 | "attribute lists in the declaration not supported" | ||
39 | 844 | ) | ||
40 | 845 | |||
41 | 846 | matched = False | 814 | matched = False |
49 | 847 | if isinstance(val, str) and isinstance(against, str): | 815 | if isinstance(val, str): |
50 | 848 | if against.startswith("$"): | 816 | if isinstance(against, str): |
51 | 849 | if against == "$MISSING": | 817 | if against.startswith("$"): |
52 | 850 | matched = False # value must not be set | 818 | if against == "$MISSING": |
53 | 851 | elif re.search(r"^\$PLUG\(%s\)$" % rules_attrib, against): | 819 | matched = False # value must not be set |
54 | 852 | matched = True | 820 | elif re.search(r"^\$PLUG\(%s\)$" % rules_attrib, against): |
55 | 853 | elif re.search(r"^\$SLOT\(%s\)$" % rules_attrib, against): | 821 | matched = True |
56 | 822 | elif re.search(r"^\$SLOT\(%s\)$" % rules_attrib, against): | ||
57 | 823 | matched = True | ||
58 | 824 | else: | ||
59 | 825 | raise SnapDeclarationException( | ||
60 | 826 | "unknown special attrib '%s'" % against | ||
61 | 827 | ) | ||
62 | 828 | elif re.search(r"^(%s)$" % against, val): | ||
63 | 854 | matched = True | 829 | matched = True |
70 | 855 | else: | 830 | elif isinstance(against, list): |
71 | 856 | raise SnapDeclarationException( | 831 | for declaration_value in against: |
72 | 857 | "unknown special attrib '%s'" % against | 832 | # if the attribute in the snap (val) is a string and the |
73 | 858 | ) | 833 | # declaration value (against) is a list, then to match, |
74 | 859 | elif re.search(r"^(%s)$" % against, val): | 834 | # val must match some entry in against |
75 | 860 | matched = True | 835 | if _check_attrib(val, declaration_value, side, rules_attrib): |
76 | 836 | matched = True | ||
77 | 837 | break | ||
78 | 861 | elif isinstance(val, list): | 838 | elif isinstance(val, list): |
79 | 862 | # if the attribute in the snap (val) is a list and the | ||
80 | 863 | # declaration value (against) is a string, then to match, | ||
81 | 864 | # against must be a regex that matches all entries in val | ||
82 | 865 | num_matched = 0 | 839 | num_matched = 0 |
86 | 866 | for i in val: | 840 | for snap_attribute in val: |
87 | 867 | if _check_attrib(i, against, side, rules_attrib): | 841 | if isinstance(against, str): |
88 | 868 | num_matched += 1 | 842 | # if the attribute in the snap (val) is a list and the |
89 | 843 | # declaration value (against) is a string, then to match, | ||
90 | 844 | # against must be a regex that matches all entries in val | ||
91 | 845 | if _check_attrib(snap_attribute, against, side, rules_attrib): | ||
92 | 846 | num_matched += 1 | ||
93 | 847 | elif isinstance(against, list): | ||
94 | 848 | for declaration_value in against: | ||
95 | 849 | # if the attribute in the snap (val) is a list and the | ||
96 | 850 | # declaration value (against) is a list, then to match, | ||
97 | 851 | # every item in against must match some entry in val | ||
98 | 852 | if _check_attrib( | ||
99 | 853 | snap_attribute, declaration_value, side, rules_attrib | ||
100 | 854 | ): | ||
101 | 855 | num_matched += 1 | ||
102 | 856 | break | ||
103 | 869 | if num_matched == len(val): | 857 | if num_matched == len(val): |
104 | 870 | matched = True | 858 | matched = True |
105 | 871 | elif isinstance(val, dict): | 859 | elif isinstance(val, dict): |
106 | @@ -880,6 +868,14 @@ class SnapReviewDeclaration(SnapReview): | |||
107 | 880 | ) | 868 | ) |
108 | 881 | if not matched: | 869 | if not matched: |
109 | 882 | break | 870 | break |
110 | 871 | elif isinstance(against, list): | ||
111 | 872 | for declaration_value in against: | ||
112 | 873 | # if the attribute in the snap (val) is a dict and the | ||
113 | 874 | # declaration value (against) is a list, then to match, | ||
114 | 875 | # val must match some entry in against | ||
115 | 876 | if _check_attrib(val, declaration_value, side, rules_attrib): | ||
116 | 877 | matched = True | ||
117 | 878 | break | ||
118 | 883 | else: | 879 | else: |
119 | 884 | matched = False | 880 | matched = False |
120 | 885 | else: # bools | 881 | else: # bools |
121 | @@ -973,18 +969,8 @@ class SnapReviewDeclaration(SnapReview): | |||
122 | 973 | val = iface[rules_attrib] | 969 | val = iface[rules_attrib] |
123 | 974 | against = rules[rules_key][rules_attrib] | 970 | against = rules[rules_key][rules_attrib] |
124 | 975 | 971 | ||
137 | 976 | if isinstance(against, list): | 972 | if _check_attrib(val, against, side, rules_attrib): |
138 | 977 | # As a practical matter, if the attribute in the | 973 | attributes_matched[rules_key]["matched"] += 1 |
127 | 978 | # constraint is a list, just one must match (it is | ||
128 | 979 | # considered a list of alternatives, aka, a list of | ||
129 | 980 | # OR constraints). | ||
130 | 981 | for i in against: | ||
131 | 982 | if _check_attrib(val, i, side, rules_attrib): | ||
132 | 983 | attributes_matched[rules_key]["matched"] += 1 | ||
133 | 984 | break | ||
134 | 985 | else: | ||
135 | 986 | if _check_attrib(val, against, side, rules_attrib): | ||
136 | 987 | attributes_matched[rules_key]["matched"] += 1 | ||
139 | 988 | else: | 974 | else: |
140 | 989 | # when the attribute is missing from the interface don't | 975 | # when the attribute is missing from the interface don't |
141 | 990 | # mark as checked (missing attributes are checked | 976 | # mark as checked (missing attributes are checked |
142 | diff --git a/reviewtools/tests/test_sr_declaration.py b/reviewtools/tests/test_sr_declaration.py | |||
143 | index 1b36992..38154f0 100644 | |||
144 | --- a/reviewtools/tests/test_sr_declaration.py | |||
145 | +++ b/reviewtools/tests/test_sr_declaration.py | |||
146 | @@ -3892,6 +3892,403 @@ slots: | |||
147 | 3892 | } | 3892 | } |
148 | 3893 | self.check_results(r, expected=expected) | 3893 | self.check_results(r, expected=expected) |
149 | 3894 | 3894 | ||
150 | 3895 | def test_check_declaration_slots_multi_value_attrs_issue(self,): | ||
151 | 3896 | review_ok_output = { | ||
152 | 3897 | "error": dict(), | ||
153 | 3898 | "warn": dict(), | ||
154 | 3899 | "info": { | ||
155 | 3900 | "declaration-snap-v2:slots:fram-devices:custom-device": {"text": "OK"} | ||
156 | 3901 | }, | ||
157 | 3902 | } | ||
158 | 3903 | |||
159 | 3904 | review_err_output = { | ||
160 | 3905 | "error": { | ||
161 | 3906 | "declaration-snap-v2:slots_installation:fram-devices:custom-device": { | ||
162 | 3907 | "text": "human review required due to 'allow-installation' constraint (interface attributes)" | ||
163 | 3908 | } | ||
164 | 3909 | }, | ||
165 | 3910 | "warn": dict(), | ||
166 | 3911 | "info": dict(), | ||
167 | 3912 | } | ||
168 | 3913 | |||
169 | 3914 | multi_value_attrs_test_data = { | ||
170 | 3915 | "iface_with_multiple_multi_value_attrs_and_multi_value_decl_matches": ( | ||
171 | 3916 | { | ||
172 | 3917 | "read-devices": [ | ||
173 | 3918 | "/dev/chunkfs/geom", | ||
174 | 3919 | "/dev/chunkfs/stats", | ||
175 | 3920 | "/dev/chunkfs/sizes", | ||
176 | 3921 | ], | ||
177 | 3922 | "devices": ["/dev/mtd[0-9]", "/dev/chunkfs/chunkfs[0-2]"], | ||
178 | 3923 | }, | ||
179 | 3924 | { | ||
180 | 3925 | "read-devices": [ | ||
181 | 3926 | "/dev/chunkfs/geom", | ||
182 | 3927 | "/dev/chunkfs/stats", | ||
183 | 3928 | "/dev/chunkfs/sizes", | ||
184 | 3929 | ], | ||
185 | 3930 | "devices": ["/dev/mtd\\[0-9\\]", "/dev/chunkfs/chunkfs\\[0-2\\]"], | ||
186 | 3931 | }, | ||
187 | 3932 | {"info": 1, "warn": 0, "error": 0}, | ||
188 | 3933 | review_ok_output, | ||
189 | 3934 | ), | ||
190 | 3935 | "iface_with_multiple_multi_value_attrs_and_multi_value_decl_different_order_matches": ( | ||
191 | 3936 | { | ||
192 | 3937 | "read-devices": [ | ||
193 | 3938 | "/dev/chunkfs/stats", | ||
194 | 3939 | "/dev/chunkfs/sizes", | ||
195 | 3940 | "/dev/chunkfs/geom", | ||
196 | 3941 | ], | ||
197 | 3942 | "devices": ["/dev/chunkfs/chunkfs[0-2]", "/dev/mtd[0-9]"], | ||
198 | 3943 | }, | ||
199 | 3944 | { | ||
200 | 3945 | "read-devices": [ | ||
201 | 3946 | "/dev/chunkfs/geom", | ||
202 | 3947 | "/dev/chunkfs/stats", | ||
203 | 3948 | "/dev/chunkfs/sizes", | ||
204 | 3949 | ], | ||
205 | 3950 | "devices": ["/dev/mtd\\[0-9\\]", "/dev/chunkfs/chunkfs\\[0-2\\]"], | ||
206 | 3951 | }, | ||
207 | 3952 | {"info": 1, "warn": 0, "error": 0}, | ||
208 | 3953 | review_ok_output, | ||
209 | 3954 | ), | ||
210 | 3955 | "iface_with_multiple_multi_value_attrs_and_single_value_list_decl_matches": ( | ||
211 | 3956 | {"devices": ["/dev/mtd1", "/dev/mtd2"]}, | ||
212 | 3957 | {"devices": ["/dev/mtd[0-9]"]}, | ||
213 | 3958 | {"info": 1, "warn": 0, "error": 0}, | ||
214 | 3959 | review_ok_output, | ||
215 | 3960 | ), | ||
216 | 3961 | "iface_with_single_value_attrs_and_single_value_list_decl_matches": ( | ||
217 | 3962 | {"devices": "/dev/mtd1"}, | ||
218 | 3963 | {"devices": ["/dev/mtd[0-9]"]}, | ||
219 | 3964 | {"info": 1, "warn": 0, "error": 0}, | ||
220 | 3965 | review_ok_output, | ||
221 | 3966 | ), | ||
222 | 3967 | "iface_with_single_value_attrs_and_single_value_decl_matches": ( | ||
223 | 3968 | {"devices": "/dev/mtd1"}, | ||
224 | 3969 | {"devices": "/dev/mtd[0-9]"}, | ||
225 | 3970 | {"info": 1, "warn": 0, "error": 0}, | ||
226 | 3971 | review_ok_output, | ||
227 | 3972 | ), | ||
228 | 3973 | "iface_with_one_multi_value_attrs_all_matching": ( | ||
229 | 3974 | {"devices": ["/dev/mtd[0-9]", "/dev/chunkfs/chunkfs[0-2]"]}, | ||
230 | 3975 | {"devices": ["/dev/mtd\\[0-9\\]", "/dev/chunkfs/chunkfs\\[0-2\\]"]}, | ||
231 | 3976 | {"info": 1, "warn": 0, "error": 0}, | ||
232 | 3977 | review_ok_output, | ||
233 | 3978 | ), | ||
234 | 3979 | "regex_in_list_of_single_attr_matches": ( # This case represents the current workaround | ||
235 | 3980 | { | ||
236 | 3981 | "read-devices": [ | ||
237 | 3982 | "/dev/chunkfs/geom", | ||
238 | 3983 | "/dev/chunkfs/stats", | ||
239 | 3984 | "/dev/chunkfs/sizes", | ||
240 | 3985 | ], | ||
241 | 3986 | "devices": ["/dev/mtd[0-9]", "/dev/chunkfs/chunkfs[0-2]"], | ||
242 | 3987 | }, | ||
243 | 3988 | { | ||
244 | 3989 | "devices": ["(/dev/mtd\\[0-9\\]|/dev/chunkfs/chunkfs\\[0-2\\])"], | ||
245 | 3990 | "read-devices": [ | ||
246 | 3991 | "(/dev/chunkfs/geom|/dev/chunkfs/stats|/dev/chunkfs/sizes)" | ||
247 | 3992 | ], | ||
248 | 3993 | }, | ||
249 | 3994 | {"info": 1, "warn": 0, "error": 0}, | ||
250 | 3995 | review_ok_output, | ||
251 | 3996 | ), | ||
252 | 3997 | "extra_iface_attr_matches": ( # Even though this test passes here, | ||
253 | 3998 | # unknown attrs are detected as part _verify_declaration check | ||
254 | 3999 | { | ||
255 | 4000 | "read-devices": [ | ||
256 | 4001 | "/dev/chunkfs/geom", | ||
257 | 4002 | "/dev/chunkfs/stats", | ||
258 | 4003 | "/dev/chunkfs/sizes", | ||
259 | 4004 | ], | ||
260 | 4005 | "devices": ["/dev/mtd[0-9]", "/dev/chunkfs/chunkfs[0-2]"], | ||
261 | 4006 | }, | ||
262 | 4007 | { | ||
263 | 4008 | "read-devices": [ | ||
264 | 4009 | "/dev/chunkfs/geom", | ||
265 | 4010 | "/dev/chunkfs/stats", | ||
266 | 4011 | "/dev/chunkfs/sizes", | ||
267 | 4012 | ], | ||
268 | 4013 | }, | ||
269 | 4014 | {"info": 1, "warn": 0, "error": 0}, | ||
270 | 4015 | review_ok_output, | ||
271 | 4016 | ), | ||
272 | 4017 | "less_items_in_iface_attr_list_but_all_matching_matches": ( | ||
273 | 4018 | {"read-devices": ["/dev/chunkfs/geom", "/dev/chunkfs/stats"]}, | ||
274 | 4019 | { | ||
275 | 4020 | "read-devices": [ | ||
276 | 4021 | "/dev/chunkfs/geom", | ||
277 | 4022 | "/dev/chunkfs/stats", | ||
278 | 4023 | "/dev/chunkfs/sizes", | ||
279 | 4024 | ], | ||
280 | 4025 | }, | ||
281 | 4026 | {"info": 1, "warn": 0, "error": 0}, | ||
282 | 4027 | review_ok_output, | ||
283 | 4028 | ), | ||
284 | 4029 | "missing_attr_in_iface_fails": ( | ||
285 | 4030 | { | ||
286 | 4031 | "read-devices": [ | ||
287 | 4032 | "/dev/chunkfs/geom", | ||
288 | 4033 | "/dev/chunkfs/stats", | ||
289 | 4034 | "/dev/chunkfs/sizes", | ||
290 | 4035 | ], | ||
291 | 4036 | }, | ||
292 | 4037 | { | ||
293 | 4038 | "read-devices": [ | ||
294 | 4039 | "/dev/chunkfs/geom", | ||
295 | 4040 | "/dev/chunkfs/stats", | ||
296 | 4041 | "/dev/chunkfs/sizes", | ||
297 | 4042 | ], | ||
298 | 4043 | "devices": ["/dev/mtd\\[0-9\\]", "/dev/chunkfs/chunkfs\\[0-2\\]"], | ||
299 | 4044 | }, | ||
300 | 4045 | {"info": 0, "warn": 0, "error": 1}, | ||
301 | 4046 | review_err_output, | ||
302 | 4047 | ), | ||
303 | 4048 | "no_matching_item_in_iface_attr_list_fails": ( | ||
304 | 4049 | { | ||
305 | 4050 | "read-devices": [ | ||
306 | 4051 | "/dev/chunkfs/geom", | ||
307 | 4052 | "/dev/chunkfs/stats", | ||
308 | 4053 | "/dev/chunkfs/sizes", | ||
309 | 4054 | ] | ||
310 | 4055 | }, | ||
311 | 4056 | {"read-devices": ["/dev/chunkfs/geom", "/dev/chunkfs/stats"]}, | ||
312 | 4057 | {"info": 0, "warn": 0, "error": 1}, | ||
313 | 4058 | review_err_output, | ||
314 | 4059 | ), | ||
315 | 4060 | "iface_with_single_value_attrs_and_multiple_value_decl_matches": ( | ||
316 | 4061 | {"devices": "/dev/mtd1"}, | ||
317 | 4062 | {"devices": ["/dev/mtd[0-9]", "/dev/chunkfs/chunkfs[0-2]"]}, | ||
318 | 4063 | {"info": 1, "warn": 0, "error": 0}, | ||
319 | 4064 | review_ok_output, | ||
320 | 4065 | ), | ||
321 | 4066 | } | ||
322 | 4067 | |||
323 | 4068 | for test, test_data in multi_value_attrs_test_data.items(): | ||
324 | 4069 | ( | ||
325 | 4070 | iface_attr_value, | ||
326 | 4071 | rule_value, | ||
327 | 4072 | expected_counts, | ||
328 | 4073 | expected_output_data, | ||
329 | 4074 | ) = test_data | ||
330 | 4075 | with self.subTest( | ||
331 | 4076 | iface_attr_value=iface_attr_value, | ||
332 | 4077 | rule_value=iface_attr_value, | ||
333 | 4078 | expected_counts=expected_counts, | ||
334 | 4079 | expected_output_data=expected_output_data, | ||
335 | 4080 | ): | ||
336 | 4081 | iface_data = {"interface": "custom-device"} | ||
337 | 4082 | iface_data.update(iface_attr_value) | ||
338 | 4083 | slots = {"fram-devices": iface_data} | ||
339 | 4084 | self.set_test_snap_yaml("slots", slots) | ||
340 | 4085 | c = SnapReviewDeclaration(self.test_name) | ||
341 | 4086 | base = { | ||
342 | 4087 | "slots": { | ||
343 | 4088 | "custom-device": { | ||
344 | 4089 | "allow-installation": { | ||
345 | 4090 | "slot-attributes": rule_value, | ||
346 | 4091 | "slot-names": ["fram-devices"], | ||
347 | 4092 | } | ||
348 | 4093 | } | ||
349 | 4094 | } | ||
350 | 4095 | } | ||
351 | 4096 | self._set_base_declaration(c, base) | ||
352 | 4097 | c.check_declaration() | ||
353 | 4098 | r = c.review_report | ||
354 | 4099 | self.check_results(r, expected_counts) | ||
355 | 4100 | self.check_results(r, expected=expected_output_data) | ||
356 | 4101 | |||
357 | 4102 | def test_check_declaration_slots_multi_value_attrs_issue_support_nested(self,): | ||
358 | 4103 | review_ok_output = { | ||
359 | 4104 | "error": dict(), | ||
360 | 4105 | "warn": dict(), | ||
361 | 4106 | "info": {"declaration-snap-v2:plugs:mnt:mount-control": {"text": "OK"}}, | ||
362 | 4107 | } | ||
363 | 4108 | |||
364 | 4109 | review_err_output = { | ||
365 | 4110 | "error": { | ||
366 | 4111 | "declaration-snap-v2:plugs_installation:mnt:mount-control": { | ||
367 | 4112 | "text": "human review required due to 'allow-installation' constraint (interface attributes)" | ||
368 | 4113 | } | ||
369 | 4114 | }, | ||
370 | 4115 | "warn": dict(), | ||
371 | 4116 | "info": dict(), | ||
372 | 4117 | } | ||
373 | 4118 | |||
374 | 4119 | multi_value_attrs_test_data = { | ||
375 | 4120 | "iface_with_list_attrs_and_single_value_decl_list_matches": ( | ||
376 | 4121 | { | ||
377 | 4122 | "mnt": [ | ||
378 | 4123 | { | ||
379 | 4124 | "what": "/dev/x*", | ||
380 | 4125 | "where": "/foo/*", | ||
381 | 4126 | "options": ["rw", "nodev"], | ||
382 | 4127 | }, | ||
383 | 4128 | { | ||
384 | 4129 | "what": "/bar/*", | ||
385 | 4130 | "where": "/baz/*", | ||
386 | 4131 | "options": ["rw", "bind"], | ||
387 | 4132 | }, | ||
388 | 4133 | ] | ||
389 | 4134 | }, | ||
390 | 4135 | { | ||
391 | 4136 | "mnt": [ | ||
392 | 4137 | { | ||
393 | 4138 | "what": r"/(bar/|dev/x)\*", | ||
394 | 4139 | "where": r"/(foo|baz)/\*", | ||
395 | 4140 | "options": "rw|bind|nodev", | ||
396 | 4141 | } | ||
397 | 4142 | ] | ||
398 | 4143 | }, | ||
399 | 4144 | {"info": 1, "warn": 0, "error": 0}, | ||
400 | 4145 | review_ok_output, | ||
401 | 4146 | ), | ||
402 | 4147 | "iface_with_single_list_attrs_and_single_value_decl_list_matches": ( | ||
403 | 4148 | { | ||
404 | 4149 | "mnt": [ | ||
405 | 4150 | { | ||
406 | 4151 | "what": "/dev/x*", | ||
407 | 4152 | "where": "/foo/*", | ||
408 | 4153 | "options": ["rw", "nodev"], | ||
409 | 4154 | } | ||
410 | 4155 | ] | ||
411 | 4156 | }, | ||
412 | 4157 | { | ||
413 | 4158 | "mnt": [ | ||
414 | 4159 | { | ||
415 | 4160 | "what": r"/(bar/|dev/x)\*", | ||
416 | 4161 | "where": r"/(foo|baz)/\*", | ||
417 | 4162 | "options": "rw|bind|nodev", | ||
418 | 4163 | } | ||
419 | 4164 | ] | ||
420 | 4165 | }, | ||
421 | 4166 | {"info": 1, "warn": 0, "error": 0}, | ||
422 | 4167 | review_ok_output, | ||
423 | 4168 | ), | ||
424 | 4169 | "iface_with_list_attrs_and_multiple_value_decl_list_matches": ( | ||
425 | 4170 | { | ||
426 | 4171 | "mnt": [ | ||
427 | 4172 | { | ||
428 | 4173 | "what": "/dev/x*", | ||
429 | 4174 | "where": "/foo/*", | ||
430 | 4175 | "options": ["rw", "nodev"], | ||
431 | 4176 | }, | ||
432 | 4177 | { | ||
433 | 4178 | "what": "/bar/*", | ||
434 | 4179 | "where": "/baz/*", | ||
435 | 4180 | "options": ["rw", "bind"], | ||
436 | 4181 | }, | ||
437 | 4182 | ] | ||
438 | 4183 | }, | ||
439 | 4184 | { | ||
440 | 4185 | "mnt": [ | ||
441 | 4186 | { | ||
442 | 4187 | "what": r"/dev/x\*", | ||
443 | 4188 | "where": r"/foo/\*", | ||
444 | 4189 | "options": ["nodev", "rw"], | ||
445 | 4190 | }, | ||
446 | 4191 | { | ||
447 | 4192 | "what": r"/bar/\*", | ||
448 | 4193 | "where": r"/baz/\*", | ||
449 | 4194 | "options": ["rw", "bind"], | ||
450 | 4195 | }, | ||
451 | 4196 | ] | ||
452 | 4197 | }, | ||
453 | 4198 | {"info": 1, "warn": 0, "error": 0}, | ||
454 | 4199 | review_ok_output, | ||
455 | 4200 | ), | ||
456 | 4201 | "iface_with_list_attrs_and_multiple_value_decl_list_with_no_alternatives_fails": ( | ||
457 | 4202 | { | ||
458 | 4203 | "mnt": [ | ||
459 | 4204 | { | ||
460 | 4205 | "what": "/dev/x*", | ||
461 | 4206 | "where": "/foo/*", | ||
462 | 4207 | "options": ["rw", "nodev"], | ||
463 | 4208 | }, | ||
464 | 4209 | { | ||
465 | 4210 | "what": "/bar/*", | ||
466 | 4211 | "where": "/baz/*", | ||
467 | 4212 | "options": ["rw", "bind"], | ||
468 | 4213 | }, | ||
469 | 4214 | ] | ||
470 | 4215 | }, | ||
471 | 4216 | { | ||
472 | 4217 | "mnt": [ | ||
473 | 4218 | {"what": r"/dev/x\*", "where": r"/foo/\*", "options": ["rw"]}, | ||
474 | 4219 | { | ||
475 | 4220 | "what": r"/bar/\*", | ||
476 | 4221 | "where": r"/baz/\*", | ||
477 | 4222 | "options": ["rw", "bind"], | ||
478 | 4223 | }, | ||
479 | 4224 | ] | ||
480 | 4225 | }, | ||
481 | 4226 | {"info": 0, "warn": 0, "error": 1}, | ||
482 | 4227 | review_err_output, | ||
483 | 4228 | ), | ||
484 | 4229 | "iface_with_extra_list_attrs_and_multiple_value_decl_list_fails": ( | ||
485 | 4230 | { | ||
486 | 4231 | "mnt": [ | ||
487 | 4232 | { | ||
488 | 4233 | "what": "/dev/x*", | ||
489 | 4234 | "where": "/foo/*", | ||
490 | 4235 | "options": ["rw", "nodev"], | ||
491 | 4236 | }, | ||
492 | 4237 | { | ||
493 | 4238 | "what": "/bar/*", | ||
494 | 4239 | "where": "/baz/*", | ||
495 | 4240 | "foo_options": ["rw", "bind"], | ||
496 | 4241 | }, | ||
497 | 4242 | ] | ||
498 | 4243 | }, | ||
499 | 4244 | { | ||
500 | 4245 | "mnt": [ | ||
501 | 4246 | { | ||
502 | 4247 | "what": r"/(bar/|dev/x)\*", | ||
503 | 4248 | "where": r"/(foo|baz)/\*", | ||
504 | 4249 | "options": "rw|bind|nodev", | ||
505 | 4250 | } | ||
506 | 4251 | ] | ||
507 | 4252 | }, | ||
508 | 4253 | {"info": 0, "warn": 0, "error": 1}, | ||
509 | 4254 | review_err_output, | ||
510 | 4255 | ), | ||
511 | 4256 | } | ||
512 | 4257 | |||
513 | 4258 | for test, test_data in multi_value_attrs_test_data.items(): | ||
514 | 4259 | ( | ||
515 | 4260 | iface_attr_value, | ||
516 | 4261 | rule_value, | ||
517 | 4262 | expected_counts, | ||
518 | 4263 | expected_output_data, | ||
519 | 4264 | ) = test_data | ||
520 | 4265 | with self.subTest( | ||
521 | 4266 | iface_attr_value=iface_attr_value, | ||
522 | 4267 | rule_value=iface_attr_value, | ||
523 | 4268 | expected_counts=expected_counts, | ||
524 | 4269 | expected_output_data=expected_output_data, | ||
525 | 4270 | ): | ||
526 | 4271 | iface_data = {"interface": "mount-control"} | ||
527 | 4272 | iface_data.update(iface_attr_value) | ||
528 | 4273 | plugs = {"mnt": iface_data} | ||
529 | 4274 | self.set_test_snap_yaml("plugs", plugs) | ||
530 | 4275 | c = SnapReviewDeclaration(self.test_name) | ||
531 | 4276 | base = { | ||
532 | 4277 | "plugs": { | ||
533 | 4278 | "mount-control": { | ||
534 | 4279 | "allow-installation": { | ||
535 | 4280 | "plug-attributes": rule_value, | ||
536 | 4281 | "plug-names": ["mnt"], | ||
537 | 4282 | } | ||
538 | 4283 | } | ||
539 | 4284 | } | ||
540 | 4285 | } | ||
541 | 4286 | self._set_base_declaration(c, base) | ||
542 | 4287 | c.check_declaration() | ||
543 | 4288 | r = c.review_report | ||
544 | 4289 | self.check_results(r, expected_counts) | ||
545 | 4290 | self.check_results(r, expected=expected_output_data) | ||
546 | 4291 | |||
547 | 3895 | def test_check_declaration_slots_deny_connection_attrib_list_match_unmatching_plug_names( | 4292 | def test_check_declaration_slots_deny_connection_attrib_list_match_unmatching_plug_names( |
548 | 3896 | self, | 4293 | self, |
549 | 3897 | ): | 4294 | ): |
550 | @@ -3973,7 +4370,7 @@ slots: | |||
551 | 3973 | expected["info"][name] = {"text": "OK"} | 4370 | expected["info"][name] = {"text": "OK"} |
552 | 3974 | self.check_results(r, expected=expected) | 4371 | self.check_results(r, expected=expected) |
553 | 3975 | 4372 | ||
555 | 3976 | def test_check_declaration_slots_allow_connection_attrib_lists_exception(self): | 4373 | def test_check_declaration_slots_allow_connection_attrib_lists_supported(self): |
556 | 3977 | """Test check_declaration - slots/allow-connection/attrib - lists match snap attribute list in decl list of lists""" | 4374 | """Test check_declaration - slots/allow-connection/attrib - lists match snap attribute list in decl list of lists""" |
557 | 3978 | slots = {"iface-foo": {"interface": "foo", "attrib1": ["b", "a"]}} | 4375 | slots = {"iface-foo": {"interface": "foo", "attrib1": ["b", "a"]}} |
558 | 3979 | self.set_test_snap_yaml("slots", slots) | 4376 | self.set_test_snap_yaml("slots", slots) |
559 | @@ -3991,8 +4388,7 @@ slots: | |||
560 | 3991 | try: | 4388 | try: |
561 | 3992 | c.check_declaration() | 4389 | c.check_declaration() |
562 | 3993 | except SnapDeclarationException: | 4390 | except SnapDeclarationException: |
565 | 3994 | return | 4391 | raise Exception("base declaration should not be invalid") # pragma: nocover |
564 | 3995 | raise Exception("base declaration should be invalid") # pragma: nocover | ||
566 | 3996 | 4392 | ||
567 | 3997 | def test_check_declaration_slots_allow_connection_attrib_list_nomatch(self): | 4393 | def test_check_declaration_slots_allow_connection_attrib_list_nomatch(self): |
568 | 3998 | """Test check_declaration - slots/allow-connection/attrib - list nomatch snap attribute str not in decl list""" | 4394 | """Test check_declaration - slots/allow-connection/attrib - list nomatch snap attribute str not in decl list""" |
LGTM - thanks for the detailed test cases.