conftest.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. """
  2. Testing
  3. =======
  4. General guidelines for writing good tests:
  5. - doctests always assume ``import networkx as nx`` so don't add that
  6. - prefer pytest fixtures over classes with setup methods.
  7. - use the ``@pytest.mark.parametrize`` decorator
  8. - use ``pytest.importorskip`` for numpy, scipy, pandas, and matplotlib b/c of PyPy.
  9. and add the module to the relevant entries below.
  10. """
  11. import os
  12. import warnings
  13. from importlib.metadata import entry_points
  14. import pytest
  15. import networkx as nx
  16. def pytest_addoption(parser):
  17. parser.addoption(
  18. "--runslow", action="store_true", default=False, help="run slow tests"
  19. )
  20. parser.addoption(
  21. "--backend",
  22. action="store",
  23. default=None,
  24. help="Run tests with a backend by auto-converting nx graphs to backend graphs",
  25. )
  26. parser.addoption(
  27. "--fallback-to-nx",
  28. action="store_true",
  29. default=False,
  30. help="Run nx function if a backend doesn't implement a dispatchable function"
  31. " (use with --backend)",
  32. )
  33. def pytest_configure(config):
  34. config.addinivalue_line("markers", "slow: mark test as slow to run")
  35. backend = config.getoption("--backend")
  36. if backend is None:
  37. backend = os.environ.get("NETWORKX_TEST_BACKEND")
  38. # nx_loopback backend is only available when testing with a backend
  39. loopback_ep = entry_points(name="nx_loopback", group="networkx.backends")
  40. if not loopback_ep:
  41. warnings.warn(
  42. "\n\n WARNING: Mixed NetworkX configuration! \n\n"
  43. " This environment has mixed configuration for networkx.\n"
  44. " The test object nx_loopback is not configured correctly.\n"
  45. " You should not be seeing this message.\n"
  46. " Try `pip install -e .`, or change your PYTHONPATH\n"
  47. " Make sure python finds the networkx repo you are testing\n\n"
  48. )
  49. config.backend = backend
  50. if backend:
  51. # We will update `networkx.config.backend_priority` below in `*_modify_items`
  52. # to allow tests to get set up with normal networkx graphs.
  53. nx.utils.backends.backends["nx_loopback"] = loopback_ep["nx_loopback"]
  54. nx.utils.backends.backend_info["nx_loopback"] = {}
  55. nx.config.backends = nx.utils.Config(
  56. nx_loopback=nx.utils.Config(),
  57. **nx.config.backends,
  58. )
  59. fallback_to_nx = config.getoption("--fallback-to-nx")
  60. if not fallback_to_nx:
  61. fallback_to_nx = os.environ.get("NETWORKX_FALLBACK_TO_NX")
  62. nx.config.fallback_to_nx = bool(fallback_to_nx)
  63. nx.utils.backends._dispatchable.__call__ = (
  64. nx.utils.backends._dispatchable._call_if_any_backends_installed
  65. )
  66. def pytest_collection_modifyitems(config, items):
  67. # Setting this to True here allows tests to be set up before dispatching
  68. # any function call to a backend.
  69. if config.backend:
  70. # Allow pluggable backends to add markers to tests (such as skip or xfail)
  71. # when running in auto-conversion test mode
  72. backend_name = config.backend
  73. if backend_name != "networkx":
  74. nx.utils.backends._dispatchable._is_testing = True
  75. nx.config.backend_priority.algos = [backend_name]
  76. nx.config.backend_priority.generators = [backend_name]
  77. backend = nx.utils.backends.backends[backend_name].load()
  78. if hasattr(backend, "on_start_tests"):
  79. getattr(backend, "on_start_tests")(items)
  80. if config.getoption("--runslow"):
  81. # --runslow given in cli: do not skip slow tests
  82. return
  83. skip_slow = pytest.mark.skip(reason="need --runslow option to run")
  84. for item in items:
  85. if "slow" in item.keywords:
  86. item.add_marker(skip_slow)
  87. # TODO: The warnings below need to be dealt with, but for now we silence them.
  88. @pytest.fixture(autouse=True)
  89. def set_warnings():
  90. warnings.filterwarnings(
  91. "ignore",
  92. category=UserWarning,
  93. message=r"Exited (at iteration \d+|postprocessing) with accuracies.*",
  94. )
  95. warnings.filterwarnings(
  96. "ignore",
  97. category=UserWarning,
  98. message=r"The hashes produced for ",
  99. )
  100. warnings.filterwarnings(
  101. "ignore", category=DeprecationWarning, message="\n\nThe `normalized`"
  102. )
  103. warnings.filterwarnings(
  104. "ignore", category=DeprecationWarning, message="maybe_regular_expander"
  105. )
  106. warnings.filterwarnings(
  107. "ignore", category=DeprecationWarning, message="metric_closure is deprecated"
  108. )
  109. @pytest.fixture(autouse=True)
  110. def add_nx(doctest_namespace):
  111. doctest_namespace["nx"] = nx
  112. # What dependencies are installed?
  113. try:
  114. import numpy as np
  115. has_numpy = True
  116. except ImportError:
  117. has_numpy = False
  118. try:
  119. import scipy as sp
  120. has_scipy = True
  121. except ImportError:
  122. has_scipy = False
  123. try:
  124. import matplotlib as mpl
  125. has_matplotlib = True
  126. except ImportError:
  127. has_matplotlib = False
  128. try:
  129. import pandas as pd
  130. has_pandas = True
  131. except ImportError:
  132. has_pandas = False
  133. try:
  134. import pygraphviz
  135. has_pygraphviz = True
  136. except ImportError:
  137. has_pygraphviz = False
  138. try:
  139. import pydot
  140. has_pydot = True
  141. except ImportError:
  142. has_pydot = False
  143. try:
  144. import sympy
  145. has_sympy = True
  146. except ImportError:
  147. has_sympy = False
  148. # List of files that pytest should ignore
  149. collect_ignore = []
  150. needs_numpy = [
  151. "algorithms/approximation/traveling_salesman.py",
  152. "algorithms/centrality/current_flow_closeness.py",
  153. "algorithms/centrality/laplacian.py",
  154. "algorithms/node_classification.py",
  155. "algorithms/non_randomness.py",
  156. "algorithms/polynomials.py",
  157. "algorithms/shortest_paths/dense.py",
  158. "algorithms/tree/mst.py",
  159. "drawing/nx_latex.py",
  160. "generators/expanders.py",
  161. "linalg/bethehessianmatrix.py",
  162. "linalg/laplacianmatrix.py",
  163. "utils/misc.py",
  164. ]
  165. needs_scipy = [
  166. "algorithms/approximation/traveling_salesman.py",
  167. "algorithms/assortativity/correlation.py",
  168. "algorithms/assortativity/mixing.py",
  169. "algorithms/assortativity/pairs.py",
  170. "algorithms/bipartite/matrix.py",
  171. "algorithms/bipartite/spectral.py",
  172. "algorithms/bipartite/link_analysis.py",
  173. "algorithms/centrality/current_flow_betweenness.py",
  174. "algorithms/centrality/current_flow_betweenness_subset.py",
  175. "algorithms/centrality/eigenvector.py",
  176. "algorithms/centrality/katz.py",
  177. "algorithms/centrality/laplacian.py",
  178. "algorithms/centrality/second_order.py",
  179. "algorithms/centrality/subgraph_alg.py",
  180. "algorithms/communicability_alg.py",
  181. "algorithms/community/divisive.py",
  182. "algorithms/community/bipartitions.py",
  183. "algorithms/distance_measures.py",
  184. "algorithms/link_analysis/hits_alg.py",
  185. "algorithms/link_analysis/pagerank_alg.py",
  186. "algorithms/node_classification.py",
  187. "algorithms/similarity.py",
  188. "algorithms/tree/mst.py",
  189. "algorithms/walks.py",
  190. "convert_matrix.py",
  191. "drawing/layout.py",
  192. "drawing/nx_pylab.py",
  193. "generators/spectral_graph_forge.py",
  194. "generators/geometric.py",
  195. "generators/expanders.py",
  196. "linalg/algebraicconnectivity.py",
  197. "linalg/attrmatrix.py",
  198. "linalg/bethehessianmatrix.py",
  199. "linalg/graphmatrix.py",
  200. "linalg/laplacianmatrix.py",
  201. "linalg/modularitymatrix.py",
  202. "linalg/spectrum.py",
  203. "utils/rcm.py",
  204. ]
  205. needs_matplotlib = ["drawing/nx_pylab.py", "generators/classic.py"]
  206. needs_pandas = ["convert_matrix.py"]
  207. needs_pygraphviz = ["drawing/nx_agraph.py"]
  208. needs_pydot = ["drawing/nx_pydot.py"]
  209. needs_sympy = ["algorithms/polynomials.py"]
  210. if not has_numpy:
  211. collect_ignore += needs_numpy
  212. if not has_scipy:
  213. collect_ignore += needs_scipy
  214. if not has_matplotlib:
  215. collect_ignore += needs_matplotlib
  216. if not has_pandas:
  217. collect_ignore += needs_pandas
  218. if not has_pygraphviz:
  219. collect_ignore += needs_pygraphviz
  220. if not has_pydot:
  221. collect_ignore += needs_pydot
  222. if not has_sympy:
  223. collect_ignore += needs_sympy