testing_utils.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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):
  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. return subprocess.run(cmd, **cfg)
  29. # Assume all elements has same data type
  30. def get_container_type(container):
  31. container_t = type(container)
  32. if container_t in [list, tuple]:
  33. if len(container) == 0:
  34. return container_t
  35. return get_container_type(container[0])
  36. return container_t
  37. class _CommonTestNamespace:
  38. # Wrap the subclasses of unittest.TestCase that are expected to be inherited from.
  39. class CommonTest(unittest.TestCase):
  40. CATCH_WARNINGS = False
  41. def __init__(self, methodName='runTest'):
  42. super(CommonTest, self).__init__(methodName=methodName)
  43. self.config = {}
  44. self.places = ['cpu']
  45. if paddle.is_compiled_with_cuda():
  46. self.places.append('gpu')
  47. @classmethod
  48. def setUpClass(cls):
  49. """
  50. Set the decorators for all test function
  51. """
  52. for key, value in cls.__dict__.items():
  53. if key.startswith('test'):
  54. decorator_func_list = ["_test_places"]
  55. if cls.CATCH_WARNINGS:
  56. decorator_func_list.append("_catch_warnings")
  57. for decorator_func in decorator_func_list:
  58. decorator_func = getattr(CommonTest, decorator_func)
  59. value = decorator_func(value)
  60. setattr(cls, key, value)
  61. def _catch_warnings(func):
  62. """
  63. Catch the warnings and treat them as errors for each test.
  64. """
  65. def wrapper(self, *args, **kwargs):
  66. with warnings.catch_warnings(record=True) as w:
  67. warnings.resetwarnings()
  68. # ignore specified warnings
  69. warning_white_list = [UserWarning]
  70. for warning in warning_white_list:
  71. warnings.simplefilter("ignore", warning)
  72. func(self, *args, **kwargs)
  73. msg = None if len(w) == 0 else w[0].message
  74. self.assertFalse(len(w) > 0, msg)
  75. return wrapper
  76. def _test_places(func):
  77. """
  78. Setting the running place for each test.
  79. """
  80. def wrapper(self, *args, **kwargs):
  81. places = self.places
  82. for place in places:
  83. paddle.set_device(place)
  84. func(self, *args, **kwargs)
  85. return wrapper
  86. def _check_output_impl(self,
  87. result,
  88. expected_result,
  89. rtol,
  90. atol,
  91. equal=True):
  92. assertForNormalType = self.assertNotEqual
  93. assertForFloat = self.assertFalse
  94. if equal:
  95. assertForNormalType = self.assertEqual
  96. assertForFloat = self.assertTrue
  97. result_t = type(result)
  98. error_msg = 'Output has diff at place:{}. \nExpect: {} \nBut Got: {} in class {}'
  99. if result_t in [list, tuple]:
  100. result_t = get_container_type(result)
  101. if result_t in [
  102. str, int, bool, set, np.bool, np.int32, np.int64, np.str
  103. ]:
  104. assertForNormalType(
  105. result,
  106. expected_result,
  107. msg=error_msg.format(paddle.get_device(), expected_result,
  108. result, self.__class__.__name__))
  109. elif result_t in [float, np.ndarray, np.float32, np.float64]:
  110. assertForFloat(
  111. np.allclose(
  112. result, expected_result, rtol=rtol, atol=atol),
  113. msg=error_msg.format(paddle.get_device(), expected_result,
  114. result, self.__class__.__name__))
  115. if result_t == np.ndarray:
  116. assertForNormalType(
  117. result.shape,
  118. expected_result.shape,
  119. msg=error_msg.format(
  120. paddle.get_device(), expected_result.shape,
  121. result.shape, self.__class__.__name__))
  122. else:
  123. raise ValueError(
  124. 'result type must be str, int, bool, set, np.bool, np.int32, '
  125. 'np.int64, np.str, float, np.ndarray, np.float32, np.float64'
  126. )
  127. def check_output_equal(self,
  128. result,
  129. expected_result,
  130. rtol=1.e-5,
  131. atol=1.e-8):
  132. """
  133. Check whether result and expected result are equal, including shape.
  134. Args:
  135. result: str, int, bool, set, np.ndarray.
  136. The result needs to be checked.
  137. expected_result: str, int, bool, set, np.ndarray. The type has to be same as result's.
  138. Use the expected result to check result.
  139. rtol: float
  140. relative tolerance, default 1.e-5.
  141. atol: float
  142. absolute tolerance, default 1.e-8
  143. """
  144. self._check_output_impl(result, expected_result, rtol, atol)
  145. def check_output_not_equal(self,
  146. result,
  147. expected_result,
  148. rtol=1.e-5,
  149. atol=1.e-8):
  150. """
  151. Check whether result and expected result are not equal, including shape.
  152. Args:
  153. result: str, int, bool, set, np.ndarray.
  154. The result needs to be checked.
  155. expected_result: str, int, bool, set, np.ndarray. The type has to be same as result's.
  156. Use the expected result to check result.
  157. rtol: float
  158. relative tolerance, default 1.e-5.
  159. atol: float
  160. absolute tolerance, default 1.e-8
  161. """
  162. self._check_output_impl(
  163. result, expected_result, rtol, atol, equal=False)
  164. class CpuCommonTest(CommonTest):
  165. def __init__(self, methodName='runTest'):
  166. super(CpuCommonTest, self).__init__(methodName=methodName)
  167. self.places = ['cpu']
  168. CommonTest = _CommonTestNamespace.CommonTest
  169. CpuCommonTest = _CommonTestNamespace.CpuCommonTest