ExpectDeprecationTrait.php 4.7 KB

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