run_sql_parser_test.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from pathlib import Path
  2. import asyncio.subprocess as subprocess
  3. import asyncio
  4. from watchfiles import awatch
  5. from termcolor import colored
  6. from datetime import datetime
  7. import orjson
  8. import os
  9. import tempfile
  10. import importlib
  11. async def run_and_output(*args: str, timeout=10) -> tuple[bytes, bytes]:
  12. p = await subprocess.create_subprocess_exec(
  13. *args,
  14. stdout=subprocess.PIPE,
  15. stderr=subprocess.PIPE,
  16. )
  17. stdout, stderr = await asyncio.wait_for(p.communicate(), timeout=timeout)
  18. return stdout, stderr
  19. async def rebuild() -> bool:
  20. print(datetime.now(), colored("rebuild...", "grey"))
  21. stdout, _ = await run_and_output("xmake")
  22. if b"error" in stdout:
  23. print(stdout.decode("utf-8"))
  24. print(datetime.now(), "-" * 40)
  25. return False
  26. else:
  27. return True
  28. async def assert_sql(sql: str, expected: dict):
  29. stdout, stderr = await run_and_output("xmake", "run", "sql-parser", sql)
  30. if b"error" in stdout:
  31. print(stdout.decode("utf-8"))
  32. print(datetime.now(), "-" * 40)
  33. print(f'other: {colored(stderr.decode("utf-8"), "yellow")}')
  34. assert False, "sql-parser error"
  35. try:
  36. output = orjson.loads(stdout)
  37. except Exception as e:
  38. output = {"error": e, "output": stdout.decode("utf-8")}
  39. open("/tmp/temp/test.py", "wb").write(
  40. f'"{sql}"\n\n'.encode("utf-8")
  41. + orjson.dumps(output, option=orjson.OPT_INDENT_2)
  42. + (b"\n\n" + stderr).replace(b"\n", b"\n# ")
  43. )
  44. assert (
  45. output == expected
  46. ), f"""{colored("sql-parser error", "red")}
  47. input: {colored(sql, "yellow")}
  48. expect: {colored(expected, "green")}
  49. actual: {colored(output, "red")}
  50. other: {colored(stderr.decode("utf-8"), "yellow")}
  51. """
  52. async def assert_sqls():
  53. import test_configs.sql_parser
  54. importlib.reload(test_configs.sql_parser)
  55. for sql, excepted in test_configs.sql_parser.sql_parser_tests:
  56. await assert_sql(sql, excepted)
  57. async def on_parser_modified():
  58. print(datetime.now(), colored("run parser tests...", "yellow"))
  59. try:
  60. await assert_sqls()
  61. except Exception as e:
  62. print(e)
  63. else:
  64. print(datetime.now(), colored("all parser tests right!", "green"))
  65. async def assert_checks():
  66. import test_configs.sql_checker
  67. importlib.reload(test_configs.sql_checker)
  68. for sql, res in test_configs.sql_checker.sql_checker_tests:
  69. stdout, stderr = await run_and_output("xmake", "run", "sql-checker", "-s", sql)
  70. print(sql, res)
  71. if res is True:
  72. assert b"error" not in stdout, (
  73. stdout.decode("utf-8") + "\n" + stderr.decode("utf-8")
  74. )
  75. assert b"error" not in stderr, (
  76. stdout.decode("utf-8") + "\n" + stderr.decode("utf-8")
  77. )
  78. elif isinstance(res, str):
  79. res = res.encode("utf-8")
  80. assert res in stderr, stderr.decode("utf-8")
  81. else:
  82. assert False, f"{res} 不是合适的结果"
  83. async def on_checker_modified():
  84. print(datetime.now(), colored("run checker tests...", "yellow"))
  85. try:
  86. await assert_checks()
  87. except Exception as e:
  88. print(colored(e, "red"))
  89. else:
  90. print(datetime.now(), colored("all checker tests right!", "green"))
  91. async def assert_sql_optimizer_check():
  92. import test_configs.sql_optimizer
  93. importlib.reload(test_configs.sql_optimizer)
  94. for sql, res in test_configs.sql_optimizer.sql_optimizer_tests:
  95. stdout, stderr = await run_and_output(
  96. "xmake", "run", "sql-optimizer", "-s", sql
  97. )
  98. print(sql)
  99. try:
  100. output = orjson.loads(stdout)
  101. if output != res:
  102. print('', colored(res, "yellow"))
  103. print('real', colored(output, "red"))
  104. print(colored(stdout.decode("utf-8"), "yellow"))
  105. print(colored(stderr.decode("utf-8"), "yellow"))
  106. assert False
  107. except Exception as e:
  108. print(e)
  109. break
  110. async def on_optimizer_modified():
  111. print(datetime.now(), colored("run optimizer tests...", "yellow"))
  112. try:
  113. await assert_sql_optimizer_check()
  114. except Exception as e:
  115. print(colored(e, "red"))
  116. else:
  117. print(datetime.now(), colored("all optimizer tests right!", "green"))
  118. async def restart():
  119. async for _ in awatch(__file__):
  120. print("restart")
  121. os.execl("/bin/python", Path(__file__).as_posix(), Path(__file__).as_posix())
  122. async def watch_parser():
  123. async for changes in awatch(
  124. "./src/parser.y", "./src/parser.l", "test_configs/sql_parser.py"
  125. ):
  126. if await rebuild():
  127. await asyncio.wait_for(on_parser_modified(), 10)
  128. async def watch_checker():
  129. async for changes in awatch(
  130. "./src/checker.cpp",
  131. "./src/checker.h",
  132. "./src/utils.h",
  133. "./src/utils.cpp",
  134. "test_configs/sql_checker.py",
  135. ):
  136. if await rebuild():
  137. await on_checker_modified()
  138. async def watch_optimizer():
  139. async for changes in awatch(
  140. "src/optimizer.cpp",
  141. "./src/utils.h",
  142. "./src/utils.cpp",
  143. "test_configs/sql_optimizer.py",
  144. ):
  145. if await rebuild():
  146. await on_optimizer_modified()
  147. async def rerun_tests():
  148. await asyncio.gather(
  149. on_parser_modified(),
  150. on_checker_modified(),
  151. )
  152. await on_optimizer_modified()
  153. async def main():
  154. await asyncio.gather(
  155. restart(),
  156. watch_parser(),
  157. watch_checker(),
  158. watch_optimizer(),
  159. rerun_tests(),
  160. )
  161. if __name__ == "__main__":
  162. asyncio.run(main())