test_yaml_loader.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. #
  2. # Copyright (c) 2023 Project CHIP Authors
  3. # All rights reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. import unittest
  18. from unittest.mock import mock_open, patch
  19. from matter_yamltests.errors import (TestStepGroupResponseError, TestStepInvalidTypeError, TestStepKeyError,
  20. TestStepNodeIdAndGroupIdError, TestStepValueAndValuesError,
  21. TestStepVerificationStandaloneError)
  22. from matter_yamltests.yaml_loader import YamlLoader
  23. def mock_open_with_parameter_content(content):
  24. file_object = mock_open(read_data=content).return_value
  25. file_object.__iter__.return_value = content.splitlines(True)
  26. return file_object
  27. @patch('builtins.open', new=mock_open_with_parameter_content)
  28. class TestYamlLoader(unittest.TestCase):
  29. def _get_wrong_values(self, valid_types, spaces=2):
  30. values = []
  31. if type(None) not in valid_types:
  32. values.append('')
  33. if str not in valid_types:
  34. values.append('A Test')
  35. if bool not in valid_types:
  36. values.append(True)
  37. if int not in valid_types:
  38. values.append(2)
  39. if float not in valid_types:
  40. values.append(2.1)
  41. if dict not in valid_types:
  42. values.append('\n' + (spaces * ' ') +
  43. 'value: True\n' + (spaces * ' ') + 'values: False')
  44. if list not in valid_types:
  45. values.append('\n' + (spaces * ' ') +
  46. '- value: Test1\n' + (spaces * ' ') + '- value: Test2')
  47. return values
  48. def test_missing_file(self):
  49. load = YamlLoader().load
  50. content = None
  51. filename, name, pics, config, tests = load(content)
  52. self.assertEqual(filename, '')
  53. self.assertEqual(name, '')
  54. self.assertEqual(pics, None)
  55. self.assertEqual(config, {})
  56. self.assertEqual(tests, [])
  57. def test_empty_file(self):
  58. load = YamlLoader().load
  59. content = ''
  60. filename, name, pics, config, tests = load(content)
  61. self.assertEqual(name, '')
  62. self.assertEqual(name, '')
  63. self.assertEqual(pics, None)
  64. self.assertEqual(config, {})
  65. self.assertEqual(tests, [])
  66. def test_key_unknown(self):
  67. load = YamlLoader().load
  68. content = '''
  69. unknown: Test Name
  70. '''
  71. self.assertRaises(TestStepKeyError, load, content)
  72. def test_key_name(self):
  73. load = YamlLoader().load
  74. content = '''
  75. name: Test Name
  76. '''
  77. _, name, _, _, _ = load(content)
  78. self.assertEqual(name, 'Test Name')
  79. def test_key_name_wrong_values(self):
  80. load = YamlLoader().load
  81. key = 'name'
  82. values = self._get_wrong_values([str])
  83. [self.assertRaises(TestStepInvalidTypeError, load,
  84. f'{key}: {x}') for x in values]
  85. def test_key_pics_string(self):
  86. load = YamlLoader().load
  87. content = '''
  88. PICS: OO.S
  89. '''
  90. _, _, pics, _, _ = load(content)
  91. self.assertEqual(pics, 'OO.S')
  92. def test_key_pics_list(self):
  93. load = YamlLoader().load
  94. content = '''
  95. PICS:
  96. - OO.S
  97. - OO.C
  98. '''
  99. _, _, pics, _, _ = load(content)
  100. self.assertEqual(pics, ['OO.S', 'OO.C'])
  101. def test_key_pics_wrong_values(self):
  102. load = YamlLoader().load
  103. key = 'PICS'
  104. values = self._get_wrong_values([str, list])
  105. [self.assertRaises(TestStepInvalidTypeError, load,
  106. f'{key}: {x}') for x in values]
  107. def test_key_config(self):
  108. load = YamlLoader().load
  109. content = '''
  110. config:
  111. name: value
  112. name2: value2
  113. '''
  114. _, _, _, config, _ = load(content)
  115. self.assertEqual(config, {'name': 'value', 'name2': 'value2'})
  116. def test_key_config_wrong_values(self):
  117. load = YamlLoader().load
  118. key = 'config'
  119. values = self._get_wrong_values([dict])
  120. [self.assertRaises(TestStepInvalidTypeError, load,
  121. f'{key}: {x}') for x in values]
  122. def test_key_tests(self):
  123. load = YamlLoader().load
  124. content = '''
  125. tests:
  126. - label: Test1
  127. - label: Test2
  128. '''
  129. _, _, _, _, tests = load(content)
  130. self.assertEqual(tests, [{'label': 'Test1'}, {'label': 'Test2'}])
  131. def test_key_tests_wrong_values(self):
  132. load = YamlLoader().load
  133. key = 'tests'
  134. values = self._get_wrong_values([list])
  135. [self.assertRaises(TestStepInvalidTypeError, load,
  136. f'{key}: {x}') for x in values]
  137. def test_key_tests_step_unknown_key(self):
  138. load = YamlLoader().load
  139. content = '''
  140. tests:
  141. - unknown: Test2
  142. '''
  143. self.assertRaises(TestStepKeyError, load, content)
  144. def test_key_tests_step_bool_keys(self):
  145. load = YamlLoader().load
  146. content = ('tests:\n'
  147. ' - {key}: {value}')
  148. keys = [
  149. 'disabled',
  150. 'fabricFiltered',
  151. ]
  152. wrong_values = self._get_wrong_values([bool], spaces=6)
  153. for key in keys:
  154. _, _, _, _, tests = load(content.format(key=key, value=True))
  155. self.assertEqual(tests, [{key: True}])
  156. for value in wrong_values:
  157. x = content.format(key=key, value=value)
  158. self.assertRaises(TestStepInvalidTypeError, load, x)
  159. def test_key_tests_step_str_keys(self):
  160. load = YamlLoader().load
  161. content = ('tests:\n'
  162. ' - {key}: {value}')
  163. keys = [
  164. 'label',
  165. 'identity',
  166. 'cluster',
  167. 'attribute',
  168. 'command',
  169. 'event',
  170. 'PICS',
  171. 'wait',
  172. ]
  173. # NOTE: 'verification' is excluded from this list despites beeing a key of type
  174. # str. This is because 'verification' key has a rule that requires it to
  175. # tied with either a 'disabled: True' or a 'command: UserPrompt'.
  176. # As such it has dedicated tests.
  177. wrong_values = self._get_wrong_values([str], spaces=6)
  178. for key in keys:
  179. _, _, _, _, tests = load(content.format(key=key, value='a string'))
  180. self.assertEqual(tests, [{key: 'a string'}])
  181. for value in wrong_values:
  182. x = content.format(key=key, value=value)
  183. self.assertRaises(TestStepInvalidTypeError, load, x)
  184. def test_key_tests_step_int_keys(self):
  185. load = YamlLoader().load
  186. content = ('tests:\n'
  187. ' - {key}: {value}')
  188. keys = [
  189. 'nodeId',
  190. 'groupId',
  191. 'minInterval',
  192. 'maxInterval',
  193. 'timedInteractionTimeoutMs',
  194. 'busyWaitMs',
  195. ]
  196. wrong_values = self._get_wrong_values([int], spaces=6)
  197. for key in keys:
  198. _, _, _, _, tests = load(content.format(key=key, value=1))
  199. self.assertEqual(tests, [{key: 1}])
  200. for value in wrong_values:
  201. x = content.format(key=key, value=value)
  202. self.assertRaises(TestStepInvalidTypeError, load, x)
  203. def test_key_tests_step_dict_keys(self):
  204. load = YamlLoader().load
  205. content = ('tests:\n'
  206. ' - command: writeAttribute\n'
  207. ' {key}: {value}')
  208. keys = [
  209. 'arguments',
  210. ]
  211. valid_value = ('\n'
  212. ' value: True\n')
  213. wrong_values = self._get_wrong_values([dict], spaces=6)
  214. for key in keys:
  215. _, _, _, _, tests = load(
  216. content.format(key=key, value=valid_value))
  217. self.assertEqual(
  218. tests, [{'command': 'writeAttribute', key: {'value': True}}])
  219. for value in wrong_values:
  220. x = content.format(key=key, value=value)
  221. self.assertRaises(TestStepInvalidTypeError, load, x)
  222. def test_key_tests_step_response_key(self):
  223. load = YamlLoader().load
  224. content = ('tests:\n'
  225. ' - response: {value}')
  226. value = ('\n'
  227. ' value: True\n')
  228. _, _, _, _, tests = load(content.format(value=value))
  229. self.assertEqual(tests, [{'response': {'value': True}}])
  230. value = ('\n'
  231. ' - value: True\n')
  232. _, _, _, _, tests = load(content.format(value=value))
  233. self.assertEqual(tests, [{'response': [{'value': True}]}])
  234. wrong_values = self._get_wrong_values([dict, list, str], spaces=6)
  235. for value in wrong_values:
  236. x = content.format(value=value)
  237. self.assertRaises(TestStepInvalidTypeError, load, x)
  238. def test_key_tests_step_endpoint_number_key(self):
  239. load = YamlLoader().load
  240. content = ('tests:\n'
  241. ' - endpoint: {value}')
  242. _, _, _, _, tests = load(content.format(value=1))
  243. self.assertEqual(tests, [{'endpoint': 1}])
  244. _, _, _, _, tests = load(content.format(value='TestKey'))
  245. self.assertEqual(tests, [{'endpoint': 'TestKey'}])
  246. wrong_values = self._get_wrong_values([str, int], spaces=6)
  247. for value in wrong_values:
  248. x = content.format(value=value)
  249. self.assertRaises(TestStepInvalidTypeError, load, x)
  250. def test_key_tests_step_event_number_key(self):
  251. load = YamlLoader().load
  252. content = ('tests:\n'
  253. ' - eventNumber: {value}')
  254. _, _, _, _, tests = load(content.format(value=1))
  255. self.assertEqual(tests, [{'eventNumber': 1}])
  256. _, _, _, _, tests = load(content.format(value='TestKey'))
  257. self.assertEqual(tests, [{'eventNumber': 'TestKey'}])
  258. wrong_values = self._get_wrong_values([str, int], spaces=6)
  259. for value in wrong_values:
  260. x = content.format(value=value)
  261. self.assertRaises(TestStepInvalidTypeError, load, x)
  262. def test_key_tests_step_verification_key(self):
  263. load = YamlLoader().load
  264. content = ('tests:\n'
  265. ' - verification: {value}\n'
  266. ' disabled: true')
  267. _, _, _, _, tests = load(content.format(value='Test Sentence'))
  268. self.assertEqual(
  269. tests, [{'verification': 'Test Sentence', 'disabled': True}])
  270. wrong_values = self._get_wrong_values([str, int], spaces=6)
  271. for value in wrong_values:
  272. x = content.format(value=value)
  273. self.assertRaises(TestStepInvalidTypeError, load, x)
  274. # TODO
  275. # 'verification',
  276. def test_key_tests_step_rule_node_id_and_group_id_are_mutually_exclusive(self):
  277. load = YamlLoader().load
  278. content = '''
  279. tests:
  280. - label: A Test Name
  281. nodeId: 0
  282. groupId: 1
  283. '''
  284. self.assertRaises(TestStepNodeIdAndGroupIdError, load, content)
  285. def test_key_tests_step_rule_group_step_should_not_expect_a_response(self):
  286. load = YamlLoader().load
  287. content = '''
  288. tests:
  289. - label: A Test Name
  290. groupId: 1
  291. response:
  292. value: An expected value
  293. '''
  294. self.assertRaises(TestStepGroupResponseError, load, content)
  295. def test_key_tests_step_rule_step_with_verification_should_be_disabled_or_interactive(self):
  296. load = YamlLoader().load
  297. content = '''
  298. tests:
  299. - label: A Test Name
  300. verification: A verification sentence
  301. '''
  302. self.assertRaises(TestStepVerificationStandaloneError, load, content)
  303. content = '''
  304. tests:
  305. - label: A Test Name
  306. verification: A verification sentence
  307. disabled: false
  308. '''
  309. self.assertRaises(TestStepVerificationStandaloneError, load, content)
  310. content = '''
  311. tests:
  312. - label: A Test Name
  313. verification: A verification sentence
  314. disabled: true
  315. '''
  316. _, _, _, _, tests = load(content)
  317. self.assertEqual(tests, [
  318. {'label': 'A Test Name', 'verification': 'A verification sentence', 'disabled': True}])
  319. content = '''
  320. tests:
  321. - label: A Test Name
  322. verification: A verification sentence
  323. command: Something
  324. '''
  325. self.assertRaises(TestStepVerificationStandaloneError, load, content)
  326. content = '''
  327. tests:
  328. - label: A Test Name
  329. verification: A verification sentence
  330. command: UserPrompt
  331. '''
  332. _, _, _, _, tests = load(content)
  333. self.assertEqual(tests, [
  334. {'label': 'A Test Name', 'verification': 'A verification sentence', 'command': 'UserPrompt'}])
  335. def test_key_tests_step_response_key_value_key(self):
  336. # NOTE: The value key can be of any type.
  337. pass
  338. def test_key_tests_step_response_key_values_key(self):
  339. load = YamlLoader().load
  340. content = ('tests:\n'
  341. ' - response:\n'
  342. ' values: {value}')
  343. _, _, _, _, tests = load(content.format(value=[]))
  344. self.assertEqual(tests, [{'response': {'values': []}}])
  345. wrong_values = self._get_wrong_values([list], spaces=8)
  346. for value in wrong_values:
  347. x = content.format(value=value)
  348. self.assertRaises(TestStepInvalidTypeError, load, x)
  349. def test_key_tests_step_response_key_error_key(self):
  350. load = YamlLoader().load
  351. content = ('tests:\n'
  352. ' - response:\n'
  353. ' error: {value}')
  354. _, _, _, _, tests = load(content.format(value='AnError'))
  355. self.assertEqual(tests, [{'response': {'error': 'AnError'}}])
  356. wrong_values = self._get_wrong_values([str], spaces=8)
  357. for value in wrong_values:
  358. x = content.format(value=value)
  359. self.assertRaises(TestStepInvalidTypeError, load, x)
  360. def test_key_tests_step_response_key_cluster_error_key(self):
  361. load = YamlLoader().load
  362. content = ('tests:\n'
  363. ' - response:\n'
  364. ' clusterError: {value}')
  365. _, _, _, _, tests = load(content.format(value=1))
  366. self.assertEqual(tests, [{'response': {'clusterError': 1}}])
  367. wrong_values = self._get_wrong_values([int], spaces=8)
  368. for value in wrong_values:
  369. x = content.format(value=value)
  370. self.assertRaises(TestStepInvalidTypeError, load, x)
  371. def test_key_tests_step_response_key_constraints_key(self):
  372. load = YamlLoader().load
  373. content = ('tests:\n'
  374. ' - response:\n'
  375. ' constraints: {value}')
  376. _, _, _, _, tests = load(content.format(value={}))
  377. self.assertEqual(tests, [{'response': {'constraints': {}}}])
  378. wrong_values = self._get_wrong_values([dict], spaces=8)
  379. for value in wrong_values:
  380. x = content.format(value=value)
  381. self.assertRaises(TestStepInvalidTypeError, load, x)
  382. def test_key_tests_step_response_key_save_as_key(self):
  383. load = YamlLoader().load
  384. content = ('tests:\n'
  385. ' - response:\n'
  386. ' saveAs: {value}')
  387. _, _, _, _, tests = load(content.format(value='AKey'))
  388. self.assertEqual(tests, [{'response': {'saveAs': 'AKey'}}])
  389. wrong_values = self._get_wrong_values([str], spaces=8)
  390. for value in wrong_values:
  391. x = content.format(value=value)
  392. self.assertRaises(TestStepInvalidTypeError, load, x)
  393. def test_rule_response_value_and_values_are_mutually_exclusive(self):
  394. load = YamlLoader().load
  395. content = ('tests:\n'
  396. ' - response:\n'
  397. ' value: 1\n'
  398. ' values: []')
  399. self.assertRaises(TestStepValueAndValuesError, load, content)
  400. # TODO Check constraints
  401. if __name__ == '__main__':
  402. unittest.main()