ExpectDeprecationTrait.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <?php
  2. namespace Drupal\Tests\Traits;
  3. use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListener as LegacySymfonyTestsListener;
  4. use Symfony\Bridge\PhpUnit\SymfonyTestsListener;
  5. use PHPUnit\Framework\AssertionFailedError;
  6. use PHPUnit\Framework\TestCase;
  7. use PHPUnit\Util\Test;
  8. /**
  9. * Adds the ability to dynamically set expected deprecation messages in tests.
  10. *
  11. * @internal
  12. * This class should only be used by Drupal core and will be removed once
  13. * https://github.com/symfony/symfony/pull/25757 is resolved.
  14. *
  15. * @todo Remove once https://github.com/symfony/symfony/pull/25757 is resolved.
  16. */
  17. trait ExpectDeprecationTrait {
  18. /**
  19. * Sets an expected deprecation message.
  20. *
  21. * @param string $message
  22. * The expected deprecation message.
  23. */
  24. protected function addExpectedDeprecationMessage($message) {
  25. $this->expectedDeprecations([$message]);
  26. }
  27. /**
  28. * Sets an expected deprecation message.
  29. *
  30. * @param string $message
  31. * The expected deprecation message.
  32. *
  33. * @deprecated in drupal:8.8.5 and is removed from drupal:9.0.0. Use
  34. * ::addExpectedDeprecationMessage() instead.
  35. *
  36. * @see https://www.drupal.org/node/3106024
  37. */
  38. protected function expectDeprecation($message) {
  39. if (strpos($message, 'ExpectDeprecationTrait::expectDeprecation') === FALSE) {
  40. @trigger_error('ExpectDeprecationTrait::expectDeprecation is deprecated in drupal:8.8.5 and is removed from drupal:9.0.0. Use ::addExpectedDeprecationMessage() instead. See https://www.drupal.org/node/3106024', E_USER_DEPRECATED);
  41. }
  42. $this->addExpectedDeprecationMessage($message);
  43. }
  44. /**
  45. * Sets expected deprecation messages.
  46. *
  47. * @param string[] $messages
  48. * The expected deprecation messages.
  49. */
  50. public function expectedDeprecations(array $messages) {
  51. if ($this instanceof TestCase) {
  52. // Ensure the class or method is in the legacy group.
  53. $groups = Test::getGroups(get_class($this), $this->getName(FALSE));
  54. if (!in_array('legacy', $groups, TRUE)) {
  55. throw new AssertionFailedError('Only tests with the `@group legacy` annotation can call `setExpectedDeprecation()`.');
  56. }
  57. // If setting an expected deprecation there is no need to be strict about
  58. // testing nothing as this is an assertion.
  59. $this->getTestResultObject()
  60. ->beStrictAboutTestsThatDoNotTestAnything(FALSE);
  61. if ($trait = $this->getSymfonyTestListenerTrait()) {
  62. // Add the expected deprecation message to the class property.
  63. $reflection_class = new \ReflectionClass($trait);
  64. $expected_deprecations_property = $reflection_class->getProperty('expectedDeprecations');
  65. $expected_deprecations_property->setAccessible(TRUE);
  66. $expected_deprecations = $expected_deprecations_property->getValue($trait);
  67. $expected_deprecations = array_merge($expected_deprecations, $messages);
  68. $expected_deprecations_property->setValue($trait, $expected_deprecations);
  69. // Register the error handler if necessary.
  70. $previous_error_handler_property = $reflection_class->getProperty('previousErrorHandler');
  71. $previous_error_handler_property->setAccessible(TRUE);
  72. $previous_error_handler = $previous_error_handler_property->getValue($trait);
  73. if (!$previous_error_handler) {
  74. $previous_error_handler = set_error_handler([$trait, 'handleError']);
  75. $previous_error_handler_property->setValue($trait, $previous_error_handler);
  76. }
  77. return;
  78. }
  79. }
  80. // Expected deprecations set by isolated tests need to be written to a file
  81. // so that the test running process can take account of them.
  82. if ($file = getenv('DRUPAL_EXPECTED_DEPRECATIONS_SERIALIZE')) {
  83. $expected_deprecations = file_get_contents($file);
  84. if ($expected_deprecations) {
  85. $expected_deprecations = array_merge(unserialize($expected_deprecations), $messages);
  86. }
  87. else {
  88. $expected_deprecations = $messages;
  89. }
  90. file_put_contents($file, serialize($expected_deprecations));
  91. return;
  92. }
  93. throw new AssertionFailedError('Can not set an expected deprecation message because the Symfony\Bridge\PhpUnit\SymfonyTestsListener is not registered as a PHPUnit test listener.');
  94. }
  95. /**
  96. * Gets the SymfonyTestsListenerTrait.
  97. *
  98. * @return \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait|null
  99. * The SymfonyTestsListenerTrait object or NULL is a Symfony test listener
  100. * is not present.
  101. */
  102. private function getSymfonyTestListenerTrait() {
  103. $test_result_object = $this->getTestResultObject();
  104. $reflection_class = new \ReflectionClass($test_result_object);
  105. $reflection_property = $reflection_class->getProperty('listeners');
  106. $reflection_property->setAccessible(TRUE);
  107. $listeners = $reflection_property->getValue($test_result_object);
  108. foreach ($listeners as $listener) {
  109. if ($listener instanceof SymfonyTestsListener || $listener instanceof LegacySymfonyTestsListener) {
  110. $reflection_class = new \ReflectionClass($listener);
  111. $reflection_property = $reflection_class->getProperty('trait');
  112. $reflection_property->setAccessible(TRUE);
  113. return $reflection_property->getValue($listener);
  114. }
  115. }
  116. }
  117. }