[tox]
minversion = 3.18.0
envlist = py3,functional,pep8

[testenv]
usedevelop = True
allowlist_externals =
  bash
  find
  rm
  env
  make
install_command = python -I -m pip install -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} {opts} {packages}
setenv =
  VIRTUAL_ENV={envdir}
  LANGUAGE=en_US
  LC_ALL=en_US.utf-8
  OS_STDOUT_CAPTURE=1
  OS_STDERR_CAPTURE=1
  OS_TEST_TIMEOUT=160
  PYTHONDONTWRITEBYTECODE=1
deps =
  -r{toxinidir}/test-requirements.txt
extras =
  zvm
  vmware
passenv =
  # LOCALE_ARCHIVE is needed when not using python from the system package
  # manager (e.g. when installed with nix) to ensure the locale-archive is
  # available to the tests. specifically this is needed by sphinx to build
  # the docs.
  LOCALE_ARCHIVE
  OS_DEBUG
  GENERATE_HASHES
# Note(sean-k-mooney):
# leaking greanthreads between tests is a bug so we should fail if it happens
# however unlike greenthreads, greenlets cannot be killed via a test fixture.
# greenlet leaks will be annotated in the test details but will not cause a
# failure. if you want to make them raise set
# NOVA_RAISE_ON_GREENLET_LEAK=1|true|yes
# All leaks of green threads have been resolved in the unit and functional
# suite
  NOVA_RAISE_ON_GREENLET_LEAK
# NOTE(sean-k-mooney) optimization is enabled by default and when enabled
# asserts are complied out. Disable optimization to allow asserts in
# nova to fire in unit and functional tests. This can be useful for
# debugging issue with fixtures and mocks.
  PYTHONOPTIMIZE
# there is also secret magic in subunit-trace which lets you run in a fail only
# mode. To do this define the TRACE_FAILONLY environmental variable.

# allow running without eventlet
  OS_NOVA_DISABLE_EVENTLET_PATCHING
commands =
  stestr run {posargs}
  env TEST_OSPROFILER=1 stestr run --combine --no-discover 'nova.tests.unit.test_profiler'
  stestr slowest

[testenv:{unit,py3,py310,py311,py312,py313}]
setenv =
  {[testenv]setenv}
# we do not have any greenlet leaks in unit tests so enforce that
# by making greenlet leaks a failure.
  NOVA_RAISE_ON_GREENLET_LEAK=True
# run the test with eventlet
  OS_NOVA_DISABLE_EVENTLET_PATCHING=False

[testenv:py312-threading]
setenv =
  {[testenv]setenv}
# we do not have any greenlet leaks in unit tests so enforce that
# by making greenlet leaks a failure.
  NOVA_RAISE_ON_GREENLET_LEAK=True
# run the test without eventlet
  OS_NOVA_DISABLE_EVENTLET_PATCHING=True

commands =
# So far we have a list of failing test cases to filter out. Also note
# that there might be unstale tests.
# Our exclude list has comments we need to remove before it can be passed
# to stestr
  bash tools/generate-exclude.sh
  stestr run {posargs} --exclude-list /tmp/exclude.txt
  stestr slowest

[testenv:functional{,-py310,-py311,-py312,-py313}]
description =
  Run functional tests.
passenv =
  {[testenv]passenv}
  GENERATE_SAMPLES
setenv =
  {[testenv]setenv}
# we do not have any greenlet leaks in functional tests so enforce that
# by making greenlet leaks a failure.
  NOVA_RAISE_ON_GREENLET_LEAK=True
# run the test with eventlet
  OS_NOVA_DISABLE_EVENTLET_PATCHING=False
# As nova functional tests import the PlacementFixture from the placement
# repository these tests are, by default, set up to run with openstack-placement
# from pypi. In the gate, Zuul will use the installed version of placement (stable
# branch version on stable gate run) OR the version of placement the Depends-On in
# the commit message suggests. If you want to run the tests with latest master from
# the placement repo, modify the dep line to point at master, example:
# deps =
#   {[testenv]deps}
#   git+https://opendev.org/openstack/placement#egg=openstack-placement
# If you want to run the test locally with an un-merged placement change,
# modify the dep line to point to your dependency or pip install placement
# into the appropriate tox virtualenv.
# NOTE: We express the requirement here instead of test-requirements
# because we do not want placement present during unit tests.
deps =
  {[testenv]deps}
  openstack-placement>=9.0.0.0b1
extras =
commands =
  stestr --test-path=./nova/tests/functional run {posargs}
  stestr slowest

[testenv:functional-without-sample-db-tests]
description =
  Run functional tests by excluding the API|Notification
  sample tests and DB tests. This env is used in
  placement-nova-tox-functional-py312 job which is defined and
  run in placement.
