test_tester.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. import os
  2. import re
  3. import sys
  4. from tempfile import TemporaryDirectory
  5. from unittest import mock
  6. import pytest
  7. from ci.ray_ci.linux_tester_container import LinuxTesterContainer
  8. from ci.ray_ci.windows_tester_container import WindowsTesterContainer
  9. from ci.ray_ci.tester import (
  10. _add_default_except_tags,
  11. _get_container,
  12. _get_all_test_query,
  13. _get_test_targets,
  14. _get_new_tests,
  15. _get_flaky_test_targets,
  16. _get_tag_matcher,
  17. )
  18. from ray_release.test import Test, TestState
  19. def _stub_test(val: dict) -> Test:
  20. test = Test(
  21. {
  22. "name": "test",
  23. "cluster": {},
  24. }
  25. )
  26. test.update(val)
  27. return test
  28. def test_get_tag_matcher() -> None:
  29. assert re.match(
  30. # simulate shell character escaping
  31. bytes(_get_tag_matcher("tag"), "utf-8").decode("unicode_escape"),
  32. "tag",
  33. )
  34. assert not re.match(
  35. # simulate shell character escaping
  36. bytes(_get_tag_matcher("tag"), "utf-8").decode("unicode_escape"),
  37. "atagb",
  38. )
  39. def test_get_container() -> None:
  40. with mock.patch(
  41. "ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
  42. return_value=None,
  43. ), mock.patch(
  44. "ci.ray_ci.windows_tester_container.WindowsTesterContainer.install_ray",
  45. return_value=None,
  46. ):
  47. container = _get_container(
  48. team="core",
  49. operating_system="linux",
  50. workers=3,
  51. worker_id=1,
  52. parallelism_per_worker=2,
  53. network=None,
  54. gpus=0,
  55. tmp_filesystem=None,
  56. )
  57. assert isinstance(container, LinuxTesterContainer)
  58. assert container.docker_tag == "corebuild"
  59. assert container.shard_count == 6
  60. assert container.shard_ids == [2, 3]
  61. container = _get_container(
  62. team="serve",
  63. operating_system="windows",
  64. workers=3,
  65. worker_id=1,
  66. parallelism_per_worker=2,
  67. network=None,
  68. gpus=0,
  69. )
  70. assert isinstance(container, WindowsTesterContainer)
  71. def test_get_empty_test_targets() -> None:
  72. with mock.patch(
  73. "subprocess.check_output",
  74. return_value="\n".encode("utf-8"),
  75. ), mock.patch(
  76. "ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
  77. return_value=None,
  78. ), mock.patch(
  79. "ray_release.test.Test.gen_from_s3",
  80. return_value=set(),
  81. ), mock.patch(
  82. "ci.ray_ci.tester._get_new_tests",
  83. return_value=set(),
  84. ), mock.patch(
  85. "ray_release.test.Test.gen_microcheck_tests",
  86. return_value=set(),
  87. ):
  88. # Test that the set of test target is empty, rather than a set of empty string
  89. assert (
  90. set(
  91. _get_test_targets(
  92. LinuxTesterContainer("core"),
  93. "targets",
  94. "core",
  95. operating_system="linux",
  96. )
  97. )
  98. == set()
  99. )
  100. def test_get_test_targets() -> None:
  101. _TEST_YAML = "flaky_tests: [//python/ray/tests:flaky_test_01]"
  102. with TemporaryDirectory() as tmp:
  103. with open(os.path.join(tmp, "core.tests.yml"), "w") as f:
  104. f.write(_TEST_YAML)
  105. test_targets = [
  106. "//python/ray/tests:high_impact_test_01",
  107. "//python/ray/tests:good_test_01",
  108. "//python/ray/tests:good_test_02",
  109. "//python/ray/tests:good_test_03",
  110. "//python/ray/tests:flaky_test_01",
  111. ]
  112. test_objects = [
  113. _stub_test(
  114. {
  115. "name": "linux://python/ray/tests:high_impact_test_01",
  116. "team": "core",
  117. "state": TestState.PASSING,
  118. Test.KEY_IS_HIGH_IMPACT: "true",
  119. }
  120. ),
  121. _stub_test(
  122. {
  123. "name": "linux://python/ray/tests:flaky_test_01",
  124. "team": "core",
  125. "state": TestState.FLAKY,
  126. Test.KEY_IS_HIGH_IMPACT: "true",
  127. }
  128. ),
  129. ]
  130. with mock.patch(
  131. "subprocess.check_output",
  132. return_value="\n".join(test_targets).encode("utf-8"),
  133. ), mock.patch(
  134. "ci.ray_ci.linux_tester_container.LinuxTesterContainer.install_ray",
  135. return_value=None,
  136. ), mock.patch(
  137. "ray_release.test.Test.gen_from_s3",
  138. return_value=test_objects,
  139. ), mock.patch(
  140. "ci.ray_ci.tester._get_new_tests",
  141. return_value=set(),
  142. ), mock.patch(
  143. "ray_release.test.Test.gen_microcheck_tests",
  144. return_value={test.get_target() for test in test_objects},
  145. ):
  146. assert set(
  147. _get_test_targets(
  148. LinuxTesterContainer("core"),
  149. "targets",
  150. "core",
  151. operating_system="linux",
  152. yaml_dir=tmp,
  153. )
  154. ) == {
  155. "//python/ray/tests:high_impact_test_01",
  156. "//python/ray/tests:good_test_01",
  157. "//python/ray/tests:good_test_02",
  158. "//python/ray/tests:good_test_03",
  159. }
  160. assert _get_test_targets(
  161. LinuxTesterContainer("core"),
  162. "targets",
  163. "core",
  164. operating_system="linux",
  165. yaml_dir=tmp,
  166. get_flaky_tests=True,
  167. ) == [
  168. "//python/ray/tests:flaky_test_01",
  169. ]
  170. assert _get_test_targets(
  171. LinuxTesterContainer("core"),
  172. "targets",
  173. "core",
  174. operating_system="linux",
  175. yaml_dir=tmp,
  176. get_flaky_tests=False,
  177. get_high_impact_tests=True,
  178. ) == [
  179. "//python/ray/tests:high_impact_test_01",
  180. ]
  181. assert _get_test_targets(
  182. LinuxTesterContainer("core"),
  183. "targets",
  184. "core",
  185. operating_system="linux",
  186. yaml_dir=tmp,
  187. get_flaky_tests=True,
  188. get_high_impact_tests=True,
  189. ) == [
  190. "//python/ray/tests:flaky_test_01",
  191. ]
  192. def test_add_default_except_tags() -> None:
  193. assert set(_add_default_except_tags("tag1,tag2").split(",")) == {
  194. "tag1",
  195. "tag2",
  196. "manual",
  197. }
  198. assert _add_default_except_tags("") == "manual"
  199. assert _add_default_except_tags("manual") == "manual"
  200. def test_get_all_test_query() -> None:
  201. assert _get_all_test_query(["a", "b"], "core") == (
  202. "attr(tags, '\\\\bteam:core\\\\b', tests(a) union tests(b))"
  203. )
  204. assert _get_all_test_query(["a"], "core", except_tags="tag") == (
  205. "attr(tags, '\\\\bteam:core\\\\b', tests(a)) "
  206. "except (attr(tags, '\\\\btag\\\\b', tests(a)))"
  207. )
  208. assert _get_all_test_query(["a"], "core", only_tags="tag") == (
  209. "attr(tags, '\\\\bteam:core\\\\b', tests(a)) "
  210. "intersect (attr(tags, '\\\\btag\\\\b', tests(a)))"
  211. )
  212. assert _get_all_test_query(["a"], "core", except_tags="tag1", only_tags="tag2") == (
  213. "attr(tags, '\\\\bteam:core\\\\b', tests(a)) "
  214. "intersect (attr(tags, '\\\\btag2\\\\b', tests(a))) "
  215. "except (attr(tags, '\\\\btag1\\\\b', tests(a)))"
  216. )
  217. @mock.patch("ci.ray_ci.tester_container.TesterContainer.run_script_with_output")
  218. @mock.patch("ray_release.test.Test.gen_from_s3")
  219. def test_get_new_tests(mock_gen_from_s3, mock_run_script_with_output) -> None:
  220. mock_gen_from_s3.return_value = [
  221. _stub_test({"name": "linux://old_test_01"}),
  222. _stub_test({"name": "linux://old_test_02"}),
  223. ]
  224. mock_run_script_with_output.return_value = "//old_test_01\n//new_test"
  225. assert _get_new_tests(
  226. "linux", LinuxTesterContainer("test", skip_ray_installation=True)
  227. ) == {"//new_test"}
  228. def test_get_flaky_test_targets() -> None:
  229. test_harness = [
  230. {
  231. "input": {
  232. "core_test_yaml": "flaky_tests: [//t1, windows://t2]",
  233. "s3": [
  234. _stub_test(
  235. {
  236. "name": "windows://t1_s3",
  237. "team": "core",
  238. "state": TestState.FLAKY,
  239. }
  240. ),
  241. _stub_test(
  242. {
  243. "name": "linux://t2_s3",
  244. "team": "ci",
  245. "state": TestState.FLAKY,
  246. }
  247. ),
  248. _stub_test(
  249. {
  250. "name": "linux://t3_s3",
  251. "team": "core",
  252. "state": TestState.FLAKY,
  253. }
  254. ),
  255. ],
  256. },
  257. "output": {
  258. "linux": ["//t1", "//t3_s3"],
  259. "windows": ["//t1_s3", "//t2"],
  260. },
  261. },
  262. {
  263. "input": {
  264. "core_test_yaml": "flaky_tests: [//t1, windows://t2]",
  265. "s3": [],
  266. },
  267. "output": {
  268. "linux": ["//t1"],
  269. "windows": ["//t2"],
  270. },
  271. },
  272. {
  273. "input": {
  274. "core_test_yaml": "flaky_tests: []",
  275. "s3": [
  276. _stub_test(
  277. {
  278. "name": "windows://t1_s3",
  279. "team": "core",
  280. "state": TestState.FLAKY,
  281. }
  282. ),
  283. _stub_test(
  284. {
  285. "name": "linux://t2_s3",
  286. "team": "ci",
  287. "state": TestState.FLAKY,
  288. }
  289. ),
  290. _stub_test(
  291. {
  292. "name": "linux://t3_s3",
  293. "team": "core",
  294. "state": TestState.FLAKY,
  295. }
  296. ),
  297. _stub_test(
  298. {
  299. "name": "linux://t4_s3",
  300. "team": "core",
  301. "state": TestState.PASSING,
  302. }
  303. ),
  304. ],
  305. },
  306. "output": {
  307. "linux": ["//t3_s3"],
  308. "windows": ["//t1_s3"],
  309. },
  310. },
  311. {
  312. "input": {
  313. "core_test_yaml": "flaky_tests: []",
  314. "s3": [],
  315. },
  316. "output": {
  317. "linux": [],
  318. "windows": [],
  319. },
  320. },
  321. ]
  322. for test in test_harness:
  323. with TemporaryDirectory() as tmp, mock.patch(
  324. "ray_release.test.Test.gen_from_s3",
  325. return_value=test["input"]["s3"],
  326. ):
  327. with open(os.path.join(tmp, "core.tests.yml"), "w") as f:
  328. f.write(test["input"]["core_test_yaml"])
  329. for os_name in ["linux", "windows"]:
  330. assert (
  331. _get_flaky_test_targets("core", os_name, yaml_dir=tmp)
  332. == test["output"][os_name]
  333. )
  334. if __name__ == "__main__":
  335. sys.exit(pytest.main(["-v", __file__]))