Coverage for test_plugins.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

62 statements  

1# -*- coding: utf-8 -*- 

2#@+leo-ver=5-thin 

3#@+node:ekr.20210907081548.1: * @file ../unittests/test_plugins.py 

4#@@first 

5"""General tests of plugins.""" 

6 

7import glob 

8import re 

9from leo.core import leoGlobals as g 

10from leo.core.leoTest2 import LeoUnitTest 

11from leo.core.leoPlugins import LeoPluginsController 

12assert g 

13 

14#@+others 

15#@+node:ekr.20210907082556.1: ** class TestPlugins(LeoUnitTest) 

16class TestPlugins(LeoUnitTest): 

17 """General tests of plugoins.""" 

18 #@+others 

19 #@+node:ekr.20210909165100.1: *3* TestPlugin.check_syntax 

20 def check_syntax(self, filename): # pylint: disable=inconsistent-return-statements 

21 with open(filename, 'r') as f: 

22 s = f.read() 

23 try: 

24 s = s.replace('\r', '') 

25 tree = compile(s + '\n', filename, 'exec') 

26 del tree # #1454: Suppress -Wd ResourceWarning. 

27 return True 

28 except SyntaxError: # pragma: no cover 

29 raise 

30 except Exception: # pragma: no cover 

31 self.fail(f"unexpected error in: {filename}") 

32 

33 #@+node:ekr.20210907082746.1: *3* TestPlugins.get_plugins 

34 def get_plugins(self): 

35 """Return a list of all plugins *without* importing them.""" 

36 excludes = ( 

37 # These are not real plugins... 

38 'babel_api.py', 

39 'babel_kill.py', 

40 'babel_lib.py', 

41 'baseNativeTree.py', 

42 'leocursor.py', 

43 'leo_cloud_server.py', 

44 'leo_mypy_plugin.py', 

45 'nested_splitter.py', 

46 'qtGui.py', 

47 'qt_gui.py', 

48 'qt_big_text.py', 

49 'qt_commands.py', 

50 'qt_events.py', 

51 'qt_frame.py', 

52 'qt_idle_time.py', 

53 'qt_main.py', 

54 'qt_quickheadlines.py', 

55 'qt_quicksearch_sub.py', 

56 'qt_text.py', 

57 'qt_tree.py', 

58 'qt_quicksearch.py', 

59 'swing_gui.py', 

60 # Experimental. 

61 'leo_pdf.py', 

62 ) 

63 plugins = g.os_path_join(g.app.loadDir, '..', 'plugins', '*.py') 

64 plugins = g.os_path_abspath(plugins) 

65 files = glob.glob(plugins) 

66 files = [z for z in files if not z.endswith('__init__.py')] 

67 files = [z for z in files if g.shortFileName(z) not in excludes] 

68 files = [g.os_path_abspath(z) for z in files] 

69 return sorted(files) 

70 #@+node:ekr.20210907081455.2: *3* TestPlugins.test_all_plugins_have_top_level_init_method 

71 def test_all_plugins_have_top_level_init_method(self): 

72 # Ensure all plugins have top-level init method *without* importing them. 

73 files = self.get_plugins() 

74 for fn in files: 

75 with open(fn, 'r') as f: 

76 s = f.read() 

77 self.assertTrue('def init():' in s or 'def init ():' in s, msg=fn) 

78 #@+node:ekr.20210907081455.3: *3* TestPlugins.test_all_qt_plugins_call_g_assertUi_qt_ 

79 def test_all_qt_plugins_call_g_assertUi_qt_(self): 

80 files = self.get_plugins() 

81 excludes = ( 

82 # Special cases, handling Qt imports in unusual ways. 

83 'backlink.py', # Qt code is optional, disabled with module-level guard. 

84 'leoscreen.py', # Qt imports are optional. 

85 'nodetags.py', # #2031: Qt imports are optional. 

86 'picture_viewer.py', # Special case. 

87 'pyplot_backend.py', 

88 'remove_duplicate_pictures.py' # Special case. 

89 ) 

90 pattern = re.compile(r'\b(QtCore|QtGui|QtWidgets)\b') # Don't search for Qt. 

91 for fn in files: 

92 if g.shortFileName(fn) in excludes: 

93 continue 

94 with open(fn, 'r') as f: 

95 s = f.read() 

96 if not re.search(pattern, s): 

97 continue 

98 self.assertTrue(re.search(r"g\.assertUi\(['\"]qt['\"]\)", s), msg=fn) 

99 #@+node:ekr.20210909161328.2: *3* TestPlugins.test_c_vnode2position 

100 def test_c_vnode2position(self): 

101 c = self.c 

102 for p in c.all_positions(): 

103 p2 = c.vnode2position(p.v) 

104 # We can *not* assert that p == p2! 

105 assert p2 

106 self.assertEqual(p2.v, p.v) 

107 assert c.positionExists(p2), 'does not exist: %s' % p2 

108 #@+node:ekr.20210909194336.57: *3* TestPlugins.test_regularizeName 

109 def test_regularizeName(self): 

110 pc = LeoPluginsController() 

111 table = ( 

112 ('x', 'x'), 

113 ('foo.bar', 'foo.bar'), 

114 ('x.py', 'leo.plugins.x'), 

115 ('leo.plugins.x', 'leo.plugins.x') 

116 ) 

117 for fn, expected in table: 

118 result = pc.regularizeName(fn) 

119 self.assertEqual(result, expected, msg=fn) 

120 # Make sure that calling regularizeName twice is benign. 

121 result2 = pc.regularizeName(result) 

122 assert result2 == result 

123 #@+node:ekr.20210909161328.4: *3* TestPlugins.test_syntax_of_all_plugins 

124 def test_syntax_of_all_plugins(self): 

125 files = self.get_plugins() 

126 for filename in files: 

127 self.check_syntax(filename) 

128 #@+node:ekr.20210909165720.1: *3* TestPlugins.xx_test_import_all_plugins 

129 def xx_test_import_of_all_plugins(self): # pragma: no cover 

130 # This works, but is slow. 

131 files = self.get_plugins() 

132 for filename in files: 

133 plugin_module = g.shortFileName(filename)[:-3] 

134 try: 

135 exec(f"import leo.plugins.{plugin_module}") 

136 except g.UiTypeException: 

137 pass 

138 except AttributeError: 

139 pass 

140 except ImportError: 

141 pass 

142 #@-others 

143#@-others 

144#@-leo