# Released under the MIT License. See LICENSE for details.
#
"""Tests for spinoff."""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
pass
[docs]
def spinoff_test(args: list[str]) -> None:
"""High level test run command; accepts args and raises CleanErrors."""
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
import os
import subprocess
from efro.util import extract_flag
from efro.terminal import Clr
from efro.error import CleanError
from batools.featureset import FeatureSet
from efrotools.project import getprojectconfig
submodule_parent = extract_flag(args, '--submodule-parent')
shared_test_parent = extract_flag(args, '--shared-test-parent')
if submodule_parent and shared_test_parent:
raise CleanError(
"spinoff-test: can't pass both submodule parent"
' and shared test parent.'
)
public = getprojectconfig(Path('.'))['public']
if shared_test_parent and public:
raise CleanError('--shared-test-parent not available in public repo.')
# A spinoff symlink means we're a spun-off project.
if os.path.islink('tools/spinoff'):
raise CleanError(
'This must be run in a src project; this appears to be a dst.'
)
if len(args) != 1:
raise CleanError('Expected 1 arg.')
featuresets = {fs.name: fs for fs in FeatureSet.get_all_for_project('.')}
testtype = args[0]
assert testtype
if testtype in featuresets:
path = f'build/spinofftest/{testtype}'
print(
f'{Clr.BLD}Running spinoff test{Clr.RST}'
f" {Clr.SBLU}{Clr.BLD}'{testtype}'{Clr.RST}"
f' {Clr.BLD}in{Clr.RST}'
f" {Clr.BLU}'{path}'{Clr.RST}"
f'{Clr.BLD}...{Clr.RST}',
flush=True,
)
# Normally we spin the project off from where we currently
# are, but for cloud builds we may want to use a dedicated
# shared source instead. (since we need a git managed source
# we need to pull *something* fresh from git instead of just
# using the files that were synced up by cloudshell).
# Here we make sure that shared source is up to date.
spinoff_src = '.'
spinoff_path = path
if shared_test_parent:
spinoff_src = 'build/spinoff_shared_test_parent'
# Need an abs target path since we change cwd in this case.
spinoff_path = os.path.abspath(path)
if bool(False):
print('TEMP BLOWING AWAY')
subprocess.run(['rm', '-rf', spinoff_src], check=True)
if os.path.exists(spinoff_src):
print(
'Pulling latest spinoff_shared_test_parent...',
flush=True,
)
subprocess.run(
['git', 'pull', '--ff-only'],
check=True,
cwd=spinoff_src,
)
else:
os.makedirs(spinoff_src, exist_ok=True)
cmd = [
'git',
'clone',
'git@github.com:efroemling/ballistica-internal.git',
spinoff_src,
]
print(
f'{Clr.BLU}Creating spinoff shared test parent'
f" at '{spinoff_src}' with command {cmd}...{Clr.RST}"
)
subprocess.run(
cmd,
check=True,
)
# If the spinoff project already exists and is submodule-based,
# bring the submodule up to date.
if os.path.exists(path):
if bool(False):
subprocess.run(['rm', '-rf', path], check=True)
submpath = os.path.join(path, 'submodules/ballistica')
if os.path.exists(submpath):
print(
f'{Clr.BLU}Pulling latest parent submodule'
f' for existing test setup...{Clr.RST}',
flush=True,
)
subprocess.run(
f'cd "{submpath}" && git checkout main && git pull',
shell=True,
check=True,
)
else:
# No spinoff project there yet; create it.
cmd = [
'./tools/spinoff',
'create',
'SpinoffTest',
spinoff_path,
'--featuresets',
testtype,
] + (['--submodule-parent'] if submodule_parent else [])
# Show the spinoff command we'd use here.
print(Clr.MAG + ' '.join(cmd) + Clr.RST, flush=True)
# Avoid the 'what to do next' help.
subprocess.run(
cmd + ['--noninteractive'],
cwd=spinoff_src,
check=True,
)
print(f'{Clr.MAG}tools/spinoff update{Clr.RST}', flush=True)
subprocess.run(['tools/spinoff', 'update'], cwd=path, check=True)
# Now let's simply run the mypy target. This will compile a
# binary, use that binary to generate dummy Python modules, and
# then check the assembled set of Python scripts. If all that
# goes through it tells us that this spinoff project is at least
# basically functional.
subprocess.run(
['make', 'mypy'],
cwd=path,
env=dict(
os.environ,
BA_APP_RUN_ENABLE_BUILDS='1',
BA_APP_RUN_BUILD_HEADLESS='1',
),
check=True,
)
# Run the binary with a --help arg and make sure it spits
# out what we expect it to.
# DISABLING: the dummy-module generation part of the mypy target
# covers this.
if bool(False):
help_output = subprocess.run(
[
'build/cmake/server-debug/staged/dist/'
'spinofftest_headless',
'--help',
],
cwd=path,
check=True,
capture_output=True,
).stdout.decode()
if '-h, --help ' not in help_output:
raise RuntimeError(
'Unexpected output when running test command.'
)
else:
raise CleanError(f"Invalid test type '{testtype}'.")
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum