test_check_dependencies.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from contextlib import contextmanager
  2. from typing import Generator, Optional
  3. from unittest.mock import patch
  4. from synapse.util.check_dependencies import (
  5. DependencyException,
  6. check_requirements,
  7. metadata,
  8. )
  9. from tests.unittest import TestCase
  10. class DummyDistribution(metadata.Distribution):
  11. def __init__(self, version: str):
  12. self._version = version
  13. @property
  14. def version(self):
  15. return self._version
  16. def locate_file(self, path):
  17. raise NotImplementedError()
  18. def read_text(self, filename):
  19. raise NotImplementedError()
  20. old = DummyDistribution("0.1.2")
  21. old_release_candidate = DummyDistribution("0.1.2rc3")
  22. new = DummyDistribution("1.2.3")
  23. new_release_candidate = DummyDistribution("1.2.3rc4")
  24. # could probably use stdlib TestCase --- no need for twisted here
  25. class TestDependencyChecker(TestCase):
  26. @contextmanager
  27. def mock_installed_package(
  28. self, distribution: Optional[DummyDistribution]
  29. ) -> Generator[None, None, None]:
  30. """Pretend that looking up any distribution yields the given `distribution`."""
  31. def mock_distribution(name: str):
  32. if distribution is None:
  33. raise metadata.PackageNotFoundError
  34. else:
  35. return distribution
  36. with patch(
  37. "synapse.util.check_dependencies.metadata.distribution",
  38. mock_distribution,
  39. ):
  40. yield
  41. def test_mandatory_dependency(self) -> None:
  42. """Complain if a required package is missing or old."""
  43. with patch(
  44. "synapse.util.check_dependencies.metadata.requires",
  45. return_value=["dummypkg >= 1"],
  46. ):
  47. with self.mock_installed_package(None):
  48. self.assertRaises(DependencyException, check_requirements)
  49. with self.mock_installed_package(old):
  50. self.assertRaises(DependencyException, check_requirements)
  51. with self.mock_installed_package(new):
  52. # should not raise
  53. check_requirements()
  54. def test_checks_ignore_dev_dependencies(self) -> None:
  55. """Bot generic and per-extra checks should ignore dev dependencies."""
  56. with patch(
  57. "synapse.util.check_dependencies.metadata.requires",
  58. return_value=["dummypkg >= 1; extra == 'mypy'"],
  59. ), patch("synapse.util.check_dependencies.RUNTIME_EXTRAS", {"cool-extra"}):
  60. # We're testing that none of these calls raise.
  61. with self.mock_installed_package(None):
  62. check_requirements()
  63. check_requirements("cool-extra")
  64. with self.mock_installed_package(old):
  65. check_requirements()
  66. check_requirements("cool-extra")
  67. with self.mock_installed_package(new):
  68. check_requirements()
  69. check_requirements("cool-extra")
  70. def test_generic_check_of_optional_dependency(self) -> None:
  71. """Complain if an optional package is old."""
  72. with patch(
  73. "synapse.util.check_dependencies.metadata.requires",
  74. return_value=["dummypkg >= 1; extra == 'cool-extra'"],
  75. ):
  76. with self.mock_installed_package(None):
  77. # should not raise
  78. check_requirements()
  79. with self.mock_installed_package(old):
  80. self.assertRaises(DependencyException, check_requirements)
  81. with self.mock_installed_package(new):
  82. # should not raise
  83. check_requirements()
  84. def test_check_for_extra_dependencies(self) -> None:
  85. """Complain if a package required for an extra is missing or old."""
  86. with patch(
  87. "synapse.util.check_dependencies.metadata.requires",
  88. return_value=["dummypkg >= 1; extra == 'cool-extra'"],
  89. ), patch("synapse.util.check_dependencies.RUNTIME_EXTRAS", {"cool-extra"}):
  90. with self.mock_installed_package(None):
  91. self.assertRaises(DependencyException, check_requirements, "cool-extra")
  92. with self.mock_installed_package(old):
  93. self.assertRaises(DependencyException, check_requirements, "cool-extra")
  94. with self.mock_installed_package(new):
  95. # should not raise
  96. check_requirements("cool-extra")
  97. def test_release_candidates_satisfy_dependency(self) -> None:
  98. """
  99. Tests that release candidates count as far as satisfying a dependency
  100. is concerned.
  101. (Regression test, see #12176.)
  102. """
  103. with patch(
  104. "synapse.util.check_dependencies.metadata.requires",
  105. return_value=["dummypkg >= 1"],
  106. ):
  107. with self.mock_installed_package(old_release_candidate):
  108. self.assertRaises(DependencyException, check_requirements)
  109. with self.mock_installed_package(new_release_candidate):
  110. # should not raise
  111. check_requirements()