reclient_helper_test.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2023 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 datetime
  6. import hashlib
  7. import os
  8. import os.path
  9. import sys
  10. import time
  11. import unittest
  12. import unittest.mock
  13. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  14. sys.path.insert(0, ROOT_DIR)
  15. import gclient_paths
  16. import reclient_helper
  17. from testing_support import trial_dir
  18. def write(filename, content):
  19. """Writes the content of a file and create the directories as needed."""
  20. filename = os.path.abspath(filename)
  21. dirname = os.path.dirname(filename)
  22. if not os.path.isdir(dirname):
  23. os.makedirs(dirname)
  24. with open(filename, 'w') as f:
  25. f.write(content)
  26. class ReclientHelperTest(trial_dir.TestCase):
  27. def setUp(self):
  28. super().setUp()
  29. self.previous_dir = os.getcwd()
  30. os.chdir(self.root_dir)
  31. def tearDown(self):
  32. os.chdir(self.previous_dir)
  33. super().tearDown()
  34. @unittest.mock.patch.dict(os.environ,
  35. {'AUTONINJA_BUILD_ID': "SOME_RANDOM_ID"},
  36. clear=True)
  37. @unittest.mock.patch('reclient_helper.datetime_now',
  38. return_value=datetime.datetime(2017, 3, 16, 20, 0, 41,
  39. 0))
  40. @unittest.mock.patch('subprocess.call', return_value=0)
  41. @unittest.mock.patch('ninja.main', return_value=0)
  42. def test_ninja_reclient_sets_path_env_vars(self, *_):
  43. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  44. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  45. 'reproxy.cfg')
  46. write('.gclient', '')
  47. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  48. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  49. write(reclient_cfg, '0.0')
  50. argv = ["ninja", "-C", "out/a", "chrome"]
  51. self.assertEqual(0, reclient_helper.run_ninja(argv))
  52. run_log_dir = os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  53. "logs",
  54. "20170316T200041.000000_SOME_RANDOM_ID")
  55. self.assertTrue(
  56. os.path.isdir(
  57. os.path.join(self.root_dir, "out", "a", ".reproxy_tmp")))
  58. self.assertTrue(
  59. os.path.isdir(
  60. os.path.join(
  61. self.root_dir, ".reproxy_cache",
  62. hashlib.md5(
  63. os.path.join(self.root_dir, "out", "a",
  64. ".reproxy_tmp").encode()).hexdigest())))
  65. self.assertTrue(os.path.isdir(run_log_dir))
  66. self.assertEqual(os.environ.get('RBE_output_dir'), run_log_dir)
  67. self.assertEqual(os.environ.get('RBE_proxy_log_dir'), run_log_dir)
  68. self.assertEqual(
  69. os.environ.get('RBE_cache_dir'),
  70. os.path.join(
  71. self.root_dir, ".reproxy_cache",
  72. hashlib.md5(
  73. os.path.join(self.root_dir, "out", "a",
  74. ".reproxy_tmp").encode()).hexdigest()))
  75. if sys.platform.startswith('win'):
  76. self.assertEqual(
  77. os.environ.get('RBE_server_address'),
  78. "pipe://%s/reproxy.pipe" % hashlib.sha256(
  79. os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  80. "logs", "20170316T200041.000000_SOME_RANDOM_ID"
  81. ).encode()).hexdigest())
  82. else:
  83. self.assertEqual(
  84. os.environ.get('RBE_server_address'),
  85. "unix:///tmp/reproxy_%s.sock" % hashlib.sha256(
  86. os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  87. "logs", "20170316T200041.000000_SOME_RANDOM_ID"
  88. ).encode()).hexdigest())
  89. @unittest.mock.patch('subprocess.call', return_value=0)
  90. @unittest.mock.patch('ninja.main', return_value=0)
  91. def test_ninja_reclient_calls_reclient_binaries(self, mock_ninja,
  92. mock_call):
  93. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  94. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  95. 'reproxy.cfg')
  96. write('.gclient', '')
  97. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  98. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  99. write(reclient_cfg, '0.0')
  100. argv = ["ninja", "-C", "out/a", "chrome"]
  101. self.assertEqual(0, reclient_helper.run_ninja(argv))
  102. mock_ninja.assert_called_once_with(argv)
  103. mock_call.assert_has_calls([
  104. unittest.mock.call([
  105. os.path.join(self.root_dir, reclient_bin_dir,
  106. 'bootstrap' + gclient_paths.GetExeSuffix()),
  107. "--re_proxy=" +
  108. os.path.join(self.root_dir, reclient_bin_dir,
  109. 'reproxy' + gclient_paths.GetExeSuffix()),
  110. "--cfg=" + os.path.join(self.root_dir, reclient_cfg)
  111. ]),
  112. unittest.mock.call([
  113. os.path.join(self.root_dir, reclient_bin_dir,
  114. 'bootstrap' + gclient_paths.GetExeSuffix()),
  115. "--shutdown",
  116. "--cfg=" + os.path.join(self.root_dir, reclient_cfg)
  117. ]),
  118. ])
  119. @unittest.mock.patch.dict(os.environ,
  120. {'AUTONINJA_BUILD_ID': "SOME_RANDOM_ID"})
  121. @unittest.mock.patch('reclient_helper.get_hostname',
  122. return_value='somehost')
  123. @unittest.mock.patch('subprocess.call', return_value=0)
  124. @unittest.mock.patch('ninja.main', return_value=0)
  125. def test_ninja_reclient_collect_metrics_cache_missing(self, *_):
  126. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  127. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  128. 'reproxy.cfg')
  129. write('.gclient', '')
  130. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  131. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  132. write(reclient_cfg, '0.0')
  133. argv = ["ninja", "-C", "out/a", "chrome"]
  134. self.assertEqual(
  135. 0, reclient_helper.run_ninja(argv, should_collect_logs=True))
  136. self.assertIn("SOME_RANDOM_ID", os.environ["RBE_invocation_id"])
  137. self.assertEqual(os.environ.get('RBE_metrics_project'),
  138. "chromium-reclient-metrics")
  139. self.assertEqual(os.environ.get('RBE_metrics_table'),
  140. "rbe_metrics.builds")
  141. self.assertEqual(
  142. os.environ.get('RBE_metrics_labels'),
  143. "source=developer,tool=ninja_reclient,"
  144. "creds_cache_status=missing,creds_cache_mechanism=UNSPECIFIED,"
  145. "host=somehost")
  146. self.assertEqual(os.environ.get('RBE_metrics_prefix'),
  147. "go.chromium.org")
  148. @unittest.mock.patch.dict(os.environ,
  149. {'AUTONINJA_BUILD_ID': "SOME_RANDOM_ID"},
  150. clear=True)
  151. @unittest.mock.patch('reclient_helper.get_hostname',
  152. return_value='somehost')
  153. @unittest.mock.patch('reclient_helper.datetime_now',
  154. return_value=datetime.datetime(2017, 3, 16, 20, 0, 41,
  155. 0))
  156. @unittest.mock.patch('subprocess.call', return_value=0)
  157. @unittest.mock.patch('ninja.main', return_value=0)
  158. def test_ninja_reclient_collect_metrics_cache_valid(self, *_):
  159. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  160. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  161. 'reproxy.cfg')
  162. cache_dir = os.path.join(
  163. self.root_dir, ".reproxy_cache",
  164. hashlib.md5(
  165. os.path.join(self.root_dir, "out", "a",
  166. ".reproxy_tmp").encode()).hexdigest())
  167. write('.gclient', '')
  168. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  169. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  170. write(reclient_cfg, '0.0')
  171. write(
  172. os.path.join(cache_dir, "reproxy.creds"), """
  173. mechanism: GCLOUD
  174. expiry: {
  175. seconds: %d
  176. }
  177. """ % (int(time.time()) + 10 * 60))
  178. argv = ["ninja", "-C", "out/a", "chrome"]
  179. self.assertEqual(
  180. 0, reclient_helper.run_ninja(argv, should_collect_logs=True))
  181. self.assertIn("SOME_RANDOM_ID", os.environ["RBE_invocation_id"])
  182. self.assertEqual(os.environ.get('RBE_metrics_project'),
  183. "chromium-reclient-metrics")
  184. self.assertEqual(os.environ.get('RBE_metrics_table'),
  185. "rbe_metrics.builds")
  186. self.assertEqual(
  187. os.environ.get('RBE_metrics_labels'),
  188. "source=developer,tool=ninja_reclient,"
  189. "creds_cache_status=valid,creds_cache_mechanism=GCLOUD,"
  190. "host=somehost")
  191. self.assertEqual(os.environ.get('RBE_metrics_prefix'),
  192. "go.chromium.org")
  193. @unittest.mock.patch.dict(os.environ,
  194. {'AUTONINJA_BUILD_ID': "SOME_RANDOM_ID"},
  195. clear=True)
  196. @unittest.mock.patch('reclient_helper.get_hostname',
  197. return_value='somehost')
  198. @unittest.mock.patch('subprocess.call', return_value=0)
  199. @unittest.mock.patch('ninja.main', return_value=0)
  200. def test_ninja_reclient_collect_metrics_cache_expired(self, *_):
  201. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  202. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  203. 'reproxy.cfg')
  204. cache_dir = os.path.join(
  205. self.root_dir, ".reproxy_cache",
  206. hashlib.md5(
  207. os.path.join(self.root_dir, "out", "a",
  208. ".reproxy_tmp").encode()).hexdigest())
  209. write('.gclient', '')
  210. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  211. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  212. write(reclient_cfg, '0.0')
  213. write(
  214. os.path.join(cache_dir, "reproxy.creds"), """
  215. mechanism: GCLOUD
  216. expiry: {
  217. seconds: %d
  218. }
  219. """ % (int(time.time())))
  220. argv = ["ninja", "-C", "out/a", "chrome"]
  221. self.assertEqual(
  222. 0, reclient_helper.run_ninja(argv, should_collect_logs=True))
  223. self.assertIn("SOME_RANDOM_ID", os.environ["RBE_invocation_id"])
  224. self.assertEqual(os.environ.get('RBE_metrics_project'),
  225. "chromium-reclient-metrics")
  226. self.assertEqual(os.environ.get('RBE_metrics_table'),
  227. "rbe_metrics.builds")
  228. self.assertEqual(
  229. os.environ.get('RBE_metrics_labels'),
  230. "source=developer,tool=ninja_reclient,"
  231. "creds_cache_status=expired,creds_cache_mechanism=GCLOUD,"
  232. "host=somehost")
  233. self.assertEqual(os.environ.get('RBE_metrics_prefix'),
  234. "go.chromium.org")
  235. @unittest.mock.patch.dict(os.environ, {})
  236. @unittest.mock.patch('subprocess.call', return_value=0)
  237. @unittest.mock.patch('ninja.main', return_value=0)
  238. def test_ninja_reclient_do_not_collect_metrics(self, *_):
  239. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  240. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  241. 'reproxy.cfg')
  242. write('.gclient', '')
  243. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  244. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  245. write(reclient_cfg, '0.0')
  246. argv = ["ninja", "-C", "out/a", "chrome"]
  247. self.assertEqual(0, reclient_helper.run_ninja(argv))
  248. self.assertEqual(os.environ.get('RBE_metrics_project'), None)
  249. self.assertEqual(os.environ.get('RBE_metrics_table'), None)
  250. self.assertEqual(os.environ.get('RBE_metrics_labels'), None)
  251. self.assertEqual(os.environ.get('RBE_metrics_prefix'), None)
  252. @unittest.mock.patch('subprocess.call', return_value=0)
  253. @unittest.mock.patch('ninja.main', return_value=0)
  254. @unittest.mock.patch('reclient_helper.datetime_now')
  255. def test_ninja_reclient_clears_log_dir(self, mock_now, *_):
  256. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  257. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  258. 'reproxy.cfg')
  259. write('.gclient', '')
  260. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  261. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  262. write(reclient_cfg, '0.0')
  263. argv = ["ninja", "-C", "out/a", "chrome"]
  264. for i in range(7):
  265. run_time = datetime.datetime(2017, 3, 16, 20, 0, 40 + i, 0)
  266. mock_now.return_value = run_time
  267. with unittest.mock.patch.dict(
  268. os.environ,
  269. {"AUTONINJA_BUILD_ID": "SOME_RANDOM_ID_%d" % i}):
  270. self.assertEqual(0, reclient_helper.run_ninja(argv))
  271. run_log_dir = os.path.join(
  272. self.root_dir, "out", "a", ".reproxy_tmp", "logs",
  273. "20170316T2000%d.000000_SOME_RANDOM_ID_%d" % (40 + i, i))
  274. self.assertTrue(os.path.isdir(run_log_dir))
  275. with open(os.path.join(run_log_dir, "reproxy.rpl"), "w") as f:
  276. print("Content", file=f)
  277. log_dir = os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  278. "logs")
  279. self.assertTrue(
  280. os.path.isdir(
  281. os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  282. "logs")))
  283. self.assertTrue(os.path.isdir(log_dir))
  284. want_remaining_dirs = [
  285. '20170316T200043.000000_SOME_RANDOM_ID_3',
  286. '20170316T200046.000000_SOME_RANDOM_ID_6',
  287. '20170316T200044.000000_SOME_RANDOM_ID_4',
  288. '20170316T200042.000000_SOME_RANDOM_ID_2',
  289. '20170316T200045.000000_SOME_RANDOM_ID_5',
  290. ]
  291. existing_log_dirs = [
  292. d for d in os.listdir(log_dir)
  293. if os.path.isdir(os.path.join(log_dir, d))
  294. ]
  295. self.assertCountEqual(existing_log_dirs, want_remaining_dirs)
  296. for d in want_remaining_dirs:
  297. self.assertTrue(
  298. os.path.isfile(
  299. os.path.join(self.root_dir, "out", "a", ".reproxy_tmp",
  300. "logs", d, "reproxy.rpl")))
  301. @unittest.mock.patch('subprocess.call', return_value=0)
  302. @unittest.mock.patch('ninja.main', side_effect=KeyboardInterrupt())
  303. def test_ninja_reclient_ninja_interrupted(self, mock_ninja, mock_call):
  304. reclient_bin_dir = os.path.join('src', 'buildtools', 'reclient')
  305. reclient_cfg = os.path.join('src', 'buildtools', 'reclient_cfgs',
  306. 'reproxy.cfg')
  307. write('.gclient', '')
  308. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  309. write(os.path.join(reclient_bin_dir, 'version.txt'), '0.0')
  310. write(reclient_cfg, '0.0')
  311. argv = ["ninja", "-C", "out/a", "chrome"]
  312. self.assertEqual(1, reclient_helper.run_ninja(argv))
  313. mock_ninja.assert_called_once_with(argv)
  314. mock_call.assert_has_calls([
  315. unittest.mock.call([
  316. os.path.join(self.root_dir, reclient_bin_dir,
  317. 'bootstrap' + gclient_paths.GetExeSuffix()),
  318. "--re_proxy=" +
  319. os.path.join(self.root_dir, reclient_bin_dir,
  320. 'reproxy' + gclient_paths.GetExeSuffix()),
  321. "--cfg=" + os.path.join(self.root_dir, reclient_cfg)
  322. ]),
  323. unittest.mock.call([
  324. os.path.join(self.root_dir, reclient_bin_dir,
  325. 'bootstrap' + gclient_paths.GetExeSuffix()),
  326. "--shutdown",
  327. "--cfg=" + os.path.join(self.root_dir, reclient_cfg)
  328. ]),
  329. ])
  330. @unittest.mock.patch('subprocess.call', return_value=0)
  331. @unittest.mock.patch('ninja.main', return_value=0)
  332. def test_ninja_reclient_cfg_not_found(self, mock_ninja, mock_call):
  333. write('.gclient', '')
  334. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  335. write(os.path.join('src', 'buildtools', 'reclient', 'version.txt'),
  336. '0.0')
  337. argv = ["ninja", "-C", "out/a", "chrome"]
  338. self.assertEqual(1, reclient_helper.run_ninja(argv))
  339. mock_ninja.assert_not_called()
  340. mock_call.assert_not_called()
  341. @unittest.mock.patch('subprocess.call', return_value=0)
  342. @unittest.mock.patch('ninja.main', return_value=0)
  343. def test_ninja_reclient_bins_not_found(self, mock_ninja, mock_call):
  344. write('.gclient', '')
  345. write('.gclient_entries', 'entries = {"buildtools": "..."}')
  346. write(os.path.join('src', 'buildtools', 'reclient_cfgs', 'reproxy.cfg'),
  347. '0.0')
  348. argv = ["ninja", "-C", "out/a", "chrome"]
  349. self.assertEqual(1, reclient_helper.run_ninja(argv))
  350. mock_ninja.assert_not_called()
  351. mock_call.assert_not_called()
  352. if __name__ == '__main__':
  353. unittest.main()