testing_utils.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # Based on https://github.com/PaddlePaddle/PaddleNLP/blob/develop/tests/common_test.py
  15. import unittest
  16. import warnings
  17. import subprocess
  18. import numpy as np
  19. import paddle
  20. __all__ = ['CommonTest', 'CpuCommonTest', 'run_script']
  21. def run_script(cmd, silent=True, wd=None, timeout=None, echo=True):
  22. # XXX: This function is not safe!!!
  23. cfg = dict(check=True, shell=True, timeout=timeout)
  24. if silent:
  25. cfg['stdout'] = subprocess.DEVNULL
  26. if wd is not None:
  27. cmd = f"cd {wd} && {cmd}"
  28. if echo:
  29. print(cmd)
  30. return subprocess.run(cmd, **cfg)
  31. # Assume all elements has same data type
  32. def get_container_type(container):
  33. container_t = type(container)
  34. if container_t in [list, tuple]:
  35. if len(container) == 0:
  36. return container_t
  37. return get_container_type(container[0])
  38. return container_t
  39. class _CommonTestNamespace:
  40. # Wrap the subclasses of unittest.TestCase that are expected to be inherited from.
  41. class CommonTest(unittest.TestCase):
  42. CATCH_WARNINGS = False
  43. def __init__(self, methodName='runTest'):
  44. super(CommonTest, self).__init__(methodName=methodName)
  45. self.config = {}
  46. self.places = ['cpu']
  47. if paddle.is_compiled_with_cuda():
  48. self.places.append('gpu')
  49. @classmethod
  50. def setUpClass(cls):
  51. """
  52. Set the decorators for all test function
  53. """
  54. for key, value in cls.__dict__.items():
  55. if key.startswith('test'):
  56. decorator_func_list = ["_test_places"]
  57. if cls.CATCH_WARNINGS:
  58. decorator_func_list.append("_catch_warnings")
  59. for decorator_func in decorator_func_list:
  60. decorator_func = getattr(CommonTest, decorator_func)
  61. value = decorator_func(value)
  62. setattr(cls, key, value)
  63. def _catch_warnings(func):
  64. """
  65. Catch the warnings and treat them as errors for each test.
  66. """
  67. def wrapper(self, *args, **kwargs):
  68. with warnings.catch_warnings(record=True) as w:
  69. warnings.resetwarnings()
  70. # Ignore specified warnings
  71. warning_white_list = [UserWarning]
  72. for warning in warning_white_list:
  73. warnings.simplefilter("ignore", warning)
  74. func(self, *args, **kwargs)
  75. msg = None if len(w) == 0 else w[0].message
  76. self.assertFalse(len(w) > 0, msg)
  77. return wrapper
  78. def _test_places(func):
  79. """
  80. Setting the running place for each test.
  81. """
  82. def wrapper(self, *args, **kwargs):
  83. places = self.places
  84. for place in places:
  85. paddle.set_device(place)
  86. func(self, *args, **kwargs)
  87. return wrapper
  88. def _check_output_impl(self,
  89. result,
  90. expected_result,
  91. rtol,
  92. atol,
  93. equal=True):
  94. assertForNormalType = self.assertNotEqual
  95. assertForFloat = self.assertFalse
  96. if equal:
  97. assertForNormalType = self.assertEqual
  98. assertForFloat = self.assertTrue
  99. result_t = type(result)
  100. error_msg = "Output has diff at place:{}. \nExpect: {} \nBut Got: {} in class {}"
  101. if result_t in [list, tuple]:
  102. result_t = get_container_type(result)
  103. if result_t in [
  104. str, int, bool, set, np.bool, np.int32, np.int64, np.str
  105. ]:
  106. assertForNormalType(
  107. result,
  108. expected_result,
  109. msg=error_msg.format(paddle.get_device(), expected_result,
  110. result, self.__class__.__name__))
  111. elif result_t in [float, np.ndarray, np.float32, np.float64]:
  112. assertForFloat(
  113. np.allclose(
  114. result, expected_result, rtol=rtol, atol=atol),
  115. msg=error_msg.format(paddle.get_device(), expected_result,
  116. result, self.__class__.__name__))
  117. if result_t == np.ndarray:
  118. assertForNormalType(
  119. result.shape,
  120. expected_result.shape,
  121. msg=error_msg.format(
  122. paddle.get_device(), expected_result.shape,
  123. result.shape, self.__class__.__name__))
  124. else:
  125. raise ValueError(
  126. "Result type must be str, int, bool, set, np.bool, np.int32, "
  127. "np.int64, np.str, float, np.ndarray, np.float32, np.float64"
  128. )
  129. def check_output_equal(self,
  130. result,
  131. expected_result,
  132. rtol=1.e-5,
  133. atol=1.e-8):
  134. """
  135. Check whether result and expected result are equal, including shape.
  136. Args:
  137. result (str|int|bool|set|np.ndarray):
  138. The result needs to be checked.
  139. expected_result (str|int|bool|set|np.ndarray): The type has to be same as
  140. result's. Use the expected result to check result.
  141. rtol (float, optional):
  142. relative tolerance, default 1.e-5.
  143. atol (float, optional):
  144. absolute tolerance, default 1.e-8
  145. """
  146. self._check_output_impl(result, expected_result, rtol, atol)
  147. def check_output_not_equal(self,
  148. result,
  149. expected_result,
  150. rtol=1.e-5,
  151. atol=1.e-8):
  152. """
  153. Check whether result and expected result are not equal, including shape.
  154. Args:
  155. result (str|int|bool|set|np.ndarray):
  156. The result needs to be checked.
  157. expected_result (str|int|bool|set|np.ndarray): The type has to be same
  158. as result's. Use the expected result to check result.
  159. rtol (float, optional):
  160. relative tolerance, default 1.e-5.
  161. atol (float, optional):
  162. absolute tolerance, default 1.e-8
  163. """
  164. self._check_output_impl(
  165. result, expected_result, rtol, atol, equal=False)
  166. class CpuCommonTest(CommonTest):
  167. def __init__(self, methodName='runTest'):
  168. super(CpuCommonTest, self).__init__(methodName=methodName)
  169. self.places = ['cpu']
  170. CommonTest = _CommonTestNamespace.CommonTest
  171. CpuCommonTest = _CommonTestNamespace.CpuCommonTest