deps = {[testenv:functional]deps}
extras =
commands =
  stestr --test-path=./nova/tests/functional run --exclude-regex '((?:api|notification)_sample_tests|functional\.db\.)' {posargs}

[testenv:pep8]
description =
  Run style checks.
deps =
  pre-commit
  {[testenv:mypy]deps}
commands =
  pre-commit run --all-files --show-diff-on-failure
  {[testenv:mypy]commands}

[testenv:mypy]
description =
  Run type checks.
# TODO(stephenfin): At some point we might wish to create a 'types' extra
deps =
  mypy
  types-decorator
  types-paramiko
  types-python-dateutil
  types-PyYAML
  types-setuptools
commands =
  mypy

[testenv:autopep8]
extras =
deps =
  pre-commit
commands =
  pre-commit run --all-files --show-diff-on-failure autopep8

[testenv:codespell]
description =
  Run codespell to check spelling.
deps =
  pre-commit
commands =
  pre-commit run --all-files --show-diff-on-failure codespell

[testenv:sphinx-lint]
description =
  Run sphinx lint checks.
deps = pre-commit
commands =
  pre-commit run --all-files --show-diff-on-failure sphinx-lint

[testenv:fast8]
description =
  Run style checks on the changes made since HEAD~. For a full run including docs, use 'pep8'
extras =
commands =
  bash tools/flake8wrap.sh -HEAD

[testenv:validate-backport]
description =
  Determine whether a backport is ready to be merged by checking whether it has
  already been merged to master or more recent stable branches.
deps =
extras =
skipsdist = true
commands =
  bash tools/check-cherry-picks.sh

[testenv:api-samples]
setenv =
  {[testenv]setenv}
  GENERATE_SAMPLES=True
  PYTHONHASHSEED=0
deps = {[testenv:functional]deps}
extras =
commands =
  stestr --test-path=./nova/tests/functional/api_sample_tests run {posargs}
  stestr slowest

[testenv:genconfig]
extras =
commands =
  oslo-config-generator --config-file=etc/nova/nova-config-generator.conf

[testenv:genpolicy]
extras =
commands =
  oslopolicy-sample-generator --config-file=etc/nova/nova-policy-generator.conf

[testenv:cover]
setenv =
  {[testenv]setenv}
  PYTHON=coverage run --source nova --parallel-mode
# run the test with eventlet
  OS_NOVA_DISABLE_EVENTLET_PATCHING=False
deps =
  {[testenv]deps}
  openstack-placement>=9.0.0.0b1
extras =
commands =
  coverage erase
  stestr run {posargs}
  {[testenv:functional-without-sample-db-tests]commands}
  coverage combine
  coverage html -d cover
  coverage xml -o cover/coverage.xml
  coverage report

[testenv:debug]
extras =
commands =
  oslo_debug_helper {posargs}

[testenv:venv]
deps =
  {[testenv]deps}
  -r{toxinidir}/doc/requirements.txt
extras =
commands =
  {posargs}

[testenv:docs]
description =
  Build main documentation.
# Note that we don't use {[testenv]deps} for deps here because we don't want
# to install (test-)requirements.txt for docs.
deps =
  -r{toxinidir}/requirements.txt
  -r{toxinidir}/doc/requirements.txt
extras =
commands =
  rm -rf doc/build/html doc/build/doctrees
  sphinx-build -W --keep-going -b html -j auto doc/source doc/build/html
  # Test the redirects. This must run after the main docs build
  whereto doc/build/html/.htaccess doc/test/redirect-tests.txt
setenv =
  OS_NOVA_DISABLE_EVENTLET_PATCHING=False


[testenv:pdf-docs]
description =
  Build PDF documentation.
deps = {[testenv:docs]deps}
extras =
commands =
  rm -rf doc/build/pdf
  sphinx-build -W --keep-going -b latex -j auto doc/source doc/build/pdf
  make -C doc/build/pdf

[testenv:api-guide]
description =
  Generate the API guide. Called from CI scripts to test and publish to docs.openstack.org.
deps = {[testenv:docs]deps}
extras =
commands =
  rm -rf api-guide/build
  sphinx-build -W --keep-going -b html -j auto api-guide/source api-guide/build/html

[testenv:api-ref]
description =
  Generate the API ref. Called from CI scripts to test and publish to docs.openstack.org.
deps = {[testenv:docs]deps}
extras =
commands =
  rm -rf api-ref/build
  sphinx-build -W --keep-going -b html -j auto api-ref/source api-ref/build/html

[testenv:releasenotes]
description =
  Generate release notes.
