Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Generic test utilities. 

3 

4""" 

5 

6import os 

7import re 

8import sys 

9 

10 

11__all__ = ['PytestTester', 'check_free_memory'] 

12 

13 

14class FPUModeChangeWarning(RuntimeWarning): 

15 """Warning about FPU mode change""" 

16 pass 

17 

18 

19class PytestTester(object): 

20 """ 

21 Pytest test runner entry point. 

22 """ 

23 

24 def __init__(self, module_name): 

25 self.module_name = module_name 

26 

27 def __call__(self, label="fast", verbose=1, extra_argv=None, doctests=False, 

28 coverage=False, tests=None, parallel=None): 

29 import pytest 

30 

31 module = sys.modules[self.module_name] 

32 module_path = os.path.abspath(module.__path__[0]) 

33 

34 pytest_args = ['--showlocals', '--tb=short'] 

35 

36 if doctests: 

37 raise ValueError("Doctests not supported") 

38 

39 if extra_argv: 

40 pytest_args += list(extra_argv) 

41 

42 if verbose and int(verbose) > 1: 

43 pytest_args += ["-" + "v"*(int(verbose)-1)] 

44 

45 if coverage: 

46 pytest_args += ["--cov=" + module_path] 

47 

48 if label == "fast": 

49 pytest_args += ["-m", "not slow"] 

50 elif label != "full": 

51 pytest_args += ["-m", label] 

52 

53 if tests is None: 

54 tests = [self.module_name] 

55 

56 if parallel is not None and parallel > 1: 

57 if _pytest_has_xdist(): 

58 pytest_args += ['-n', str(parallel)] 

59 else: 

60 import warnings 

61 warnings.warn('Could not run tests in parallel because ' 

62 'pytest-xdist plugin is not available.') 

63 

64 pytest_args += ['--pyargs'] + list(tests) 

65 

66 try: 

67 code = pytest.main(pytest_args) 

68 except SystemExit as exc: 

69 code = exc.code 

70 

71 return (code == 0) 

72 

73 

74def _pytest_has_xdist(): 

75 """ 

76 Check if the pytest-xdist plugin is installed, providing parallel tests 

77 """ 

78 # Check xdist exists without importing, otherwise pytests emits warnings 

79 from importlib.util import find_spec 

80 return find_spec('xdist') is not None 

81 

82 

83def check_free_memory(free_mb): 

84 """ 

85 Check *free_mb* of memory is available, otherwise do pytest.skip 

86 """ 

87 import pytest 

88 

89 try: 

90 mem_free = _parse_size(os.environ['SCIPY_AVAILABLE_MEM']) 

91 msg = '{0} MB memory required, but environment SCIPY_AVAILABLE_MEM={1}'.format( 

92 free_mb, os.environ['SCIPY_AVAILABLE_MEM']) 

93 except KeyError: 

94 mem_free = _get_mem_available() 

95 if mem_free is None: 

96 pytest.skip("Could not determine available memory; set SCIPY_AVAILABLE_MEM " 

97 "variable to free memory in MB to run the test.") 

98 msg = '{0} MB memory required, but {1} MB available'.format( 

99 free_mb, mem_free/1e6) 

100 

101 if mem_free < free_mb * 1e6: 

102 pytest.skip(msg) 

103 

104 

105def _parse_size(size_str): 

106 suffixes = {'': 1e6, 

107 'b': 1.0, 

108 'k': 1e3, 'M': 1e6, 'G': 1e9, 'T': 1e12, 

109 'kb': 1e3, 'Mb': 1e6, 'Gb': 1e9, 'Tb': 1e12, 

110 'kib': 1024.0, 'Mib': 1024.0**2, 'Gib': 1024.0**3, 'Tib': 1024.0**4} 

111 m = re.match(r'^\s*(\d+)\s*({0})\s*$'.format('|'.join(suffixes.keys())), 

112 size_str, 

113 re.I) 

114 if not m or m.group(2) not in suffixes: 

115 raise ValueError("Invalid size string") 

116 

117 return float(m.group(1)) * suffixes[m.group(2)] 

118 

119 

120def _get_mem_available(): 

121 """ 

122 Get information about memory available, not counting swap. 

123 """ 

124 try: 

125 import psutil 

126 return psutil.virtual_memory().available 

127 except (ImportError, AttributeError): 

128 pass 

129 

130 if sys.platform.startswith('linux'): 

131 info = {} 

132 with open('/proc/meminfo', 'r') as f: 

133 for line in f: 

134 p = line.split() 

135 info[p[0].strip(':').lower()] = float(p[1]) * 1e3 

136 

137 if 'memavailable' in info: 

138 # Linux >= 3.14 

139 return info['memavailable'] 

140 else: 

141 return info['memfree'] + info['cached'] 

142 

143 return None