run
  1  #!/usr/bin/env python3
  2  
  3  """
  4  Build arti and rpc client library, and run RPC integration tests.
  5  
  6  ENVIRONMENT VARIABLES:
  7  
  8  - CARGO: the cargo binary to use.  Defaults to "cargo".
  9  - PYTHON3: path to a python binary to use.  Defaults to `sys.executable`
 10  - ARTI_RPC_TEST_DIR: A location in which to store arti's state and config.
 11    Defaults to ".arti_rpc_test" at the top level of this git repo.
 12  
 13  ARGUMENTS:
 14  
 15  - `--arti-dir=DIR`: Override ARTI_RPC_TEST_DIR.
 16  """
 17  
 18  from __future__ import annotations
 19  
 20  import argparse
 21  import os
 22  import sys
 23  import subprocess
 24  
 25  
 26  def parent(path: str, n: int = 1):
 27      """
 28      Return the parent of (the parent of (... the directory of path)).
 29  
 30      The number of "parent of"s is controlled by "n"
 31      """
 32      for _ in range(n):
 33          path = os.path.split(path)[0]
 34      return path
 35  
 36  
 37  def cargo_build(packages: list[str], extra_flags: list[str] = []):
 38      """
 39      Use cargo to build all of the rust packages in `packages`,
 40      passing `extra_flags` on the command line.
 41      """
 42      args = [
 43          CARGO,
 44          "build",
 45          "--profile",
 46          CARGO_PROFILE,
 47      ]
 48      for p in packages:
 49          args.extend(["-p", p])
 50      args.extend(extra_flags)
 51  
 52      outcome = subprocess.run(args, cwd=TOPLEVEL)
 53      outcome.check_returncode()
 54  
 55  
 56  ######
 57  # Parse the command line.
 58  
 59  parser = argparse.ArgumentParser(
 60      prog="run", description="Invoke Arti RPC integration tests"
 61  )
 62  parser.add_argument("--arti-dir", help="Location for Arti proxy storage and config")
 63  parser.add_argument("remainder", nargs="*", help="Passed directly to arti_rpc_tests")
 64  args = parser.parse_args()
 65  
 66  ######
 67  # Set up locations and paths from the environment.
 68  
 69  CARGO_PROFILE = "quicktest"
 70  TOPLEVEL = os.path.abspath(parent(os.path.dirname(__file__), n=3))
 71  CARGO = os.environ.get("CARGO", "cargo")
 72  PYTHON3 = os.environ.get("PYTHON3", sys.executable)
 73  
 74  lib_extension = {"win32": "dll", "darwin": "dylib"}.get(sys.platform, "so")
 75  
 76  ARTI = os.path.join(TOPLEVEL, "target", CARGO_PROFILE, "arti")
 77  LIBRPC = os.path.join(
 78      TOPLEVEL, "target", CARGO_PROFILE, "libarti_rpc_client_core." + lib_extension
 79  )
 80  PYRPC = os.path.join(TOPLEVEL, "python", "arti_rpc", "src")
 81  PYRPC_TESTS = os.path.join(TOPLEVEL, "python", "arti_rpc_tests", "src")
 82  
 83  if args.arti_dir is not None:
 84      ARTI_RPC_TEST_DIR = args.arti_dir
 85  else:
 86      ARTI_RPC_TEST_DIR = os.environ.get(
 87          "ARTI_RPC_TEST_DIR", os.path.join(TOPLEVEL, ".arti_rpc_test")
 88      )
 89  os.makedirs(ARTI_RPC_TEST_DIR, mode=0o700, exist_ok=True)
 90  
 91  #####
 92  # Build Arti, and make sure it is there.
 93  
 94  cargo_build(["arti", "arti-rpc-client-core"], ["--all-features"])
 95  
 96  if not os.path.exists(ARTI):
 97      print("whoops no arti at", ARTI)
 98      sys.exit(1)
 99  if not os.path.exists(ARTI):
100      print("whoops no librpc at", LIBRPC)
101      sys.exit(1)
102  
103  #####
104  # Set up the environment expected by `arti_rpc_tests`.
105  
106  os.environ["ARTI"] = ARTI
107  os.environ["LIBARTI_RPC_CLIENT_CORE"] = LIBRPC
108  os.environ["ARTI_RPC_TEST_DIR"] = ARTI_RPC_TEST_DIR
109  
110  # Note that we're prepending these locations to PYTHONPATH.
111  # We want to use these versions, not ones that might happen to be installed.
112  pathelts = [PYRPC, PYRPC_TESTS]
113  try:
114      pathelts.append(os.environ["PYTHONPATH"])
115  except KeyError:
116      pass
117  os.environ["PYTHONPATH"] = os.pathsep.join(pathelts)
118  
119  # Run `arti_rpc_tests` and wait for it to finish.
120  outcome = subprocess.run([PYTHON3, "-m", "arti_rpc_tests"] + args.remainder)
121  # Give an error if it failed.
122  outcome.check_returncode()