deps = {[testenv:docs]deps}
extras =
commands =
  rm -rf releasenotes/build
  sphinx-build -W --keep-going -b html -j auto releasenotes/source releasenotes/build/html

[testenv:all-docs]
description =
  Build all documentation including API guides and refs.
deps = {[testenv:docs]deps}
extras =
commands =
  {[testenv:docs]commands}
  {[testenv:api-guide]commands}
  {[testenv:api-ref]commands}
  {[testenv:releasenotes]commands}

[testenv:bandit]
extras =
commands = bandit -r nova -x tests -n 5 -ll

[flake8]
# E125 is deliberately excluded. See
# https://github.com/jcrocholl/pep8/issues/126. It's just wrong.
#
# Most of the whitespace related rules (E12* and E131) are excluded
# because while they are often useful guidelines, strict adherence to
# them ends up causing some really odd code formatting and forced
# extra line breaks. Updating code to enforce these will be a hard sell.
#
# H405 is another one that is good as a guideline, but sometimes
# multiline doc strings just don't have a natural summary
# line. Rejecting code for this reason is wrong.
#
# E251 Skipped due to https://github.com/jcrocholl/pep8/issues/301
#
# W504 skipped since you must choose either W503 or W504 (they conflict)
#
# E731 temporarily skipped because of the number of
# these that have to be fixed
enable-extensions = H106,H203,H904
ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E251,H405,W504,E731,H238
exclude =  .venv,.git,.tox,dist,*lib/python*,*egg,build,releasenotes
# To get a list of functions that are more complex than 25, set max-complexity
# to 25 and run 'tox -epep8'.
# 39 is currently the most complex thing we have
# TODO(jogo): get this number down to 25 or so
max-complexity = 40

[hacking]
import_exceptions =
  collections.abc
  nova.i18n
  typing

[flake8:local-plugins]
extension =
  N307 = checks:import_no_db_in_virt
  N309 = checks:no_db_session_in_public_api
  N310 = checks:use_timeutils_utcnow
  N311 = checks:import_no_virt_driver_import_deps
  N312 = checks:import_no_virt_driver_config_deps
  N313 = checks:capital_cfg_help
  N316 = checks:assert_true_instance
  N317 = checks:assert_equal_type
  N335 = checks:assert_raises_regexp
  N319 = checks:no_translate_logs
  N337 = checks:no_import_translation_in_tests
  N320 = checks:no_setting_conf_directly_in_tests
  N322 = checks:no_mutable_default_args
  N323 = checks:check_explicit_underscore_import
  N324 = checks:use_jsonutils
  N332 = checks:check_api_version_decorator
  N326 = checks:CheckForTransAdd
  N334 = checks:assert_true_or_false_with_in
  N336 = checks:dict_constructor_with_list_copy
  N338 = checks:assert_equal_in
  N339 = checks:check_http_not_implemented
  N340 = checks:check_greenthread_spawns
  N341 = checks:check_no_contextlib_nested
  N342 = checks:check_config_option_in_central_place
  N350 = checks:check_policy_registration_in_central_place
  N351 = checks:check_policy_enforce
  N343 = checks:check_doubled_words
  N348 = checks:no_os_popen
  N352 = checks:no_log_warn
  N349 = checks:CheckForUncalledTestClosure
  N353 = checks:check_context_log
  N355 = checks:no_assert_equal_true_false
  N356 = checks:no_assert_true_false_is_not
  N357 = checks:check_uuid4
  N358 = checks:return_followed_by_space
  N359 = checks:no_redundant_import_alias
  N360 = checks:yield_followed_by_space
  N361 = checks:assert_regexpmatches
  N362 = checks:privsep_imports_not_aliased
  N363 = checks:did_you_mean_tuple
  N364 = checks:nonexistent_assertion_methods_and_attributes
  N365 = checks:useless_assertion
  N366 = checks:check_assert_has_calls
  N367 = checks:do_not_alias_mock_class
  N368 = checks:do_not_use_mock_class_as_new_mock_value
  N369 = checks:check_lockutils_rwlocks
  N370 = checks:check_six
  N371 = checks:import_stock_mock
  N372 = checks:check_set_daemon
  N373 = checks:check_eventlet_primitives
  N374 = checks:check_eventlet_yield
  N375 = checks:check_threading_event_mock
paths =
  ./nova/hacking

[testenv:bindep]
# Do not install any requirements. We want this to be fast and work even if
# system dependencies are missing, since it's used to tell you what system
# dependencies are missing! This also means that bindep must be installed
# separately, outside of the requirements files, and develop mode disabled
# explicitly to avoid unnecessarily installing the checked-out repo too
usedevelop = False
deps = bindep
extras =
commands =
  bindep test
