PageRenderTime 31ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/components/policy/tools/generate_policy_source_test.py

https://github.com/chromium/chromium
Python | 473 lines | 437 code | 18 blank | 18 comment | 2 complexity | 6972999d941ad5eee7553556bb5b529c MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. #!/usr/bin/env python3
  2. # Copyright 2016 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. import codecs
  6. import unittest
  7. from unittest.mock import patch, mock_open, call
  8. from typing import NamedTuple
  9. import generate_policy_source
  10. import generate_policy_source_test_data as test_data
  11. from generate_policy_source import PolicyDetails
  12. class PolicyData(NamedTuple):
  13. policy_id: int
  14. chunk_number: int
  15. field_number: int
  16. class PolicyGenerationTest(unittest.TestCase):
  17. TEMPLATES_JSON = {
  18. "risk_tag_definitions": [{
  19. "name": "full-admin-access",
  20. "description": "full-admin-access-desc",
  21. "user-description": "full-admin-access-user-desc"
  22. }],
  23. "policy_definitions": [{
  24. "name": "ExampleStringPolicy",
  25. "type": "string",
  26. "schema": {
  27. "type": "string"
  28. },
  29. "supported_on": ["chrome_os:1-"],
  30. "id": 1,
  31. "tags": [],
  32. "caption": "ExampleStringPolicy caption",
  33. "desc": "ExampleStringPolicy desc"
  34. }, {
  35. "name": "ExampleBoolPolicy",
  36. "type": "main",
  37. "schema": {
  38. "type": "boolean"
  39. },
  40. "supported_on": ["chrome_os:1-"],
  41. "id": 2,
  42. "tags": [],
  43. "caption": "ExampleBoolPolicy caption",
  44. "desc": "ExampleBoolPolicy desc",
  45. }, {
  46. "name": "ExampleBoolMergeMetapolicy",
  47. "type": "main",
  48. "schema": {
  49. "type": "boolean"
  50. },
  51. "supported_on": ["chrome_os:1-"],
  52. "features": {
  53. "metapolicy_type": "merge",
  54. },
  55. "id": 3,
  56. "tags": [],
  57. "caption": "ExampleBoolMergeMetapolicy caption",
  58. "desc": "ExampleBoolMergeMetapolicy desc",
  59. }, {
  60. "name": "ExampleBoolPrecedenceMetapolicy",
  61. "type": "main",
  62. "schema": {
  63. "type": "boolean"
  64. },
  65. "supported_on": ["chrome_os:1-"],
  66. "features": {
  67. "metapolicy_type": "precedence",
  68. },
  69. "id": 4,
  70. "tags": [],
  71. "caption": "ExampleBoolPrecedenceMetapolicy caption",
  72. "desc": "ExampleBoolPrecedenceMetapolicy desc",
  73. }, {
  74. "name": "CloudOnlyPolicy",
  75. "type": "main",
  76. "schema": {
  77. "type": "boolean"
  78. },
  79. "features": {
  80. "cloud_only": True,
  81. },
  82. "supported_on": ["chrome_os:1-", "android:1-"],
  83. "id": 5,
  84. "tags": [],
  85. "caption": "CloudOnlyPolicy caption",
  86. "desc": "CloudOnlyPolicy desc",
  87. }, {
  88. "name": "CloudManagementEnrollmentToken",
  89. "type": "string",
  90. "schema": {
  91. "type": "string"
  92. },
  93. "supported_on": ["chrome_os:1-", "android:1-"],
  94. "id": 6,
  95. "tags": [],
  96. "caption": "CloudManagementEnrollmentToken caption",
  97. "desc": "CloudManagementEnrollmentToken desc"
  98. }, {
  99. "name": "ChunkZeroLastFieldBooleanPolicy",
  100. "type": "main",
  101. "schema": {
  102. "type": "boolean"
  103. },
  104. "supported_on": ["chrome_os:1-"],
  105. "id": 1015,
  106. "tags": [],
  107. "caption": "ChunkZeroLastFieldBooleanPolicy caption",
  108. "desc": "ChunkZeroLastFieldBooleanPolicy desc.",
  109. }, {
  110. "name": "ChunkOneFirstFieldBooleanPolicy",
  111. "type": "main",
  112. "schema": {
  113. "type": "boolean"
  114. },
  115. "supported_on": ["chrome_os:1-"],
  116. "id": 1016,
  117. "tags": [],
  118. "caption": "ChunkOneFirstFieldBooleanPolicy caption",
  119. "desc": "ChunkOneFirstFieldBooleanPolicy desc.",
  120. }, {
  121. "name": "ChunkOneLastFieldBooleanPolicy",
  122. "type": "main",
  123. "schema": {
  124. "type": "boolean"
  125. },
  126. "supported_on": ["chrome_os:1-"],
  127. "id": 1815,
  128. "tags": [],
  129. "caption": "ChunkOneLastFieldBooleanPolicy caption",
  130. "desc": "ChunkOneLastFieldBooleanPolicy desc.",
  131. }, {
  132. "name": "ChunkTwoFirstFieldStringPolicy",
  133. "type": "string",
  134. "schema": {
  135. "type": "string"
  136. },
  137. "supported_on": ["chrome_os:1-"],
  138. "id": 1816,
  139. "tags": [],
  140. "caption": "ChunkTwoFirstFieldStringPolicy caption",
  141. "desc": "ChunkTwoFirstFieldStringPolicy desc"
  142. }, {
  143. "name": "ChunkTwoLastFieldStringPolicy",
  144. "type": "string",
  145. "schema": {
  146. "type": "string"
  147. },
  148. "supported_on": ["chrome_os:1-"],
  149. "id": 2615,
  150. "tags": [],
  151. "caption": "ChunkTwoLastFieldStringPolicy caption",
  152. "desc": "ChunkTwoLastFieldStringPolicy desc"
  153. }],
  154. "policy_atomic_group_definitions": []
  155. }
  156. def setUp(self):
  157. self.maxDiff = 10000
  158. self.chrome_major_version = 94
  159. self.target_platform = 'chrome_os'
  160. self.all_target_platforms = ['win', 'mac', 'linux', 'chromeos', 'fuchsia']
  161. self.risk_tags = generate_policy_source.RiskTags(self.TEMPLATES_JSON)
  162. self.policies = [
  163. generate_policy_source.PolicyDetails(policy, self.chrome_major_version,
  164. self.target_platform,
  165. self.risk_tags.GetValidTags())
  166. for policy in self.TEMPLATES_JSON['policy_definitions']
  167. ]
  168. self.risk_tags.ComputeMaxTags(self.policies)
  169. policy_details_set = list(map((lambda x: x.name), self.policies))
  170. policies_already_in_group = set()
  171. self.policy_atomic_groups = [
  172. generate_policy_source.PolicyAtomicGroup(group, policy_details_set,
  173. policies_already_in_group)
  174. for group in self.TEMPLATES_JSON['policy_atomic_group_definitions']
  175. ]
  176. def testDefaultValueGeneration(self):
  177. """Tests generation of default policy values."""
  178. # Bools
  179. stmts, expr = generate_policy_source._GenerateDefaultValue(True)
  180. self.assertListEqual([], stmts)
  181. self.assertEqual('base::Value(true)', expr)
  182. stmts, expr = generate_policy_source._GenerateDefaultValue(False)
  183. self.assertListEqual([], stmts)
  184. self.assertEqual('base::Value(false)', expr)
  185. # Ints
  186. stmts, expr = generate_policy_source._GenerateDefaultValue(33)
  187. self.assertListEqual([], stmts)
  188. self.assertEqual('base::Value(33)', expr)
  189. # Strings
  190. stmts, expr = generate_policy_source._GenerateDefaultValue('foo')
  191. self.assertListEqual([], stmts)
  192. self.assertEqual('base::Value("foo")', expr)
  193. # Empty list
  194. stmts, expr = generate_policy_source._GenerateDefaultValue([])
  195. self.assertListEqual(
  196. ['base::Value default_value(base::Value::Type::LIST);'], stmts)
  197. self.assertEqual('std::move(default_value)', expr)
  198. # List with values
  199. stmts, expr = generate_policy_source._GenerateDefaultValue([1, '2'])
  200. self.assertListEqual([
  201. 'base::Value default_value(base::Value::Type::LIST);',
  202. 'default_value.Append(base::Value(1));',
  203. 'default_value.Append(base::Value("2"));'
  204. ], stmts)
  205. self.assertEqual('std::move(default_value)', expr)
  206. # Recursive lists are not supported.
  207. stmts, expr = generate_policy_source._GenerateDefaultValue([1, []])
  208. self.assertListEqual([], stmts)
  209. self.assertIsNone(expr)
  210. # Arbitary types are not supported.
  211. stmts, expr = generate_policy_source._GenerateDefaultValue(object())
  212. self.assertListEqual([], stmts)
  213. self.assertIsNone(expr)
  214. def _assertCallsEqual(self, expected_output, call_args_list):
  215. # Convert mocked write calls into actual content that would be written
  216. # to the file. Elements of call_args_list are call objects, which are
  217. # two-tuples of (positional args, keyword args). With call[0] we first
  218. # fetch the positional args, which are an n-tuple, and with call[0][0]
  219. # we get the first positional argument, which is the string that is
  220. # written into the file.
  221. actual_output = ''.join(call[0][0] for call in call_args_list)
  222. # Strip whitespace from the beginning and end of expected and actual
  223. # output and verify that they are equal.
  224. self.assertEqual(expected_output.strip(), actual_output.strip())
  225. def testWriteCloudPolicyProtobuf(self):
  226. is_full_runtime_values = [False, True]
  227. output_path = 'mock_cloud_policy_proto'
  228. for is_full_runtime in is_full_runtime_values:
  229. with patch('codecs.open', mock_open()) as mocked_file:
  230. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  231. generate_policy_source._WriteCloudPolicyProtobuf(
  232. self.policies,
  233. self.policy_atomic_groups,
  234. self.target_platform,
  235. f,
  236. self.risk_tags,
  237. is_full_runtime=is_full_runtime)
  238. full_runtime_comment = '//' if is_full_runtime else ''
  239. full_runtime_suffix = '_full_runtime' if is_full_runtime else ''
  240. with self.subTest(is_full_runtime=is_full_runtime):
  241. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  242. expected_formatted = test_data.EXPECTED_CLOUD_POLICY_PROTOBUF % {
  243. "full_runtime_comment": full_runtime_comment,
  244. "full_runtime_suffix": full_runtime_suffix,
  245. }
  246. self._assertCallsEqual(expected_formatted,
  247. mocked_file().write.call_args_list)
  248. def testWriteChromeSettingsProtobuf(self):
  249. is_full_runtime_values = [False, True]
  250. output_path = 'mock_chrome_settings_proto'
  251. for is_full_runtime in is_full_runtime_values:
  252. with patch('codecs.open', mock_open()) as mocked_file:
  253. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  254. generate_policy_source._WriteChromeSettingsProtobuf(
  255. self.policies,
  256. self.policy_atomic_groups,
  257. self.target_platform,
  258. f,
  259. self.risk_tags,
  260. is_full_runtime=is_full_runtime)
  261. full_runtime_comment = '//' if is_full_runtime else ''
  262. full_runtime_suffix = '_full_runtime' if is_full_runtime else ''
  263. with self.subTest(is_full_runtime=is_full_runtime):
  264. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  265. expected_formatted = test_data.EXPECTED_CHROME_SETTINGS_PROTOBUF % {
  266. "full_runtime_comment": full_runtime_comment,
  267. "full_runtime_suffix": full_runtime_suffix,
  268. }
  269. self._assertCallsEqual(expected_formatted,
  270. mocked_file().write.call_args_list)
  271. def testWritePolicyProto(self):
  272. output_path = 'mock_write_policy_proto'
  273. with patch('codecs.open', mock_open()) as mocked_file:
  274. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  275. generate_policy_source._WritePolicyProto(f, self.policies[0])
  276. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  277. self._assertCallsEqual(test_data.EXPECTED_POLICY_PROTO,
  278. mocked_file().write.call_args_list)
  279. def testGetMetapoliciesOfType(self):
  280. merge_metapolicies = generate_policy_source._GetMetapoliciesOfType(
  281. self.policies, "merge")
  282. self.assertListEqual(["ExampleBoolMergeMetapolicy"], merge_metapolicies)
  283. self.assertEqual(1, len(merge_metapolicies))
  284. precedence_metapolicies = generate_policy_source._GetMetapoliciesOfType(
  285. self.policies, "precedence")
  286. self.assertListEqual(["ExampleBoolPrecedenceMetapolicy"],
  287. precedence_metapolicies)
  288. self.assertEqual(1, len(precedence_metapolicies))
  289. invalid_metapolicies = generate_policy_source._GetMetapoliciesOfType(
  290. self.policies, "invalid")
  291. self.assertListEqual([], invalid_metapolicies)
  292. self.assertEqual(0, len(invalid_metapolicies))
  293. def testWritePolicyConstantHeader(self):
  294. output_path = 'mock_policy_constants_h'
  295. for target_platform in self.all_target_platforms:
  296. with patch('codecs.open', mock_open()) as mocked_file:
  297. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  298. generate_policy_source._WritePolicyConstantHeader(
  299. self.policies,
  300. self.policy_atomic_groups,
  301. target_platform,
  302. f,
  303. self.risk_tags,
  304. )
  305. with self.subTest(target_platform=target_platform):
  306. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  307. if target_platform == 'win':
  308. windows_only_part = test_data.POLICY_CONSTANTS_HEADER_WIN_ONLY_PART
  309. else:
  310. windows_only_part = ''
  311. expected_formatted = test_data.EXPECTED_POLICY_CONSTANTS_HEADER % {
  312. "windows_only_part": windows_only_part,
  313. }
  314. self._assertCallsEqual(expected_formatted,
  315. mocked_file().write.call_args_list)
  316. def testWritePolicyConstantSource(self):
  317. output_path = 'mock_policy_constants_cc'
  318. for target_platform in self.all_target_platforms:
  319. with patch('codecs.open', mock_open()) as mocked_file:
  320. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  321. generate_policy_source._WritePolicyConstantSource(
  322. self.policies,
  323. self.policy_atomic_groups,
  324. target_platform,
  325. f,
  326. self.risk_tags,
  327. )
  328. with self.subTest(target_platform=target_platform):
  329. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  330. if target_platform == 'win':
  331. windows_only_part = test_data.POLICY_CONSTANTS_SOURCE_WIN_ONLY_PART
  332. else:
  333. windows_only_part = ''
  334. expected_formatted = test_data.EXPECTED_POLICY_CONSTANTS_SOURCE % {
  335. "windows_only_part": windows_only_part,
  336. }
  337. self._assertCallsEqual(expected_formatted,
  338. mocked_file().write.call_args_list)
  339. def testWriteChromeOSPolicyConstantsHeader(self):
  340. output_path = 'mock_policy_constants_h'
  341. with patch('codecs.open', mock_open()) as mocked_file:
  342. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  343. generate_policy_source._WriteChromeOSPolicyConstantsHeader(
  344. self.policies,
  345. self.policy_atomic_groups,
  346. self.target_platform,
  347. f,
  348. self.risk_tags,
  349. )
  350. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  351. self._assertCallsEqual(test_data.EXPECTED_CROS_POLICY_CONSTANTS_HEADER,
  352. mocked_file().write.call_args_list)
  353. def testWriteChromeOSPolicyConstantsSource(self):
  354. output_path = 'mock_policy_constants_cc'
  355. with patch('codecs.open', mock_open()) as mocked_file:
  356. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  357. generate_policy_source._WriteChromeOSPolicyConstantsSource(
  358. self.policies,
  359. self.policy_atomic_groups,
  360. self.target_platform,
  361. f,
  362. self.risk_tags,
  363. )
  364. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  365. self._assertCallsEqual(test_data.EXPECTED_CROS_POLICY_CONSTANTS_SOURCE,
  366. mocked_file().write.call_args_list)
  367. def testWriteAppRestrictions(self):
  368. output_path = 'app_restrictions_xml'
  369. with patch('codecs.open', mock_open()) as mocked_file:
  370. with codecs.open(output_path, 'w', encoding='utf-8') as f:
  371. generate_policy_source._WriteAppRestrictions(
  372. self.policies,
  373. self.policy_atomic_groups,
  374. self.target_platform,
  375. f,
  376. self.risk_tags,
  377. )
  378. mocked_file.assert_called_once_with(output_path, 'w', encoding='utf-8')
  379. self._assertCallsEqual(test_data.EXPECTED_APP_RESTRICTIONS_XML,
  380. mocked_file().write.call_args_list)
  381. def testChunkNumberAndFieldNumber(self):
  382. test_data = [
  383. # Last top-level policy
  384. PolicyData(policy_id=1015, chunk_number=0, field_number=1017),
  385. # First policy in chunk 1
  386. PolicyData(policy_id=1016, chunk_number=1, field_number=1),
  387. # Last policy in chunk 1
  388. PolicyData(policy_id=1815, chunk_number=1, field_number=800),
  389. # First policy in chunk 2
  390. PolicyData(policy_id=1816, chunk_number=2, field_number=1),
  391. # Last policy in chunk 2
  392. PolicyData(policy_id=2615, chunk_number=2, field_number=800),
  393. # First policy in chunk 3
  394. PolicyData(policy_id=2616, chunk_number=3, field_number=1),
  395. # Last policy in chunk 3
  396. PolicyData(policy_id=3415, chunk_number=3, field_number=800),
  397. # First policy in chunk 501
  398. PolicyData(policy_id=401016, chunk_number=501, field_number=1),
  399. # Last policy in chunk 501
  400. PolicyData(policy_id=401815, chunk_number=501, field_number=800),
  401. # First policy in chunk 502
  402. PolicyData(policy_id=401816, chunk_number=502, field_number=1),
  403. # Last policy in chunk 502
  404. PolicyData(policy_id=402615, chunk_number=502, field_number=800),
  405. # First policy in chunk 503
  406. PolicyData(policy_id=402616, chunk_number=503, field_number=1),
  407. # Last policy in chunk 503
  408. PolicyData(policy_id=403415, chunk_number=503, field_number=800),
  409. ]
  410. for policy_data in test_data:
  411. self.assertEqual(
  412. generate_policy_source._ChunkNumber(policy_data.policy_id),
  413. policy_data.chunk_number)
  414. self.assertEqual(
  415. generate_policy_source._FieldNumber(policy_data.policy_id,
  416. policy_data.chunk_number),
  417. policy_data.field_number)
  418. if __name__ == '__main__':
  419. unittest.main()