1
+ from __future__ import annotations
2
+
1
3
import getopt
2
4
import os
3
5
import re
6
+ import shutil
4
7
import sys
5
8
import traceback
6
9
import unittest
7
-
8
- try :
9
- this_file = __file__
10
- except NameError :
11
- this_file = sys .argv [0 ]
12
-
13
- win32com_src_dir = os .path .abspath (os .path .join (this_file , "../.." ))
10
+ from collections .abc import Callable
11
+ from textwrap import dedent
12
+ from types import ModuleType
13
+ from typing import NoReturn
14
14
15
15
import win32com
16
16
17
+ this_folder = os .path .dirname (__file__ )
18
+ win32com_src_dir = os .path .abspath (os .path .join (this_folder , ".." ))
17
19
# We'd prefer the win32com namespace to be the parent of __file__ - ie, our source-tree,
18
20
# rather than the version installed - otherwise every .py change needs a full install to
19
21
# test!
20
22
# We can't patch win32comext as most of them have a .pyd in their root :(
21
- # This clearly ins 't ideal or perfect :)
23
+ # This clearly isn 't ideal or perfect :)
22
24
win32com .__path__ [0 ] = win32com_src_dir
23
25
24
26
import pythoncom
25
- import win32com .client
27
+ import win32com .client . gencache
26
28
from pywin32_testutil import TestLoader , TestRunner
27
29
from win32com .test .util import (
28
30
CapturingFunctionTestCase ,
35
37
verbosity = 1 # default unittest verbosity.
36
38
37
39
38
- def GenerateAndRunOldStyle ():
39
- from . import GenTestScripts
40
-
41
- GenTestScripts .GenerateAll ()
42
- try :
43
- pass #
44
- finally :
45
- GenTestScripts .CleanAll ()
46
-
47
-
48
- def CleanGenerated ():
49
- import shutil
50
-
51
- import win32com
52
-
40
+ def CleanGenerated () -> None :
53
41
if os .path .isdir (win32com .__gen_path__ ):
54
42
if verbosity > 1 :
55
- print ("Deleting files from %s" % ( win32com .__gen_path__ ) )
43
+ print (f "Deleting files from { win32com .__gen_path__ } " )
56
44
shutil .rmtree (win32com .__gen_path__ )
57
- import win32com .client .gencache
58
45
59
46
win32com .client .gencache .__init__ () # Reset
60
47
61
48
62
- def RemoveRefCountOutput (data ) :
63
- while 1 :
49
+ def RemoveRefCountOutput (data : str ) -> str :
50
+ while True :
64
51
last_line_pos = data .rfind ("\n " )
65
52
if not re .match (r"\[\d+ refs\]" , data [last_line_pos + 1 :]):
66
53
break
@@ -72,47 +59,45 @@ def RemoveRefCountOutput(data):
72
59
return data
73
60
74
61
75
- def ExecuteSilentlyIfOK (cmd , testcase ) :
62
+ def ExecuteSilentlyIfOK (cmd : str , testcase : TestCase ) -> str :
76
63
f = os .popen (cmd )
77
64
data = f .read ().strip ()
78
65
rc = f .close ()
79
66
if rc :
80
67
print (data )
81
- testcase .fail ("Executing '%s ' failed (%d)" % ( cmd , rc ) )
68
+ testcase .fail (f "Executing '{ cmd } ' failed ({ rc } )" )
82
69
# for "_d" builds, strip the '[xxx refs]' line
83
70
return RemoveRefCountOutput (data )
84
71
85
72
86
73
class PyCOMTest (TestCase ):
87
74
no_leak_tests = True # done by the test itself
88
75
89
- def testit (self ):
76
+ def testit (self ) -> None :
90
77
# Check that the item is registered, so we get the correct
91
78
# 'skipped' behaviour (and recorded as such) rather than either
92
79
# error or silence due to non-registration.
93
80
RegisterPythonServer (
94
- os .path .join (
95
- os .path .dirname (__file__ ), ".." , "servers" , "test_pycomtest.py"
96
- ),
81
+ os .path .join (win32com_src_dir , "servers" , "test_pycomtest.py" ),
97
82
"Python.Test.PyCOMTest" ,
98
83
)
99
84
100
85
# Execute testPyComTest in its own process so it can play
101
86
# with the Python thread state
102
- fname = os .path .join (os . path . dirname ( this_file ) , "testPyComTest.py" )
87
+ fname = os .path .join (this_folder , "testPyComTest.py" )
103
88
cmd = f'{ sys .executable } "{ fname } " -q 2>&1'
104
89
data = ExecuteSilentlyIfOK (cmd , self )
105
90
106
91
107
92
class PippoTest (TestCase ):
108
- def testit (self ):
93
+ def testit (self ) -> None :
109
94
# Check we are registered before spawning the process.
110
95
from win32com .test import pippo_server
111
96
112
97
RegisterPythonServer (pippo_server .__file__ , "Python.Test.Pippo" )
113
98
114
99
python = sys .executable
115
- fname = os .path .join (os . path . dirname ( this_file ) , "testPippo.py" )
100
+ fname = os .path .join (this_folder , "testPippo.py" )
116
101
cmd = f'{ python } "{ fname } " 2>&1'
117
102
ExecuteSilentlyIfOK (cmd , self )
118
103
@@ -121,35 +106,44 @@ def testit(self):
121
106
# function in that module if the module isn't unitest based...
122
107
unittest_modules = [
123
108
# Level 1 tests - fast and few dependencies - good for CI!
124
- """testIterators testvbscript_regexp testStorage
125
- testStreams testWMI policySemantics testShell testROT
126
- testxslt testCollections
127
- errorSemantics.test testArrays
128
- testClipboard
129
- testConversionErrors
130
- """ .split (),
109
+ [
110
+ "testIterators" ,
111
+ "testvbscript_regexp" ,
112
+ "testStorage" ,
113
+ "testStreams" ,
114
+ "testWMI" ,
115
+ "policySemantics" ,
116
+ "testShell" ,
117
+ "testROT" ,
118
+ "testxslt" ,
119
+ "testCollections" ,
120
+ "errorSemantics.test" ,
121
+ "testArrays" ,
122
+ "testClipboard" ,
123
+ "testConversionErrors" ,
124
+ ],
131
125
# Level 2 tests - wants our demo COM objects registered.
132
126
# (these are strange; on github CI they get further than expected when
133
127
# our objects are not installed, so fail to quietly fail with "can't
134
128
# register" like they do locally. So really just a nod to CI)
135
- """
136
- testAXScript testDictionary testServers testvb testMarshal
137
- """ .split (),
129
+ ["testAXScript" , "testDictionary" , "testServers" , "testvb" , "testMarshal" ],
138
130
# Level 3 tests - Requires Office or other non-free stuff.
139
- """testMSOffice.TestAll testMSOfficeEvents.test testAccess.test
140
- testExplorer.TestAll testExchange.test
141
- """ .split (),
131
+ [
132
+ "testMSOffice.TestAll" ,
133
+ "testMSOfficeEvents.test" ,
134
+ "testAccess.test" ,
135
+ "testExplorer.TestAll" ,
136
+ "testExchange.test" ,
137
+ ],
142
138
# Level 4 tests - we try and run `makepy` over every typelib installed!
143
- """testmakepy.TestAll
144
- """ .split (),
139
+ ["testmakepy.TestAll" ],
145
140
]
146
141
147
142
# A list of other unittest modules we use - these are fully qualified module
148
143
# names and the module is assumed to be unittest based.
149
144
unittest_other_modules = [
150
145
# Level 1 tests.
151
- """win32com.directsound.test.ds_test
152
- """ .split (),
146
+ ["win32com.directsound.test.ds_test" ],
153
147
# Level 2 tests.
154
148
[],
155
149
# Level 3 tests.
@@ -192,7 +186,9 @@ def testit(self):
192
186
]
193
187
194
188
195
- def get_test_mod_and_func (test_name , import_failures ):
189
+ def get_test_mod_and_func (
190
+ test_name : str , import_failures : list [tuple [str , BaseException ]]
191
+ ) -> tuple [None , None ] | tuple [ModuleType , Callable [[], object ] | None ]:
196
192
if test_name .find ("." ) > 0 :
197
193
mod_name , func_name = test_name .split ("." )
198
194
else :
@@ -202,19 +198,21 @@ def get_test_mod_and_func(test_name, import_failures):
202
198
try :
203
199
__import__ (fq_mod_name )
204
200
mod = sys .modules [fq_mod_name ]
205
- except :
206
- import_failures .append ((mod_name , sys . exc_info ()[: 2 ] ))
201
+ except Exception as error :
202
+ import_failures .append ((mod_name , error ))
207
203
return None , None
208
204
func = None if func_name is None else getattr (mod , func_name )
209
205
return mod , func
210
206
211
207
212
208
# Return a test suite all loaded with the tests we want to run
213
- def make_test_suite (test_level = 1 ):
209
+ def make_test_suite (
210
+ test_level : int ,
211
+ ) -> tuple [unittest .TestSuite , list [tuple [str , BaseException ]]]:
214
212
suite = unittest .TestSuite ()
215
- import_failures = []
213
+ import_failures : list [ tuple [ str , BaseException ]] = []
216
214
loader = TestLoader ()
217
- for i in range (testLevel ):
215
+ for i in range (test_level ):
218
216
for mod_name in unittest_modules [i ]:
219
217
mod , func = get_test_mod_and_func (mod_name , import_failures )
220
218
if mod is None :
@@ -234,12 +232,12 @@ def make_test_suite(test_level=1):
234
232
for test_class in custom_test_cases [i ]:
235
233
suite .addTest (unittest .defaultTestLoader .loadTestsFromTestCase (test_class ))
236
234
# other "normal" unittest modules.
237
- for i in range (testLevel ):
235
+ for i in range (test_level ):
238
236
for mod_name in unittest_other_modules [i ]:
239
237
try :
240
238
__import__ (mod_name )
241
- except :
242
- import_failures .append ((mod_name , sys . exc_info ()[: 2 ] ))
239
+ except Exception as error :
240
+ import_failures .append ((mod_name , error ))
243
241
continue
244
242
245
243
mod = sys .modules [mod_name ]
@@ -253,13 +251,15 @@ def make_test_suite(test_level=1):
253
251
return suite , import_failures
254
252
255
253
256
- def usage (why ):
257
- print (why )
258
- print ()
259
- print ("win32com test suite" )
260
- print ("usage: testall [-v] test_level" )
261
- print (" where test_level is an integer 1-3. Level 1 tests are quick," )
262
- print (" level 2 tests invoke Word, IE etc, level 3 take ages!" )
254
+ def usage (why : str ) -> NoReturn :
255
+ print (f"""\
256
+ { why }
257
+
258
+ win32com test suite
259
+ usage: testall [-v] test_level
260
+ where test_level is an integer 1-4. Level 1 tests are quick,
261
+ level 2 tests invoke Word, IE etc, level 3 take ages!
262
+ level 4 we try and run `makepy` over every typelib installed""" )
263
263
sys .exit (1 )
264
264
265
265
@@ -271,44 +271,47 @@ def usage(why):
271
271
for opt , val in opts :
272
272
if opt == "-v" :
273
273
verbosity += 1
274
- testLevel = 2 # default to quick test with local objects
275
- test_names = []
276
- for arg in args :
277
- try :
278
- testLevel = int (arg )
279
- if testLevel < 0 or testLevel > 4 :
280
- raise ValueError ("Only levels 1-4 are supported" )
281
- except ValueError :
282
- test_names .append (arg )
283
- if test_names :
274
+
275
+ if len (args ) > 1 :
276
+ usage (
277
+ "Only a single test level argument is supported. "
278
+ + "Test names are not supported yet"
279
+ )
280
+ try :
281
+ test_level = int (args [0 ])
282
+ except IndexError :
283
+ test_level = 2 # default to quick test with local objects
284
+ except ValueError :
284
285
usage ("Test names are not supported yet" )
286
+ if test_level < 1 or test_level > 4 :
287
+ usage ("Only levels 1-4 are supported" )
285
288
CleanGenerated ()
286
289
287
- suite , import_failures = make_test_suite (testLevel )
290
+ suite , import_failures = make_test_suite (test_level )
288
291
if verbosity :
289
292
if hasattr (sys , "gettotalrefcount" ):
290
- print ("This is a debug build - memory leak tests will also be run." )
291
- print ("These tests may take *many* minutes to run - be patient!" )
292
- print ("(running from python.exe will avoid these leak tests)" )
293
+ print (
294
+ dedent ("""\
295
+ This is a debug build - memory leak tests will also be run.
296
+ These tests may take *many* minutes to run - be patient!
297
+ (running from python.exe will avoid these leak tests""" )
298
+ )
293
299
print (
294
- "Executing level %d tests - %d test cases will be run "
295
- % ( testLevel , suite .countTestCases ())
300
+ f "Executing level { test_level } tests "
301
+ + f"- { suite .countTestCases ()} test cases will be run"
296
302
)
297
303
if verbosity == 1 and suite .countTestCases () < 70 :
298
304
# A little row of markers so the dots show how close to finished
299
305
print ("|" * suite .countTestCases ())
300
- testRunner = TestRunner (verbosity = verbosity )
301
- testResult = testRunner .run (suite )
306
+ testResult = TestRunner (verbosity = verbosity ).run (suite )
302
307
if import_failures :
303
308
testResult .stream .writeln (
304
309
"*** The following test modules could not be imported ***"
305
310
)
306
- for mod_name , ( exc_type , exc_val ) in import_failures :
307
- desc = "\n " .join (traceback .format_exception_only (exc_type , exc_val ))
311
+ for mod_name , error in import_failures :
312
+ desc = "\n " .join (traceback .format_exception_only (type ( error ), error ))
308
313
testResult .stream .write (f"{ mod_name } : { desc } " )
309
- testResult .stream .writeln (
310
- "*** %d test(s) could not be run ***" % len (import_failures )
311
- )
314
+ testResult .stream .writeln (f"*** { import_failures } test(s) could not be run ***" )
312
315
313
316
# re-print unit-test error here so it is noticed
314
317
if not testResult .wasSuccessful ():
0 commit comments