decorate-silo-mode-tests 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. import os
  3. import os.path
  4. import pathlib
  5. import shutil
  6. import tempfile
  7. from typing import Iterator
  8. import click
  9. from sentry.runner import configure
  10. configure()
  11. _manifest_files_env = {
  12. "SENTRY_MODEL_MANIFEST_FILE_PATH": pathlib.Path(__file__).absolute().parent.parent,
  13. "GETSENTRY_MODEL_MANIFEST_FILE_PATH": pathlib.Path(__file__)
  14. .absolute()
  15. .parent.parent.parent.joinpath("getsentry"),
  16. }
  17. from sentry.testutils.modelmanifest import HybridCloudTestVisitor, ModelManifest
  18. def find_test_cases_matching(model_name: str) -> Iterator[HybridCloudTestVisitor]:
  19. for env_name, path_root in _manifest_files_env.items():
  20. manifest = ModelManifest.open(os.environ[env_name])
  21. model_test_ids = manifest.connections[
  22. manifest.get_or_create_id(manifest.model_names, model_name)
  23. ]
  24. for test_id, test_visitor in manifest.each_hybrid_cloud_test(path_root):
  25. if test_id not in model_test_ids:
  26. continue
  27. test_visitor.load()
  28. yield test_visitor
  29. def pick(prompt, options):
  30. choice = ""
  31. while choice not in options:
  32. choice = input(prompt + " (" + ", ".join(options) + ") ") # noqa
  33. return choice
  34. @click.command()
  35. @click.argument("target_model", required=True)
  36. def main(target_model: str):
  37. """
  38. Script to decorate the given target test for silo tests, making it easier to deploy changes to given tests.
  39. """
  40. for test_visitor in find_test_cases_matching(target_model):
  41. print(f"Trying {test_visitor.test_name} in {test_visitor.test_file_path}") # noqa
  42. if not test_visitor.decorator_was_stable:
  43. print(f"Found {test_visitor.test_name}") # noqa
  44. try:
  45. if pick("Skip this test?", ["y", "n"]) == "y":
  46. continue
  47. except EOFError:
  48. continue
  49. try:
  50. silo_mode = pick("silo mode?", ["all", "no", "control", "region"])
  51. set_stable = pick("set stable?", ["y", "n"]) == "y"
  52. except EOFError:
  53. continue
  54. _rewrite(test_visitor, f"{silo_mode}_silo_test", set_stable)
  55. def _decorate(target_test_silo_mode, set_stable, lineno, match_line):
  56. if not match_line:
  57. return False
  58. if not match_line[0] == lineno:
  59. return False
  60. ws = b" " * match_line[1]
  61. if set_stable:
  62. return ws + f"@{target_test_silo_mode}(stable=True)\n".encode()
  63. else:
  64. return ws + f"@{target_test_silo_mode}\n".encode()
  65. def _rewrite(test_visitor: HybridCloudTestVisitor, target_test_silo_mode, set_stable):
  66. path = test_visitor.test_file_path
  67. import_line = f"from sentry.testutils.silo import {target_test_silo_mode}\n".encode()
  68. if not test_visitor.decorator_match_line and not test_visitor.func_match_line:
  69. raise Exception(f"Could not find test case {test_visitor.target_symbol_parts}!")
  70. with tempfile.NamedTemporaryFile(delete=False) as tf:
  71. with open(path) as f:
  72. if not test_visitor.import_match_line:
  73. tf.write(import_line)
  74. for i, line in enumerate(f.readlines()):
  75. i += 1
  76. if test_visitor.import_match_line and test_visitor.import_match_line[0] == i:
  77. tf.write(import_line)
  78. continue
  79. if newline := _decorate(
  80. target_test_silo_mode, set_stable, i, test_visitor.decorator_match_line
  81. ):
  82. # If the decorator type is not changing, keep the original line.
  83. # If the decorator that existed was stable, don't replace it, keep it.
  84. if test_visitor.decorator_was_stable:
  85. tf.write(line.encode("utf8"))
  86. else:
  87. tf.write(newline)
  88. continue
  89. if not test_visitor.decorator_match_line and (
  90. newline := _decorate(
  91. target_test_silo_mode, set_stable, i, test_visitor.func_match_line
  92. )
  93. ):
  94. tf.write(newline)
  95. tf.write(line.encode("utf8"))
  96. tf.close()
  97. shutil.move(tf.name, path)
  98. if __name__ == "__main__":
  99. main()